在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. Echarts 5 动态按需引入图表

    官网提供的按需引入方法为全量按需引入,在打包分离中,仍旧存在使用不到的图表被打包进去. 例如:组件A使用了折线图.柱状图,组件B只用到了折线图,但是打包组件B的时候,柱状图也就被打包进去. 本文提供一 ...

  2. c# 复制文件夹内所有文件到另外一个文件夹

    /// <summary> /// 开始转移 /// </summary> /// <param name="sender"></para ...

  3. Linux 磁盘分区和格式化

    分区 常用命令行工具: fdisk:适用于 MBR 分区表 gdisk:适用于 GPT 分区表 parted:适用于 MBR 和 GPT 分区表,功能更强大.它还有一个 GUI 版本,名为 gpart ...

  4. 【测试平台开发】——04Flask后端api开发实战(一)

    一.测试平台开发模式 要开发一套平台有两种开发模式,一个[大而全],一个[小而简]. 说道[大而全]想到目前大型项目都使用的语言[JAVA],[小而简]想到的是[Python]语言. 重武器(大而全) ...

  5. IP服务正常,域名服务异常,报400 badrequest

    IP的情况下,访问接口都正常,使用域名访问,报错400 badrequest 经确认,ssl配置无问题,证书文件本身无问题 最后查出来原因,是域名格式的问题,原域名中包含_,需要修改为- 排查过程: ...

  6. 利用水墨映客图床作为COS服务器

    目录 利用水墨映客作为COS服务器 利用picGo配合typora上传图片 安装PicGo(以Windows为例) 安装lankong插件 在SpringBoot中开发图片上传工具类 设置图片上传请求 ...

  7. DRBD - Distributed Replication Block Device

    Ref https://computingforgeeks.com/install-and-configure-drbd-on-centos-rhel https://www.veritas.com/ ...

  8. CSS – W3Schools 学习笔记 (2)

    CSS Combinators Link to W3Schools body p = body 里的所有 p descendant body > p = body first child lay ...

  9. GPT最佳实践:五分钟打造你自己的GPT

    前几天OpenAI的My GPTs栏目还是灰色的,就在今天已经开放使用了.有幸第一时间体验了一把生成自己的GPT,效果着实惊艳!!!我打造的GPT模型我会放到文章末尾,大家感兴趣也可以自己体验一下. ...

  10. C#/.NET/.NET Core技术前沿周刊 | 第 5 期(2024年9.9-9.15)

    前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...