我啥时候应该使用 FutureBuilder?

Posted

技术标签:

【中文标题】我啥时候应该使用 FutureBuilder?【英文标题】:When should I use a FutureBuilder?我什么时候应该使用 FutureBuilder? 【发布时间】:2019-01-29 16:02:44 【问题描述】:

我想知道什么时候应该使用 future builder。例如,如果我想发出一个 http 请求并在列表视图中显示结果,一旦你打开视图,我应该使用未来的构建器还是只构建一个 ListViewBuilder 之类的:

new ListView.builder(
        itemCount: _features.length,
        itemBuilder: (BuildContext context, int position) 
...stuff here...

此外,如果我不想构建列表视图而是一些更复杂的东西,例如圆形图表,我是否必须使用未来的构建器?

希望它足够清楚!

【问题讨论】:

你不必,你也可以定义一个带有小部件的列表,并用 setState 更新它。当您设置状态时,列表视图将更新。 doStuff()async var stuff = await getStuff(); setState(()list = stuff;); 如果你当然使用 StatefulWidget。 明白。谢谢! 【参考方案1】:

FutureBuilder 删除样板代码。

假设您想在页面启动时从后端获取一些数据,并在数据到来之前显示一个加载器。

ListBuilder 的任务:

有两个状态变量dataFromBackendisLoadingFlag 在启动时,设置isLoadingFlag = true,并在此基础上显示loader。 一旦数据到达,使用从后端获得的数据设置数据并设置isLoadingFlag = false(显然在setState 内部) 我们需要在创建小部件时有一个if-else。如果isLoadingFlagtrue,则显示loader,否则显示data。失败时,显示错误消息。

FutureBuilder 的任务:

在 Future Builder 的 future 中给异步任务 基于connectionState,显示消息(loadingactive(streams)done) 基于data(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');
        
      ,
    )

性能影响:

我刚刚查看了FutureBuilder 代码以了解使用此代码对性能的影响。

FutureBuilder 只是一个StatefulWidget,其state 变量为_snapshot 初始状态为_snapshot = AsyncSnapshot&lt;T&gt;.withData(ConnectionState.none, widget.initialData); 它正在订阅我们通过构造函数发送的future,并在此基础上更新state

示例:

widget.future.then<void>((T data) 
    if (_activeCallbackIdentity == callbackIdentity) 
      setState(() 
        _snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
      );
    
, onError: (Object error) 
  if (_activeCallbackIdentity == callbackIdentity) 
    setState(() 
      _snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error);
    );
  
);

所以FutureBuilder 是我们通常所做的包装/样板,因此不应该对性能产生任何影响

【讨论】:

所以我不需要它,但这个决定将是一个糟糕的决定,因为它对我有很大帮助。谢谢! 非常好的问题。在您提出问题后,我查看了颤振代码。 @SamarthAgarwal 我更新了与性能相关的答案。谢谢 @DineshBalasubramanian 感谢您的快速更新。此外,您是否知道在我们与FutureBuilder 一起使用的未来中使用setState() 的任何副作用。我试过了,结果发现未来是递归执行的。你能怀疑什么吗? 你的意思是 setStatefuture 方法内?能否提供一些简单的代码sn-p 如果我想在按下按钮时获取数据并显示循环进度指示器直到完成,该怎么办?现在可以使用FutureBuilder 吗?如果是,如何?【参考方案2】:

FutureBuilder 示例

如果您想在异步调用后对小部件进行排序,请使用 FutureBuilder()

class _DemoState extends State<Demo> 

@override
Widget build(BuildContext context) 
  return FutureBuilder<String>(
    future: downloadData(), // function where you call your api
    builder: (BuildContext context, AsyncSnapshot<String> snapshot)   // AsyncSnapshot<Your object type>
      if( snapshot.connectionState == ConnectionState.waiting)
          return  Center(child: Text('Please wait its loading...'));
      else
          if (snapshot.hasError)
            return Center(child: Text('Error: $snapshot.error'));
          else
            return Center(child: new Text('$snapshot.data'));  // snapshot.data  :- get your object which is pass from your downloadData() function
      
    ,
  );

Future<String> downloadData()async
  //   var response =  await http.get('https://getProjectList');    
  return Future.value("Data download successfully"); // return your response


在future builder中,它调用future函数来等待结果,一旦产生结果,它就会调用我们构建widget的builder函数。

AsyncSnapshot 有 3 个状态:

    connectionState.none = 在这种状态下,future 为空 connectionState.waiting = [future] 不为空,但尚未完成 connectionState.done = [future] 不为空,并且已完成。如果未来成功完成,[AsyncSnapshot.data] 将设置为未来完成的值。如果它以错误完成,[AsyncSnapshot.hasError] 将为真

【讨论】:

需要注意的是,downloadData()函数每次都会通过build(...)函数执行。最佳做法是调用 downloadData() 并将返回的 Future 保存到 State 对象中的变量中,并在将来引用该变量:参数【参考方案3】:

FutureBuilder 是一个 Widget,可帮助您执行一些异步函数,并根据该函数的结果更新您的 UI。

我列出了一些用例,你为什么会使用 FutureBuilder?

    如果您想在异步任务之后渲染小部件,请使用它。

    我们可以通过简单地使用ConnectionState.waiting来处理加载过程

    不需要任何自定义错误控制器。可以简单处理错误dataSnapshot.error != null

    因为我们可以在构建器中处理异步任务,所以我们不需要任何setState(() _isLoading = false; );

当我们使用 FutureBuilder 小部件时,我们需要检查未来状态,即未来是否已解决等等。有多种状态如下:

    ConnectionState.none:表示future为null,使用initialData作为defaultValue。

    ConnectionState.active:表示future不为null但还没有解决。

    ConnectionState.waiting:表示未来正在解决,我们很快就会得到结果。

    ConnectionState.done:表示未来已经解决了。

一个简单的实现

这里的 OrdersProvider 是一个提供者类,而 fetchAndSetOrders() 是该提供者类的方法。

body: FutureBuilder(
        future: Provider.of<OrdersProvider>(context, listen: false)
            .fetchAndSetOrders(),
        builder: (context, dataSnapshot) 
          if (dataSnapshot.connectionState == ConnectionState.waiting) 
            return Center(
              child: CircularProgressIndicator(),
            );
           else 
            if (dataSnapshot.error != null) 
              return Center(
                child: Text('An error occured'),
              );
             else 
              return Consumer<OrdersProvider>(
                builder: (context, orderData, child) => ListView.builder(
                  itemCount: orderData.orders.length,
                  itemBuilder: (context, i) => OrderItem(orderData.orders[i]),
                ),
              );
            
          
        ,
      ),

【讨论】:

以上是关于我啥时候应该使用 FutureBuilder?的主要内容,如果未能解决你的问题,请参考以下文章

Firebase:我啥时候应该使用 refreshToken?

我啥时候应该使用助手? [关闭]

我啥时候应该使用“while 循环”?

我啥时候应该在 C 中使用 malloc,啥时候不应该?

我啥时候应该在 C 中使用 malloc,啥时候不应该?

我啥时候应该使用导航控制器?