我啥时候应该使用 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 的任务:
有两个状态变量dataFromBackend
和isLoadingFlag
在启动时,设置isLoadingFlag = true
,并在此基础上显示loader
。
一旦数据到达,使用从后端获得的数据设置数据并设置isLoadingFlag = false
(显然在setState
内部)
我们需要在创建小部件时有一个if-else
。如果isLoadingFlag
是true
,则显示loader
,否则显示data
。失败时,显示错误消息。
FutureBuilder 的任务:
在 Future Builder 的future
中给异步任务
基于connectionState
,显示消息(loading
、active(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
代码以了解使用此代码对性能的影响。
StatefulWidget
,其state
变量为_snapshot
初始状态为_snapshot = AsyncSnapshot<T>.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()
的任何副作用。我试过了,结果发现未来是递归执行的。你能怀疑什么吗?
你的意思是 setState
在 future
方法内?能否提供一些简单的代码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?的主要内容,如果未能解决你的问题,请参考以下文章