Le problème:
J'ai 2 onglets utilisant le contrôleur d'onglets par défaut, comme ceci:
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(),
],
),
),
);
}
}
La DealList()
est un StatefulWidget
qui est construit comme ceci:
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(),
);
},
);
}
}
Avec ce qui précède, voici ce qui se passe chaque fois que je reviens à l'onglet DealList()
: il se recharge.
Existe-t-il un moyen d’empêcher la réexécution de FutureBuilder lorsqu’il est fait une fois? (Il est prévu que l'utilisateur utilise RefreshIndicator pour recharger. Par conséquent, la modification des onglets ne doit rien déclencher, à moins que l'utilisateur ne le fasse explicitement.)
Il y a deux problèmes ici, le premier:
Lorsque TabController
bascule les onglets, il décharge l'ancienne arborescence de widgets pour économiser de la mémoire. Si vous souhaitez modifier ce comportement, vous devez mélanger AutomaticKeepAliveClientMixin
à l'état de votre widget Onglet.
class _DealListState extends State<DealList> with AutomaticKeepAliveClientMixin<DealList> {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context); // need to call super method.
return /* ... */
}
}
Le deuxième problème concerne votre utilisation du FutureBuilder
- Si vous fournissez un nouveau Future
à un FutureBuilder
, il ne peut pas dire que les résultats seraient identiques à ceux du la dernière fois, il doit donc reconstruire. (N'oubliez pas que Flutter peut appeler votre méthode de construction jusqu'à une fois par image).
return FutureBuilder(
future: this.loadDeals(), // Creates a new future on every build invocation.
/* ... */
);
Au lieu de cela, vous souhaitez affecter l'avenir à un membre de votre classe State dans initState, puis transmettre cette valeur à FutureBuilder
. Le garantit que l'avenir est le même pour les reconstructions ultérieures. Si vous voulez forcer l’État à recharger les offres, vous pouvez toujours créer une méthode qui réaffecte le _loadingDeals
membre et appelle setState
.
Future<...> _loadingDeals;
@override
void initState() {
_loadingDeals = loadDeals(); // only create the future once.
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context); // because we use the keep alive mixin.
return new FutureBuilder(future: _loadingDeals, /* ... */);
}