Dheeraj Pundir
Dheeraj Pundir

Reputation: 23

Flutter: Child Widget's looses state when parent's sate changes

I am learning Flutter and struggling with some state management issue.

I have a HomeScreen widget which contains Scaffold and a BottomNavigationBar. To switch pages based on the selected tab in BottomNavigationBar I am using PageView with PageController. Here is how the build method of HomeScreen widget looks:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: _currentIndex == 2,
      appBar: AppBar(...properties),
      body: PageView(
        controller: _pageController,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[...items],
        currentIndex: _currentIndex,
        onTap: _onItemTapped,  //This changes state _currentIndex and calls the method _pageController.jumpToPage(_currentIndex);
      ),
    );
  }

If you notice I am using a property extendBodyBehindAppBar: _currentIndex == 2 which is using _currentIndex state and this is causing issue. When I tap on the last Tab on the BottomNavigationBar the state the _currentIndex changes to 2 and thus the extendBodyBehindAppBar is set as true which makes entire Scaffold rebuild itself and the state of the PageView is lost.

If comment out the line extendBodyBehindAppBar: _currentIndex == 2, then even if the Scaffold rebuilds the state of the PageView widget is preserved.

As of my understanding the Flutter should keep the state of child Widgets even if the parent rebuilds because the WidgetTree is not changed or re-arranged. I tried using Key on PageView but nothing worked.

Any help is very much appreciated.

Upvotes: 2

Views: 563

Answers (1)

Taur
Taur

Reputation: 564

refer to Lucas Salton Cardinali post on medium you need to use PageStorage to persit the state of the child you need after being destroyed.

here the example retrieved from the same page:

import 'package:flutter/material.dart';

// ...

void main() => runApp(MyApp());

// ...

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final List<Widget> pages = <Widget>[
    ColorBoxPage(
      key: PageStorageKey('pageOne'),
    ),
    ColorBoxPage(
      key: PageStorageKey('pageTwo'),
    )
  ];
  int currentTab = 0;
  final PageStorageBucket _bucket = PageStorageBucket();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Persistence Example"),
      ),
      body: PageStorage(
        child: pages[currentTab],
        bucket: _bucket,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: currentTab,
        onTap: (int index) {
          setState(() {
            currentTab = index;
          });
        },
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'page 1',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            label: 'page2',
          ),
        ],
      ),
    );
  }
}

class ColorBoxPage extends StatelessWidget {
  ColorBoxPage({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemExtent: 250.0,
      itemBuilder: (context, index) => Container(
        padding: EdgeInsets.all(10.0),
        child: Material(
          color: index % 2 == 0 ? Colors.cyan : Colors.deepOrange,
          child: Center(
            child: Text(index.toString()),
          ),
        ),
      ),
    );
  }
}

Upvotes: 1

Related Questions