Flutter 中的 TabBarView 深度解析
在许多应用程序中,我们经常会看到顶部有多个标签的布局。每个标签在被选中时,下方会显示与之对应的内容。这种布局在 UI 设计中被广泛应用,因为它可以在有限的空间内提供大量的信息。而在 Flutter 中,我们可以通过使用 TabBar
和 TabBarView
来轻松实现这种布局。
一、基本用法
TabBarView的基本概念。
TabBarView是由两个部分组成的:TabBar
和TabBarView
本身。
TabBar通常用于显示选项卡
,而TabBarView用于显示与选项卡相关联的内容
。- 当用户点击选项卡时,TabBarView将显示与选中选项卡相关联的内容。
文字总是枯燥的,来一个最简单的示例代码吧
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final List<Tab> myTabs = <Tab>[
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: myTabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('TabBarView Example'),
bottom: TabBar(
tabs: myTabs,
),
),
body: TabBarView(
children: <Widget>[
Center(
child: Text('Tab 1'),
),
Center(
child: Text('Tab 2'),
),
Center(
child: Text('Tab 3'),
),
],
),
),
);
}
}
在这个示例中,我们定义了三个选项卡,每个选项卡都有一个标题。然后,在Scaffold部件中,我们定义了一个AppBar,它包含了TabBar。在TabBarView中,我们定义了三个子部件,分别与三个选项卡相关联。
(首先定义了一个
DefaultTabController
,这是控制TabBar
和TabBarView
交互的必要组件。在DefaultTabController
中,我们设置了length
参数为3,这意味着我们将拥有3个标签。)
基本的介绍完了,让我们来进阶一下。
二、进阶:嵌套,动态显示,图文选项卡
嵌套TabBarView
有时,您可能需要在一个TabBarView中嵌套另一个TabBarView。例如,如果您正在开发一个应用程序,其中每个选项卡都需要进一步细分为子选项卡,那么您可能需要使用嵌套的TabBarView。
以下是一个示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
tabBarTheme: TabBarTheme(
labelColor: Colors.black,
unselectedLabelColor: Colors.black.withOpacity(0.6),
),
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final List<Tab> myTabs = <Tab>[
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
];
final List<Tab> myNestedTabs = <Tab>[
Tab(text: 'Nested Tab 1'),
Tab(text: 'Nested Tab 2'),
Tab(text: 'Nested Tab 3'),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: myTabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('TabBarView Example'),
bottom: TabBar(
tabs: myTabs,
),
),
body: TabBarView(
children: <Widget>[
DefaultTabController(
length: myNestedTabs.length,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
toolbarHeight: 0, // 将内嵌的 AppBar 的高度设置为 0,以隐藏它
bottom: TabBar(
tabs: myNestedTabs,
),
),
body: TabBarView(
children: <Widget>[
Center(
child: Text('嵌套 Tab 1'),
),
Center(
child: Text('嵌套 Tab 2'),
),
Center(
child: Text('嵌套 Tab 3'),
),
],
),
),
),
Center(
child: Text('Tab 2'),
),
Center(
child: Text('Tab 3'),
),
],
),
),
);
}
}
在这个示例中,我们定义了一个嵌套的TabBarView。在第一个选项卡中,我们定义了一个包含嵌套TabBar和TabBarView的Column部件。TabBar用于显示嵌套选项卡,而TabBarView用于显示与选中的嵌套选项卡相关联的内容。注意,在第一个TabBarView中,我们使用了一个Expanded部件,以便内容可以填充整个可用空间。
动态TabBar和TabBarView
有时,您可能需要根据应用程序的状态动态地生成TabBar和TabBarView。例如,如果您正在开发一个电子商务应用程序,其中每个选项卡都需要动态显示商品类别,那么您可能需要动态生成TabBar和TabBarView。以下是一个示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> categories = ['Category 1', 'Category 2', 'Category 3'];
List<Widget> tabs = [];
@override
void initState() {
super.initState();
for (String category in categories) {
tabs.add(
Tab(
text: category,
),
);
}
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('TabBarView Example'),
bottom: TabBar(
tabs: tabs,
),
),
body: TabBarView(
children: categories.map((category) {
return Center(
child: Text(category),
);
}).toList(),
),
),
);
}
}
在这个示例中,我们定义了一个状态类MyHomePage,并在其中定义了一个categories列表和一个tabs列表。
在initState方法中,我们遍历categories列表,并为每个类别创建一个Tab部件,并将其添加到tabs列表中。
然后,在build方法中,我们使用tabs列表来动态生成TabBar,使用categories列表来动态生成TabBarView
。
自定义TabBar和TabBarView
如果标准的TabBar和TabBarView不符合您的需求,您可以使用Flutter的自定义部件来创建自定义TabBar和TabBarView。以下是一个示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final List<String> categories = ['Category 1', 'Category 2', 'Category 3'];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: categories.length,
child: Scaffold(
appBar: AppBar(
title: Text('TabBarView Example'),
bottom: PreferredSize(
preferredSize: Size.fromHeight(48.0),
child: Container(
color: Colors.white,
child: TabBar(
isScrollable: true,
indicatorColor: Colors.red,
labelColor: Colors.red,
unselectedLabelColor: Colors.black,
tabs: categories.map((category) {
return Tab(
child: Text(
category.toUpperCase(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
);
}).toList(),
),
),
),
),
body: TabBarView(
children: categories.map((category) {
return Center(
child: Text(category),
);
}).toList(),
),
),
);
}
}
在这个示例中,我们使用了自定义TabBar和TabBarView。在TabBar中,我们设置了一些属性,如isScrollable、indicatorColor和labelColor,以自定义选项卡的外观。在TabBar中,我们还使用了一个自定义的Text部件,以自定义选项卡的文本样式。在TabBarView中,我们使用categories列表来动态生成选项卡的内容。
图片选项卡
除了标准的文本选项卡,Flutter的TabBarView还支持图片选项卡
图文选项卡
要创建图片选项卡,您可以使用Tab部件的icon属性。以下是一个示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final List<Tab> myTabs = <Tab>[
Tab(
icon: Icon(Icons.home),
),
Tab(
icon: Icon(Icons.search),
),
Tab(
icon: Icon(Icons.person),
),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: myTabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('TabBarView Example'),
bottom: TabBar(
tabs: myTabs,
),
),
body: TabBarView(
children: <Widget>[
Center(
child: Text('Home'),
),
Center(
child: Text('Search'),
),
Center(
child: Text('Profile'),
),
],
),
),
);
}
}
在这个示例中,我们定义了三个选项卡,并在每个选项卡中添加了一个图标。要添加图标,我们只需要在Tab部件中使用icon属性,并将其设置为一个Icon部件。在TabBarView中,我们定义了与每个选项卡相关联的内容。
图片加文字的tab
上面的感觉不够好用
让我们来丰富一下
- 图标加文本
- 设置间距
- 设置文本大小,图标大小
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
final List<Tab> myTabs = <Tab>[
Tab(
child: Column(
children: [
Icon(
Icons.home,
size: 24, // 调整图标大小
),
SizedBox(width: 8.0), // 调整文本和图标之间的距离
Text(
'Home',
style: TextStyle(fontSize: 16.0), // 调整文本大小
),
],
),
),
Tab(
child: Column(
children: [
Icon(
Icons.person,
size: 24, // 调整图标大小
),
SizedBox(width: 8.0), // 调整文本和图标之间的距离
Text(
'Profile',
style: TextStyle(fontSize: 16.0), // 调整文本大小
),
],
),
),
];
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: widget.myTabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('TabBarView Example'),
bottom: TabBar(
tabs: widget.myTabs,
),
),
body: TabBarView(
children: <Widget>[
Center(
child: Text('Home tab content'),
),
Center(
child: Text('Profile tab content'),
),
],
),
),
);
}
}
圆角指示器
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
final List<String> _tabs = ["Tab 1", "Tab 2", "Tab 3"];
@override
void initState() {
super.initState();
_tabController = TabController(length: _tabs.length, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TabBar Example'),
backgroundColor: Colors.transparent, // TabBar 的背景颜色
elevation: 0, // 去除阴影
bottom: PreferredSize(
preferredSize: Size.fromHeight(48.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
color: Colors.blue, // TabBar 背景颜色
),
child: TabBar(
controller: _tabController,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(50.0), // 指示器圆角半径
color: Colors.white60, // 选中 Tab 的背景颜色
),
unselectedLabelColor: Colors.white.withOpacity(0.7),
labelColor: Colors.blue,
tabs: _tabs.map((String name) => Tab(text: name)).toList(),
),
),
),
),
body: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50.0),
topRight: Radius.circular(50.0),
),
color: Colors.white,
),
child: TabBarView(
controller: _tabController,
children: <Widget>[
Container(
color: Colors.white,
child: Center(
child: Text('Tab 1 Content'),
),
),
Container(
color: Colors.white,
child: Center(
child: Text('Tab 2 Content'),
),
),
Container(
color: Colors.white,
child: Center(
child: Text('Tab 3 Content'),
),
),
],
),
),
);
}
}
三、TabBarView结合PageView
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
final List<String> _tabs = ["选项卡一", "选项卡二", "选项卡三"]; // 选项卡的标题
@override
void initState() {
super.initState();
_tabController = TabController(length: _tabs.length, vsync: this); // 创建 TabController
}
@override
void dispose() {
_tabController.dispose(); // 释放 TabController
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TabBarView and PageView 示例'),
),
body: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.white, // TabBar 的背景颜色
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
), // 设置 TabBar 的边角为圆角
),
child: TabBar(
controller: _tabController, // 设置 TabBar 的控制器为 _tabController
indicatorColor: Colors.blue, // 设置指示器的颜色
labelColor: Colors.blue, // 设置选中的标签的文本颜色
unselectedLabelColor: Colors.grey, // 设置未选中的标签的文本颜色
tabs: _tabs.map((String name) => Tab(text: name)).toList(), // 创建选项卡
),
),
Expanded(
child: TabBarView(
controller: _tabController, // 设置 TabBarView 的控制器为 _tabController
children: <Widget>[
PageView(
children: <Widget>[
Container(
color: Colors.red, // 页面的背景颜色
child: Center(
child: Text('选项卡一的页面一'),
),
),
Container(
color: Colors.green, // 页面的背景颜色
child: Center(
child: Text('选项卡一的页面二'),
),
),
Container(
color: Colors.blue, // 页面的背景颜色
child: Center(
child: Text('选项卡一的页面三'),
),
),
],
),
PageView(
children: <Widget>[
Container(
color: Colors.pink, // 页面的背景颜色
child: Center(
child: Text('选项卡二的页面一'),
),
),
Container(
color: Colors.orange, // 页面的背景颜色
child: Center(
child: Text('选项卡二的页面二'),
),
),
Container(
color: Colors.purple, // 页面的背景颜色
child: Center(
child: Text('选项卡二的页面三'),
),
),
],
),
PageView(
children: <Widget>[
Container(
color: Colors.teal, // 页面的背景颜色
child: Center(
child: Text('选项卡三的页面一'),
),
),
Container(
color: Colors.yellow, // 页面的背景颜色
child: Center(
child: Text('选项卡三的页面二'),
),
),
Container(
color: Colors.brown, // 页面的背景颜色
child: Center(
child: Text('选项卡三的页面三'),
),
),
],
),
],
),
),
],
),
);
}
}
我们创建了一个带有三个选项卡的 TabBar
。每个选项卡中都有一个 PageView
,其中包含三个页面。
TabBarView的最佳实践和性能优化
懒加载页面
- 懒加载页面:为了提高性能,您可以使用
PageView.builder
或ListView.builder
等构建器方法来动态创建页面,以便在需要时才加载页面。这样可以避免同时加载所有页面所带来的性能开销。
设置默认选中的页面、滚动到指定页面等。
- 使用控制器:使用
TabController
或PageController
等控制器可以让您更好地控制TabBarView
和PageView
的行为,例如设置默认选中的页面、滚动到指定页面等。
避免重复渲染
- 避免重复渲染:在
TabBarView
和PageView
中,每次切换选项卡都会触发整个页面的重绘,这可能会导致性能问题。为了避免这种情况,您可以使用AutomaticKeepAliveClientMixin
来保持页面的状态,并避免重复渲染。
预加载页面
-
预加载页面:如果您需要在用户切换选项卡时立即显示页面,而不是等待页面加载完成,您可以使用
PageView
的preloadPagesCount
属性来预加载页面。这会在用户切换选项卡时提前加载页面,以提高用户体验。 -
优化页面布局:对于每个页面,尽可能使用简单的布局和组件,以避免复杂的布局和组件所带来的性能开销。您可以使用
const
构造函数来创建静态的组件,以避免不必要的重绘。
避免使用过多的页面
- 避免使用过多的页面:在
TabBarView
中,如果您有太多的选项卡和页面,可能会导致性能问题。为了避免这种情况,您可以使用ListView
或GridView
等组件来实现分页效果,以减少页面的数量。
禁用滑动手势
- 禁用滑动手势:如果您不需要用户通过滑动手势来切换页面,可以将
PageView
的physics
属性设置为NeverScrollableScrollPhysics()
来禁用滑动手势。这可以减少用户误操作和提高性能。
这篇,就到这里吧