【Flutter】使用PageStorage在页面切换时保存状态

Tab切换时的问题


在一个SPA的Flutter项目中可以用StatefulWidget保存页面状态,但是当出现页面切换的情况时,例如通过底部导航栏(BottomNavigationBar)或者标签栏(TabBar)组件来切换页面内容,每次导航栏或标签栏切换页面时,之前的页面就被清理了。
出现上面问题的原因是,之前页面的状态(State)没有被保留下来,状态的reset导致页面发生了初始化。
在这里插入图片描述
我们使用TabBarView做成的页面的中,从第一页面调到第二页再返回后,
之前的状态丢失了。我们期待的效果应该是下面这样:
在这里插入图片描述

代码示例


通过代码展示如何实现Tab切换时的状态保存。
首先通过TabBarTabBarView实现基本的APP,如下:
(参考Tab页切换)

main.dart

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

void main() => runApp(
      MaterialApp(
        debugShowCheckedModeBanner: false,
        home: MyApp(),
      ),
    );

class TabInfo {
  String label;
  Widget widget;
  TabInfo(this.label, this.widget);
}

class MyApp extends StatelessWidget {
  final List<TabInfo> _tabs = [
    TabInfo("FIRST", Page1()),
    TabInfo("SECOND", Page2()),
    TabInfo("THIRD", Page3()),
  ];

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: _tabs.length,
      child: Scaffold(
        appBar: AppBar(
          title: Text('Tab Controller'),
          bottom: PreferredSize(
            child: TabBar(
              isScrollable: true,
              tabs: _tabs.map((TabInfo tab) {
                return Tab(text: tab.label);
              }).toList(),
            ),
            preferredSize: Size.fromHeight(30.0),
          ),
        ),
        body: TabBarView(children: _tabs.map((tab) => tab.widget).toList()),
      ),
    );
  }
}

page1.dart

import 'package:flutter/material.dart';


class Page1 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _Page1State();
  }
}

class Page1Params {
  int counter1 = 0;
  int counter2 = 0;
}

class _Page1State extends State<Page1> {
  Page1Params _params = Page1Params();

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          Text(
            '${_params.counter1}',
            style: TextStyle(
              fontSize: 48,
            ),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.remove, size: 32.0),
                onPressed: () {
                  setState(() {
                    _params.counter1--;
                  });
                },
              ),
              IconButton(
                icon: Icon(Icons.add, size: 32.0),
                onPressed: () {
                  setState(() {
                    _params.counter1++;
                  });
                },
              ),
            ],
          ),
          Text(
            '${_params.counter2}',
            style: TextStyle(
              fontSize: 48,
            ),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.remove, size: 32.0),
                onPressed: () {
                  setState(() {
                    _params.counter2--;
                  });
                },
              ),
              IconButton(
                icon: Icon(Icons.add, size: 32.0),
                onPressed: () {
                  setState(() {
                    _params.counter2++;
                  });
                },
              ),
            ],
          )
        ],
      ),
    );
  }
}

page2.dart 、page3.dart

page2和3简单实现如下:

import 'package:flutter/material.dart';

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

到此为止,我们实现了上面图1的状态
在这里插入图片描述

使用PageStorage管理状态


Flutter提供了PageStorage用于页面切换时的状态保存。使用方法很简单:

1. 构造函数中传入StorageKey

为Page1 添加一个带参数的构造函数,通过其将Key直接传给super,后续通过此key即可恢复状态

class Page1 extends StatefulWidget {
  Page1({Key key}) : super(key: key); //增加一行

  @override
  State<StatefulWidget> createState() {
    return _Page1State();
  }
}

2. 创建widget时指定key

class MyApp extends StatelessWidget {
  final List<TabInfo> _tabs = [
    TabInfo(
      "FIRST", 
      Page1(key: PageStorageKey<String>("key_Page1")) // 指定key
    ), 
    TabInfo("SECOND", Page2()),
    TabInfo("THIRD", Page3()),
  ];

3. 读取state

重写State类的didChangeDependencies方法, 在里面通过readStatePageStorage读取并恢复被保存的状态。

class _Page1State extends State<Page1> {
  Page1Params _params;

  @override
  void didChangeDependencies() { //重写此方法
    Page1Params p = PageStorage.of(context).readState(context);
    if (p != null) {
      _params = p;
    } else {
      _params = Page1Params();
    }
    super.didChangeDependencies();
  }

didChangeDependencies会紧跟在initState之后被调用,便于进行state初始化

4. 保存state

相应的,在我们setState的同时通过writeState将状态保存进PageStorage

@override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          Text(
            '${_params.counter1}',
            style: TextStyle(
              fontSize: 48,
            ),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.remove, size: 32.0),
                onPressed: () {
                  setState(() {
                    _params.counter1--;
                  });
                  PageStorage.of(context).writeState(context, _params);
                },
              ),
              IconButton(
                icon: Icon(Icons.add, size: 32.0),
                onPressed: () {
                  setState(() {
                    _params.counter1++;
                  });
                  PageStorage.of(context).writeState(context, _params);
                },
              ),
            ],
          ),
          Text(
            '${_params.counter2}',
            style: TextStyle(
              fontSize: 48,
            ),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.remove, size: 32.0),
                onPressed: () {
                  setState(() {
                    _params.counter2--;
                  });
                  PageStorage.of(context).writeState(context, _params);
                },
              ),
              IconButton(
                icon: Icon(Icons.add, size: 32.0),
                onPressed: () {
                  setState(() {
                    _params.counter2++;
                  });
                  PageStorage.of(context).writeState(context, _params);
                },
              ),
            ],
          )
        ],
      ),
    );
  }

至此,我们可以实现期望效果
在这里插入图片描述


总结


总结一下通过页面切换时通过PageStorage保存并恢复状态的一般步骤:

  1. StatefulWidget 类增加带PageStorageKey的构造函数
  2. 创建widget时传入PageStorageKey
  3. 重写didChangeDependencies,通过readState 恢复state
  4. setState时,通过writeState 保存state
发布了116 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/vitaviva/article/details/105313672