前言

最近在用flutter写一个小项目,在写主页面(底部导航栏+子页面)时遇到的一个问题:当点击底部item切换到另一页面, 再返回此页面时会重走它的initState方法(我们一般在initState中发起网络请求,或者初始化的操作),导致不必要的开销

根据Tab动态加载页面

我们先定义两个页面PageA和PageB

  1. class PageA extends StatefulWidget {
  2. _PageAState createState() => _PageAState();
  3. }
  4. class _PageAState extends State<PageA> {
  5. @override
  6. void initState() {
  7. super.initState();
  8. print("pageA init state");
  9. }
  10. @override
  11. Widget build(BuildContext context) {
  12. return Container(
  13. color: Colors.orangeAccent,
  14. child: Center(
  15. child: Text("page A"),
  16. ),
  17. );
  18. }
  19. }
  20. class PageB extends StatefulWidget {
  21. _PageBState createState() => _PageBState();
  22. }
  23. class _PageBState extends State<PageB> {
  24. @override
  25. void initState() {
  26. super.initState();
  27. print("pageB init state");
  28. }
  29. @override
  30. Widget build(BuildContext context) {
  31. return Container(
  32. color: Colors.blueAccent,
  33. child: Center(
  34. child: Text("page B"),
  35. ),
  36. );
  37. }
  38. }

定义Tab主页面

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatefulWidget {
  4. _MyAppState createState() => _MyAppState();
  5. }
  6. class _MyAppState extends State<MyApp> {
  7. int _tabIndex = 0;
  8. List<Widget> _tabWidget = [
  9. PageA(),
  10. PageB()
  11. ];
  12. @override
  13. Widget build(BuildContext context) {
  14. return MaterialApp(
  15. title: 'tab demo',
  16. home: Scaffold(
  17. bottomNavigationBar: BottomNavigationBar(
  18. items: _createBottomItems(),
  19. currentIndex: this._tabIndex,
  20. onTap: (index) {
  21. setState(() {
  22. this._tabIndex = index;
  23. });
  24. },
  25. ),
  26. body: this._tabWidget.elementAt(this._tabIndex),
  27. ),
  28. );
  29. }
  30. }
  31. // 创建底部导航item
  32. List<BottomNavigationBarItem> _createBottomItems() {
  33. return [
  34. BottomNavigationBarItem(
  35. icon: Icon(Icons.home),
  36. title: Text("首页")
  37. ),
  38. BottomNavigationBarItem(
  39. icon: Icon(Icons.insert_emoticon),
  40. title: Text("我的")
  41. )
  42. ];

运行后发现,每次切换tab都会调用initState,这显然不符合我们的正常的需求,有下面两种解决方式

IndexedStack

IndexedStack可以控制子元素的显示和隐藏,并且会缓存所有的元素,不会每次都重新创建子元素

  1. @override
  2. Widget build(BuildContext context) {
  3. return MaterialApp(
  4. title: 'tab demo',
  5. home: Scaffold(
  6. bottomNavigationBar: BottomNavigationBar(
  7. items: _createBottomItems(),
  8. currentIndex: this._tabIndex,
  9. onTap: (index) {
  10. setState(() {
  11. this._tabIndex = index;
  12. });
  13. },
  14. ),
  15. body: IndexedStack(
  16. children: this._tabWidget,
  17. index: this._tabIndex,
  18. )
  19. ),
  20. );
  21. }

运行后发现还是有个问题,IndexedStack在初始化的时候会初始化所有的子元素,pageA和pageB的initState会同时调用,这明显还是不符合我们的需求

正确来说应该是切换到具体页面的时候才进行初始化,而不是一开始就加载所有的页面的数据,避免资源浪费

PageView + AutomaticKeepAliveClientMixin

使用PageView支持多个view切换,并且不会一次加载完所有的页面

  1. PageController _pageController;
  2. @override
  3. void initState() {
  4. super.initState();
  5. this._pageController =PageController(initialPage: this._tabIndex, keepPage: true);
  6. }
  7. @override
  8. Widget build(BuildContext context) {
  9. return MaterialApp(
  10. title: 'tab demo',
  11. home: Scaffold(
  12. bottomNavigationBar: BottomNavigationBar(
  13. items: _createBottomItems(),
  14. currentIndex: this._tabIndex,
  15. onTap: (index) {
  16. setState(() {
  17. this._tabIndex = index;
  18. _pageController.jumpToPage(index);
  19. });
  20. },
  21. ),
  22. body: PageView(
  23. children: this._tabWidget,
  24. controller: _pageController,
  25. ),
  26. ),
  27. );
  28. }
  29. }

使用PageView可以正常切换,但是每次切换Tab的时候还是会重复调用initState,我们还需要在子页面实现AutomaticKeepAliveClientMixin

  1. class _PageAState extends State<PageA> with AutomaticKeepAliveClientMixin {
  2. @override
  3. bool get wantKeepAlive => true;
  4. ...
  5. }
  6. class _PageBState extends State<PageB> with AutomaticKeepAliveClientMixin {
  7. @override
  8. bool get wantKeepAlive => true;
  9. ...
  10. }

实现了AutomaticKeepAliveClientMixin就不会每次切换Tab都调用initState了,这也是google推荐的方式

最后发现PageView可以左右滑动切换,这个可以通过设置physics为NeverScrollableScrollPhysics()来禁止滑动

  1. PageView(
  2. children: this._tabWidget,
  3. controller: _pageController,
  4. physics: NeverScrollableScrollPhysics(),
  5. )

