Flutter - 如何仅在读取共享首选项后才绘制小部件?

Flutter - how do I make a widget draw only after shared preferences have been read?

我正在尝试通过创建一个绘制随机路径的应用程序来掌握 Flutter(在完成之前它会变得更高级!)。用户可以更改路径中的图块数量,该值保存在共享首选项中。如果他们不更改图块数,则会将其设置为默认值 15.5。

我遇到的问题是它试图在完成读取共享首选项之前绘制路径,并且由于未设置图块数而崩溃。我可以设置默认值,但它会先绘制,然后用不同数量的图块重新绘制,这是我不想要的 - 我希望它先绘制用户设置的图块数量,并且只使用如果用户没有设置数字,则为默认值。

我想我需要使用 FutureBuilder,但我无法在网上找到很多我可以理解的东西。这是我目前所拥有的(这是 main.dart):

void main() {
  runApp(MaterialApp(
    title: "Path Maker",
    home: PathMaker(),
  ));
}

class PathMaker extends StatefulWidget {

  int numTiles;

  PathMaker({
        Key key,
        this.numTiles
    }): super(key: key);

  @override
  _PathMakerState createState() => new _PathMakerState();

}

class _PathMakerState extends State<PathMaker> {

  _PathMakerState();

  @override
  void initState() {
    super.initState();

    getPreferences();

  }

  @override
  void dispose() {
    super.dispose();
  }

  void getPreferences() async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      // Get timing variables
      Set<String> keys = prefs.getKeys();
      if(keys.contains('numTiles')) {
        numTiles = prefs.getInt('numTiles');
      }else {
        numTiles = 15.5;
      }
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Path Maker',
      theme: new ThemeData(
        primarySwatch: Colors.green,
      ),
      home: new Path(widget.numTiles),
    );
  }

}

(path.dart 是另一个定义 Path class 的文件 - 我认为它不相关,但如果需要可以 post 它)。所以,基本上,我希望它仅在 getPreferences 完成后调用构建。我该如何处理?我是否完全采用了错误的方法?

您使用 FutureBuilder 的方法是正确的。

它有两个参数,future 预期一些 Future 这是延迟计算或潜在值。在您的情况下,这是来自 SharedPreferences.getInstance()Future<SharedPreferences> returned。

然后 FutureBuilder 期望 builder。将使用上下文和与您未来的类型相对应的 AsyncSnapshot 调用构建器。 AsyncSnapshot 是最近与异步计算交互的表示 - 在您的情况下是共享首选项实例的检索。 AsyncSnapshot.connectionState 允许我们根据 Future 的状态决定渲染什么部件。 FutureBuilder example 详细介绍了不同的状态,现在我们只看 ConnectionState.done

done 状态下,快照包含一个错误 - 我们不打算在这里处理 - 或者 data,您的异步函数调用的 return 值.

以你的例子为例,我们可以创建一个FutureBuilder<SharedPreferences>。泛型表示未来将return的类型。我们将来自共享首选项 getInstance 方法的未来分配给未来参数和一个需要上下文和异步快照的构建器闭包。

有了这个,您的代码可能如下所示:

class _PathMakerState extends State<PathMaker> {
  _PathMakerState();

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  void setTiles(SharedPreferences prefs) {
    // Get timing variables
    Set<String> keys = prefs.getKeys();
    if(keys.contains('numTiles')) {
      widget.numTiles = prefs.getDouble('numTiles');
    }else {
      widget.numTiles = 15.5;
    }
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Path Maker',
      theme: new ThemeData(
        primarySwatch: Colors.green,
      ),
      home: FutureBuilder<SharedPreferences>(
        future: SharedPreferences.getInstance(),
        builder: (BuildContext context, AsyncSnapshot<SharedPreferences> snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.done:
              // get the tile number from the shared preference instance
              // stored in snapshot.data
              setTiles(snapshot.data);
              return Path(widget.numTiles);
            default:
              return Text("Loading...");
          }
        }
      ),
    );
  }

}