Yash Jha
Yash Jha

Reputation: 167

Flutter TabBar not keeping state when swithing tabs even after using AutomaticKeepAliveClientMixin and super.build(context)

My app has two tabs and switching the tabs doesn't keep its state. I searched the web and found to use AutomaticKeepAliveClientMixin with wantKeepAlive as true and super.build(context). I did exactly what was told by other users across the web and stackoverflow but the problem still exists. Here is my code:

void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

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

@override
Widget build(BuildContext context) {
super.build(context);
return MaterialApp(
  title: 'Flutter GridView',
  debugShowCheckedModeBanner: false,
  theme: ThemeData(
    primaryColor: Colors.blue,
    accentColor: Colors.white,
  ),
  home: DefaultTabController(
    length: 2,
          child: Scaffold(
      drawer: Drawer(),
      backgroundColor: Colors.blueAccent,
      appBar: AppBar(
        backgroundColor: Colors.blueAccent,
        title: Text('AIO'),
        bottom: TabBar(
          tabs: <Widget>[
            Tab(icon: Icon(Icons.search)),
            Tab(icon: Icon(Icons.favorite)),
          ],
        ),
      ),
      body: TabBarView(
        children: <Widget>[
          gridView,
          SecondPage(),
        ],
      ),
    ),
  ),
);
}}

Upvotes: 1

Views: 2581

Answers (3)

Hashem Aboonajmi
Hashem Aboonajmi

Reputation: 13860

You should use KeepAliveStateMixin in tab level. here is complete example:

enter image description here

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        bottomNavigationBar: const TabBar(
          tabs: [
            Tab(icon: Icon(Icons.home)),
            Tab(icon: Icon(Icons.settings)),
            Tab(icon: Icon(Icons.person)),
          ],
        ),
        body: TabBarView(children: [
          const CounterTab(
              //key: PageStorageKey('COUNTER'),
              ),
          Container(
            color: Colors.green,
          ),
          Container(
            color: Colors.blue,
          ),
        ]),
      ),
    );
  }
}

class CounterTab extends StatefulWidget {
  const CounterTab({super.key});

  @override
  State<CounterTab> createState() => _CounterTabState();
}

class _CounterTabState extends State<CounterTab>
    with AutomaticKeepAliveClientMixin {
  int _counter = 0;
  @override
  void initState() {
    super.initState();
    print('state created');
  }

  @override
  Widget build(BuildContext context) {
super.build(context);
    return Container(
      color: Colors.purple,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            '$_counter',
            style: const TextStyle(fontSize: 60),
          ),
          ElevatedButton(
            onPressed: () {
              setState(() {
                _counter++;
              });
            },
            child: const Text('Increment'),
          ),
        ],
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;
}

Upvotes: 0

odiggity
odiggity

Reputation: 1566

My issue was slightly different than OPs but I want to post it here in case anyone else is running into the same issue. If you're not using a TabBarView (in my case I can't use TabBarView because it doesn't seem to work with dynamically sized pages) and a Center to hold your tab pages like this:

        Center(
          child: [
            Page1(),
            Page2(),
          ][_tabIndex],
        )

then the AutomaticKeepAliveClientMixin that I added to Page1State and Page2State was not working for me. I had to use an IndexedStack in order for it to work:

        IndexedStack(
          index: _tabIndex,
          children: [
            Page1(),
            Page2()
          ],
        )

So it looks like this:

@override
Widget build(BuildContext context) {
  return Column(
    mainAxisSize: MainAxisSize.min,
    children: <Widget>[
      Container(
        child: TabBar(controller: _tabController, tabs: [
          Tab(text: "Tab 1"),
          Tab(text: "Tab 2"),
        ]),
      ),
      IndexedStack(
        index: _tabIndex,
        children: [
          Page1(),
          Page2()
        ],
      ),
    ],
  );
}

Upvotes: 0

Elvan
Elvan

Reputation: 11

Use PageStorage, PageStorageBucket and PageStorageKey.

Create an instance field of PageStorageBucket, wrap your TabBarView with PageStorage, then add PageStorageKey to your GridView.

class _MyAppState extends State<MyApp> {
  final PageStorageBucket bucket = PageStorageBucket();
  //...

  body: PageStorage(
    bucket: bucket,
    child: TabBarView(
      children: <Widget>[
        gridView, // add to GridView(key: PageStorageKey('MyGridViewKey'), //...
        SecondPage(),
      ],
    ),
  ),
  //...

Upvotes: 1

Related Questions