How to cache FutureBuilder async results with screen navigation when using BottomNavigationBar?

Viewed 1013

I am using BottomNavigationBar to display a list of three menus. When user selects the Gallery I render a Stateful component that renders FutureBuilder in its build method. This works as expected, with the exception that when user navigates to another screen, Gallery widget is disposed off and thus I lose all of the images I just fetched. How do I cache them effectively?

home-page.dart widget:

final List<Widget> _menuOptions = <Widget>[
    Text(
      'Schedules',
      style: optionStyle
    ),
    Text(
      'Stats',
      style: optionStyle
    ),    
    GalleryPage(key: PageStorageKey('gallery'))
  ];

void _onMenuSelected(int index){
    setState(() {      
      _selectedIndex = index;
    });
  }
@override
  Widget build(BuildContext context){
    //...
    body: Center(
      child: _menuOptions.elementAt(_selectedIndex),
    ),
    bottomNavigationBar: BottomNavigationBar(
      items: const <BottomNavigationBarItem>[
        BottomNavigationBarItem(
          icon: Icon(Icons.schedule),
          title: Text('Schedules'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.satellite),
          title: Text('Stats'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.image),
          title: Text('Gallery'),          
        ),
      ],
      currentIndex: _selectedIndex,
      selectedItemColor: Colors.amber[800],
      onTap: _onMenuSelected,
    )

gallery-page.dart widget:

class GalleryPage extends StatefulWidget {
  GalleryPage({Key key}) : super(key: key);

  @override
  _GalleryPageState createState() => _GalleryPageState();
}

class _GalleryPageState extends State<GalleryPage> with AutomaticKeepAliveClientMixin {
  Future<List<GalleryImage>> futureGalleryImages;
  final AsyncMemoizer _memoizer = AsyncMemoizer();

  @override
  void deactivate() {
    // TODO: implement deactivate
    super.deactivate();    
    print('deactiveating');
  }

  @override
  bool get wantKeepAlive => true;

  @override
  void initState() {
    super.initState();
    _populateGalleryImages();
  }



@override
  Widget build(BuildContext context) {
    super.build(context);
    return FutureBuilder(
      future: this.futureGalleryImages,
      builder: (BuildContext context, AsyncSnapshot snapshot) {
         //...
         return _createListView(context, snapshot);
        }
      },
    );
  }

  Future _populateGalleryImages() {    
    return this._memoizer.runOnce(() async {
      this.futureGalleryImages = GalleryService().fetchGalleryImages();
    });
  }

Things I have tried:

  1. Using the AutomaticKeepAliveClientMixin mixin with the wantKeepAlive property set to true. This however doesn't work as my component always enters the deactive life cycle and is always disposed. The data fetching occurs every time. I was hoping to use AsyncMemoizer if the component did not get disposed.

  2. Using PageStorageKey when instantiating the component as seen above. Did not work.

Any suggestions on what I am doing wrong?

0 Answers
Related