今天看群里有人遇到一个这样的问题,有多个tab,点击可以关闭tab,关闭后其他页面不要重新build,实现下面这种选项卡关闭的效果
效果图:
当点击Tab0 关闭按钮的时候
flutter: 点击了Tab0 Remove
flutter: HomeSubPage0 Dispose
home_sub_pages.dart
import 'package:flutter/material.dart';
class HomeSubPage extends StatefulWidget {
final int index;
const HomeSubPage({super.key, required this.index});
@override
State<HomeSubPage> createState() => _HomeSubPageState();
}
class _HomeSubPageState extends State<HomeSubPage>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
print('HomeSubPage${widget.index} Build');
return Center(
child: Text('Page ${widget.index}'),
);
}
@override
void dispose() {
super.dispose();
print("HomeSubPage${widget.index} Dispose");
}
@override
bool get wantKeepAlive => true;
}
home_provider.dart
import 'package:flutter/material.dart';
import 'home_sub_pages.dart';
class _PageItem {
final Widget page;
final String tab;
_PageItem({required this.page, required this.tab});
}
class HomeProvider extends ChangeNotifier {
late final List<_PageItem> items;
int currentIndex = 0;
int lastIndex = 2;
late TabController tabController;
late TickerProvider vsync;
HomeProvider() {
// 使用固定的字符串作为key
// 最重要的就是key
items = [
_PageItem(
page: const HomeSubPage(
index: 0,
key: ValueKey("page-0"),
),
tab: "Tab 0"),
_PageItem(
page: const HomeSubPage(
index: 1,
key: ValueKey("page-1"),
),
tab: "Tab 1"),
_PageItem(
page: const HomeSubPage(
index: 2,
key: ValueKey("page-2"),
),
tab: "Tab 2"),
];
}
void setCurrentIndex(int index) {
currentIndex = index;
notifyListeners();
}
void removeSubPage(int index) {
if (items.length == 1) return;
if (index < 0 || index >= items.length) return;
// 计算新的currentIndex
if (currentIndex >= items.length - 1) {
currentIndex = items.length - 2;
}
items.removeAt(index);
resetTabController();
notifyListeners();
}
void addSubPage() {
lastIndex++;
var page = HomeSubPage(
index: lastIndex,
key: ValueKey("page-${lastIndex}"),
);
items.add(_PageItem(page: page, tab: "Tab${lastIndex}"));
resetTabController();
notifyListeners();
}
void resetTabController() {
if (tabController.length != items.length) {
tabController.dispose();
tabController = TabController(
length: items.length,
vsync: vsync,
initialIndex: currentIndex,
);
}
}
}
home_page.dart
import 'package:flutter/material.dart';
import 'package:login_provider_demo/pages/home/home_provider.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
@override
void initState() {
final provider = context.read<HomeProvider>();
provider.vsync = this;
provider.tabController =
TabController(length: provider.items.length, vsync: this);
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
title: const Text("Home"),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(40),
child: Consumer<HomeProvider>(
builder: (context, provider, child) {
return TabBar(
controller: provider.tabController,
onTap: (value) {
provider.tabController.index = value;
context.read<HomeProvider>().setCurrentIndex(value);
},
tabs: provider.items.asMap().keys.map(
(index) {
final e = provider.items[index];
return Tab(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Text(
e.tab,
textAlign: TextAlign.center,
)),
SizedBox(
width: 30,
child: IconButton(
onPressed: () {
print("点击了Tab${index} Remove");
provider.removeSubPage(index);
},
icon: const Icon(Icons.clear)),
)
],
),
);
},
).toList());
},
),
),
),
body: Consumer<HomeProvider>(builder: (context, provider, child) {
return TabBarView(
controller: provider.tabController,
children: provider.items.map((e) => e.page).toList());
}),
floatingActionButton: FloatingActionButton(
onPressed: () {
HomeProvider homeProvider = context.read<HomeProvider>();
homeProvider.addSubPage();
},
child: const Icon(Icons.add),
),
);
}
@override
void dispose() {
// tabController.removeListener(_handleTabSelection);
// tabController.dispose();
super.dispose();
}
@override
bool get wantKeepAlive => true;
}
main.dart
import 'package:flutter/material.dart';
import 'home_page.dart';
import 'home_provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: ChangeNotifierProvider(
create: (_) => HomeProvider(),
child: const HomePage(),
));
}
}