flutter TabBarView和PageView的页面状态保存问题

我们在使用TabBar或者BottomNavigationBar的时候,都会配合使用 TabBarView和PageView 进行页面展示,但如果不做特别处理的话,我们会发现在每次切换页面之后,页面都会重新刷新(比如一个列表页面,拉到最后一条数据,然后跳转到其它Tab再跳转回来,列表又变成从第一条数据开始展示),实在是很不友好。这里有两种办法解决:

  • PageStorageKey
  • AutomaticKeepAliveClientMixin

两种方法都能记录页面状态,两者又有些异同:

  1. 两者都是在 TabBarView 和 PageView 的子页面进行设置
  2. 设置 PageStorageKey 的页面每次都是会重新走生命周期,也就是每次进入页面都会重新调用 initState() 和 build 等一系列方法;而设置 AutomaticKeepAliveClientMixin 的页面只在第一次进入的时候会走页面的生命周期,后面不会重新调用。

1、PageStorageKey

class TabPageTestController extends StatefulWidget {
  final String title;

  TabPageTestController({Key key, this.title}): super(key: key);
  
  @override
  _TabPageTestControllerState createState() => _TabPageTestControllerState();
}

class _TabPageTestControllerState extends State<TabPageTestController>{
  List<String> dates = <String>[];
 
  @override
  void initState() {
    super.initState();
    for (var i = 1; i < 20; i++) {
      dates.add('${widget.title}, item$i');//如 “页面1,item1”
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      key: PageStorageKey<String>(widget.title),//这里记录 ListView 的状态
      itemCount: dates.length,
      itemBuilder: (BuildContext context, int index) {
        return ListTile( title: Text(dates[index]),);
      },
    );
  }
}

2、AutomaticKeepAliveClientMixin

class TabPageTestController extends StatefulWidget {
  final String title;

  TabPageTestController({Key key, this.title}): super(key: key);
  
  @override
  _TabPageTestControllerState createState() => _TabPageTestControllerState();
}

// with AutomaticKeepAliveClientMixin 记录页面状态
class _TabPageTestControllerState extends State<TabPageTestController> with AutomaticKeepAliveClientMixin {
  List<String> dates = <String>[];

  //AutomaticKeepAliveClientMixin 需添加
  @override
  bool get wantKeepAlive => true; 

  @override
  void initState() {
    super.initState();
    for (var i = 1; i < 20; i++) {
      dates.add('${widget.title}, item$i');//如 “页面1,item1”
    }
  }
  
  @override
  Widget build(BuildContext context) {
    super.build(context);//AutomaticKeepAliveClientMixin 之后添加,其实添不添加这句都能记录状态,最好还是添加一下
    return ListView.builder(
      key: PageStorageKey<String>(widget.title),
      itemCount: dates.length,
      itemBuilder: (BuildContext context, int index) {
        return ListTile( title: Text(dates[index]),);
      },
    );
  }
}

后续:在后面使用中,发现 NestedScrollView + TabBar + TabBarView/PageView 使用上面 AutomaticKeepAliveClientMixin 进行页面缓存也不是很友好,因为如果 TabBarView/PageView 装载的子页面有滚动视图的话(如下面代码),如果滚动其中一个子页面的列表,其它的子页面里面的列表也会同步滚动,网上有人找到原因:列表滚动同步解决

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

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    super.dispose();
    _tabController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFF8F8F8),
      appBar: AppBar( ),
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              ...
              bottom: TabBar(
                  controller: _tabController,
                  labelColor: Colors.redAccent,
                  unselectedLabelColor: Colors.black,
                  indicatorColor: Colors.redAccent,
                  tabs: [
                    Tab(text: "Tab1"),
                    Tab(text: "Tab2"),
                    Tab(text: "Tab3"),
                  ]
              ),
            )
          ];
        },
        body: TabBarView(
          controller: _tabController,
          children: [
            TabPageTestController(title: 'Tab1',),
            TabPageTestController(title: 'Tab2',),
            TabPageTestController(title: 'Tab3',),
          ],
        ),
      ),
    );
  }
}

版权声明:本文为zhumj_zhumj原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。