Flutter Pageview loses index on orientation change

737

Add final Key _key = GlobalKey(); to HomeState and pass key to both PageView constructors. This will tell Flutter that it is the same PageView and does needs to be moved, not replaced when the orientation changes.

As your code is written, a new PageView element is created every time without maintaining state, thus using the PageController's initialValue when built.

Unrelated Suggestion

PageView's are scrollable, and as your code is written, if a user scrolls, it will not update the BottomNavigationBar. Below are a couple of approaches depending on the user experience you are after.

PageView(
  // use never scrollable physics if you only want the user to change pages via the nav bar
  physics: NeverScrollableScrollPhysics(),
  // use onPageChanged to sync scroll and nav bar
  onPageChanged: (page) {
    // remove this line from nav bar onTap
    setState(() => _page = page);
  },
  key: _key,
  controller: _controller,
  children: [
    Text("ONE"),
    Text("TWO"),
    Text("THREE"),
    Text("FOUR"),
  ],
),
Share:
737
user2885978
Author by

user2885978

Updated on December 17, 2022

Comments

  • user2885978
    user2885978 over 1 year

    There have been similar issues recently, but they have been resolved and closed on GitHub. As I'm a newbie, I could be missing something here. After an orientation change, the page index reverts to zero, but the selected BottomNavigationBarItem is as it was. Here page "ONE" is being displayed, but tab "FOUR" was selected prior to rotating the device Screenshot

    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Pageview Orientation Bug',
          home: HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      int _page = 0;
      PageController _controller = PageController();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: OrientationBuilder(
            builder: (context,orientation) {
              return orientation == Orientation.portrait
               ? Column(
                children: [
                  Expanded(child: Container(color: Colors.grey)),
                  Expanded(
                    child: PageView(
                      controller: _controller,
                      children: [Text("ONE"),  Text("TWO"),Text("THREE"), Text("FOUR"),],
                    ),
                  ),
                ],
              )
              
             :  Row(
                children: [
                  Expanded(child: Container(color: Colors.grey)),
                  Expanded(
                    child: PageView(
                      controller: _controller,
                      children: [Text("ONE"),  Text("TWO"),Text("THREE"), Text("FOUR"),],
                    ),
                  ),
                ],
              );
            }
          ),
          bottomNavigationBar: BottomNavigationBar(
            type: BottomNavigationBarType.fixed,
            currentIndex: _page,
            onTap: (page) {
              setState(() {
                _page = page;
              });
              _controller.jumpToPage(page);
            },
            items: [
              BottomNavigationBarItem(
                icon: Icon(Icons.directions_bike),
                label: "ONE",
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.directions_boat_outlined),
                label: "TWO",
               ),
              BottomNavigationBarItem(
                icon: Icon(Icons.directions_car),
                label: "THREE",
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.directions_run),
                label: "FOUR",
              ),
            ],
          ),
        );
      }
    }
    
    
  • user2885978
    user2885978 over 3 years
    Thanks, the initialPage could be set in the controller to 0, but PageView does not seem to have an initial page, but it does have a controller, will try.
  • Lee3
    Lee3 over 3 years
    Ah yes, my bad. That was just my first thought, but I don't think it is going to work. I will update with a working solution and explanation asap.
  • user2885978
    user2885978 over 3 years
    That looks like the key insight, as the second paragraph in your answer - a new PageView created. Many thanks. Will try.
  • user2885978
    user2885978 over 3 years
    Your suggestion to provide a GlobalKey() to both PageView constructors is not something I would have thought of even trying, feels like playing a trick on the framework, but I've learnt something useful. Thanks.
  • Lee3
    Lee3 over 3 years
    Glad I could help. This is actually exactly one of the purposes of GlobalKeys. Check out this video: youtube.com/watch?v=kn0EOS-ZiIc for an intro on Keys in Flutter. It's a nice intro to the topic and will provide a little more understanding of how Widgets and Keys work.
  • user2885978
    user2885978 over 3 years
    The video says it for me: "They can be used to preserve the users scroll location" How did I miss that? Thanks again.