Here is example of test app: dartpad
Inside app we have 2 main routes: ListPage
and ListItemViewScreen
.
ListItemViewScreen
is child route from ListPage
.
In this case, ShellRoute
used only for example
Problem: when we click on any item in list(row 81 - push), ListPage
rebuild and _loadData()
function is called. And when we go back from ListItemViewScreen
, ListPage
rebuild too.
How to prevent this unnecessary rebuild?
This happens because you actually call the function _loadData()
within the build
method. This is generally a bad practice.
Instead, convert the widget into a StatefulWidget
, set up a member that will hold the future, assign value to it in initState
, and use this member in the FutureBuilder
as future
(see _loadDataFuture
):
class ListPage extends StatefulWidget {
final String type;
const ListPage({Key? key, required this.type}) : super(key: key);
@override
State<ListPage> createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
late final Future<int> _loadDataFuture;
Future<int> _loadData() {
print("Emulate load data from DB: ${widget.type}");
return Future.value(1);
}
@override
void initState() {
super.initState();
_loadDataFuture = _loadData();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _loadDataFuture,
builder: (context, snapshot) => ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
context.push("/list/${widget.type}/view/$index");
},
child: Padding(
padding: const EdgeInsets.all(20),
child: Text("item_$index"),
),
);
},
),
);
}
}
It is possible that at some point you do want to re-execute the future after it is completed. In the case, remove the final
from the declaration of _loadDataFutre
, and if you'd like to trigger a reload, use:
setState(() {
_loadDataFuture = _loadData();
});