DefaultTabController
DefaultTabController 是一个InheritedWidget(继承组件) ,它会在组件树中向下注入一个 TabController。
省去手动创建 TabController、混入 SingleTickerProviderStateMixin、手动dispose释放控制器的样板代码。
less
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MaterialApp(home: HomePage()));
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
initialIndex: 0,
child: Scaffold(
appBar: AppBar(
title: const Text("顶部Tab"),
bottom: const TabBar(
tabs: [
Tab(text: "标签1"),
Tab(text: "标签2"),
Tab(text: "标签3"),
],
),
),
body: const TabBarView(
children: [
Center(child: Text("页面1")),
Center(child: Text("页面2")),
Center(child: Text("页面3")),
],
),
),
);
}
}
无法监听页面切换。只能主动控制跳转。
ini
TabController? controller = DefaultTabController.of(context); // 跳转到第2个tab(下标从0开始) controller?.animateTo(1);
TabController
可以监听切面切换。更灵活。
less
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MaterialApp(home: TabPage()));
}
class TabPage extends StatefulWidget {
const TabPage({super.key});
@override
State<TabPage> createState() => _TabPageState();
}
class _TabPageState extends State<TabPage> with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
//初始化控制器,length必须等于tab数量
_tabController = TabController(length: 3, vsync: this, initialIndex: 0);
//监听切换
_tabController.addListener(() {
if (!_tabController.indexIsChanging) {
debugPrint("选中下标 = ${_tabController.index}");
}
});
_tabController.animation?.addListener(() {
double value = _tabController.animation!.value;
print("滑动进度:$value");
});
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("TabController演示"),
bottom: TabBar(
controller: _tabController, //绑定控制器
tabs: const [
Tab(text: "标签1"),
Tab(text: "标签2"),
Tab(text: "标签3"),
],
),
),
body: TabBarView(
controller: _tabController, //同一个控制器
children: [
Center(
child: Column(
children: [
Text("第一个页面"),
GestureDetector(
child: Text("跳转到第二页"),
onTap: () {
_tabController.animateTo(1);
},
),
GestureDetector(
child: Text("跳转到第三页"),
onTap: () {
_tabController.animateTo(
2,
duration: Duration(milliseconds: 300), //动画时长
curve: Curves.ease,
);
},
),
],
),
),
Center(child: Text("页面2")),
Center(child: Text("页面3")),
],
),
);
}
}
自定义底部Tab
- 底部tab用BottomNavigationBar。
- 不同tab对应的内容用IndexedStack,记得用SafeArea。
less
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(getRootWidget());
}
Widget getRootWidget() {
return MaterialApp(routes: {"/": (context) => MainPage()}, initialRoute: "/");
}
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
final List<Map<String, dynamic>> _tabbarItemSettings = [ {"icon": Icons.home_outlined, "active_icon": Icons.home, "title": "首页"}, { "icon": Icons.category_outlined, "active_icon": Icons.category, "title": "分类", }, { "icon": Icons.shopping_cart_outlined, "active_icon": Icons.shopping_cart, "title": "购物车", }, {"icon": Icons.person_outline, "active_icon": Icons.person, "title": "我的"}, ];
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: IndexedStack(
index: _currentIndex,
children: List.generate(4, (int index) {
return Container(
color: Colors.blueAccent,
child: Center(
child: Text("${_tabbarItemSettings[index]["title"]}"),
),
);
}),
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (int index) {
setState(() {
_currentIndex = index;
});
},
selectedItemColor: Colors.green,
unselectedItemColor: Colors.cyan,
showUnselectedLabels: true,
items: List.generate(4, (int index) {
return BottomNavigationBarItem(
icon: Icon(_tabbarItemSettings[index]["icon"]! as IconData?),
activeIcon: Icon(
_tabbarItemSettings[index]["active_icon"]! as IconData?,
),
label: _tabbarItemSettings[index]["title"],
);
}),
),
);
}
}