我对Flutter还是比较陌生,目前正与FutureBuilders苦苦挣扎。 我已经读过Remi在thread上的回答,该回答基本上说明了Flutter的哲学,即小部件构建应该是幂等的。我从理论上得到了原理,但是实际上如何工作?
请考虑以下代码段:
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Consumer<DataPresenter>(
builder: (context, presenter, _) {
return DefaultFutureBuilder<List<Data>>(
future: presenter.data(),
builder: (context, data) {
// bla bla
}
);
}
),
// More widgets
],
),
),
);
}
这是我的DefaultFutureBuilder
:
class DefaultFutureBuilder<T> extends StatelessWidget {
final Future<T> future;
final Widget Function(BuildContext, T data) builder;
final Widget Function(BuildContext) errorBuilder;
DefaultFutureBuilder({
@required this.future,
@required this.builder,
this.errorBuilder,
});
@override
Widget build(BuildContext context) {
var errBuilder = errorBuilder ?? _buildDefaultErrorScreen;
return FutureBuilder<T>(
future: future,
builder: (context, snapshot) {
var indicator = Center(child: CircularProgressIndicator());
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData)
return builder(context, snapshot.data);
else if (snapshot.hasError)
return errBuilder(context);
}
return indicator;
},
);
}
Widget _buildDefaultErrorScreen(BuildContext context) {
// default
}
}
现在我知道对presenter.data()
的调用(获取一些异步数据)不是洁净的。但是,我要做,当演示者通知消费者时,我想重新获取数据,与此同时,由于框架的恶作剧,Flutter重建我的小部件树时,我不想获取数据。
我最初的想法是构建演示者,使其仅在当前没有任何数据时才提取数据。然后,我可以通过其他方法将数据设置为null,并通知侦听器以重建受影响的小部件子树。
像这样:
class DataPresenter extends ChangeNotifier {
// fields, services, bla bla
List<Data> _data;
Future<List<Data>> data() async {
if (_data == null) {
var response = await _service.fetchMyStuff(params: params);
_data = response.data;
}
return _data;
}
}
此解决方案的问题在于,当我第一次获取数据时发生的任何重建都会引起另一个请求。
如果有人能指出我没有得到框架/架构的哪一部分,我将不胜感激。
答案 0 :(得分:3)
我认为这可能会解决您的问题,我对此问题感到非常烦恼,您所做的任何事情都会重新构建您的应用。所以他们解决问题的方式是记住我的未来。现在,这可能对您不起作用。如果它将Consumer
重建视为新的未来,那么您会很好,因为如果FutureBuilder
这样做,Consumer
将会重建,如果我理解正确的话,这就是您想要的。这就是你的工作。
//Initialize at top of class
final AsyncMemoizer _memoizer = AsyncMemoizer();
Future<void> _someFuture() {
return this._memoizer.runOnce(() async {
//Do your async work
});
}
然后_someFuture()
将成为您传递给FutureBuilder
的对象。这是一篇关于此问题的好文章。
Flutter: My FutureBuilder Keeps Firing!
答案 1 :(得分:0)
我会建议一个没有futureBuilder的解决方案
您在首次构建无状态小组件后获取数据
WidgetsBinding.instance.addPostFrameCallback((_) => Provider.of<DataPresenter>(context, listen: false).fetchdata());
您的_data
列表中的值为null
或lenght==0
class DataPresenter extends ChangeNotifier {
// fields, services, bla bla
List<Data> _data;
// getter for the Consumer
List<Data> get data => _data;
Future<List<Data>> fetchdata() async {
_data = await _service.fetchMyStuff(params: params);
if (_data == null)
_data = new List(); //difference between null and length==0
//rebuild Consumer
notifyListeners();
}
}
没有FutureBilder的页面
class ProvPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// calls after the widget ist build
WidgetsBinding.instance.addPostFrameCallback((_) => Provider.of<TodoListModel>(context, listen: false).refreshTasks());
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => DataPresenter()),
],
child: Scaffold(
body: SafeArea(
child: Stack(
children: [
Consumer<DataPresenter>(
builder: (context, presenter, _) {
//get current Values vom the DataPresenter._data
List<Data> currData = presenter.data;
// firstime fetch ??
if(currData == null)
return CircularProgressIndicator();
// DataPresenter.getData is finished
return new RefreshIndicator(
onRefresh: () => presenter.fetchdata(),
child: new Text("length " + currData.length.toString())
);
},
),
// More widgets
],
),
),
),
);
}
}
答案 2 :(得分:0)
如果您想重建提供商,则可以使用
switch -File (gc <yourfile>) -Regex {
'--------' {$skipnext=$true}
'^C:\\test' {if ($skipnext) {$skipnext = $false} else {$_}}
}
}
此方法对于“拉动刷新”或“重试错误”之类的功能很有用,可随时重新启动特定的提供程序。
可从以下网站获得文档: https://riverpod.dev/docs/concepts/combining_providers#faq https://pub.dev/documentation/riverpod/latest/all/ProviderContainer/refresh.html https://pub.dev/documentation/flutter_riverpod/latest/all/BuildContextX/refresh.html