Search code examples
flutterflutter-futurebuilder

Flutter Futurebuilder inside TabBarView not triggering the future after initial application load while switching between tabs?


I am new to flutter and trying to implement a tabview in homePage in the flutter app.

The first tab is populated from data from firebase remote config and second tab is populated by using Futurebuilder. When I switch the tabs the future function is not triggering. It is only triggered during initial application load. Whenever I switch tabs and come back to 2nd tab. The futurebuilder's future function is not triggered again.

Can someone give any solutions for this.?

Container(
                  width: MediaQuery.of(context).size.width,
                  color: Colors.white,
                  child: GridView.count(
                    shrinkWrap: true,
                    physics: BouncingScrollPhysics(),
                    padding: const EdgeInsets.all(4.0),
                    childAspectRatio: 1.0,
                    crossAxisCount: isTablet ? 2 : 1,
                    crossAxisSpacing: 4.0,
                    mainAxisSpacing: 4.0,
                    children: [
                      FutureBuilder(
                          future: _getBookmarks,
                          builder:
                              (BuildContext context, AsyncSnapshot snapshot) {
                            var listWidget;
                            if (snapshot.connectionState ==
                                ConnectionState.done) {
                              if (snapshot.data.length == 0) {
                                listWidget = Container(
                                    child: Center(
                                  child: Text("No Favorites to Display!"),
                                ));
                              } else {
                                listWidget = ListView.builder(
                                  itemCount: snapshot.data.length,
                                  itemBuilder: (context, index) {
                                    final bookmarks = snapshot.data[index];
                                    return BuildFavoriteCard(
                                        bookmarks, context);
                                  },
                                );
                              }
                            } else {
                              listWidget = Center(
                                child: CircularProgressIndicator(),
                              );
                            }
                            return listWidget;
                          })
                    ],
                  ))

Solution

  • Here's an example combining the TabBar and FutureBuilder examples of the Flutter documentation.

    If you run this, you will see that a new future is created each time you navigate to the first tab (since the TabBarView's content is rebuilt).

    I would assume that this is currently not working for you since your future _getBookmarks is defined somewhere higher up in the widget tree (in the part that is not rebuilt by switching tabs).

    The solution would be to move the future inside your TabBarView widget.

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const TabBarDemo());
    }
    
    class TabBarDemo extends StatelessWidget {
      const TabBarDemo({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: DefaultTabController(
            length: 2,
            child: Scaffold(
              appBar: AppBar(
                bottom: const TabBar(
                  tabs: [
                    Tab(icon: Icon(Icons.directions_car)),
                    Tab(icon: Icon(Icons.directions_transit)),
                  ],
                ),
                title: const Text('Tabs Demo'),
              ),
              body: TabBarView(
                children: [
                  Center(
                    child: MyStatefulWidget(),
                  ),
                  Icon(Icons.directions_transit),
                ],
              ),
            ),
          ),
        );
      }
    }
    
    /// This is the stateful widget that the main application instantiates.
    class MyStatefulWidget extends StatefulWidget {
      MyStatefulWidget({
        Key? key,
      }) : super(key: key);
    
      @override
      State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
    }
    
    /// This is the private State class that goes with MyStatefulWidget.
    class _MyStatefulWidgetState extends State<MyStatefulWidget> {
      Future<String>? _calculation;
    
      @override
      void initState() {
        _calculation = Future<String>.delayed(
          const Duration(seconds: 2),
          () => 'Data Loaded',
        );
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return DefaultTextStyle(
          style: Theme.of(context).textTheme.headline2!,
          textAlign: TextAlign.center,
          child: FutureBuilder<String>(
            future:
                _calculation, // calculation, // a previously-obtained Future<String> or null
            builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
              List<Widget> children;
              if (snapshot.hasData) {
                children = <Widget>[
                  const Icon(
                    Icons.check_circle_outline,
                    color: Colors.green,
                    size: 60,
                  ),
                  Padding(
                    padding: const EdgeInsets.only(top: 16),
                    child: Text('Result: ${snapshot.data}'),
                  ),
                ];
              } else if (snapshot.hasError) {
                children = <Widget>[
                  const Icon(
                    Icons.error_outline,
                    color: Colors.red,
                    size: 60,
                  ),
                  Padding(
                    padding: const EdgeInsets.only(top: 16),
                    child: Text('Error: ${snapshot.error}'),
                  )
                ];
              } else {
                children = const <Widget>[
                  SizedBox(
                    child: CircularProgressIndicator(),
                    width: 60,
                    height: 60,
                  ),
                  Padding(
                    padding: EdgeInsets.only(top: 16),
                    child: Text('Awaiting result...'),
                  )
                ];
              }
              return Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: children,
                ),
              );
            },
          ),
        );
      }
    }