Search code examples
flutterflutter-go-router

flutter go_router unnecessary rebuild on push and go back


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?


Solution

  • 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();
    });