何时在 Flutter 中使用 FutureBuilder

When to use FutureBuilder in Flutter

我想知道何时使用 FutureBuilder 及其用途下面的代码:

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool done = false;

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

  Future<void> wait() async {
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      done = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    print('is built');
    return done ? Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    ) : Scaffold(body: CircularProgressIndicator(),);
  }
}

考虑到我还想优化我的应用程序以减少后端读取(在 FutureBuilder 中我会多次读取)这一事实,在哪些情况下 FutureBuilder 会代替上述设置为我工作。我正在寻找 FutureBuilder 比上述设置更有用和正确的情况。

FutureBuilder

根据与 Future 交互的最新快照自行构建的小部件。

future 一定是早先得到的,例如在 State.initState、State.didUpdateWidget 或 State.didChangeDependencies 期间。在构建 FutureBuilder 时,不得在 State.build 或 StatelessWidget.build 方法调用期间创建它。

如果未来与 FutureBuilder 同时创建,则每次重建 FutureBuilder 的父级时,异步任务将是 restarted.A 一般准则是假设每次构建方法可以在每一帧调用,并将省略的调用视为优化。

文档是入门和了解什么小部件在什么条件下做什么的好方法...

https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

实际上,如果您不想,您永远不需要使用 FutureBuilder Widget。如果您正确优化了 FutureBuilder Widget,您代码中的逻辑将与 FutureBuilder Widget 完全相同。

这段代码和你的完全一样:

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool done = false;
  late Future myFuture;

  @override
  void initState() {
    myFuture = wait();
    super.initState();
  }

  Future<bool> wait() async {
    await Future.delayed(const Duration(seconds: 2));
    return true;
  }

  @override
  Widget build(BuildContext context) {
    print('is built');
    return FutureBuilder(
        future: myFuture,
        builder: (BuildContext context, snapshot) {
           if(snapshot.connectionState == ConnectionState.waiting) {
              return const Scaffold(body: CircularProgressIndicator(),);
           } else {
              return Scaffold(
                 body: Center(
                    child: Column(
                       mainAxisAlignment: MainAxisAlignment.center,
                       children: <Widget>[
                          const Text(
                             'You have pushed the button this many times:',
                          ),
                          Text(
                              '',
                              style: Theme.of(context).textTheme.headline4,
                          ),
                       ],
                     ),
                   ),
                floatingActionButton: FloatingActionButton(
                   onPressed: () {},
                   tooltip: 'Increment',
                   child: const Icon(Icons.add),
                ),
             );
           }
        }
     );
  }
}

FutureBuilder用于处理return你想显示的数据的“异步”调用。

例如,假设我们必须从服务器获取 List<String>

API 调用:

  Future<List<String>> getStringList() async {
    try {
      return Future.delayed(Duration(seconds: 1)).then(
        (value) => ['data1', 'data2', 'data3', 'data4'],
      );
    } catch (e) {
      throw Exception(e);
    }
  }

我们如何使用 FutureBuilder:

处理上述 API 调用状态(加载、数据、错误...等)
FutureBuilder<List<String>?>(
        future: getStringList(),
        builder: (context, snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.waiting:
              return Center(
                child: CircularProgressIndicator(),
              );
            case ConnectionState.done:
              if (snapshot.hasError)
                return Text(snapshot.error.toString());
              else
                return ListView(
                  children: snapshot.data!.map((e) => Text(e)).toList(),
                );

            default:
              return Text('Unhandle State');
          }
        },
      ),

如我们所见,我们不必为错误...等创建带有变量 isLoading boolString 的状态 class。 FutureBuilder 节省了我们的时间。

但是

由于FutureBuilder是一个widget并且存在于widget树中,重建可以使FutureBuilder再次运行

所以我想说你可以在知道不会重建时使用 FutureBuilder,但是有很多方法(在互联网上)可以防止 FutureBuilder 在以下情况下再次被调用重建发生了,但它对我不起作用并导致意外行为。

老实说,与使用 FutureBuilder 相比,我更喜欢使用任何状态管理解决方案以不同的 class 处理状态,因为它更安全(重建不会影响它)、更有用且更易于阅读(从 UI).

传播业务逻辑

FutureBuilder 只是一个 StatefulWidget,它的状态变量是 _snapshot

初始状态为_snapshot = AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData);

FutureBuilder 通常用于删除样板代码。

考虑您想要在页面启动时从后端获取一些数据并显示加载程序直到数据到来。

它正在订阅我们通过构造函数发送的未来,并根据它更新状态。

FutureBuilder 的任务:

  • 给Future Builder的future异步任务
  • 基于连接状态,显示消息(加载、活动(流)、完成)
  • 基于数据(snapshot.hasError),显示视图

FutureBuilder 的优势

  • 不使用两个状态变量和setState
  • 响应式编程(FutureBuilder 将负责在数据到达时更新视图)

示例:

FutureBuilder<String>(
future: _fetchNetworkCall, // async work
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
   switch (snapshot.connectionState) {
     case ConnectionState.waiting: return Text('Loading....');
     default:
       if (snapshot.hasError)
          return Text('Error: ${snapshot.error}');
       else
      return Text('Result: ${snapshot.data}');
    }
  },
)

因此可以得出结论,Future 构建器是我们通常所做的wrapper/boilerplate,因此不应该有任何性能影响。

FutureBuilder 是 Fl​​utter 的一个小部件,可让您轻松确定 Future 的当前状态并选择在该状态下显示的内容。

你可以参考这个:https://walkingtree.tech/futurebuilder-flutter-widget/

其中一个用例是 FutureBuilder 在小部件树内的特定部分更新,而不是像您在示例中实现的那样完整的小部件树。

  Future<void> wait() async {
    return Future.delayed(Duration(seconds: 2));

}

此处 setState() => 更新小部件树。 因此,在 FutureBuilder 中使用相同的代码,您可以更改小部件树中特定位置的 UI 值。