Flutter: Save page state when using bottom Nav

Flutter: Save page state when using bottom Nav

In this article, I will solve one of the common problems. The problem is when they switch between two pages using bottom navigation it doesn't keep the state of the page.

I am going to solve this problem using IndexedStack and if you know about this then probably you `already knew this. There are some chances you don't know how to use IndexedStack then you should read and get the idea.

Get Started

First of all, we will create a simple application that will have two pages and we will use bottom navigation for switching pages.

Our Project structure looks something like this.

├── android
├── ios
├── lib
│   ├── main.dart
│   ├── home.dart
│   └── screens
│       ├── page1.dart
│       └── page2.dart
└── pubspec.yaml

Step 1

Let's create/edit our first file main.dart. This page will have very little code and it doesn't require any explanation.

import 'package:demo_app/home.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

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

Step 2

Let's create the second file home.dart and this is going to have bottom nav logic.

import 'package:demo_app/screens/page1.dart';
import 'package:demo_app/screens/page2.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final List<Widget> pages = [Page1(), Page2()];
  int selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Save Page State'),
      ),
      body: pages[selectedIndex],
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.exposure_minus_1_outlined),
            label: 'Increment',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.plus_one_outlined),
            label: 'Decrement',
          ),
        ],
        currentIndex: selectedIndex,
        onTap: (index){
            setState(() {
                selectedIndex = index;
            });
        },
      ),
    );
  }
}
  • We will have 2 page
    • page1: This screen has an increment on the counter.
    • page2: This screen has a decrement of the counter.
  • At once there will be one tab selected, so there will a variable which will hold the active tab index.

Step 3

Let's create page1.dart, this page will be created inside the screens directory. you can refer to the project structure above.

  • This page will have an incrementing counter.
import 'package:flutter/material.dart';

class Page1 extends StatefulWidget {
  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        Center(
          child: Text(
            '$count',
            style: Theme.of(context).textTheme.headline2,
          ),
        ),
        RaisedButton(
          onPressed: () {
            setState(() {
              count++;
            });
          },
          child: Text('Increment'),
        )
      ],
    );
  }
}

Step 4

Let's create page2.dart, this page will be created inside the screens directory.

  • This page will have a decrement counter.
import 'package:flutter/material.dart';

class Page2 extends StatefulWidget {
  @override
  _Page2State createState() => _Page2State();
}

class _Page2State extends State<Page2> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        Center(
          child: Text(
            '$count',
            style: Theme.of(context).textTheme.headline2,
          ),
        ),
        RaisedButton(
          onPressed: () {
            setState(() {
              count--;
            });
          },
          child: Text('Decrement'),
        )
      ],
    );
  }
}

Final

Now we will persist the state of the page and for that, we will use IndexedStack. This is one of simplest solution which can help you persist the state.

IndexedStack is a stack where you can bring a widget to the top of the stack by telling the index of the widget from the children(List<Widget>)

import 'package:demo_app/screens/page1.dart';
import 'package:demo_app/screens/page2.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final List<Widget> pages = [Page1(), Page2()];
  int selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Save Page State'),
      ),
//    body: pages[selectedIndex]
      body: IndexedStack(
        index: selectedIndex,
        children: pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.exposure_minus_1_outlined),
            label: 'Increment',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.plus_one_outlined),
            label: 'Decrement',
          ),
        ],
        currentIndex: selectedIndex,
        onTap: (index){
            setState(() {
                selectedIndex = index;
            });
        },
      ),
    );
  }
}

After using this you can switch the screen and you will find that the state of the counter doesn't loose. This was the thing which we were trying to achieve.

Conclusion

I hope you got the idea and you learned something new today. If you liked this want to support me then please share your valuable feedback or share this article with your friends.