flutter?TabBarView?動態(tài)添加刪除頁面的示例代碼
在TabBarView 動態(tài)添加頁面后刪除其中一個頁面會導致后面的頁面狀態(tài)錯誤或刪除的頁面不正確。出現(xiàn)這種問題是由于創(chuàng)建子頁面時沒有為子頁面設置唯一的key導致的。下面是錯誤的代碼:
void addNewPage() { _pageCount++; setState(() { String title = "頁面$_pageCount"; PageContent page = PageContent(data: title, pageId: _pageCount,); PageData data = PageData(data: title, pageId: _pageCount, content: page); listPages.add(data); nowIndex = listPages.length -1; resetTabController(); }); }
如上面的代碼所示, 在創(chuàng)建PageContent 組件時如果沒有指定全局唯一的key, 關閉頁面時就會導致后面的頁面被再次build或刪除錯誤的頁面,正確的代碼如下
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(); }); }
指定了全局唯一key后在刪除子頁面,后續(xù)頁面就可以正常顯示。
所有代碼如下
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 動態(tài)添加刪除頁面的文章就介紹到這了,更多相關flutter TabBarView 刪除頁面內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot創(chuàng)建監(jiān)聽器的方法示例
在Java中,監(jiān)聽器(Listener)是一種設計模式,它允許對象在 特定事件 發(fā)生時 自動執(zhí)行某些操作,這種設計模式通常用于實現(xiàn) 發(fā)布-訂閱模型,本文給大家介紹了SpringBoot創(chuàng)建監(jiān)聽器的方法示例,感興趣的通過可以參考一下2024-04-04Spring中的@PropertySource注解源碼詳細解析
這篇文章主要介紹了Spring中的@PropertySource注解源碼詳細解析,@PropertySource注解,標注在配置類@Configuration上面,下面主要分析一下@PropertySource注解的處理過程,也就是怎么把配置信息從.properies文件放到environment中的,需要的朋友可以參考下2024-01-01SpringBoot讀取Resource目錄下文件的四種方式總結
在Spring?Boot項目中,經常需要獲取resources目錄下的文件,這些文件可以包括配置文件、模板文件、靜態(tài)資源等,本文將介紹四種常用的方法來獲取resources目錄下的文件,需要的朋友可以參考下2023-08-08