Maintaining the Flutter app state with a different approach

Mustafa Tahir
3 min readJun 10, 2022

--

While developing an application, it is a major concern how our application would perform when we use Tabs or Bottom Navigation Bar kind of Widgets (Almost everything in Flutter is a widget).

As for a sample example, let's consider we have three Tabs

Users

Tab2

Tab3

on the BottomNavBar, and on the very 1st index, we are having an API call request which gets us, Fake users. In addition, the 2nd and 3rd indexes would be having ListViews respectively.

Here comes the major concern, if I switch between these indexes, the pages would load every time.

To get rid of this issue, we will be using PageView() widget, on our main screen, which does have a Bottom Navigation Bar in it.

What’s in PageView()

PageController

Physics

Children

PageController: Receives index

Physics: Whether to allow switching between the tabs or handle it manually

Children: List of Widgets

Implementation of PageView() and parameters

Pic#1

Code View

Snippet #1

import 'package:flutter/material.dart';
import 'package:maintaining_states_flutter_concept/Screens/tab_three.dart';
import 'package:maintaining_states_flutter_concept/Screens/tab_two.dart';
import 'package:maintaining_states_flutter_concept/Screens/users.dart';

class MainOne extends StatefulWidget {
const MainOne({Key? key}) : super(key: key);

@override
State<MainOne> createState() => _MainOneState();
}

class _MainOneState extends State<MainOne> {

PageController? pageController;
int? index;
List<Widget>? widgets;

@override
void initState() {
super.initState();

index = 0;
widgets = [
const Users(),
const TabTwo(),
const TabThree(),
];

pageController = PageController(initialPage: index!);
}

@override
void dispose() {
pageController!.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Maintaining States - Concept"),
),
body: PageView(
physics: const NeverScrollableScrollPhysics(),
controller: pageController,
children: widgets!,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: index!,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.person),label: 'Users'),
BottomNavigationBarItem(icon: Icon(Icons.category_outlined),label: 'Tab2'),
BottomNavigationBarItem(icon: Icon(Icons.category_sharp),label: 'Tab3'),
],
type: BottomNavigationBarType.fixed,
onTap: (v) {
setState(() {
index = v;
});
},
),
);
}
}

Now its time to implement the code that will “Save the State”

As I mentioned that Bottom Bar would be having three tabs. For each tab we will implement it manually

Take TabTwo snippet for instance.

Snippet #2

import 'package:flutter/material.dart';

class TabTwo extends StatefulWidget {
const TabTwo({Key? key}) : super(key: key);

@override
State<TabTwo> createState() => _TabTwoState();
}

class _TabTwoState extends State<TabTwo> {
@override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
itemCount: 22,
itemBuilder: (context, index) {

return const Card(
child: ListTile(
title: Text("TabTwo"),
),
);
},
);
}
}

Note: We must have Stateful Widget to implement State saving

On this line .. class _TabTwoState extends State<TabTwo>

add “with AutomaticKeepAliveClientMixin”.

AutomaticKeepAliveClientMixin: Allows the subtrees to request to be kept alive in lazy list.

Next, after “buildContext()”, add .. “super.build(context);”

Next, at the end of “buildContext()”, add

@override 
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;

Do this for the other Two widgets as well and you’re good to go

After above implementation

Snippet #3

import 'package:flutter/material.dart';

class TabTwo extends StatefulWidget {
const TabTwo({Key? key}) : super(key: key);

@override
State<TabTwo> createState() => _TabTwoState();
}

class _TabTwoState extends State<TabTwo> with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return ListView.builder(
shrinkWrap: true,
itemCount: 22,
itemBuilder: (context, index) {

return const Card(
child: ListTile(
title: Text("TabTwo"),
),
);
},
);
}

@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}

After running the app, you will see the magic.

So, this concludes the article.

I hope you have enjoyed reading this one.

Please clap 👏 this one if you think you have learned something new today!

Stay tuned for more articles.

You can find me on GitHub, and YouTube.

--

--