Flutter: PageController does not keep page despite keepPage: true

5.5k Views Asked by At

Steps to reproduce: Swipe to the left, switch to the red Tab and then switch to the purple Tab again.

What is: After executing the steps to reproduce, the PageView of the first Tab is at the blue page again.

What should be: After executing the steps to reproduce, the PageView of the first Tab is still at the green page.

What am I missing here?

import "package:flutter/material.dart";
import "package:flutter/services.dart";

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

class MyApp extends StatelessWidget {
  @override
    Widget build(BuildContext context) {
      SystemChrome.setEnabledSystemUIOverlays([]);
      return MaterialApp(
        home: MyHome()
      );
    }
}

class MyHomeState extends State<MyHome> with TickerProviderStateMixin {
  TabController tabController;
  @override
    void initState() {
      tabController = tabController?? TabController(
        vsync: this,
        length: 2
      );
      super.initState();
    }
  @override
    void dispose() {
      tabController.dispose();
      super.dispose();
    }
  @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: Column(
          children: [
            TabBar(
              controller: tabController,
              tabs: [
                Container(
                  color: Colors.purple,
                  width: 100.0,
                  height: 50.0
                ),
                Container(
                  color: Colors.pink,
                  width: 100.0,
                  height: 50.0
                )
              ]
            ),
            Expanded(
              child: TabBarView(
                controller: tabController,
                children: [
                  MyPageView(),
                  MyPageView(),
                ],
              )
            )
          ]
        )
      );
    }
}
class MyHome extends StatefulWidget {
  @override
    State<StatefulWidget> createState() {
      return MyHomeState();
    }
}

class MyPageViewState extends State<MyPageView> {
  PageController pageController;
  @override
    void initState() {
      pageController = pageController?? PageController();
      super.initState();
    }
    @override
      void dispose() {
        pageController.dispose();
        super.dispose();
      }
  @override
    Widget build(BuildContext context) {
      return PageView(
        controller: pageController,
        children: [
          Container(
            color: Colors.blue
          ),
          Container(
            color: Colors.green
          )
        ]
      );
    }
}
class MyPageView extends StatefulWidget {
  @override
    State<StatefulWidget> createState() {
      return MyPageViewState();
    }
}
2

There are 2 best solutions below

0
On

This is problem is solved with AutomaticKeepAliveClientMixin. I used this before, but due to confusion on the wrong state. It obviously has to be mixed in with the class that contains the state you want to keep alive.

0
On

I had this issue and found a much easier method to do this and that is to create a variable, say int index = 0 for example outside the statefulwidget class such that when that widget is created, there should be getters and setter before even creating the state, so when the class is called using the Navigator() the index of the page is unaltered. see example below

import 'package:flutter/material.dart';

int value = 0;

class MainpageScreen extends StatefulWidget {
  static const routeName = '/main';
  int getPage() {
    return value;
  }

  void setPage(int page) {
    value = page;
  }

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

class _MainpageScreenState extends State<MainpageScreen> {
  PageController _pageController;
  List<Widget> pages = [
    //...
  ];
  @override
  void initState() {
    _pageController = PageController(
      keepPage: true,
      initialPage: widget.getPage(),
    );
    super.initState();
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
        itemCount: 5,
        onPageChanged: (index) => setState(() {
          widget.setPage(index);
          _pageController.animateToPage(index,
              duration: Duration(milliseconds: 300), curve: Curves.ease);
        }),
        itemBuilder: (ctx, index) => pages[index],
        controller: _pageController,
      ),
      bottomNavigationBar: BottomNavigationBar(
          currentIndex: widget.getPage(),
          items: [
            //...
          ],
          onTap: (index) => setState(() {
                widget.setPage(index);
                _pageController.jumpToPage(index);
              })),
    );
  }
}