Location>code7788 >text

flutter TabBarView Dynamic Add/Remove Pages

Popularity:122 ℃/2024-11-14 17:05:22
import 'package:flutter/'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( colorScheme: (seedColor: ), primaryColor: , scaffoldBackgroundColor: , dialogBackgroundColor: , 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: , vsync: this); }); } @override bool get wantKeepAlive => true; void addNewPage() { _pageCount++; setState(() { String title = "page $_pageCount"; PageContent page = PageContent(data: title, pageId: _pageCount, key: ValueKey(title),); PageData data = PageData(data: title, pageId: _pageCount, content: page); (data); nowIndex = -1; resetTabController(); }); } //Select a page void onSelectPage(PageData page) { //The page has been selected int selIndex = 0; for (int index = 0; index < ; index++) { PageData item = listPages[index]; if ( == ) { selIndex = index; break; } } //No changes to selected pages if (selIndex == nowIndex) { return; } setState(() { nowIndex = selIndex; tabController?.animateTo(nowIndex); }); } //close page void onClosePage(PageData data) { int closedIndex = 0; for (int index = 0; index < ; index++) { PageData item = listPages[index]; if ( == ) { closedIndex = index; break; } } setState(() { (closedIndex); if (closedIndex <= nowIndex) { nowIndex--; } if (nowIndex < 0) { nowIndex = 0; } else if (nowIndex >= ) { nowIndex = -1; } resetTabController(); }); } void resetTabController() { if (tabController?.length != ) { tabController?.dispose(); tabController = TabController( length: , vsync: this, initialIndex: nowIndex, ); } } @override Widget build(BuildContext context) { super.build(context); return Scaffold( appBar: AppBar( bottom: PreferredSize( preferredSize: const (40), child: TabBar( controller: tabController, tabs: ((item) => Tab(child: TitleBarItem(data: item, closeCallback: (data) => onClosePage(data)),)).toList(), ), ), ), body: TabBarView( controller: tabController, children: ((item) => ).toList(), ), floatingActionButton: FloatingActionButton( onPressed: () => addNewPage(), tooltip: 'Increment', child: const Icon(), ), ); } } 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("Initialize page content controller:${}"); setState(() { for (int index = 0; index <= 30; index++) { ("${} - $index"); } }); super.initState(); } @override void dispose() { print("Release page content controller:${}"); super.dispose(); } @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { print("Build page ${}"); return Container( alignment: , child: Column( children: [ Text(), Expanded( child: ( itemExtent: 30, itemCount: , 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()), IconButton( onPressed: () => closeCallback(data), icon: const Icon()) ], ), ); } }