flutter TabBarView 动态添加删除页面
在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 动态添加删除页面的更多相关文章
- 编辑 Ext 表格(一)——— 动态添加删除行列
一.动态增删行 在 ext 表格中,动态添加行主要和表格绑定的 store 有关, 通过对 store 数据集进行添加或删除,就能实现表格行的动态添加删除. (1) 动态添加表格的行 gridS ...
- 用Javascript动态添加删除HTML元素实例 (转载)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- jquery动态添加删除div--事件绑定,对象克隆
我想做一个可以动态添加删除div的功能.中间遇到一个问题,最后在manong123.com开发文摘 版主的热心帮助下解答了(答案在最后) 使用到的jquery方法和思想就是:事件的绑定和销毁(unbi ...
- JS动态添加删除html
本功能要求是页面传一个List 集合给后台而且页面可以动态添加删除html代码需求如下: 下面是jsp页面代码 <%@ page language="java" pageEn ...
- js实现网页收藏功能,动态添加删除网址
<html> <head> <title> 动态添加删除网址 </title> <meta charset="utf-8"&g ...
- jQuery动态添加删除CSS样式
jQuery框架提供了两个CSS样式操作方法,一个是追加样式addClass,一个是移除样式removeClass,下面通过一个小例子讲解用法. jQuery动态追加移除CSS样式 <!DOCT ...
- C#控制IIS动态添加删除网站
我的目的是在Winform程序里面,可以直接启动一个HTTP服务端,给下游客户连接使用. 查找相关技术,有两种方法: 1.使用C#动态添加网站应用到IIS中,借用IIS的管理能力来提供HTTP接口.本 ...
- Angular-表单动态添加删除
angular本身不允许去操作DOM,在angular的角度来说,所有操作都以数据为核心,剩下的事情由angular来完成.所以说,想清楚问题的根源,解决起来也不是那么困难. 前提 那么,要做的这个添 ...
- jQuery动态添加删除select项
// 添加 function col_add() { var selObj = $("#mySelect"); var value="value"; var t ...
- zookeeper动态添加/删除集群中实例(zookeeper 3.6)
一,用来作为demo操作的zookeeper集群中的实例: 机器名:zk1 server.1=172.18.1.1:2888:3888 机器名:zk2 server.2=172.18.1.2:2888 ...
随机推荐
- luoguP3330 [ZJOI2011] 看电影--组合数学--高精度
\(luoguP3330\) [ZJOI2011] 看电影 废了老命想题解 $$luogu$$ $$HZOI$$ 题意 到了难得的假期,小白班上组织大家去看电影.但由于假期里看电影的人太多,很难做到让 ...
- 通过 GitHub Actions 实现代码的自动编译和发布
GitHub Actions 是一个非常强大的工具,可以用来实现各种自动化任务,包括自动编译和发布 release.以下是一个基本的工作流程,展示如何使用 GitHub Actions 实现这一目标: ...
- tar 解压文件时提示 Ignoring unknown extended header keyword
在 Linux 上使用 tar 解压文件时出现下列提示: tar: Ignoring unknown extended header keyword 'LIBARCHIVE.xattr.com.app ...
- KernelWarehouse:英特尔开源轻量级涨点神器,动态卷积核突破100+ | ICML 2024
动态卷积学习n个静态卷积核的线性混合,加权使用它们输入相关的注意力,表现出比普通卷积更优越的性能.然而,它将卷积参数的数量增加了n倍,因此并不是参数高效的.这导致不能探索n>100的设置(比典型 ...
- Angular 学习笔记 (消毒 sanitizer)
refer : https://www.intricatecloud.io/2019/10/using-angular-innerhtml-to-display-user-generated-cont ...
- Spring —— 整合JUnit
整合JUnit
- 系统编程-进程-wait、waitpid和WIFEXITED系列宏 超级详解
1. wait.waitpid 函数简介 补充:对于waitpid,如果返回值为0,表示指定去等待的子进程尚未结束. 该系列宏的使用方法展示: PART1 实验思路: 使用wait系统调用让父进程给 ...
- for循环遍历的盗版笔记
遍历一个List有如下几种方法 5 6 是 java8 增强for循环底层由Iterator实现 增强for的出现时替代迭代器的,所以在遍历集合或者遍历数组就可以使用增强for去完成 增强for循 ...
- Android中VSYNC代表什么
在 Android 中,VSYNC(Vertical Synchronization)是一个垂直同步信号,用于协调显示刷新和绘图操作.VSYNC 信号的主要作用是控制屏幕刷新频率与图形渲染的同步,以确 ...
- vue项目中的package.json的private选项的作用
{ "name": "项目名称", "description": "描述", "version": ...