# Flutter教程 - 5 页面Scaffold
在之前创建页面的时候,使用的是 Scaffold
创建页面, Scaffold
可以设置标题、body
、悬浮按钮,还可以设置tabbar等,下面就围绕 Scaffold
,讲解一下页面相关的设置。
# 5.1 底部导航栏
先看效果,当点击下面的tabbar的时候,切换显示不同的页面:
下面开始实现上面的效果。
首先我们需要创建三个tabbar的页面,用于切换的时候显示,这里使用 StatefulWidget
来创建三个页面。
home.dart
import 'package:flutter/material.dart';
/// 主页tab页面
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Widget build(BuildContext context) {
return const Center(child: Text("主页"));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在 body
部分显示文字,用于区分哪个页面,下面定义的两个页面也是一样的。
message.dart
import 'package:flutter/material.dart';
/// 消息tab页面
class MessagePage extends StatefulWidget {
const MessagePage({Key? key}) : super(key: key);
State<MessagePage> createState() => _MessagePageState();
}
class _MessagePageState extends State<MessagePage> {
Widget build(BuildContext context) {
return const Center(child: Text("消息"));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
my.dart
import 'package:flutter/material.dart';
/// 我的tab页面
class MyPage extends StatefulWidget {
const MyPage({Key? key}) : super(key: key);
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
Widget build(BuildContext context) {
return const Center(child: Text("我的"));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
下面在 main.dart
引入三个页面,然后在点击tabbar的时候切换。
实现代码:
import 'package:flutter/material.dart';
// 1. 首先引入三个页面
import 'tab/home.dart';
import 'tab/message.dart';
import 'tab/my.dart';
void main() => runApp(const MyApp());
/// App根Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
// 2.创建三个页面
final List<Widget> _tabPages = const [
HomePage(),
MessagePage(),
MyPage()
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tab Demo'),
),
// 3.在这里通过_currentIndex获取指定的page用于显示
body: _tabPages[_currentIndex],
// ------------------- 创建tabbar ---------------------
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex, // 设置当前选中的tab
fixedColor: Colors.green, // 设置tab选中的颜色
iconSize: 25, // 设置图标的大小
type: BottomNavigationBarType.fixed, // 如果tab大于3个,则需要设置该属性才能显示
items: const [ // 设置tabbar按钮
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: "首页"
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: "消息"
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
label: "我的"
),
],
onTap: (index) { // 点击tabbar按钮的时候触发
// 4.当点击tabbar的时候修改_currentIndex,从而让body部分修改指向的页面,实现切换页面
setState(() {
_currentIndex = index;
});
},
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
- 首先引入三个页面;
- 创建三个页面;
- 在
body
部分,通过_tabPages[_currentIndex]
指定显示的页面,然后当点击tabbar的时候,修改_currentIndex
的值,实现页面的切换; - 点击tabbar的时候,会触发
onTap
方法,传入的参数是当前点击的tabbar的index
,将index
赋值给_currentIndex
,实现页面切换。
# 5.2 悬浮按钮
在前面Flutter初体验中,实现计数器功能的时候,已经简单使用了悬浮按钮,下面使用悬浮按钮,结合tabbar实现下面的效果。
在tabbar中添加了5个按钮,中间的按钮的图标比较大。这个该如何实现呢?
其实这里是使用了悬浮按钮 FloatingActionButton
遮挡了中间的tabbar的按钮而已。
所以这里涉及到一个问题,就是如何设置悬浮按钮 FloatingActionButton
的位置。
下面展示代码实现:
首先定义了5个tabbar按钮对应的页面,在前面tabbar的基础上,再定义两个页面。
focus.dart
import 'package:flutter/material.dart';
/// 主页tab页面
class FocusPage extends StatefulWidget {
const FocusPage({Key? key}) : super(key: key);
State<FocusPage> createState() => _FocusPageState();
}
class _FocusPageState extends State<FocusPage> {
Widget build(BuildContext context) {
return const Center(child: Text("关注"));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
publish.dart
import 'package:flutter/material.dart';
/// 主页tab页面
class PublishPage extends StatefulWidget {
const PublishPage({Key? key}) : super(key: key);
State<PublishPage> createState() => _PublishPageState();
}
class _PublishPageState extends State<PublishPage> {
Widget build(BuildContext context) {
return const Center(child: Text("发布"));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
下面主要是tabbar的实现:
import 'package:flutter/material.dart';
// 首先引入页面
import 'tab/home.dart';
import 'tab/message.dart';
import 'tab/my.dart';
import 'tab/focus.dart';
import 'tab/publish.dart';
void main() => runApp(const MyApp());
/// App根Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
// 创建页面
final List<Widget> _tabPages = const [
HomePage(),
FocusPage(),
PublishPage(),
MessagePage(),
MyPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tab Demo'),
),
// 在这里通过_currentIndex获取指定的page用于显示
body: _tabPages[_currentIndex],
// ------------------- 创建tabbar ---------------------
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex, // 设置当前选中的tab
fixedColor: Colors.green, // 设置tab选中的颜色
iconSize: 25, // 设置图标的大小
type: BottomNavigationBarType.fixed, // 如果tab大于3个,则需要设置该属性才能显示
items: const [ // 设置tabbar的5个按钮
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: "首页"
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: "关注"
),
BottomNavigationBarItem(
icon: Icon(Icons.add),
label: "发布"
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: "消息"
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
label: "我的"
),
],
onTap: (index) { // 点击tabbar按钮的时候触发
// 当点击tabbar的时候修改_currentIndex,从而让body部分修改指向的页面,实现切换页面
setState(() {
_currentIndex = index;
});
}
),
floatingActionButton: Container( // 这里使用Container包裹,是为了调整FloatingActionButton的大小和微调一下按钮位置
height: 60,
width: 60,
padding: const EdgeInsets.all(2),
margin: const EdgeInsets.only(top:6),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30)
),
child: FloatingActionButton(
backgroundColor: _currentIndex == 2 ? Colors.green : Colors.blue, // 这里主要是设置中间按钮的颜色,当选中了tabbar中间的按钮的时候,则将悬浮按钮的颜色改为绿色
onPressed: () {
// 当点击悬浮按钮的时候,选中发布页面
setState(() {
_currentIndex = 2;
});
},
child: const Icon(Icons.add),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, // 设置悬浮按钮的位置
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
这里有几个关键的点:
- 首先设置tabbar的5个按钮和对应的页面;
- 使用
floatingActionButton
属性设置悬浮按钮,因为floatingActionButton
没有属性设置大小,所以使用Container
包裹floatingActionButton
,用于设置悬浮按钮的大小,同时使用margin属性微调一下悬浮按钮的位置; - 设置悬浮按钮的位置,主要通过
floatingActionButtonLocation
属性来设置,可以设置居上、居中还是居右; - 还有一个点就是点击了悬浮按钮的时候,要修改
_currentIndex
,这样才能实现页面的切换,另外在设置悬浮按钮颜色的时候,也是通过_currentIndex
属性来判断的,如果当前点击的是中间的按钮,则将按钮设置为红色。
悬浮按钮 FloatingActionButton
还有一些属性,需要的时候可以查阅一下:
属性 | 类型 | 作用 |
---|---|---|
child | Widget | 在按钮上显示的子组件,通常是一个 Icon 。 |
tooltip | String | 长按按钮时显示的提示文本。 |
backgroundColor | Color | 按钮的背景颜色。 |
foregroundColor | Color | 按钮的前景颜色,通常用于子组件的颜色。 |
elevation | double | 按钮的阴影深度。 |
highlightElevation | double | 按钮被按下时的阴影深度。 |
disabledElevation | double | 按钮被禁用时的阴影深度。 |
onPressed | VoidCallback | 按钮被点击时触发的回调函数。 |
mini | bool | 是否显示小型的浮动操作按钮。 |
shape | ShapeBorder | 按钮的形状,可以是圆形或自定义形状。 |
clipBehavior | Clip | 裁剪行为,控制按钮的子组件是否被裁剪到按钮的边界内。 |
focusColor | Color | 按钮获取焦点时的颜色。 |
hoverColor | Color | 鼠标悬停在按钮上时的颜色。 |
focusElevation | double | 按钮获取焦点时的阴影深度。 |
hoverElevation | double | 鼠标悬停在按钮上时的阴影深度。 |
splashColor | Color | 点击按钮时溅出的颜色。 |
focusNode | FocusNode | 用于控制按钮的焦点状态。 |
autofocus | bool | 是否自动获取焦点。 |
# 5.3 收缩侧边栏
在实际开发的过程中,我们经常会使用到可以展开和隐藏的侧边栏,在 Scaffffold
组件里,可以通过 drawer
参数可以定义左侧边栏,通过 endDrawer
可以定义右侧边栏。侧边栏默认是隐藏的,我们可以通过手指滑动显示侧边栏,也可以通过点击按钮显示侧边栏。
# 1 实现侧边栏
下面展示一下侧边栏的实现。
实现代码:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// App根Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Drawer Demo'),
),
body: const Center(
child: Text("Doubi")
),
// ------------添加左侧边栏---------------
drawer: const Drawer(
child: Text('左侧边栏'),
),
// ------------添加右侧边栏---------------
endDrawer: const Drawer(
child: Text('右侧边栏')
)
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
只需要在 Scaffold
中添加 drawer
属性,就可以添加左侧边栏,添加 endDrawer
就可以添加右侧边栏。
添加了 drawer
和 endDrawer
属性后,导航栏会显示打开侧边栏的按钮,点击按钮就可以显示侧边栏,通过在屏幕边缘滑动屏幕也可以显示侧边栏。
实现效果:
如果想在侧边栏布局,只需要在 Drawer
组件的 child
属性中进行布局即可。
# 2 UserAccountsDrawerHeader
Flutter
也提供了内置的侧边栏布局的组件,例如 UserAccountsDrawerHeader
,下面展示一下 UserAccountsDrawerHeader
的使用。
先看布局效果:
UserAccountsDrawerHeader
只是侧边栏上面的部分,下面的 收藏 和 系统设置选项是另外的布局。
实现代码:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// App根Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Drawer Demo'),
),
body: const Center(child: Text("Doubi")),
// ------------添加左侧边栏---------------
drawer: Drawer(
child: Column(
children: const [
// ------------UserAccountsDrawerHeader 的使用---------------
UserAccountsDrawerHeader(
accountName: Text("逗比笔记"),
accountEmail: Text("www.doubibiji.com"),
currentAccountPicture: CircleAvatar( // 设置一个圆形的头像
backgroundImage:
NetworkImage("http://doubibiji.com/open-assets/img/telangpu.jpg"),
),
decoration: BoxDecoration( // 设置背景,也可以设置背景图片
color: Colors.lightBlue
),
),
// ------------继续添加两个选项---------------
ListTile(
title: Text("收藏"),
leading: CircleAvatar(child: Icon(Icons.favorite)),
),
Divider(),
ListTile(
title: Text("系统设置"),
leading: CircleAvatar(child: Icon(Icons.settings)),
)
],
)));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
通过上面的代码,可以看到 UserAccountsDrawerHeader
提供了设置账号、邮箱、头像、背景等属性,可以根据需要进行设置。
下表列出了 UserAccountsDrawerHeader
组件的常用属性以及它们的作用:
属性 | 类型 | 作用 |
---|---|---|
currentAccountPicture | Widget | 当前用户的头像或图片。 |
otherAccountsPictures | List<Widget> | 其他用户的头像或图片。 |
accountName | Widget | 当前用户的名称或标题。 |
accountEmail | Widget | 当前用户的电子邮件地址。 |
onDetailsPressed | VoidCallback? | 当用户详细信息被点击时触发的回调函数。 |
arrowColor | Color | 右侧箭头的颜色。 |
margin | EdgeInsetsGeometry? | 组件的外边距。 |
decoration | Decoration? | 组件的装饰样式,例如背景、阴影等。 |
请注意,这里没有使用 将属性名括起来,这样可能会使属性与普通文本混淆。在实际编程和文档编写中,使用
来标识属性名是一种更为清晰和规范的做法。
# 3 DrawerHeader
如果 UserAccountsDrawerHeader
不满足自己的需求,可以自己进行自己的布局。
在进行自己布局的时候,我们还可以借助一个 DrawerHeader
组件,DrawerHeader
是 Flutter 中的一个组件,用于在侧边抽屉(Drawer)中创建一个头部区域。DrawerHeader
组件通常作为 Drawer
组件的子组件之一,用于增加抽屉的内容和样式。
举个栗子:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// App根Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Drawer Demo'),
),
body: const Center(child: Text("Doubi")),
// ------------添加左侧边栏---------------
drawer: Drawer(
child: Column(
children: [
// ------------DrawerHeader 的使用---------------
DrawerHeader(
decoration: const BoxDecoration(
color: Colors.blue, // 设置背景颜色
),
child: SizedBox(
width: double.infinity,
child: Column(
children: [
Text('我是一个头部')
],
),
)
),
// ------------继续添加两个选项---------------
const ListTile(
title: Text("收藏"),
leading: CircleAvatar(child: Icon(Icons.favorite)),
),
const Divider(),
const ListTile(
title: Text("系统设置"),
leading: CircleAvatar(child: Icon(Icons.settings)),
)
],
)
)
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
显示效果:
可以在 DrawerHeader
中进行自己的布局,如果 DrawerHeader
满足不了需求,那就自己进行布局吧。
# 5.4 标题栏AppBar
前面已经使用了标题栏,下面介绍一下其他的设置。
先看效果:
可以修改左右侧的按钮,标题栏颜色,标题居中等。
代码如下:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// App根Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Drawer Demo'), // 标题
centerTitle: true, // 标题居中
backgroundColor:Colors.red, // 背景颜色
leading:IconButton( // 左侧按钮
icon: const Icon(Icons.west),
onPressed: (){
print('点击左侧按钮');
}),
actions: [ // 右侧按钮,可以有多个
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
print('点击了搜索按钮');
}),
IconButton(
icon: const Icon(Icons.more_horiz),
onPressed: (){
print('点击了更多按钮');
})
],
),
body: const Center(child: Text("Doubi")),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
下表列出了 AppBar
组件的常用属性以及它们的作用:
属性 | 类型 | 作用 |
---|---|---|
leading | Widget? | 在标题前面显示的小部件,通常是一个返回按钮或菜单图标。 |
automaticallyImplyLeading | bool | 控制是否自动显示默认的返回按钮(true 为自动显示)。 |
title | Widget? | 标题部分显示的小部件,通常是一个 Text 或 Image 。 |
actions | List<Widget>? | 在标题后面显示的小部件列表,通常是一些操作按钮。 |
flexibleSpace | Widget? | 可伸缩的空间,通常用于添加一些背景图像或效果。 |
bottom | PreferredSizeWidget? | 在标题下面显示的小部件,通常是一个选项卡栏或按钮组。 |
elevation | double | AppBar 的阴影深度。 |
backgroundColor | Color? | AppBar 的背景颜色。 |
brightness | Brightness | 控制 AppBar 文字和图标的颜色主题。 |
iconTheme | IconThemeData | 控制 AppBar 内部图标的颜色和大小。 |
textTheme | TextTheme? | 控制 AppBar 内部文本的样式。 |
centerTitle | bool | 标题是否居中显示。 |
titleSpacing | double? | 标题左右两侧的空间。 |
toolbarOpacity | double | 工具栏部分的透明度。 |
bottomOpacity | double | 底部部分的透明度。 |
在 AppBar
下是可以通过 bottom
属性添加 tabbar
的。
举个栗子:
在上面的实现中,可以点击标签进行切换,也可以滑动进行切换。
下面看一下实现代码,这个实现的逻辑要比之前组件的实现的稍微复杂一些。
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// App根Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
// 1.首先需要混入SingleTickerProviderStateMixin
class _MainPageState extends State<MainPage>
with SingleTickerProviderStateMixin {
// 2.定义TabController
late TabController _tabController;
// 生命周期函数,当组件初始化的时候会被调用
void initState() {
super.initState();
// 3.初始化TabController,其中第一个参数表示的tabbar的个数
_tabController = TabController(length: 5, vsync: this);
_tabController.addListener(() {
// 监听当前选中的哪个标签页
if (_tabController.animation!.value == _tabController.index) {
print("选中标签页:${_tabController.index}"); //获取点击或滑动页面的索引值
}
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('TabBar Demo'),
// 4.通过AppBar的bottom设置TabBar
bottom: TabBar(
controller: _tabController, // 设置tabController
tabs: const [ // 设置tabbar的各个标签
Tab(child: Text("关注")),
Tab(child: Text("推荐")),
Tab(child: Text("热榜")),
Tab(child: Text("发现")),
Tab(child: Text("视频"))
],
),
),
// 5.设置TabBarView
body: TabBarView(
controller: _tabController, // 设置tabController
children: const [ // 当切换tabbar的标签时,会切换TabBarView中的各个组件
Center(child: Text("关注")),
Center(child: Text("推荐")),
Center(child: Text("热榜")),
Center(child: Text("发现")),
Center(child: Text("视频"))
],
));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
- 首先需要在
State
类上混入SingleTickerProviderStateMixin
; - 需要定义属性
tabController
,并在生命周期函数中初始化tabController
; - 在
AppBar
的bottom
属性中定义TabBar
,同时传入tabController
和 各个标签页; - 在
body
中使用TabBarView
组件定义切换的组件,当切换TabBar
的时候,会根据index
切换TabBarView
子组件中的组件。
TabBar有很多样式可以调整,下表列出了 TabBar
组件的常用属性以及它们的作用:
属性 | 类型 | 作用 |
---|---|---|
controller | TabController? | 控制选项卡的状态和交互。 |
tabs | List<Widget> | 选项卡的列表,通常是一组 Tab 小部件。 |
isScrollable | bool | 是否允许选项卡可以滚动。 |
indicatorColor | Color? | 选中选项卡下方指示器的颜色。 |
indicatorWeight | double | 选中选项卡下方指示器的高度。 |
indicatorPadding | EdgeInsetsGeometry | 选中选项卡下方指示器的内边距。 |
indicator | Decoration? | 选中选项卡下方指示器的装饰样式。 |
indicatorSize | TabBarIndicatorSize | 选中选项卡下方指示器的尺寸。 |
labelColor | Color? | 选中选项卡文本的颜色。 |
unselectedLabelColor | Color? | 未选中选项卡文本的颜色。 |
labelStyle | TextStyle? | 选中选项卡文本的样式。 |
unselectedLabelStyle | TextStyle? | 未选中选项卡文本的样式。 |
下面简单演示一下:
TabBar(
controller: _tabController, // 设置tabController
indicatorColor: Colors.red, // 设置指示器的颜色
indicatorWeight: 4, // 设置指示器的高度
labelColor: Colors.white, // 标签的文字颜色
indicatorPadding: const EdgeInsets.fromLTRB(0, 5, 0, 5), // 设置指示器的边距
indicatorSize: TabBarIndicatorSize.label, // 设置指示器的宽度和标签一一样宽
tabs: const [ // 设置tabbar的各个标签
Tab(child: Text("关注")),
Tab(child: Text("推荐")),
Tab(child: Text("热榜")),
Tab(child: Text("发现")),
Tab(child: Text("视频"))
],
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
显示效果:
也可以直接通过 indicator 设置指示器的样式,举个栗子:
TabBar(
controller: _tabController, // 设置tabController
labelColor: Colors.white, // 标签的文字颜色
indicatorPadding: const EdgeInsets.all(5), // 设置指示器的边距
indicator: BoxDecoration( // 设置指示器的样式
color: Colors.grey,
borderRadius: BorderRadius.circular(10)
),
tabs: const [ // 设置tabbar的各个标签
Tab(child: Text("关注")),
Tab(child: Text("推荐")),
Tab(child: Text("热榜")),
Tab(child: Text("发现")),
Tab(child: Text("视频"))
],
),
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
显示效果:
# 5.5 BottomNavigationBar结合TabBar
如果我们的页面底部是 BottomNavigationBar
,然后在一个页面中使用 TabBar
,该如何实现呢?
效果如下:
选中首页的时候,顶部有 TabBar
,选择消息的时候,没有 TabBar
。
这里实现的思路是很简单的,在之前实现底部导航栏的基础上,在首页添加标题栏 AppBar
,在 AppBar
的基础上添加TabBar即可。
这里有一个点就是:在 Scaffold
中是可以嵌套 Scaffold
的。所以本身 Main
页面已经是一个 Scaffold
了,还需要在首页的页面使用 Scaffold
。
代码如下:
main.dart还是之前实现地图导航栏时候的文件
import 'package:flutter/material.dart';
// 1. 首先引入三个页面
import 'tab/home.dart';
import 'tab/message.dart';
import 'tab/my.dart';
void main() => runApp(const MyApp());
/// App根Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
// 2.创建三个页面
final List<Widget> _tabPages = const [
HomePage(),
MessagePage(),
MyPage()
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tab Demo'),
),
// 3.在这里通过_currentIndex获取指定的page用于显示
body: _tabPages[_currentIndex],
// ------------------- 创建tabbar ---------------------
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex, // 设置当前选中的tab
fixedColor: Colors.green, // 设置tab选中的颜色
iconSize: 25, // 设置图标的大小
type: BottomNavigationBarType.fixed, // 如果tab大于3个,则需要设置该属性才能显示
items: const [ // 设置tabbar按钮
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: "首页"
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: "消息"
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
label: "我的"
),
],
onTap: (index) { // 点击tabbar按钮的时候触发
// 4.当点击tabbar的时候修改_currentIndex,从而让body部分修改指向的页面,实现切换页面
setState(() {
_currentIndex = index;
});
},
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
然后修改 HomePage
,在 HomePage
中添加 AppBar,在 AppBar
的 bottom
中添加 TabBar
。
home.dart,代码基本上是上一节的代码。
import 'package:flutter/material.dart';
/// 主页tab页面
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
// 2.定义TabController
late TabController _tabController;
// 生命周期函数,当组件初始化的时候会被调用
void initState() {
super.initState();
// 3.初始化TabController,其中第一个参数表示的tabbar的个数
_tabController = TabController(length: 5, vsync: this);
_tabController.addListener(() {
// 监听当前选中的哪个标签页
if (_tabController.animation!.value == _tabController.index) {
print("选中标签页:${_tabController.index}"); //获取点击或滑动页面的索引值
}
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Home"),
bottom: TabBar(
controller: _tabController, // 设置tabController
tabs: const [ // 设置tabbar的各个标签
Tab(child: Text("关注")),
Tab(child: Text("推荐")),
Tab(child: Text("热榜")),
Tab(child: Text("发现")),
Tab(child: Text("视频"))
],
),
),
// 5.设置TabBarView
body: TabBarView(
controller: _tabController, // 设置tabController
children: const [ // 当切换tabbar的标签时,会切换TabBarView中的各个组件
Center(child: Text("关注")),
Center(child: Text("推荐")),
Center(child: Text("热榜")),
Center(child: Text("发现")),
Center(child: Text("视频"))
],
));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
显示效果:
好像有不干净的东西进眼睛里了,两个标题栏是什么鬼?
其实 title
是一个组件,我们可以将 TabBar
设置到 title
的位置。
appBar: AppBar(
title: TabBar(
controller: _tabController, // 设置tabController
tabs: const [ // 设置tabbar的各个标签
Tab(child: Text("关注")),
Tab(child: Text("推荐")),
Tab(child: Text("热榜")),
Tab(child: Text("发现")),
Tab(child: Text("视频"))
],
),
),
2
3
4
5
6
7
8
9
10
11
12
显示效果:
效果差不多,演示有点丑,调整一下样式。
import 'package:flutter/material.dart';
/// 主页tab页面
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
// 2.定义TabController
late TabController _tabController;
// 生命周期函数,当组件初始化的时候会被调用
void initState() {
super.initState();
// 3.初始化TabController,其中第一个参数表示的tabbar的个数
_tabController = TabController(length: 5, vsync: this);
_tabController.addListener(() {
// 监听当前选中的哪个标签页
if (_tabController.animation!.value == _tabController.index) {
print("选中标签页:${_tabController.index}"); //获取点击或滑动页面的索引值
}
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 40, // 修改标题栏高度
backgroundColor: Colors.white, // 设置背景颜色为白色
title: TabBar(
indicatorColor: Colors.red, // 指示器的颜色
labelColor: Colors.red, // 标签的文字颜色
indicatorPadding: const EdgeInsets.all(5), // 设置指示器的边距
indicatorSize: TabBarIndicatorSize.label, // 设置指示器的宽度
unselectedLabelColor: Colors.black, // 未选中标签的颜色
controller: _tabController, // 设置tabController
tabs: const [ // 设置tabbar的各个标签
Tab(child: Text("关注")),
Tab(child: Text("推荐")),
Tab(child: Text("热榜")),
Tab(child: Text("发现")),
Tab(child: Text("视频"))
],
),
),
// 5.设置TabBarView
body: TabBarView(
controller: _tabController, // 设置tabController
children: const [ // 当切换tabbar的标签时,会切换TabBarView中的各个组件
Center(child: Text("关注")),
Center(child: Text("推荐")),
Center(child: Text("热榜")),
Center(child: Text("发现")),
Center(child: Text("视频"))
],
));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
调整了一下标题栏 和 tabbar 的样式,显示效果如下:
已经差不多了,如果需要再调整一下样式,例如将最上面的标题栏的阴影去掉,可以在 main.dart
中的 AppBar
中添加属性 elevation: 0
。这里就不演示了。
# 5.6 缓存页面
接下来还是在之前实现底部导航栏的基础上,重新修改首页的代码。
如下:
home.dart
import 'package:flutter/material.dart';
/// 主页tab页面
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _count = 0;
Widget build(BuildContext context) {
return Center(
//--------------添加一个计数器,点击按钮的时候----------------
child: Column(
children: [
const SizedBox(height: 50),
Text("$_count", style: const TextStyle(fontSize: 30)),
const SizedBox(height: 50),
ElevatedButton(onPressed: () {
setState(() {
_count++;
});
}, child: const Text("按钮")),
]
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
显示效果如下:
没毛病。
每次点击按钮的时候,数字会加一,但是当切换到消息页面的时候,重新切换回首页的时候,之前增加的数字消失了。
没办法保存状态。
# 5.7 全局修改标题栏样式
在前面我们针对标题栏进行了一些设置,但是如果针对每个页面的标题栏都进行设置,例如居中设置,那也太麻烦了。、
所以我们可以在 MaterialApp
中通过 theme
来设置。
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
// ---- 全局设置标题栏样式
appBarTheme: const AppBarTheme(
color: Colors.blue, // 设置标题栏的背景颜色
elevation: 4, // 设置标题栏的阴影
centerTitle: true,
titleTextStyle: TextStyle(
color: Colors.white, // 设置标题文字颜色
fontSize: 20, // 设置标题文字大小
fontWeight: FontWeight.bold, // 设置标题文字的粗细
))),
home: const MainPage(),
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15