参考来源:https://blog.bombox.org/2019-02-23/flutter-tab-keep-alive/#more

flutter 切换tab后保留tab状态的更多相关文章

  1. easyui tree 编辑后保留原先状态

    $(function () { var selected = $('#depttree').tree('getSelected'); $('#depttree').tree({ checkbox: f ...

  2. Flutter——TabBar组件(顶部Tab切换组件)

    TabBar组件的常用属性: 属性 描述 tabs 显示的标签内容,一般使用 Tab 对象,也可以是其他的Widget  controller TabController 对象 isScrollabl ...

  3. 很好用的Tab标签切换功能,延迟Tab切换。

    一个网页,Tab标签的切换是常见的功能,但我发现很少有前端工程师在做该功能的时候,会为用户多想想,如果你觉得鼠标hover到标签上,然后切换到相应的内容,就那么简单的话,你将是一个不合格的前端工程师啊 ...

  4. easyui修复浏览器刷新后,tab页全部关闭的问题

    一.问题描述 使用easyui搭建的上左右页面布局,当我们在右侧打开了tab页,发现点击浏览器的刷新按钮后,整个页面会被重新渲染,导致所有打开的tab页都被关闭,回到初始状态的问题. 这个问题虽然不影 ...

  5. 微信小程序自定义tab,多层tab嵌套实现

    小程序最近是越来越火了-- 做小程序有一段时间了,总结一下项目中遇到的问题及解决办法吧. 项目中有个多 tab 嵌套的需求,进入程序主界面下面有两个 tab,进入A模块后,A模块最底下又有多个tab, ...

  6. 在Bootstrap开发框架的工作流模块中实现流程完成后更新资料状态处理

    在开发查看流程表单明细的时候,在Web界面中,我们往往通过使用@RenderPage实现页面内容模块化的隔离,减少复杂度,因此把一些常用的如审批.撤销.会签.阅办等等的流程步骤都放到了通用处理的页面V ...

  7. 低版本系统兼容的ActionBar(四)添加Tab+添加自定义的Tab视图+Fragment

    在ActionBar中添加Tab是很有用的技巧.在support V7库的支持下,我们几乎可以用和之前一样的方式来添加Tab,对于Tab来说,我们可以和MenuItem一样,给他定义自己的视图.我这里 ...

  8. 刨根问底U3D---如何退出Play模式后保留数据更改

    实际中遇到的需求 在做一款对抗类游戏,目前正在调整游戏的平衡性 所以就产生了一个需求 希望可以在Play模式时候对数据源做的更改可以在退出时候被保存下来. 举个Case, 比如 有一个炮塔 可以发射子 ...

  9. C# 版本的 计时器类:精确到微秒 秒后保留一位小数 支持年月日时分秒带单位的输出

    class TimeCount { // 临时变量,存放当前类能表示的最大年份值 ; /// <summary> /// 获取毫秒能表示的最大年份数 /// </summary> ...

随机推荐

  1. C++面试高频题

    作者:守望者1028链接:https://www.nowcoder.com/discuss/55353来源:牛客网 面试高频题: 校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我也忘记了 ...

  2. /build-impl.xml:1030: The module has not been deployed.(netbean javaweb)

    我在netbean上创建了一个javaweb,这个项目创建成功了,但是运行时却有了错误,错误贴图如下 报错: The module has not been deployed.  See the se ...

  3. python基础编程: 函数示例、装饰器、模块、内置函数

    目录: 函数示例 装饰器 模块 内置函数 一.函数示例: 1.为什么使用函数之模块化程序设计: 不使用模块程序设计的缺点: 1.体系结构不清晰,可主读性差: 2.可扩展性差: 3.程序冗长: 2.定义 ...

  4. PAT Basic 1030 完美数列 (25 分)

    给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列. 现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列. 输入格 ...

  5. java线程基础巩固---数据同步引入并结合jconsole,jstack以及汇编指令认识synchronized关键字

    对于多线程编程而言其实老生成谈的就是数据同步问题,接下来就会开始接触这块的东东,比较麻烦,但是也是非常重要,所以按部就班的一点点去专研它,下面开始. 数据同步引入: 这里用之前写过的银行叫号的功能做为 ...

  6. jmeter 压力测试tcp

    cmd下管理员执行 jmeter 界面 英文版 中文切换以后不能执行 最多跑905个线程 线程限制 查看结果数 界面会跑死 windows环境 注意:测试1万的tcp并发连接  大爷的 window最 ...

  7. web.xml中<welcome-file-list>标签不起作用

    之前也都提到过,web.xml会通过<servlet>和<servlet-mapping>来确定url和指定contoller文件,乃至于jsp页面的联系. 但是有一个< ...

  8. Python把多行文本合并

    在引用论文时,往往格式出错,出现非常多行,这样操作非常不方便.这种方法讲多行合并之后,再处理: # 文件空格和回车键处理工具infile = r'C:\Users\SAM\Desktop\新建文本文档 ...

  9. 部署dashboard

    1.获取k8s版本: 2.访问dashboard的github:https://github.com/kubernetes/dashboard/releases,然后找到对应的版本 3.然后将yaml ...

  10. RestTemplate响应值乱码

    @Bean public RestTemplate getRestTemplate(){ RestTemplate restTemplate = new RestTemplate(); //解决中文乱 ...