PageView and BottomNavigationBar lagging

198 views Asked by At

I implemented a very simple system:

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  State<MyApp> createState() => MainState();
}
class MainState extends State<MyApp> {

  @override
  void initState() {
    futureData = getModelsList();
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(          
     home:Scaffold(
      bottomNavigationBar: BottomNavigationBar(
      type:BottomNavigationBarType.shifting,
      items: const <BottomNavigationBarItem>[
        BottomNavigationBarItem(
          icon: Icon(Icons.home,
            color: Colors.white,
          ),label: "",
          backgroundColor: Colors.grey,
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.description,
            color: Colors.white,
          ),label: "",
          backgroundColor: Colors.grey,
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.calculate,
            color: Colors.white,
          ),label: "",
          backgroundColor: Colors.grey,
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.question_answer,
            color: Colors.white,
          ),label: "",
          backgroundColor: Colors.grey,
        ),

      ],
      currentIndex: _selectedIndex,
      selectedItemColor: Colors.amber[800],
      onTap: (newIndex) {
        setState(() {
          _selectedIndex = newIndex;
          pageController.animateToPage(_selectedIndex, duration: Duration(milliseconds: 300), curve: Curves.ease);
        });
      },
    ),
        body: PageView(
          controller: pageController,
          onPageChanged: (newIndex){
            setState(() {
              _selectedIndex = newIndex;
            });
          },
        children: [
          buildPage1(),
          buildPage1(),
          buildPage1(),
          buildPage1(),
        ]
      ),

      )

Whats is happening is if the buildPage1 is a simple page like only a Text('Page1') everything is running fine. But if I make this page more complex the navigation or animation from the actual page to selected page is strange. I dont know how to say in english but isnt running smootly, like a lag...

I am using setState to rebuild the screen with new selected index. Is the same methods that I saw in the internet. So sure that I am losing something here.

EDIT1: When I remove these lines from pageView:

 onPageChanged: (newIndex){
   setState(() {
      _selectedIndex = newIndex;
   });
 },

and remove these:

onTap: (newIndex) {
        setState(() {
          _selectedIndex = newIndex;
          pageController.animateToPage(newIndex, duration: const Duration(milliseconds: 1000), curve: Curves.ease);
        });
      },

Everything works fine. If I keep see how many time my buildPage is called when I go from first page to last page:

building mainPage
inside buildFace()
inside buildFace()
inside buildFace()
inside buildFace()
building mainPage
inside buildFace()
inside buildFace()
inside buildFace()
inside buildFace()
building mainPage
inside buildFace()
inside buildFace()
inside buildFace()
inside buildFace()

Off course that I am in throuble...

If I remove the KeelAlivePage I am receiving this:

building mainPage
inside buildFace()
inside buildFace()
inside buildFace()
inside buildFace()
building mainPage
inside buildFace()
inside buildFace()
inside buildFace()
inside buildFace()

EDIT2: If I remove only the setState from pageView I am receiving only this:

building bottomnavmenu

If I remove only this line pageController.animateToPage(newIndex, duration: const Duration(milliseconds: 500), curve: Curves.ease); and keep the setState in pageView :

building bottomnavmenu

But here the page dont change.

EDIT3: Well, what is happen is that for some reason the pageview is calling recursively. So I setState in dropdown the page also call setState...So everything works fine if I only remove the setState in PageView. So when I click in a button in navbar the page is going to the selected page because the pageController.animateToPage... and only the navbar is calling one time and I receive:

building bottomnavmenu 

But with the setState in pageview disable it means that I cant update the navbar!!!

EDIT4: I was testing the pageController.animateToPage. and here is the point. Using animateTo will pass from 0 to 3 like 0-1-2-3 to create the animation effect. If I use jumpToPage everything works fine. This happens because when i am in zero page and go to the 3 onChange will be called everytime that index change. So this is why so many call. So atleast I found the issue. Sad that I want to use animateTo...

1

There are 1 answers

6
arshia_sir On BEST ANSWER

It seems like you're experiencing performance issues when transitioning between pages in your BottomNavigationBar and PageView setup. The code you've shared looks fine, but there are a few optimizations and suggestions you can consider to potentially improve the smoothness of the navigation and animations:

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  State<MyApp> createState() => MainState();
}

class MainState extends State<MyApp> {
  late Future<List<Model>> futureData;

  @override
  void initState() {
    futureData = getModelsList();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.shifting,
          // ... BottomNavigationBarItem items ...
          onTap: (newIndex) {
            setState(() {
              _selectedIndex = newIndex;
              pageController.animateToPage(
                _selectedIndex,
                duration: Duration(milliseconds: 200),
                curve: Curves.easeInOut,
              );
            });
          },
        ),
        body: PageView(
          controller: pageController,
          onPageChanged: (newIndex) {
            setState(() {
              _selectedIndex = newIndex;
            });
          },
          children: [
            KeepAlivePage(buildPage1()),
            KeepAlivePage(buildPage1()),
            KeepAlivePage(buildPage1()),
            KeepAlivePage(buildPage1()),
          ],
        ),
      ),
    );
  }
}

class KeepAlivePage extends StatefulWidget {
  const KeepAlivePage(this.child);
  final Widget child;

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

class _KeepAlivePageState extends State<KeepAlivePage>
    with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context); // To keep the state alive
    return widget.child;
  }
}
  1. Minimize Rebuilding Widgets: Complex widgets can lead to performance issues if they're being rebuilt too frequently. Make sure that your buildPage1() function isn't doing expensive operations or creating unnecessary widgets every time it's called.
  2. Lazy Loading Content: If your pages are complex, you could consider lazy-loading their content. Instead of building all the pages at once, you could build the content only when the respective page is selected. This can help reduce the initial load time and memory consumption.
  3. Use AutomaticKeepAliveClientMixin: If you're dealing with pages that have expensive UI or data fetching, consider using the AutomaticKeepAliveClientMixin to keep the state of inactive pages alive. This way, the state of previously visited pages won't be discarded, which can help improve the transition between pages.
  4. Performance Profiling: Use Flutter's performance profiling tools (like the Performance Overlay and Timeline) to identify which part of your code is causing the performance lag. This will help you pinpoint areas that need optimization.
  5. Avoid Heavy Operations in initState: While your current initState is fetching data, avoid performing heavy operations that might block the UI thread. Consider using a FutureBuilder or another approach to fetch data asynchronously without blocking the UI.
  6. Reduce Animation Duration: In your onTap method of the BottomNavigationBar, you're using a duration of 300 milliseconds for the animation. You can try reducing this duration to see if it improves the perceived smoothness of the transition.
  7. Use PageController with a KeepAlive Mixin: When using PageView, the pages that are not currently visible may get disposed to save memory. You can use a KeepAlive mixin to ensure that the pages are kept alive even when they are not currently visible.
  8. Update Dependencies: Make sure you are using the latest versions of Flutter and related packages. Sometimes, performance improvements and bug fixes are introduced in newer releases.