Flutter有状态组件是指可以在生命周期内维护状态的组件,可以通过数据的变化来改变自身的外观和行为。
1 StatefulWidget
- 在Flutter中自定义组件其实就是一个类,这个类需要继承StatelessWidget或StatefulWidget。
- StatelessWidget,无状态组件,状态不可变的Widget,前期接触比较多。
- StatefulWidget,有状态组件,持有的状态可能在Widget生命周期中改变。
1-1 无状态计数器
- Android Studio使用快捷键
stless + Tab
,生成StatelessWidget代码块。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue,),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
int countNum = 0;
HomePage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('无状态计数器')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$countNum', style: Theme.of(context).textTheme.headline1),
const SizedBox(height: 100),
ElevatedButton(
onPressed: (){
countNum++; // 增加时值会改变,页面不会变
// setState(() {countNum++;}); // 在StatelessWidget中无法调用
print(countNum);
},
child: const Text('增加'),
),
],
),
),
);
}
}
1-2 有状态计数器
- Android Studio使用快捷键
stful + Tab
,生成StatefulWidget代码块。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget { // 有状态组件
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _numCount = 0;
Widget build(BuildContext context) {
print(_numCount); // 打印出来
return Scaffold(
appBar: AppBar(title: const Text('有状态计数器')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$_numCount', style: Theme.of(context).textTheme.headline2),
const SizedBox(height: 60),
ElevatedButton(
onPressed: (){
setState(() {
_numCount++;
});
},
child: const Text('增加'),
),
],
),
),
);
}
}
1-3 实现浮动按钮
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget { // 有状态组件
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _numCount = 0;
Widget build(BuildContext context) {
print(_numCount); // 打印出来
return Scaffold(
appBar: AppBar(title: const Text('实现浮动按钮')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$_numCount', style: Theme.of(context).textTheme.headline2),
const SizedBox(height: 60),
ElevatedButton(
onPressed: (){
setState(() {
_numCount++;
});
},
child: const Text('增加'),
),
],
),
),
floatingActionButton: FloatingActionButton( // 浮动按钮
onPressed: (){
setState(() {
_numCount++;
});
},
child: const Icon(Icons.add), // 按钮样式
),
);
}
}
1-4 实现动态列表
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<String> _list = []; // _list是私有属性,必须加final
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('实现动态列表')),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: (){
setState(() { // 改变数据必须加上setState
_list.add('新增列表');
});
},
),
body: ListView(
// children: const [
// ListTile(title: Text('列表')),
// ListTile(title: Text('列表')),
// ListTile(title: Text('列表')),
// ],
children: _list.map((v){
return ListTile(title: Text(v));
}).toList(),
),
);
}
}
2 自定义底部导航
- BottomNavigationBar,底部导航条,是Scaffold组件的参数。
- 参数说明
- items(List底部导航条按钮的集合)、currentIndex(默认选中第几个)。
- iconSize(icon)、onTap(选中变化回调函数)、fixedColor(选中颜色)。
- type(BottomNavigationBarType.fixed、BottomNavigationBarType.shifting)。
2-1 实现底部导航
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const Tabs(),
);
}
}
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("实现底部导航")),
bottomNavigationBar: BottomNavigationBar(
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
],
),
);
}
}
2-2 实现页面切换
# 例如:myflutter(项目名)目录结构如下
# - pages/
# - tabs/
# - user.dart
# - home.dart
# - setting.dart
# - category.dart
# - tabs.dart
# - main.dart
(1) main.dart
import './pages/tabs.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const Tabs(),
);
}
}
(2) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("实现页面切换")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(3) user.dart
import 'package:flutter/material.dart';
class UserPage extends StatefulWidget {
const UserPage({Key? key}) : super(key: key);
State<UserPage> createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
Widget build(BuildContext context) {
return const Center(
child: Text("用户信息"),
);
}
}
(4) home.dart
import 'package:flutter/material.dart';
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("首页"),
);
}
}
(5) setting.dart
import 'package:flutter/material.dart';
class SettingPage extends StatefulWidget {
const SettingPage({Key? key}) : super(key: key);
State<SettingPage> createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
Widget build(BuildContext context) {
return const Center(
child: Text("系统设置"),
);
}
}
(6) category.dart
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
const CategoryPage({Key? key}) : super(key: key);
State<CategoryPage> createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
Widget build(BuildContext context) {
// return const Center(
// child: Text("分类"),
// );
return ListView(
children: const [
ListTile(title: Text("列表1")),
ListTile(title: Text("列表2")),
ListTile(title: Text("列表3")),
],
);
}
}
3 凸起按钮的导航
- FloatingActionButton简称FAB,可实现浮动按钮,也可实现底部凸起导航,是Scaffold组件的参数。
- 属性说明
- elevation(未点击时的阴影)、hignlightElevation(点击时的阴影值,默认为12.0)。
- onPressed(点击事件回调)、shape(可定义FAB的形状等)、mini(是否为mini类型,默认false)。
- child(子视图,一般为Icon,不推荐使用文字)、tooltip(FAB被长按时显示,也是无障碍功能)。
# 例如:myflutter(项目名)目录结构如下,除tabs.dart外,其余内容不变,多了message.dart文件
# - pages/
# - tabs/
# - user.dart
# - home.dart
# - setting.dart
# - message.dart【✔】
# - category.dart
# - tabs.dart【✔】
# - main.dart
3-1 tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), MessagePage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("凸起按钮的导航")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 25, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.message),
label: "消息",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
floatingActionButton: Container(
height: 60, // 调整FloatingActionButton的大小
width: 60,
padding: const EdgeInsets.all(5),
margin: const EdgeInsets.only(top: 5), // 调整FloatingActionButton的位置
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex==2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: (){
setState(() {
_currentIndex=2; // 点击凸起+按钮会显示消息页面
});
},
),
), // 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
3-2 message.dart
import 'package:flutter/material.dart';
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("消息"),
);
}
}
4 Drawer抽屉视图
- Scaffold组件中传入drawer参数可定义左侧边栏,传入endDrawer可定义右侧边栏。
- 侧边栏默认是隐藏的,可通过手指滑动显示侧边栏,也可通过点击按钮显示侧边栏。
4-1 创建抽屉头部
- DrawerHeader常见属性:decoration(顶部背景颜色)、child(子元素)、padding(内边距)、margin(外边距)。
import './tabs/user.dart'; // tabs.dart,其余文件内容不变
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), MessagePage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("创建抽屉头部")),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: DrawerHeader(
decoration: const BoxDecoration(
color: Colors.yellow,
image: DecorationImage(
image: NetworkImage("http://www.m58.link/QKBLQ"),
fit: BoxFit.cover,
),
),
child: Column(
children: const [
ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage("http://www.m58.link/ymHZl"),
),
title: Text("钱三两", style: TextStyle(color: Colors.blue)),
),
ListTile(title: Text("邮箱:Siren1996@qq.com")),
],
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text("个人中心"),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text("系统设置"),
),
const Divider(),
],
),
),
endDrawer: const Drawer(child: Text("右侧侧边栏")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 25, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
currentIndex: _currentIndex,
onTap: (index){setState(() {_currentIndex = index;});},
items: const [
BottomNavigationBarItem(icon:Icon(Icons.home), label: "首页"),
BottomNavigationBarItem(icon:Icon(Icons.category), label: "分类"),
BottomNavigationBarItem(icon:Icon(Icons.message), label: "消息"),
BottomNavigationBarItem(icon:Icon(Icons.settings), label: "设置"),
BottomNavigationBarItem(icon:Icon(Icons.people), label: "用户"),
],
),
floatingActionButton: Container( // 调整FloatingActionButton的大小
height: 60, width: 60, padding: const EdgeInsets.all(5),
margin: const EdgeInsets.only(top: 5), // 调整FloatingActionButton的位置
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex==2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: (){setState(() {_currentIndex=2;});},
),
), // 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
4-2 账户抽屉头部
- UserAccountsDrawerHeader常见属性
- currentAccountPicture(账户头像)、otherAccountsPictures(用来设置当前账户的其他账户头像)。
- decoration(顶部背景颜色)、accountName(账户名)、accountEmail(账户邮箱)、margin(外边距)。
import './tabs/user.dart'; // tabs.dart,其余文件内容不变
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), MessagePage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("账户抽屉头部")),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: UserAccountsDrawerHeader(
accountName: const Text("钱三两"),
accountEmail: const Text("Siren1996@qq.com"),
otherAccountsPictures: [
Image.network("http://www.m58.link/uPqPD"),
Image.network("http://www.m58.link/uFOCk"),
Image.network("http://www.m58.link/fKFLL"),
],
currentAccountPicture: const CircleAvatar(
backgroundImage: NetworkImage("http://www.m58.link/ymHZl"),
),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage("http://www.m58.link/DClbg"),
),
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text("个人中心"),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text("系统设置"),
),
const Divider(),
],
),
),
endDrawer: const Drawer(child: Text("右侧侧边栏")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 25, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
currentIndex: _currentIndex,
onTap: (index){setState(() {_currentIndex = index;});},
items: const [
BottomNavigationBarItem(icon:Icon(Icons.home), label: "首页"),
BottomNavigationBarItem(icon:Icon(Icons.category), label: "分类"),
BottomNavigationBarItem(icon:Icon(Icons.message), label: "消息"),
BottomNavigationBarItem(icon:Icon(Icons.settings), label: "设置"),
BottomNavigationBarItem(icon:Icon(Icons.people), label: "用户"),
],
),
floatingActionButton: Container( // 调整FloatingActionButton的大小
height: 60, width: 60, padding: const EdgeInsets.all(5),
margin: const EdgeInsets.only(top: 5), // 调整FloatingActionButton的位置
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex==2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: (){setState(() {_currentIndex=2;});},
),
), // 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
5 自定义顶部按钮
- AppBar自定义顶部按钮图标、颜色,属性如下。
- actions(通常使用IconButton来表示,可以放按钮组件)、iconTheme(图标样式)。
- bottom(通常放tabBar,标题下面显示一个Tab导航栏)、backgroundColor(导航的背景颜色)。
- title(标题,通常显示为当前界面的标题文字,可放组件)、centerTitle(标题是否居中显示)。
- leading(在标题前面显示的一个控件,通常为首页显示应用的logo,或其他界面的返回按钮)。
5-1 实现顶部按钮
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // 去掉右上角dubug图标
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
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 Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.menu), onPressed: (){print("左侧按钮图标");},
),
backgroundColor: Colors.red, // 导航的背景颜色
title: const Text("实现顶部按钮"),
actions: [ // 右侧的按钮图标
IconButton(
icon: const Icon(Icons.search), onPressed: (){print("搜索");},
),
IconButton(
icon: const Icon(Icons.more_horiz), onPressed: (){print("更多");},
),
],
),
);
}
}
5-2 顶部滑动导航
- TabBar常见属性
- indicatorSize(指示器大小计算方式)。
- TabBarIndicatorSize.label:跟文字等宽。
- TabBarIndicatorSize.tab:跟每个tab等宽。
- unselectedLabelColor(未选中label颜色)、unselectedLabelStyle(未选中label的Style)。
- indicatorPadding(底部指示器的Padding)、indicator(指示器decoration,例如边框等)。
- tabs(显示的标签内容,一般使用Tab对象,也可以是其他组件)、isScrollable(是否可以滚动)。
- controller(TabController对象)、indicatorColor(指示器颜色)、indicatorWeight(指示器高度)。
- labelColor(选中label颜色)、labelStyle(选中label样式)、labelPadding(每个label的padding值)。
- indicatorSize(指示器大小计算方式)。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // 去掉右上角dubug图标
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
// 生命周期函数(组件初始化时触发)
void initState() {
super.initState(); // length(导航标题)够多可左右滑动
_tabController=TabController(length: 5, vsync: this);
}
List<String> list = [];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton( // 左侧的按钮图标
icon: const Icon(Icons.menu), onPressed: (){print("左侧按钮图标");},
),
backgroundColor: Colors.red, // 导航的背景颜色
title: const Text("顶部滑动导航"),
actions: [ // 右侧的按钮图标
IconButton(
icon: const Icon(Icons.search), onPressed: (){print("搜索");},
),
IconButton(
icon: const Icon(Icons.more_horiz), onPressed: (){print("更多");},
),
],
bottom: TabBar(
isScrollable: true, // 设置可以左右滚动
indicatorColor: Colors.white, // 指示器颜色
indicatorWeight: 2, // 指示器高度
indicatorPadding: const EdgeInsets.all(5), // 底部指示器的Padding
// indicatorSize: TabBarIndicatorSize.label, // 与文字等宽,默认tab
labelColor: Colors.yellow, // 选中的label颜色
unselectedLabelColor: Colors.white, // 未选中的label颜色
labelStyle: const TextStyle(fontSize: 15), // 选中的label样式
unselectedLabelStyle: const TextStyle(fontSize: 12),
indicator: BoxDecoration( // 指示器边框
color: Colors.blue,
borderRadius: BorderRadius.circular(10), // 边框角幅度
),
controller: _tabController, // 配置controller需要去掉const
tabs: const [
Tab(child: Text("关注")),
Tab(child: Text("热门")),
Tab(child: Text("视频")),
Tab(child: Text("图片")),
Tab(child: Text("推荐")),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
// const Text("我是关注"),
ListView(children: const [ListTile(title: Text("动态关注"))]),
ListView(children: const [ListTile(title: Text("热门信息"))]),
ListView(children: const [ListTile(title: Text("视频加载"))]),
ListView(children: const [ListTile(title: Text("图片赏析"))]),
ListView(children: const [ListTile(title: Text("推荐列表"))]),
],
),
);
}
}
6 底部与顶部结合
- PreferredSize可以改变AppBar高度,AutomaticKeepAliveClientMixin可以快速实现页面缓存功能。
- 但是通过混入的方式实现不是很优雅,所以有必要对AutomaticKeepAliveClientMixin混入进行封装。
6-1 底部页面使用导航
# 例如:myflutter(项目名)目录结构如下,只更改main.dart、tabs.dart、home.dart的内容,其余不变
# - pages/
# - tabs/
# - user.dart
# - home.dart【✔】
# - setting.dart
# - message.dart
# - category.dart
# - tabs.dart【✔】
# - main.dart【✔】
(1) main.dart
import './pages/tabs.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // 去掉右上角的debug标识
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const Tabs(),
);
}
}
(2) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), MessagePage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.red,
title: const Text("底部页面使用导航"),
),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: UserAccountsDrawerHeader(
accountName: const Text("钱三两"),
accountEmail: const Text("Siren1996@qq.com"),
otherAccountsPictures: [
Image.network("http://www.m58.link/uPqPD"),
Image.network("http://www.m58.link/uFOCk"),
Image.network("http://www.m58.link/fKFLL"),
],
currentAccountPicture: const CircleAvatar(
backgroundImage: NetworkImage("http://www.m58.link/ymHZl"),
),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage("http://www.m58.link/DClbg"),
),
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text("个人中心"),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text("系统设置"),
),
const Divider(),
],
),
),
// endDrawer: const Drawer(child: Text("右侧侧边栏")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 25, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
currentIndex: _currentIndex,
onTap: (index){setState(() {_currentIndex = index;});},
items: const [
BottomNavigationBarItem(icon:Icon(Icons.home), label: "首页"),
BottomNavigationBarItem(icon:Icon(Icons.category), label: "分类"),
BottomNavigationBarItem(icon:Icon(Icons.message), label: "消息"),
BottomNavigationBarItem(icon:Icon(Icons.settings), label: "设置"),
BottomNavigationBarItem(icon:Icon(Icons.people), label: "用户"),
],
),
floatingActionButton: Container( // 调整FloatingActionButton的大小
height: 60, width: 60, padding: const EdgeInsets.all(5),
margin: const EdgeInsets.only(top: 5), // 调整FloatingActionButton的位置
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex==2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: (){setState(() {_currentIndex=2;});},
),
), // 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
(3) home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
void initState() {
super.initState();
_tabController=TabController(length: 8, vsync: this);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize( // 可以配置appBar的高度
preferredSize: const Size.fromHeight(40),
child: AppBar(
elevation: 0.5,
backgroundColor: Colors.white,
title: SizedBox( // 修改TabBar的高度
height: 30,
child: TabBar(
labelStyle: const TextStyle(fontSize: 15),
isScrollable: true,
indicatorColor: Colors.red, // 底部指示器的颜色
labelColor: Colors.red,
unselectedLabelColor: Colors.black, // label未选中的颜色
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
tabs: const [
Tab(child: Text("关注")), Tab(child: Text("热门")),
Tab(child: Text("生活")), Tab(child: Text("娱乐")),
Tab(child: Text("绘画")), Tab(child: Text("设计")),
Tab(child: Text("编程")), Tab(child: Text("职场")),
],
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [
ListView(children: const [
ListTile(title: Text("关注列表01")), ListTile(title: Text("关注列表02")),
ListTile(title: Text("关注列表03")), ListTile(title: Text("关注列表04")),
ListTile(title: Text("关注列表05")), ListTile(title: Text("关注列表06")),
ListTile(title: Text("关注列表07")), ListTile(title: Text("关注列表08")),
ListTile(title: Text("关注列表09")), ListTile(title: Text("关注列表10")),
ListTile(title: Text("关注列表11")), ListTile(title: Text("关注列表12")),
ListTile(title: Text("关注列表13")), ListTile(title: Text("关注列表14")),
ListTile(title: Text("关注列表15")), ListTile(title: Text("关注列表16")),
ListTile(title: Text("关注列表17")), ListTile(title: Text("关注列表18")),
ListTile(title: Text("关注列表19")), ListTile(title: Text("关注列表20")),
]),
const Text("热门"), const Text("生活"), const Text("娱乐"), const Text("绘画"),
const Text("设计"), const Text("编程"), const Text("职场"),
],
),
);
}
}
6-2 自定义的缓存组件
# 例如:myflutter(项目名)目录结构如下,只更改tabs.dart、home.dart的内容,其余不变
# - pages/
# - tabs/
# - user.dart
# - home.dart【✔】
# - setting.dart
# - message.dart
# - category.dart
# - tabs.dart【✔】
# - tools/
# - KeepAliveWrapper.dart【✔】
# - main.dart
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), MessagePage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.red,
title: const Text("自定义的缓存组件"),
),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: UserAccountsDrawerHeader(
accountName: const Text("钱三两"),
accountEmail: const Text("Siren1996@qq.com"),
otherAccountsPictures: [
Image.network("http://www.m58.link/uPqPD"),
Image.network("http://www.m58.link/uFOCk"),
Image.network("http://www.m58.link/fKFLL"),
],
currentAccountPicture: const CircleAvatar(
backgroundImage: NetworkImage("http://www.m58.link/ymHZl"),
),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage("http://www.m58.link/DClbg"),
),
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text("个人中心"),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text("系统设置"),
),
const Divider(),
],
),
),
// endDrawer: const Drawer(child: Text("右侧侧边栏")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 25, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
currentIndex: _currentIndex,
onTap: (index){setState(() {_currentIndex = index;});},
items: const [
BottomNavigationBarItem(icon:Icon(Icons.home), label: "首页"),
BottomNavigationBarItem(icon:Icon(Icons.category), label: "分类"),
BottomNavigationBarItem(icon:Icon(Icons.message), label: "消息"),
BottomNavigationBarItem(icon:Icon(Icons.settings), label: "设置"),
BottomNavigationBarItem(icon:Icon(Icons.people), label: "用户"),
],
),
floatingActionButton: Container( // 调整FloatingActionButton的大小
height: 60, width: 60, padding: const EdgeInsets.all(5),
margin: const EdgeInsets.only(top: 5), // 调整FloatingActionButton的位置
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex==2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: (){setState(() {_currentIndex=2;});},
),
), // 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
(2) home.dart
import 'package:flutter/material.dart';
import '../../tools/KeepAliveWrapper.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
void initState() {
super.initState();
_tabController=TabController(length: 8, vsync: this);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize( // 可以配置appBar的高度
preferredSize: const Size.fromHeight(40),
child: AppBar(
elevation: 0.5,
backgroundColor: Colors.white,
title: SizedBox( // 修改TabBar的高度
height: 30,
child: TabBar(
labelStyle: const TextStyle(fontSize: 15),
isScrollable: true,
indicatorColor: Colors.red, // 底部指示器的颜色
labelColor: Colors.red,
unselectedLabelColor: Colors.black, // label未选中的颜色
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
tabs: const [
Tab(child: Text("关注")), Tab(child: Text("热门")),
Tab(child: Text("生活")), Tab(child: Text("娱乐")),
Tab(child: Text("绘画")), Tab(child: Text("设计")),
Tab(child: Text("编程")), Tab(child: Text("职场")),
],
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [ // KeepAliveWrapper自定义缓存组件
KeepAliveWrapper(child: ListView(children: const [
ListTile(title: Text("关注列表01")), ListTile(title: Text("关注列表02")),
ListTile(title: Text("关注列表03")), ListTile(title: Text("关注列表04")),
ListTile(title: Text("关注列表05")), ListTile(title: Text("关注列表06")),
ListTile(title: Text("关注列表07")), ListTile(title: Text("关注列表08")),
ListTile(title: Text("关注列表09")), ListTile(title: Text("关注列表10")),
ListTile(title: Text("关注列表11")), ListTile(title: Text("关注列表12")),
ListTile(title: Text("关注列表13")), ListTile(title: Text("关注列表14")),
ListTile(title: Text("关注列表15")), ListTile(title: Text("关注列表16")),
ListTile(title: Text("关注列表17")), ListTile(title: Text("关注列表18")),
ListTile(title: Text("关注列表19")), ListTile(title: Text("关注列表20")),
])),
const Text("热门"), const Text("生活"), const Text("娱乐"), const Text("绘画"),
const Text("设计"), const Text("编程"), const Text("职场"),
],
),
);
}
}
(3) KeepAliveWrapper.dart
import 'package:flutter/material.dart';
class KeepAliveWrapper extends StatefulWidget {
const KeepAliveWrapper (
{Key? key, this.child, this.keepAlive = true}
) : super(key: key);
final Widget? child;
final bool keepAlive;
State<KeepAliveWrapper> createState() => _KeepAliveWrapperState();
}
class _KeepAliveWrapperState extends State<KeepAliveWrapper>
with AutomaticKeepAliveClientMixin {
Widget build(BuildContext context) {
return widget.child!;
}
bool get wantKeepAlive => widget.keepAlive;
void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
if (oldWidget.keepAlive != widget.keepAlive) {
// keepAlive状态需要更新,实现在AutomaticKeepAliveClientMixin中
updateKeepAlive();
}
super.didUpdateWidget(oldWidget);
}
}
6-3 获取选项卡索引值
# 例如:myflutter(项目名)目录结构如下,只更改tabs.dart、home.dart的内容,其余不变
# - pages/
# - tabs/
# - user.dart
# - home.dart【✔】
# - setting.dart
# - message.dart
# - category.dart
# - tabs.dart【✔】
# - tools/
# - KeepAliveWrapper.dart
# - main.dart
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), MessagePage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.red,
title: const Text("获取选项卡索引值"),
),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: UserAccountsDrawerHeader(
accountName: const Text("钱三两"),
accountEmail: const Text("Siren1996@qq.com"),
otherAccountsPictures: [
Image.network("http://www.m58.link/uPqPD"),
Image.network("http://www.m58.link/uFOCk"),
Image.network("http://www.m58.link/fKFLL"),
],
currentAccountPicture: const CircleAvatar(
backgroundImage: NetworkImage("http://www.m58.link/ymHZl"),
),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage("http://www.m58.link/DClbg"),
),
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text("个人中心"),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text("系统设置"),
),
const Divider(),
],
),
),
// endDrawer: const Drawer(child: Text("右侧侧边栏")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 25, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
currentIndex: _currentIndex,
onTap: (index){setState(() {_currentIndex = index;});},
items: const [
BottomNavigationBarItem(icon:Icon(Icons.home), label: "首页"),
BottomNavigationBarItem(icon:Icon(Icons.category), label: "分类"),
BottomNavigationBarItem(icon:Icon(Icons.message), label: "消息"),
BottomNavigationBarItem(icon:Icon(Icons.settings), label: "设置"),
BottomNavigationBarItem(icon:Icon(Icons.people), label: "用户"),
],
),
floatingActionButton: Container( // 调整FloatingActionButton的大小
height: 60, width: 60, padding: const EdgeInsets.all(5),
margin: const EdgeInsets.only(top: 5), // 调整FloatingActionButton的位置
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex==2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: (){setState(() {_currentIndex=2;});},
),
), // 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
(2) home.dart
import 'package:flutter/material.dart';
import '../../tools/KeepAliveWrapper.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
late TabController _tabController;
void initState() {
super.initState();
_tabController=TabController(length: 8, vsync: this);
_tabController.addListener(() { // 监听_tabController的改变事件
// print(_tabController.index); // 获取两次
if (_tabController.animation!.value==_tabController.index) {
print(_tabController.index); // 获取索引值方法①
} // 获取点击或滑动页面的索引值
});
}
// 组件销毁时触发
void dispose() { // 例如点击底部导航的分类
super.dispose();
_tabController.dispose(); // 销毁_tabController
}
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize( // 可以配置appBar的高度
preferredSize: const Size.fromHeight(40),
child: AppBar(
elevation: 0.5,
backgroundColor: Colors.white,
title: SizedBox( // 修改TabBar的高度
height: 30,
child: TabBar(
labelStyle: const TextStyle(fontSize: 15),
isScrollable: true,
indicatorColor: Colors.red, // 底部指示器的颜色
labelColor: Colors.red,
unselectedLabelColor: Colors.black, // label未选中的颜色
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
// onTap: (index) { // 只能监听点击事件
// print(index); // 没法监听滑动
// }, // 获取索引值方法②
tabs: const [
Tab(child: Text("关注")), Tab(child: Text("热门")),
Tab(child: Text("生活")), Tab(child: Text("娱乐")),
Tab(child: Text("绘画")), Tab(child: Text("设计")),
Tab(child: Text("编程")), Tab(child: Text("职场")),
],
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [ // KeepAliveWrapper自定义缓存组件
KeepAliveWrapper(child: ListView(children: const [
ListTile(title: Text("关注列表01")), ListTile(title: Text("关注列表02")),
ListTile(title: Text("关注列表03")), ListTile(title: Text("关注列表04")),
ListTile(title: Text("关注列表05")), ListTile(title: Text("关注列表06")),
ListTile(title: Text("关注列表07")), ListTile(title: Text("关注列表08")),
ListTile(title: Text("关注列表09")), ListTile(title: Text("关注列表10")),
ListTile(title: Text("关注列表11")), ListTile(title: Text("关注列表12")),
ListTile(title: Text("关注列表13")), ListTile(title: Text("关注列表14")),
ListTile(title: Text("关注列表15")), ListTile(title: Text("关注列表16")),
ListTile(title: Text("关注列表17")), ListTile(title: Text("关注列表18")),
ListTile(title: Text("关注列表19")), ListTile(title: Text("关注列表20")),
])),
const Text("热门"), const Text("生活"), const Text("娱乐"), const Text("绘画"),
const Text("设计"), const Text("编程"), const Text("职场"),
],
),
);
}
}
7 Flutter路由详解
- Flutter中的路由即页面跳转,通过Navigator组件管理路由导航,并提供了管理堆栈的方法。
- 例如Navigator.push和Navigator.pop,提供了基本路由和命名路由两种配置路由跳转的方式。
7-1 普通路由基本使用
# 例如:myflutter(项目名)目录结构如下,修改“自定义底部导航”的“实现页面切换”代码
# - pages/
# - tabs/
# - user.dart
# - home.dart【✔】
# - setting.dart
# - category.dart【✔】
# - tabs.dart【✔】
# - form.dart【✔】
# - search.dart【✔】
# - main.dart
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("普通路由基本使用")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) form.dart
import 'package:flutter/material.dart';
class FormPage extends StatefulWidget {
const FormPage({Key? key}) : super(key: key);
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("表单"),
),
body: const Center(
child: Text("表单页面"),
),
);
}
}
(3) home.dart
import '../form.dart';
import '../search.dart';
import 'package:flutter/material.dart';
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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: (){
Navigator.of(context).push( // 跳转路由
MaterialPageRoute(builder: (BuildContext context) {
return const SearchPage(); // 首页跳转搜索页面
})
);
}, child: const Text("搜索")),
const SizedBox(height: 20),
ElevatedButton(onPressed: (){
Navigator.of(context).push( // 跳转路由
MaterialPageRoute(builder: (BuildContext context) {
return const FormPage(); // 首页跳转表单页面
})
);
}, child: const Text("跳转到表单页面")),
],
),
);
}
}
(4) search.dart
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
const SearchPage({Key? key}) : super(key: key);
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("搜索"),
),
body: const Center(
child: Text("搜索页面"),
),
);
}
}
(5) category.dart
import '../search.dart';
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
const CategoryPage({Key? key}) : super(key: key);
State<CategoryPage> createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: (){
Navigator.of(context).push( // 跳转路由
MaterialPageRoute(builder: (BuildContext context) {
return const SearchPage(); // 分类跳转搜索页面
})
);
}, child: const Text("搜索")),
],
),
);
}
}
7-2 普通路由跳转传值
# 例如:myflutter(项目名)目录结构如下,修改“普通路由基本使用”代码
# - pages/
# - tabs/
# - user.dart
# - home.dart【✔】
# - setting.dart
# - category.dart
# - tabs.dart【✔】
# - news.dart【✔】
# - form.dart
# - search.dart
# - main.dart
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("普通路由跳转传值")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) news.dart
import 'package:flutter/material.dart';
class NewsPage extends StatefulWidget { // 新闻页面接收上个页面传来的参数
final String title; final int aid;
const NewsPage({Key? key, this.title="新闻", required this.aid}) : super(key: key);
State<NewsPage> createState() => _NewsPageState();
}
class _NewsPageState extends State<NewsPage> {
void initState() {
super.initState(); print(widget.aid); print(widget.title);
}
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {Navigator.pop(context);}, // 返回到上一页
child: const Icon(Icons.home),
),
appBar: AppBar(title: Text(widget.title)), // 获取NewsPage里面定义的title
body: const Center(child: Text("新闻页面")),
);
}
}
(3) home.dart
import '../news.dart';
import '../form.dart';
import '../search.dart';
import 'package:flutter/material.dart';
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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: (){
Navigator.of(context).push( // 跳转路由
MaterialPageRoute(builder: (BuildContext context) {
return const SearchPage(); // 首页跳转搜索页面
})
);
}, child: const Text("搜索")),
const SizedBox(height: 20),
ElevatedButton(onPressed: (){
Navigator.of(context).push( // 跳转路由
MaterialPageRoute(builder: (BuildContext context) {
return const FormPage(); // 首页跳转表单页面
})
);
}, child: const Text("跳转到表单页面")),
const SizedBox(height: 20),
ElevatedButton(onPressed: (){
Navigator.of(context).push( // 跳转路由
MaterialPageRoute(builder: (BuildContext context) {
return const NewsPage(title: "我是标题", aid: 12);
})
);
}, child: const Text("跳转传值")),
],
),
);
}
}
7-3 命名路由基本使用
# 例如:myflutter(项目名)目录结构如下,修改“普通路由基本使用”代码,并添加news.dart文件
# - pages/
# - tabs/
# - user.dart
# - home.dart【✔】
# - setting.dart
# - category.dart【✔】
# - tabs.dart【✔】
# - news.dart【✔】
# - form.dart
# - search.dart
# - main.dart【✔】
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("命名路由基本使用")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) main.dart
import './pages/tabs.dart';
import './pages/news.dart';
import './pages/form.dart';
import './pages/search.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
// home: const Tabs(),
initialRoute: "/", // 初始化路由
routes: { // 配置路由
"/": (context) => const Tabs(),
"/news": (context) => const NewsPage(),
"/form": (context) => const FormPage(),
"/search": (context) {
return const SearchPage();
},
},
);
}
}
(3) news.dart
import 'package:flutter/material.dart';
class NewsPage extends StatefulWidget {
const NewsPage({Key? key}) : super(key: key);
State<NewsPage> createState() => _NewsPageState();
}
class _NewsPageState extends State<NewsPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("新闻"),
),
body: const Center(
child: Text("新闻页面"),
),
);
}
}
(4) home.dart
import '../search.dart';
import 'package:flutter/material.dart';
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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: (){
Navigator.of(context).push( // 跳转路由
MaterialPageRoute(builder: (BuildContext context) {
return const SearchPage(); // 首页跳转搜索页面
})
);
}, child: const Text("基本路由跳转,引入组件search.dart")),
const SizedBox(height: 20),
ElevatedButton(onPressed: (){
Navigator.pushNamed(context, "/news");
}, child: const Text("命令路由跳转,main.dart中配置路由")),
],
),
);
}
}
(5) category.dart
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
const CategoryPage({Key? key}) : super(key: key);
State<CategoryPage> createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: (){
Navigator.pushNamed(context, "/search");
}, child: const Text("命名路由跳转到search")),
],
),
);
}
}
7-4 命名路由传值操作
- 命名路由传值需将路由配置移到外面,首先定义Map类型的routes,调用onGenerateRoute处理。
- 然后在对应的页面进行接收传值,在页面中定义arguments用于传值,最后再跳转页面实现传参。
# 例如:myflutter(项目名)目录结构如下,修改“命名路由基本使用”代码,并添加shop.dart文件
# - pages/
# - tabs/
# - user.dart
# - home.dart【✔】
# - setting.dart
# - category.dart
# - tabs.dart【✔】
# - news.dart
# - form.dart【✔】
# - shop.dart【✔】
# - search.dart
# - main.dart【✔】
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("命名路由传值操作")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) shop.dart
import 'package:flutter/material.dart';
class ShopPage extends StatefulWidget {
final Map arguments; // 接收命名路由跳转传值
const ShopPage({Key? key, required this.arguments}) : super(key: key);
State<ShopPage> createState() => _ShopPageState();
}
class _ShopPageState extends State<ShopPage> {
void initState() {
super.initState();
print(widget.arguments);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("商店"),
),
body: const Center(
child: Text("商店页面"),
),
);
}
}
(3) form.dart
import 'package:flutter/material.dart';
class FormPage extends StatefulWidget { // 其他页面跳转到form页面
final Map arguments; // 进行命名路由传值
const FormPage({Key? key, required this.arguments}) : super(key: key);
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
void initState() {
super.initState();
print(widget.arguments); // 打印输出
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("表单"),
),
body: const Center(
child: Text("表单页面"),
),
);
}
}
(4) main.dart
import './pages/tabs.dart';
import './pages/news.dart';
import './pages/form.dart';
import './pages/shop.dart';
import './pages/search.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Map routes = { // 定义一个Map类型的路由
"/": (context) => const Tabs(),
"/news": (context) => const NewsPage(),
"/form": (context, {arguments}) => FormPage(arguments: arguments),
"/shop": (context, {arguments}) {
return ShopPage(arguments: arguments);
},
"/search": (context) {
return const SearchPage();
},
};
MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
// home: const Tabs(),
initialRoute: "/", // 初始化路由
onGenerateRoute: (RouteSettings settings) { // 固定写法,配置onGenerateRoute
print(settings);
print(settings.name); // “/news”或“/search”
print(settings.arguments);
final String? name = settings.name; // 统一处理
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(
context, arguments: settings.arguments
)
);
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context)
);
return route;
}
}
return null;
},
);
}
}
(5) home.dart
import '../search.dart';
import 'package:flutter/material.dart';
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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: (){
Navigator.of(context).push( // 跳转路由
MaterialPageRoute(builder: (BuildContext context) {
return const SearchPage(); // 首页跳转搜索页面
})
);
}, child: const Text("基本路由跳转,引入组件search.dart")),
const SizedBox(height: 20),
ElevatedButton(onPressed: (){
Navigator.pushNamed(context, "/news");
}, child: const Text("命令路由跳转,main.dart中配置路由")),
const SizedBox(height: 20),
ElevatedButton(onPressed: (){
Navigator.pushNamed(context, "/form", arguments: {
"title": "命名路由传值",
"aid": 20
});
}, child: const Text("命令路由传值操作,跳转到form页面")),
const SizedBox(height: 20),
ElevatedButton(onPressed: (){
Navigator.pushNamed(context, "/shop", arguments: {
"title": "命名路由传值",
"aid": 30
});
}, child: const Text("命令路由传值操作,跳转到shop页面")),
],
),
);
}
}
7-5 命名路由代码抽离
# 例如:myflutter(项目名)目录结构如下,修改“命名路由传值操作”代码,并添加routes.dart文件
# - pages/
# - tabs/
# - user.dart
# - home.dart
# - setting.dart
# - category.dart
# - tabs.dart【✔】
# - news.dart
# - form.dart
# - shop.dart
# - search.dart
# - routes
# - routes.dart【✔】
# - main.dart【✔】
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("命名路由代码抽离")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) main.dart
import './routes/routes.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
// home: const Tabs(),
initialRoute: "/", // 初始化路由
onGenerateRoute: onGenerateRoute, // 固定写法,配置onGenerateRoute
);
}
}
(3) routes.dart
import '../pages/tabs.dart';
import '../pages/news.dart';
import '../pages/form.dart';
import '../pages/shop.dart';
import '../pages/search.dart';
import 'package:flutter/material.dart';
Map routes = { // 定义一个Map类型的路由
"/": (context) => const Tabs(),
"/news": (context) => const NewsPage(),
"/form": (context, {arguments}) => FormPage(arguments: arguments),
"/shop": (context, {arguments}) {
return ShopPage(arguments: arguments);
},
"/search": (context) {
return const SearchPage();
},
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
print(settings);
print(settings.name); // “/news”或“/search”
print(settings.arguments);
final String? name = settings.name; // 统一处理
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(
context, arguments: settings.arguments
)
);
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context)
);
return route;
}
}
return null;
};
7-6 返回到上一级路由
# 例如:myflutter(项目名)目录结构如下
# - pages/
# - tabs/
# - user.dart
# - home.dart
# - setting.dart
# - category.dart
# - user/
# - login.dart
# - tabs.dart
# - routes
# - routes.dart
# - main.dart
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("返回到上一级路由")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) user.dart
import 'package:flutter/material.dart';
class UserPage extends StatefulWidget {
const UserPage({Key? key}) : super(key: key);
State<UserPage> createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: () {
print("执行跳转");
Navigator.pushNamed(context, "/login");
}, child: const Text("登录")),
const SizedBox(height: 40),
ElevatedButton(onPressed: () {
}, child: const Text("注册")),
],
),
);
}
}
(3) main.dart
import './routes/routes.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
// home: const Tabs(),
initialRoute: "/", // 初始化路由
onGenerateRoute: onGenerateRoute, // 固定写法,配置onGenerateRoute
);
}
}
(4) login.dart
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("登录页面")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("登录跳转演示,执行登录后返回到上一个页面"),
const SizedBox(height: 40),
ElevatedButton(onPressed: () {
Navigator.of(context).pop(); // 返回到上一级页面
}, child: const Text("执行登录")),
],
),
),
);
}
}
(5) home.dart
import 'package:flutter/material.dart';
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("首页展示"),
);
}
}
(6) routes.dart
import '../pages/tabs.dart';
import '../pages/user/login.dart';
import 'package:flutter/material.dart';
Map routes = { // 定义一个Map类型的路由
"/": (context) => const Tabs(),
"/login": (context) => const LoginPage(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
final String? name = settings.name; // 统一处理
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(
context, arguments: settings.arguments
)
);
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context)
);
return route;
}
}
return null;
};
(7) setting.dart
import 'package:flutter/material.dart';
class SettingPage extends StatefulWidget {
const SettingPage({Key? key}) : super(key: key);
State<SettingPage> createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
Widget build(BuildContext context) {
return const Center(
child: Text("系统设置"),
);
}
}
(8) category.dart
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
const CategoryPage({Key? key}) : super(key: key);
State<CategoryPage> createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
Widget build(BuildContext context) {
return const Center(
child: Text("分类页面"),
);
}
}
7-7 Flutter中替换路由
- 比如:registerFirst > registerSecond > registerThird > registerFirst。
- registerSecond页,通过pushReplacementNamed()跳转到registerThird页。
Navigator.of(context).pushReplacementNamed('/registerSecond');
。- registerThird页,通过点击registerThird的返回按钮,返回registerFirst页。
# 例如:myflutter(项目名)目录结构如下,修改“返回到上一级路由”代码
# - pages/
# - tabs/
# - user.dart【✔】
# - home.dart
# - setting.dart
# - category.dart
# - user/
# - login.dart
# - registerFirst.dart【✔】
# - registerSecond.dart【✔】
# - registerThird.dart【✔】
# - tabs.dart【✔】
# - routes
# - routes.dart【✔】
# - main.dart
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Flutter中替换路由")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) user.dart
import 'package:flutter/material.dart';
class UserPage extends StatefulWidget {
const UserPage({Key? key}) : super(key: key);
State<UserPage> createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: () {
print("执行跳转");
Navigator.pushNamed(context, "/login");
}, child: const Text("登录")),
const SizedBox(height: 40),
ElevatedButton(onPressed: () {
Navigator.pushNamed(context, "/registerFirst");
}, child: const Text("注册")),
],
),
);
}
}
(3) routes.dart
import '../pages/tabs.dart';
import '../pages/user/login.dart';
import 'package:flutter/material.dart';
import '../pages/user/registerThird.dart';
import '../pages/user/registerFirst.dart';
import '../pages/user/registerSecond.dart';
Map routes = { // 定义一个Map类型的路由
"/": (context) => const Tabs(),
"/login": (context) => const LoginPage(),
"/registerThird": (context) => const RegisterThirdPage(),
"/registerFirst": (context) => const RegisterFirstPage(),
"/registerSecond": (context) => const RegisterSecondPage(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
final String? name = settings.name; // 统一处理
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(
context, arguments: settings.arguments
)
);
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context)
);
return route;
}
}
return null;
};
(4) registerFirst.dart
import 'package:flutter/material.dart';
class RegisterFirstPage extends StatefulWidget {
const RegisterFirstPage({Key? key}) : super(key: key);
State<RegisterFirstPage> createState() => _RegisterFirstPageState();
}
class _RegisterFirstPageState extends State<RegisterFirstPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("注册第一步")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("注册第一步"),
const SizedBox(height: 40),
ElevatedButton(onPressed: () {
Navigator.pushNamed(context, "/registerSecond");
}, child: const Text("下一步")),
],
),
),
);
}
}
(5) registerThird.dart
import 'package:flutter/material.dart';
class RegisterThirdPage extends StatefulWidget {
const RegisterThirdPage({Key? key}) : super(key: key);
State<RegisterThirdPage> createState() => _RegisterThirdPageState();
}
class _RegisterThirdPageState extends State<RegisterThirdPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("注册第三步")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("注册第三步"),
const SizedBox(height: 40),
ElevatedButton(onPressed: () {
}, child: const Text("完成注册")),
],
),
),
);
}
}
(6) registerSecond.dart
import 'package:flutter/material.dart';
class RegisterSecondPage extends StatefulWidget {
const RegisterSecondPage({Key? key}) : super(key: key);
State<RegisterSecondPage> createState() => _RegisterSecondPageState();
}
class _RegisterSecondPageState extends State<RegisterSecondPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("注册第二步")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("注册第二步"),
const SizedBox(height: 40),
ElevatedButton(onPressed: () {
// 命名路由跳转
// Navigator.pushNamed(context, "/registerThird");
// 替换路由跳转
Navigator.of(context).pushReplacementNamed("/registerThird");
}, child: const Text("下一步")),
],
),
),
);
}
}
7-8 返回到根路由方法
- 用户中心 > registerFirst > registerSecond > registerThird > 用户中心。
- registerThird注册成功后返回用户中心,此时需要用返回到根路由的方法。
# 例如:myflutter(项目名)目录结构如下,修改“Flutter中替换路由”代码
# - pages/
# - tabs/
# - user.dart
# - home.dart
# - setting.dart
# - category.dart
# - user/
# - login.dart
# - registerFirst.dart
# - registerSecond.dart
# - registerThird.dart【✔】
# - tabs.dart【✔】
# - routes
# - routes.dart
# - main.dart【✔】
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
final int index;
const Tabs({Key? key, this.index=0}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
void initState() {
super.initState();
_currentIndex=widget.index;
}
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("返回到根路由方法")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) main.dart
import './routes/routes.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
// home: const Tabs(),
initialRoute: "/login", // 初始化路由,首次运行加载登录页
onGenerateRoute: onGenerateRoute, // 固定写法,配置onGenerateRoute
);
}
}
(3) registerThird.dart
import '../tabs.dart';
import 'package:flutter/material.dart';
class RegisterThirdPage extends StatefulWidget {
const RegisterThirdPage({Key? key}) : super(key: key);
State<RegisterThirdPage> createState() => _RegisterThirdPageState();
}
class _RegisterThirdPageState extends State<RegisterThirdPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("注册第三步")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("注册第三步"),
const SizedBox(height: 40),
ElevatedButton(onPressed: () {
Navigator.of(context).pushAndRemoveUntil( // 返回到根页面
MaterialPageRoute(builder: (BuildContext context) {
return const Tabs(index: 3); // index指定返回的页面
}), (route) => false
);
}, child: const Text("完成注册")),
],
),
),
);
}
}
7-9 同风格的路由跳转
- MaterialPageRoute组件可以使用和平台风格一致的路由切换动画。
- 例如在iOS上会左右滑动切换,而在Android上则会上下滑动切换。
- CupertinoPageRoute是Cupertino组件库提供的iOS风格的路由切换组件。
- 如果在Android上也想使用左右切换风格,可以使用CupertinoPageRoute。
- 在iOS中appBarTheme的标题是自动居中的,而在Android中是自动居左的。
# 例如:myflutter(项目名)目录结构如下,修改“返回到根路由方法”代码
# - pages/
# - tabs/
# - user.dart
# - home.dart
# - setting.dart
# - category.dart
# - user/
# - login.dart
# - registerFirst.dart
# - registerSecond.dart
# - registerThird.dart
# - tabs.dart【✔】
# - routes
# - routes.dart【✔】
# - main.dart【✔】
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
final int index;
const Tabs({Key? key, this.index=0}) : super(key: key);
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
void initState() {
super.initState();
_currentIndex=widget.index;
}
final List<Widget> _pages = const [
HomePage(), CategoryPage(), SettingPage(), UserPage(),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("同风格的路由跳转")),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.red, // 选中的颜色
iconSize: 40, // 配置底部菜单大小
type: BottomNavigationBarType.fixed, // 4个以上菜单需配置此项
// currentIndex: 1, // 默认选中分类(第二个)
currentIndex: _currentIndex,
onTap: (index){
// print(index); // 获取点击的索引值
setState(() { // 注意调用setState
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon:Icon(Icons.home),
label: "首页",
),
BottomNavigationBarItem(
icon:Icon(Icons.category),
label: "分类",
),
BottomNavigationBarItem(
icon:Icon(Icons.settings),
label: "设置",
),
BottomNavigationBarItem( // 没有type菜单图标会被挤掉
icon:Icon(Icons.people),
label: "用户",
),
],
),
);
}
}
(2) main.dart
import './routes/routes.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // 去掉右上角debug图标
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: const AppBarTheme( // 全局配置主题
centerTitle: true // 主题标题全部居中显示
),
),
// home: const Tabs(),
initialRoute: "/login", // 初始化路由,首次运行加载登录页
onGenerateRoute: onGenerateRoute, // 固定写法,配置onGenerateRoute
);
}
}
(3) routes.dart
import '../pages/tabs.dart';
import '../pages/user/login.dart';
import 'package:flutter/cupertino.dart'; // 配置iOS风格的路由,引入库
// import 'package:flutter/material.dart';
import '../pages/user/registerThird.dart';
import '../pages/user/registerFirst.dart';
import '../pages/user/registerSecond.dart';
Map routes = { // 定义一个Map类型的路由
"/": (context) => const Tabs(),
"/login": (context) => const LoginPage(),
"/registerThird": (context) => const RegisterThirdPage(),
"/registerFirst": (context) => const RegisterFirstPage(),
"/registerSecond": (context) => const RegisterSecondPage(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
final String? name = settings.name; // 统一处理
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
// final Route route = MaterialPageRoute(
final Route route = CupertinoPageRoute( // 替换MaterialPageRoute
builder: (context) => pageContentBuilder(
context, arguments: settings.arguments
)
);
return route;
} else {
// final Route route = MaterialPageRoute(
final Route route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context)
);
return route;
}
}
return null;
};