Flutter小技巧|标签页面子页面切换数据保留:Mixin与PageView

2,212 阅读2分钟

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

Hi 👋

我的个人项目扫雷Elic 无尽天梯梦见账本隐私访问记录
类型游戏财务工具
AppStoreElicUmemi隐私访问记录

更多专栏:

Lawliet的独立开发碎碎念

Lawliet的iOS游园会

Lawliet的iOS底层实验室

Lawliet的iOS逆向实验室

Lawliet的刷题小本本

Lawliet的Flutter实验室

Flutter小技巧|从侧索引条封装看手势处理与clamp函数

Flutter小技巧|Widget初始化方法重写&链式调用&排序

Flutter小技巧|通过多样式Cell看断言

Flutter小技巧|ListView滑动到分组头部

Flutter小技巧|第三方库导入与网络数据展示(三种形式)

前言

前文Flutter小技巧|第三方库导入与网络数据展示(三种形式)中可以发现一个问题,每次切换页面,都会重新请求数据。而实际开发中,我们会对数据进行保留,在需要的时候再进行数据更新。

16-05.gif

Mixin

调整继承关系

多继承 AutomaticKeepAliveClientMixin

class MessagePage extends StatefulWidget {
  @override
  _MessagePageState createState() => _MessagePageState();
}

class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClientMixin<MessagePage> {
    ...
}

重写 wantKeepAlive

class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClientMixin<MessagePage> {
    ...

    @override
    bool get wantKeepAlive => true;
}

调整 build 方法

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(...);
  }

调整 MyHomePage (标签Tab页)

原先我们的切换Tab的逻辑是这样的:

class _MyHomePageDataSource extends State<MyHomePage> {
  int _selectedTab = 0;
  final List<Widget> _pages = [
      MessagePage(), 
      ContactsPage(), 
      MommentPage(), 
      MinePage()
      ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _pages[_selectedTab],
      ...
    );

并非所有的子页面的 widget 都在标签容器的 widget 树中,导致每次切换都会去重新生成渲染。

  • 我们需要做如下调整:
    • 构建 PageController
    • 调整 BodyPageView, 并绑定 PageControllerpages
    • 通过 PageController 切换子页面
class _MyHomePageDataSource extends State<MyHomePage> {
  int _selectedTab = 0;
  final List<Widget> _pages = [
    MessagePage(),
    ContactsPage(),
    MommentPage(),
    MinePage()
  ];
  // 一、 构建 PageController
  final PageController _pageController = PageController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        // 二、 调整 Body 为 PageView, 并绑定 PageController 和 pages
      body: PageView(
        controller: _pageController,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        selectedItemColor: Colors.green,
        unselectedItemColor: Colors.grey,
        currentIndex: _selectedTab,
        onTap: (idx) {
          setState(() {
            _selectedTab = idx;
            // 三、 通过 PageController 切换子页面
            _pageController.jumpToPage(_selectedTab);
          });
        },
        items: const [...],
      ),
    );
  }
}

检查效果

17-01.gif

PageView

滑动切换子页面

17-02.gif

在使用总突然发现可以通过滑动切换 PageView 的子页面。但是底部的标签选中没有变化,下面我们来处理一下。

同步标签切换

通过 onPageChanged 来监听页面的切换,同步修改选中标签。

body: PageView(
        onPageChanged: (idx) {
          setState(() {
            _selectedTab = idx;
          });
        },
        controller: _pageController,
        children: _pages,
      ),

关闭滑动切换

当然,不需要滑动的话可以通过 physics 来进行关闭。

body: PageView(
    physics: const NeverScrollableScrollPhysics(),
    controller: _pageController,
    children: _pages,
),

完整代码

GitHub