在TabBarView 动态添加页面后删除其中一个页面会导致后面的页面状态错误或删除的页面不正确。出现这种问题是由于创建子页面时没有为子页面设置唯一的key导致的。

 1   void addNewPage() {
2 _pageCount++;
3 setState(() {
4 String title = "页面$_pageCount";
5 PageContent page = PageContent(data: title, pageId: _pageCount,);
6 PageData data = PageData(data: title, pageId: _pageCount, content: page);
7 listPages.add(data);
8 nowIndex = listPages.length -1;
9 resetTabController();
10 });
11 }

如上面的代码所示, 在创建PageContent 组件时如果没有指定全局唯一的key, 关闭页面时就会导致后面的页面被再次build或删除错误的页面,正确的代码如下

 1 void addNewPage() {
2 _pageCount++;
3 setState(() {
4 String title = "页面$_pageCount";
5 PageContent page = PageContent(data: title, pageId: _pageCount, key: ValueKey(title),);
6 PageData data = PageData(data: title, pageId: _pageCount, content: page);
7 listPages.add(data);
8 nowIndex = listPages.length -1;
9 resetTabController();
10 });
11 }

指定了全局唯一key后在删除子页面,后续页面就可以正常显示。

所有代码如下

import 'package:flutter/material.dart';

void main() {
runApp(const MainApp());
} class MainApp extends StatelessWidget {
const MainApp({super.key}); @override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.white),
primaryColor: Colors.white,
scaffoldBackgroundColor: Colors.white,
dialogBackgroundColor: Colors.white,
useMaterial3: true,
),
home: const PageMain(),
/*
home: ChangeNotifierProvider(
create: (context) => HomeProvider(),
builder: (context, child) => const HomePage(),
),
*/
);
}
} class PageData {
final String data;
final int pageId;
final Widget content; PageData({
required this.data,
required this.pageId,
required this.content,
});
} class _StatePageMain extends State<PageMain> with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
final List<PageData> listPages = <PageData>[];
int nowIndex = 0;
int _pageCount = 0;
TabController? tabController; @override
void initState() {
super.initState();
setState(() {
tabController = TabController(length: listPages.length, vsync: this);
});
} @override
bool get wantKeepAlive => true; void addNewPage() {
_pageCount++;
setState(() {
String title = "页面$_pageCount";
PageContent page = PageContent(data: title, pageId: _pageCount, key: ValueKey(title),);
PageData data = PageData(data: title, pageId: _pageCount, content: page);
listPages.add(data);
nowIndex = listPages.length -1;
resetTabController();
});
} //选中某个页面
void onSelectPage(PageData page) {
//页面已经选中
int selIndex = 0;
for (int index = 0; index < listPages.length; index++) {
PageData item = listPages[index];
if (item.pageId == page.pageId) {
selIndex = index;
break;
}
}
//选中页面没有更改
if (selIndex == nowIndex) {
return;
}
setState(() {
nowIndex = selIndex;
tabController?.animateTo(nowIndex);
});
} //关闭页面
void onClosePage(PageData data) {
int closedIndex = 0;
for (int index = 0; index < listPages.length; index++) {
PageData item = listPages[index];
if (item.pageId == data.pageId) {
closedIndex = index;
break;
}
}
setState(() {
listPages.removeAt(closedIndex);
if (closedIndex <= nowIndex) {
nowIndex--;
}
if (nowIndex < 0) {
nowIndex = 0;
} else if (nowIndex >= listPages.length) {
nowIndex = listPages.length -1;
}
resetTabController();
});
} void resetTabController() {
if (tabController?.length != listPages.length) {
tabController?.dispose();
tabController = TabController(
length: listPages.length,
vsync: this,
initialIndex: nowIndex,
);
}
} @override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
bottom: PreferredSize(
preferredSize: const Size.fromHeight(40),
child: TabBar(
controller: tabController,
tabs: listPages.map((item) => Tab(child: TitleBarItem(data: item, closeCallback: (data) => onClosePage(data)),)).toList(),
),
),
),
body: TabBarView(
controller: tabController,
children: listPages.map((item) => item.content).toList(),
),
floatingActionButton: FloatingActionButton(
onPressed: () => addNewPage(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
} class PageMain extends StatefulWidget {
const PageMain({super.key}); @override
State<PageMain> createState() => _StatePageMain();
} class _StatePageContent extends State<PageContent> with AutomaticKeepAliveClientMixin {
List<String> listItems = <String>[]; @override
void initState() {
print("初始化页面内容控制器:${widget.data}");
setState(() {
for (int index = 0; index <= 30; index++) {
listItems.add("${widget.data} - $index");
}
});
super.initState();
} @override
void dispose() {
print("释放页面内容控制器:${widget.data}");
super.dispose();
} @override
bool get wantKeepAlive => true; @override
Widget build(BuildContext context) {
print("Build页面 ${widget.data}");
return Container(
alignment: Alignment.center,
child: Column(
children: [
Text(widget.data),
Expanded(
child: ListView.builder(
itemExtent: 30,
itemCount: listItems.length,
itemBuilder: (context, index) {
return Text(listItems[index]);
}
),
),
],
),
);
}
} class PageContent extends StatefulWidget {
final int pageId;
final String data; const PageContent({super.key, required this.data, required this.pageId}); @override
State<PageContent> createState() {
return _StatePageContent();
}
} typedef ClickCallback = void Function(PageData data); class TitleBarItem extends StatelessWidget {
final PageData data;
final ClickCallback closeCallback; const TitleBarItem({
super.key,
required this.data,
required this.closeCallback,
}); @override
Widget build(BuildContext context) {
return SizedBox(
width: 200,
child: Row(
children: [
Expanded(child: Text(data.data)),
IconButton(
onPressed: () => closeCallback(data),
icon: const Icon(Icons.close))
],
),
);
}
}

flutter TabBarView 动态添加删除页面的更多相关文章

  1. 编辑 Ext 表格(一)——— 动态添加删除行列

    一.动态增删行 在 ext 表格中,动态添加行主要和表格绑定的 store 有关, 通过对 store 数据集进行添加或删除,就能实现表格行的动态添加删除.   (1) 动态添加表格的行  gridS ...

  2. 用Javascript动态添加删除HTML元素实例 (转载)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. jquery动态添加删除div--事件绑定,对象克隆

    我想做一个可以动态添加删除div的功能.中间遇到一个问题,最后在manong123.com开发文摘 版主的热心帮助下解答了(答案在最后) 使用到的jquery方法和思想就是:事件的绑定和销毁(unbi ...

  4. JS动态添加删除html

    本功能要求是页面传一个List 集合给后台而且页面可以动态添加删除html代码需求如下: 下面是jsp页面代码 <%@ page language="java" pageEn ...

  5. js实现网页收藏功能,动态添加删除网址

    <html> <head> <title> 动态添加删除网址 </title> <meta charset="utf-8"&g ...

  6. jQuery动态添加删除CSS样式

    jQuery框架提供了两个CSS样式操作方法,一个是追加样式addClass,一个是移除样式removeClass,下面通过一个小例子讲解用法. jQuery动态追加移除CSS样式 <!DOCT ...

  7. C#控制IIS动态添加删除网站

    我的目的是在Winform程序里面,可以直接启动一个HTTP服务端,给下游客户连接使用. 查找相关技术,有两种方法: 1.使用C#动态添加网站应用到IIS中,借用IIS的管理能力来提供HTTP接口.本 ...

  8. Angular-表单动态添加删除

    angular本身不允许去操作DOM,在angular的角度来说,所有操作都以数据为核心,剩下的事情由angular来完成.所以说,想清楚问题的根源,解决起来也不是那么困难. 前提 那么,要做的这个添 ...

  9. jQuery动态添加删除select项

    // 添加 function col_add() { var selObj = $("#mySelect"); var value="value"; var t ...

  10. zookeeper动态添加/删除集群中实例(zookeeper 3.6)

    一,用来作为demo操作的zookeeper集群中的实例: 机器名:zk1 server.1=172.18.1.1:2888:3888 机器名:zk2 server.2=172.18.1.2:2888 ...

随机推荐

  1. kafka 运维

    查看kafka topic列表 #集群需要先执行unset JMX_PORT ./kafka-topics.sh --zookeeper gitee-zookeeper:2181 --list 查看t ...

  2. Zabbix创建模板(templates)及监控项(item)

    Zabbix监控--Zabbix创建模板(templates)及监控项(item) 生产环境中,有一个简单的原则,那就是无监控不上线,监控系统开源方案中,zabbix也算不错的选择.由于其系统接口的开 ...

  3. armbian挂载sd卡记录

    mkdir -p /mnt/mmctouch  /etc/init.d/mount.shvim /etc/init.d/mount.sh内容见图mount /dev/mmcblk1p1 /mnt/mm ...

  4. java_类方法&对象方法

    int new; 类方法 不能写入和访问其中的对象属性 可以直接通过类调用 通过类调用类方法,没有具体的对象, 所以 不可以访问对象属性, 但是可以访问类属性 public static void d ...

  5. 【2019年第一篇总结】之Mac安装Python系列软件目录汇总

    1.第一步,安装Python环境 <[Mac + Python]苹果系统之安装Python3.6.x环境> 2.安装PyCharm并激活 <[转载][Pycharm编辑器破解步骤]之 ...

  6. 一分钟搭建Ghost个人网站博客系统

    什么是Ghost博客系统 Ghost 是一款设计简约.主题精致的个人博客系统,Ghost支持多用户创建和编辑,支持Markdown格式撰写文章,编辑的内容可即时预览. 创建轻量云主机 这里默认你已经有 ...

  7. Feign 动态设定服务器名称 与 调用接口

    1. 新增编码器(由于使用了动态的Feign,所以不能像正常使用Feign一样指定configuration配置编码器) import feign.RequestTemplate; import fe ...

  8. 基于Service Worker实现WebRTC局域网大文件传输能力

    基于Service Worker实现WebRTC局域网大文件传输能力 Service Worker是一种驻留在用户浏览器后台的脚本,能够拦截和处理网络请求,从而实现丰富的离线体验.缓存管理和网络效率优 ...

  9. 前端微服务qiankun 2.x主子应用通信代码片段

    主应用代码 主应用工程里面源代码新建qiankun/index.js,通信代码如下: import { initGlobalState } from "qiankun"; impo ...

  10. 【笔记】理解CSS3之Flexbox布局

    前言 先看一个例子,html代码如下: <ul> <li>1</li> <li>2</li> <li>3</li> ...