Flutter Переключение на вкладку Перезагружает виджеты и запускает FutureBuilder
Проблема:
У меня есть 2 вкладки с помощью контроллера вкладок по умолчанию, например так:
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
drawer: Menu(),
appBar: AppBar(
title: Container(
child: Text('Dashboard'),
),
bottom: TabBar(
tabs: <Widget>[
Container(
padding: EdgeInsets.all(8.0),
child: Text('Deals'),
),
Container(
padding: EdgeInsets.all(8.0),
child: Text('Viewer'),
),
],
),
),
body: TabBarView(
children: <Widget>[
DealList(),
ViewersPage(),
],
),
),
);
}
}
DealList()
это StatefulWidget
который построен так:
Widget build(BuildContext context) {
return FutureBuilder(
future: this.loadDeals(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print('Has error: ${snapshot.hasError}');
print('Has data: ${snapshot.hasData}');
print('Snapshot data: ${snapshot.data}');
return snapshot.connectionState == ConnectionState.done
? RefreshIndicator(
onRefresh: showSomething,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: snapshot.data['deals'].length,
itemBuilder: (context, index) {
final Map deal = snapshot.data['deals'][index];
print('A Deal: ${deal}');
return _getDealItem(deal, context);
},
),
)
: Center(
child: CircularProgressIndicator(),
);
},
);
}
}
С учетом вышесказанного, вот что происходит, когда я переключаюсь обратно на DealList()
Вкладка: перезагружается.
Есть ли способ предотвратить повторный запуск FutureBuilder, когда это будет сделано один раз? (план состоит в том, чтобы пользователь использовал RefreshIndicator для перезагрузки. Поэтому изменение вкладок не должно вызывать ничего, если это явно не сделано пользователем.)
1 ответ
Здесь есть два вопроса, первый:
Когда TabController
переключает вкладки, выгружает старое дерево виджетов для экономии памяти. Если вы хотите изменить это поведение, вам нужно смешать AutomaticKeepAliveClientMixin
в состояние вкладки виджета.
class _DealListState extends State<DealList> with AutomaticKeepAliveClientMixin<DealList> {
@overrride
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context); // need to call super method.
return /* ... */
}
}
Вторая проблема заключается в использовании вами FutureBuilder
- Если вы предоставите новый Future
к FutureBuilder
, он не может сказать, что результаты будут такими же, как в прошлый раз, поэтому он должен восстановить. (Помните, что Flutter может вызывать ваш метод сборки до одного кадра).
return FutureBuilder(
future: this.loadDeals(), // Creates a new future on every build invocation.
/* ... */
);
Вместо этого вы хотите назначить будущее члену вашего класса State в initState, а затем передать это значение FutureBuilder
, Гарантирует, что будущее будет таким же при последующих перестроениях. Если вы хотите заставить Государство перезагрузить сделки, вы всегда можете создать метод, который переназначает _loadingDeals
член и звонки setState
,
Future<...> _loadingDeals;
@override
void initState() {
_loadingDeals = loadDeals(); // only create the future once.
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context); // from keep alive mixin.
return new FutureBuilder(future: _loadingDeals, /* ... */);
}