1. 初始化项目
- 项目目录
- 入口文件与入口方法
- Material Design (Google 推出的前端 UI解决方案)
- 官网: m3.material.io/
- 中文网: material-io.cn/
- Flutter 中一切内容都是组件(Widget)
- 无状态组件(
StatelessWidget) - 有状态组件(
StatefulWidget)
- 无状态组件(
1.1. 体验 Flutter
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return Container(
child: const Center(
child: Text('Hello Flutter', textDirection: TextDirection.ltr),
),
);
}
}
1.2. APP 结构
MaterialApptitle(任务管理器中的标题)home(主内容)debugShowCheckedModeBanner(是否显示左上角调试标记)
ScaffoldappBar(应用头部)body(应用主体)floatingActionButton(浮动按钮)drawer(左侧抽屉菜单)endDrawer(右侧抽屉菜单)
// main.dart
import 'package:flutter/material.dart';
import '01_basic/01_Hello.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: '学习 Flutter',
home: Home(),
// 是否显示调试标记
debugShowCheckedModeBanner: false,
);
}
}
// 01_basic/01_Hello.dart
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('首页'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const HelloFlutter(),
);
}
}
class HelloFlutter extends StatelessWidget {
const HelloFlutter({super.key});
@override
Widget build(BuildContext context) {
return Container(
child: const Center(
child: Text('Hello Flutter', textDirection: TextDirection.ltr),
),
);
}
}
2. 基础组件
2.1. 常用
2.1.1. 文本 Text
- Text
TextDirection(文本方向)TextStyle(文本样式)Colors(颜色)FontWeight(字体粗细)FontStyle(字体样式)
TextAlign(文本对齐)TextOverflow(文本溢出)maxLines(指定显示的行数)
- RichText 与 TextSpan
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('Text 文本'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const TextDemo(),
);
}
}
class TextDemo extends StatelessWidget {
const TextDemo({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text(
'知识有两种,一种是你知道的,一种是你知道在哪里能找到的!',
textDirection: TextDirection.ltr,
// 指定文本样式
style: TextStyle(
// 指定文字大小
fontSize: 30.0,
// 指定文字颜色
color: Colors.red,
// 文字粗细
fontWeight: FontWeight.w500,
// 斜体字体
fontStyle: FontStyle.italic,
// 文本修饰,中划线
decoration: TextDecoration.lineThrough,
// 修饰颜色
decorationColor: Colors.blue,
),
// 文本对齐方式
textAlign: TextAlign.center,
// 显示的行数
maxLines: 2,
// 文本溢出
overflow: TextOverflow.ellipsis,
// 文本缩放比例
textScaleFactor: 1.5,
),
// RichText 里面可以包含多个不同的文本样式
RichText(
// TextSpan 类似与 html 中的 span 标签
text: const TextSpan(
text: 'Hello',
style: TextStyle(
fontSize: 40.0,
color: Colors.red,
),
children: [
TextSpan(
text: ' Flutter!',
style: TextStyle(
fontSize: 40.0,
color: Colors.blue,
),
),
TextSpan(
text: '你好世界',
style: TextStyle(
fontSize: 30.0,
color: Colors.black45,
),
),
],
)),
],
);
}
}
2.1.2. 设置自定义字体
- 下载并导入字体
- fonts.google.com/
- 解压压缩包,将字体文件复制到 Flutter 项目中
- 在
pubspec.yaml中声明字体 - 使用
- 为整个应用设置默认自定义字体
- 为某个组件设置自定义字体
// pubspec.yaml
flutter:
fonts:
- family: CustomFamily
fonts:
- asset: fonts/FZSJ-QINGYAXK.TTF
weight: 300
- family: MyFamily
fonts:
- asset: fonts/FZZJ-DHTCSJW.TTF
weight: 300
import 'package:flutter/material.dart';
// import '01_basic/01_Hello.dart';
import '01_basic/02_text.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '学习 Flutter',
home: const Home(),
// 设置全局主题
theme: ThemeData(
// 自定义文本样式
fontFamily: 'CustomFamily',
),
// 是否显示调试标记
debugShowCheckedModeBanner: false,
);
}
}
TextSpan(
text: '你好世界',
style: TextStyle(
fontSize: 30.0,
color: Colors.black45,
// 给组件设置自定义字体
fontFamily: 'MyFamily',
),
),
2.1.3. Icon
- Icon
- Flutter 中的图标库
Icon(Icons.具体名称)
- 在线预览
2.1.4. Color
- Color (自定义颜色)
- Flutter 中通过
ARGB来声明颜色 const Color(0xFF42A5F5); // 16进制的ARGB = 透明度+六位十六进制颜色const Color.fromARGB(0xFF, Ox42, 0xA5, 0xF5);const Color.fromARGB(255, 66,165,245);const Color.fromRGBO(66, 165, 245, 1.0); // O = Opacity
- Flutter 中通过
- Colors (英文字母声明的颜色)
Colors.red
RichText(
// TextSpan 类似与 html 中的 span 标签
text: const TextSpan(
text: 'Hello',
style: TextStyle(
fontSize: 40.0,
color: Color.fromARGB(255, 0, 0, 255),
),
children: [
TextSpan(
text: ' Flutter!',
style: TextStyle(
fontSize: 40.0,
color: Color.fromRGBO(255, 0, 255, 0.5),
),
),
TextSpan(
text: '你好世界',
style: TextStyle(
fontSize: 30.0,
color: Colors.black45,
// 给组件设置自定义字体
fontFamily: 'MyFamily',
),
),
],
)),
2.2. 布局
2.2.1. Container(类似于 div)
child(声明子组件)padding(margin)Edgelnsets(all()、 fromLTRB()、 only())
decorationBoxDecoration(边框、圆角、渐变、阴影、背景色、背景图片)
alignmentAlignment(内容对齐)
transformMatrix4 (平移-translate、旋转-rotate、缩放-scale、斜切-skew)
class ContainerDemo extends StatelessWidget {
const ContainerDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
// 设置内边距
padding: const EdgeInsets.all(10.0),
margin: const EdgeInsets.fromLTRB(10.0, 30.0, 0.0, 5.0),
// 内容的对齐方式
alignment: Alignment.center,
// 平移
// transform: Matrix4.translationValues(100.0, 0, 0),
// 旋转
// transform: Matrix4.rotationZ(-0.1),
// 斜切
transform: Matrix4.skew(0.2, 0.1),
decoration: BoxDecoration(
// 设置边框
// border: Border(
// top: BorderSide(
// width: 10.0,
// color: Colors.red,
// ),
// bottom: BorderSide(
// width: 10.0,
// color: Colors.red,
// ),
// right: BorderSide(
// width: 10.0,
// color: Colors.red,
// ),
// left: BorderSide(
// width: 10.0,
// color: Colors.red,
// ),
// ),
border: Border.all(
width: 10.0,
color: Colors.blue,
),
// 设置边框圆角
// borderRadius: BorderRadius.all(Radius.circular(30.0)),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(30.0),
),
// 设置背景颜色
color: Colors.lightGreen[100],
// 设置渐变后,背景色会失效
gradient: const LinearGradient(
colors: [
Colors.lightBlue,
Colors.white12,
]
)
),
child: const Text(
'此诗是高适与董大久别重逢,经过短暂的聚会以后,又各奔他方的赠别之作。作品勾勒了送别时晦暗寒冷的愁人景色,表现了诗人当时处在困顿不达的境遇之中,但没有因此沮丧、沉沦,既表露出诗人对友人远行的依依惜别之情,也展现出诗人豪迈豁达的胸襟。',
style: TextStyle(
fontSize: 20,
),
),
);
}
}
2.2.2. 线性布局
- Column
Column中的主轴方向是垂直方向mainAxisAlignment: MainAxisAlignment主轴对产方式crossAxisAlignment: CrossAxisAlignment交叉抽对产方式children: 内容
- Row
Row中的主轴方向是水平方向 (其他属性与Column一致)
class ColumnRowDemo extends StatelessWidget {
const ColumnRowDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
// 设置盒子的背景色
color: Colors.lightGreen,
child: Column(
// 主轴的对齐方式
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// 交叉轴的对齐方式
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.access_alarm, size: 50),
const Icon(Icons.search, size: 50),
const Icon(Icons.settings, size: 50),
const Icon(Icons.add_a_photo, size: 50),
Container(
height: 140,
color: Colors.red[100],
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.access_alarm, size: 50),
Icon(Icons.search, size: 50),
Icon(Icons.settings, size: 50),
Icon(Icons.add_a_photo, size: 50),
],
),
),
],
),
);
}
}
2.2.3. 弹性布局
- Flex
direction(声明主轴方向)mainAxisAlignment(声明主轴对齐方式)textDirection(声明水平方向的排列顺序)crossAxisAlignment(声明交叉轴对产方式)verticalDirection(声明垂直方向的排列顺序)children(声明子组件)
- Expanded (可伸缩组件)
flex(声明弹性布局所占比例)child(声明子组件)
class FlexDemo extends StatelessWidget {
const FlexDemo({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
// 验证 Expanded
Row(
children: [
Container(
width: 100,
height: 50,
color: Colors.lightBlue,
),
Expanded(child: Container(
height: 50,
color: Colors.lightGreen,
)),
],
),
const Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceAround,
// 声明水平方向的排列方式
textDirection: TextDirection.rtl,
children: [
Icon(Icons.access_alarm, size: 50),
Icon(Icons.search, size: 50),
Icon(Icons.settings, size: 50),
Icon(Icons.add_a_photo, size: 50),
],
),
Flex(
direction: Axis.horizontal,
children: [
Expanded(
flex: 2,
child: Container(
height: 50,
color: Colors.amber,
),
),
Expanded(
flex: 1,
child: Container(
height: 50,
color: Colors.deepOrangeAccent,
),
)
],
),
Container(
height: 100,
margin: const EdgeInsets.all(50),
child: Flex(
direction: Axis.vertical,
// 声明垂直方向的排列顺序
verticalDirection: VerticalDirection.up,
children: [
Expanded(
flex: 2,
child: Container(
height: 50,
color: Colors.amber,
),
),
// Spacer 间隙组件
const Spacer(
flex: 1,
),
Expanded(
flex: 1,
child: Container(
height: 50,
color: Colors.deepOrangeAccent,
),
)
],
),
),
],
);
}
}
2.2.4. 流式布局
- Wrap (解决内容溢出问题)
spacing(主轴方向子组件的间距)alignment(主轴方向的对齐方式)runSpacing(纵轴方向子组件的间距)runAlignment(纵轴方向的对产方式)
- chip (标签)
- CircleAvatar (圆形头像)
class WrapDemo extends StatelessWidget {
const WrapDemo({super.key});
@override
Widget build(BuildContext context) {
List<String> _list = ['曹操', '司马懿', '曹仁', '曹洪', '张辽', '许褚'];
List<Widget> _weiguo() {
return _list.map((item) => Chip(
avatar: const CircleAvatar(
backgroundColor: Colors.pink,
child: Text('魏'),
),
label: Text(item),
)).toList();
}
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Wrap(
spacing: 18.0, // 水平方向的间隙
runSpacing: 10.0, // 垂直方向的间隙
alignment: WrapAlignment.spaceAround, // 主轴方向的对齐方式
runAlignment: WrapAlignment.spaceAround, // 交叉轴的对齐方式
children: _weiguo(),
),
const Wrap(
children: [
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.blue,
child: Text('蜀'),
),
label: Text('刘备'),
),
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.blue,
child: Text('蜀'),
),
label: Text('关羽'),
),
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.blue,
child: Text('蜀'),
),
label: Text('张飞'),
),
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.blue,
child: Text('蜀'),
),
label: Text('赵云'),
),
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.blue,
child: Text('蜀'),
),
label: Text('诸葛亮'),
),
Chip(
avatar: CircleAvatar(
backgroundColor: Colors.blue,
child: Text('蜀'),
),
label: Text('黄忠'),
),
],
),
],
);
}
}
2.2.5. 层叠布局
- Stack (层叠组件 - 类似 css 中的
z-index)alignment(声明未定位子组件的对齐方式)textDirection(声明未定位子组件的排列顺序)
- Positioned (绝对定位组件)
child(声明子组件)left、top、right、bottomwidth、height
- Networklmage (网络图片组件)
NetworkImage('图片地址')<uses-permission android:name="android.permission.INTERNET" />
class StackDemo extends StatelessWidget {
const StackDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey[300],
child: Stack(
// 声明未定位的子组件的排序方式
textDirection: TextDirection.rtl,
// 声明未定位的子组件的对齐方式
alignment: AlignmentDirectional.bottomCenter,
children: [
const CircleAvatar(
backgroundImage: NetworkImage(
'https://i2.hdslb.com/bfs/sycp/creative_img/202206/fe08712b4ba1e3aa7bbf2b8af6c0c6c9.jpg'),
radius: 200,
),
Positioned(
top: 50,
right: 40,
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'热卖',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
),
const Text(
'Hello',
style: TextStyle(
fontSize: 20,
),
),
],
),
);
}
}
2.2.6. Card
- card (卡片)
child子组件color背景色shadowColor阴影色elevation阴影高度shape边框样式margin外边距
- ListTile (列表瓦片)
leading(头部组件)title(标题)subtitle(子标题)
class CardDemo extends StatelessWidget {
const CardDemo({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
Card(
margin: const EdgeInsets.all(14),
color: Colors.lightBlue,
// 阴影颜色
shadowColor: Colors.grey,
// 阴影高度
elevation: 20,
// 设置卡片边框
shape: RoundedRectangleBorder(
// 边框圆角
borderRadius: BorderRadius.circular(30),
side: const BorderSide(
color: Colors.pink,
width: 10,
),
),
child: const Column(
children: [
ListTile(
leading: Icon(Icons.verified_user_outlined, size: 50),
title: Text('张三',
style: TextStyle(
fontSize: 20,
)),
subtitle: Text('董事长',
style: TextStyle(
fontSize: 12,
)),
),
// 分割线组件
Divider(),
ListTile(
title: Text('电话:133333333333',
style: TextStyle(
fontSize: 20,
)),
),
ListTile(
title: Text('地址:xxxxxx',
style: TextStyle(
fontSize: 20,
)),
),
],
),
),
const Card(
margin: EdgeInsets.all(14),
child: Column(
children: [
ListTile(
leading: Icon(Icons.verified_user_outlined, size: 50),
title: Text('李四',
style: TextStyle(
fontSize: 20,
)),
subtitle: Text('董事长',
style: TextStyle(
fontSize: 12,
)),
),
// 分割线组件
Divider(),
ListTile(
title: Text('电话:133333333333',
style: TextStyle(
fontSize: 20,
)),
),
ListTile(
title: Text('地址:xxxxxx',
style: TextStyle(
fontSize: 20,
)),
),
],
),
),
],
);
}
}
2.3. 按钮
-
Flutter 1.22 之前
FlatButton(扁平按钮)RaisedButton(凸起按钮)OutlineButton(轮廓按钮)
-
Flutter 1.22 之后
TextButton(文本按钮-用来替换 FlatButton)ElevatedButton(凸起按钮-用来替换 RaisedButton)OutlinedButton(轮廓按钮-用来替换 OutlineButton)
-
按钮主题
-
按钮类型
- 图标按钮
IconButtonTextButton.icon()ElevatedButton.icon()OutlinedButton.icon()
ButtonBar(按钮组)FloatingActionButton(浮动按钮)BackButton(回退按钮)CloseButton(关闭按钮)
- 图标按钮
class ButtonDemo extends StatelessWidget {
const ButtonDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10),
child: Wrap(
children: [
TextButton(
onPressed: () {
print('点击了 TextButton');
},
onLongPress: () {
print('长按 onLongPress');
},
child: const Text('TextButton'),
),
ElevatedButton(
onPressed: () {
print('点击了 ElevatedButton');
},
onLongPress: () {
print('长按 onLongPress');
},
child: const Text('ElevatedButton'),
),
OutlinedButton(
onPressed: () {
print('点击了 OutlinedButton');
},
onLongPress: () {
print('长按 onLongPress');
},
child: const Text('OutlinedButton'),
),
],
),
);
}
}
2.3.1. 按钮的相关样式
OutlinedButton(
onPressed: () {
print('点击了 OutlinedButton');
},
onLongPress: () {
print('长按 onLongPress');
},
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
const TextStyle(
fontSize: 30,
),
),
foregroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
// 按下按钮的前景色
return Colors.red;
}
// 默认状态的颜色
return Colors.blue;
}),
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
// 按下按钮的背景色
return Colors.yellow;
}
// 默认状态的颜色
return Colors.white;
}),
shadowColor: MaterialStateProperty.all(Colors.yellow),
elevation: MaterialStateProperty.all(20),
side: MaterialStateProperty.all(
const BorderSide(
color: Colors.green,
width: 2,
),
),
// 声明按钮形状
shape: MaterialStateProperty.all(
const StadiumBorder(
side: BorderSide(
color: Colors.green,
width: 2,
),
),
),
// 设置按钮大小
minimumSize: MaterialStateProperty.all(const Size(200, 100)),
// 设置水波纹的颜色
overlayColor: MaterialStateProperty.all(Colors.amber),
),
child: const Text('轮廓按钮'),
)
2.3.2. 其他按钮
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('卡片'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const ButtonDemo(),
// 浮动按钮
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: const Icon(Icons.add),
backgroundColor: Colors.pink[100],
elevation: 0,
),
);
}
}
class ButtonDemo extends StatelessWidget {
const ButtonDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10),
child: Wrap(
children: [
TextButton(
onPressed: () {
print('点击了 TextButton');
},
onLongPress: () {
print('长按 onLongPress');
},
child: const Text('TextButton'),
),
ElevatedButton(
onPressed: () {
print('点击了 ElevatedButton');
},
onLongPress: () {
print('长按 onLongPress');
},
child: const Text('ElevatedButton'),
),
OutlinedButton(
onPressed: () {
print('点击了 OutlinedButton');
},
onLongPress: () {
print('长按 onLongPress');
},
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
const TextStyle(
fontSize: 30,
),
),
foregroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
// 按下按钮的前景色
return Colors.red;
}
// 默认状态的颜色
return Colors.blue;
}),
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
// 按下按钮的背景色
return Colors.yellow;
}
// 默认状态的颜色
return Colors.white;
}),
shadowColor: MaterialStateProperty.all(Colors.yellow),
elevation: MaterialStateProperty.all(20),
side: MaterialStateProperty.all(
const BorderSide(
color: Colors.green,
width: 2,
),
),
// 声明按钮形状
shape: MaterialStateProperty.all(
const StadiumBorder(
side: BorderSide(
color: Colors.green,
width: 2,
),
),
),
// 设置按钮大小
minimumSize: MaterialStateProperty.all(const Size(200, 100)),
// 设置水波纹的颜色
overlayColor: MaterialStateProperty.all(Colors.amber),
),
child: const Text('轮廓按钮'),
),
// 通过 OutlinedButtonTheme 给按钮设置样式
OutlinedButtonTheme(
data: OutlinedButtonThemeData(
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(Colors.red),
),
),
child: OutlinedButton(
onPressed: () {
print('点击了 OutlinedButton');
},
onLongPress: () {
print('长按了 OutlinedButton');
},
// 内部的样式会覆盖 buttonTheme 的样式
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(Colors.blue),
),
child: const Text('轮廓按钮'),
)),
// IconButton
IconButton(
// 图标颜色
color: Colors.red,
// 水波纹颜色
splashColor: Colors.lightBlue,
// 高亮时的颜色
highlightColor: Colors.purple,
tooltip: '长按提示文字',
onPressed: () {
print('点击了 IconButton');
},
icon: const Icon(Icons.add_a_photo)),
TextButton.icon(
onPressed: () {
print('点击了 TextButton.icon');
},
icon: const Icon(Icons.add_circle),
label: const Text('文本按钮')
),
ElevatedButton.icon(
onPressed: () {
print('点击了 TextButton.icon');
},
icon: const Icon(Icons.add_circle),
label: const Text('凸起按钮')
),
OutlinedButton.icon(
onPressed: () {
print('点击了 TextButton.icon');
},
icon: const Icon(Icons.add_circle),
label: const Text('轮廓按钮')
),
// 按钮组
Container(
color: Colors.pink[100],
width: double.infinity,
// ButtonBar 水平方向显示不全时,会纵向排列
child: ButtonBar(
children: [
ElevatedButton(
onPressed: () {},
child: const Text('按钮一'),
),
ElevatedButton(
onPressed: () {},
child: const Text('按钮二'),
),
ElevatedButton(
onPressed: () {},
child: const Text('按钮二'),
),
ElevatedButton(
onPressed: () {},
child: const Text('按钮二'),
),
ElevatedButton(
onPressed: () {},
child: const Text('按钮二'),
),
],
),
),
// 返回按钮
BackButton(
color: Colors.red,
onPressed: () {},
),
// 关闭按钮
CloseButton(
color: Colors.red,
onPressed: () {},
),
],
),
);
}
}
2.4. 图片
lmage.asset(加载本地图片)- Flutter 项目下,创建图片存储目录
- 在 pubspec.yaml中的 flutter 部分添加图片配置
- 在代码中加载图片
lmage.network(加载网络图片)- 保证网络畅通
- 设置网络访问权限
- 允许 http 协议访问
class ImageDemo extends StatelessWidget {
const ImageDemo({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
// 加载本地图片
Image.asset(
'images/1.jpg',
width: 200,
height: 200,
fit: BoxFit.cover,
),
// 加载网络图片
Image.network(
'https://pic.netbian.com/uploads/allimg/230922/164533-16953723335065.jpg',
repeat: ImageRepeat.repeat,
// 颜色混合模式
colorBlendMode: BlendMode.colorDodge,
color: Colors.red,
),
Image.asset('images/2.webp'),
// Image.asset('images/客厅.png'),
],
);
}
}
2.5. 列表
2.5.1. SingleChildScrollView
SingleChildScrollView(类似Android 中的 Scrollview)child(子组件)padding(内边距)scrollDirection(滚动方向:Axis.horizontalAxis.vertical)reverse(初始滚动位置,false头部、true尾部)physicsClampingScrollPhysics: Android 下微光效果BouncingScrollPhysics: ios 下弹性效果
class SingleChildScrollViewDemo extends StatelessWidget {
const SingleChildScrollViewDemo({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: [
// 验证水平滚动
SingleChildScrollView(
// 设置滚动方向
scrollDirection: Axis.horizontal,
// 设置内边距
padding: const EdgeInsets.all(10),
// 从尾部开始滚动
reverse: true,
child: Row(
children: [
OutlinedButton(
onPressed: () {},
child: const Text('按钮一'),
),
OutlinedButton(
onPressed: () {},
child: const Text('按钮二'),
),
OutlinedButton(
onPressed: () {},
child: const Text('按钮三'),
),
OutlinedButton(
onPressed: () {},
child: const Text('按钮四'),
),
OutlinedButton(
onPressed: () {},
child: const Text('按钮五'),
),
OutlinedButton(
onPressed: () {},
child: const Text('按钮六'),
),
],
),
),
// 验证垂直滚动
SingleChildScrollView(
// 设置滚动方向
scrollDirection: Axis.vertical,
// 设置内边距
padding: const EdgeInsets.all(10),
// 从头部开始滚动
reverse: false,
// 弹性下拉效果
physics: const BouncingScrollPhysics(),
child: Column(
children: List.generate(100, (index) => OutlinedButton(
onPressed: () {},
child: Text('按钮$index'),
)),
),
),
],
);
}
}
2.5.2. ListView
ListView- 加载列表的组件 (加载所有 Widgets,适用 Widget 较少的场景)
ListTile(leading、title、subtitle、trailing、selected)
ListView.builder- 性能比默认构造函数高,适用 widget 较多的场景按需加载 Widget,
ListView.separated- 也可以按需加载,比
ListView.builder多了分隔器
- 也可以按需加载,比
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('ListView'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const ListViewDemo(),
);
}
}
class ListViewDemo extends StatelessWidget {
const ListViewDemo({super.key});
@override
Widget build(BuildContext context) {
return const SingleChildScrollView(
child: Column(
children: [
ListViewBasic(),
ListViewHorizontal(),
],
),
);
}
}
class ListViewBasic extends StatelessWidget {
const ListViewBasic({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
child: ListView(
// 设置滚动方向
scrollDirection: Axis.vertical,
children: [
ListTile(
leading: const Icon(
Icons.access_alarm,
size: 50,
),
title: const Text('标题'),
subtitle: const Text('子标题'),
trailing: const Icon(Icons.keyboard_arrow_right),
selected: true,
selectedTileColor: Colors.blue[100],
),
const ListTile(
leading: Icon(
Icons.audio_file,
size: 50,
),
title: Text('标题'),
subtitle: Text('子标题'),
trailing: Icon(Icons.keyboard_arrow_right),
),
const ListTile(
leading: Icon(
Icons.notifications,
size: 50,
),
title: Text('标题'),
subtitle: Text('子标题'),
trailing: Icon(Icons.keyboard_arrow_right),
),
const ListTile(
leading: Icon(
Icons.chat,
size: 50,
),
title: Text('标题'),
subtitle: Text('子标题'),
trailing: Icon(Icons.keyboard_arrow_right),
),
const ListTile(
leading: Icon(
Icons.person,
size: 50,
),
title: Text('标题'),
subtitle: Text('子标题'),
trailing: Icon(Icons.keyboard_arrow_right),
),
],
),
);
}
}
class ListViewHorizontal extends StatelessWidget {
const ListViewHorizontal({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 100,
color: Colors.white,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
width: 160,
color: Colors.amber,
),
Container(
width: 160,
color: Colors.red,
),
Container(
width: 160,
color: Colors.green,
),
Container(
width: 160,
color: Colors.pink,
),
Container(
width: 160,
color: Colors.purple,
),
],
),
);
}
}
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('ListView'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const ListViewDemo(),
);
}
}
class ListViewDemo extends StatelessWidget {
const ListViewDemo({super.key});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
const ListViewBasic(),
const ListViewHorizontal(),
ListViewBuilderDemo(),
ListViewSeperatedDemo(),
],
),
);
}
}
class ListViewBasic extends StatelessWidget {
const ListViewBasic({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
child: ListView(
// 设置滚动方向
scrollDirection: Axis.vertical,
children: [
ListTile(
leading: const Icon(
Icons.access_alarm,
size: 50,
),
title: const Text('标题'),
subtitle: const Text('子标题'),
trailing: const Icon(Icons.keyboard_arrow_right),
selected: true,
selectedTileColor: Colors.blue[100],
),
const ListTile(
leading: Icon(
Icons.audio_file,
size: 50,
),
title: Text('标题'),
subtitle: Text('子标题'),
trailing: Icon(Icons.keyboard_arrow_right),
),
const ListTile(
leading: Icon(
Icons.notifications,
size: 50,
),
title: Text('标题'),
subtitle: Text('子标题'),
trailing: Icon(Icons.keyboard_arrow_right),
),
const ListTile(
leading: Icon(
Icons.chat,
size: 50,
),
title: Text('标题'),
subtitle: Text('子标题'),
trailing: Icon(Icons.keyboard_arrow_right),
),
const ListTile(
leading: Icon(
Icons.person,
size: 50,
),
title: Text('标题'),
subtitle: Text('子标题'),
trailing: Icon(Icons.keyboard_arrow_right),
),
],
),
);
}
}
class ListViewHorizontal extends StatelessWidget {
const ListViewHorizontal({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 100,
color: Colors.white,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
width: 160,
color: Colors.amber,
),
Container(
width: 160,
color: Colors.red,
),
Container(
width: 160,
color: Colors.green,
),
Container(
width: 160,
color: Colors.pink,
),
Container(
width: 160,
color: Colors.purple,
),
],
),
);
}
}
class ListViewBuilderDemo extends StatelessWidget {
ListViewBuilderDemo({super.key});
final List<Widget> users = List<Widget>.generate(
20,
(index) => OutlinedButton(
onPressed: () {},
child: Text('姓名 $index'),
));
@override
Widget build(BuildContext context) {
return Container(
height: 150,
color: Colors.white,
child: ListView.builder(
itemCount: users.length,
// 当前元素的高度
itemExtent: 30,
padding: const EdgeInsets.all(10),
itemBuilder: (context, index) {
return users[index];
},
),
);
}
}
class ListViewSeperatedDemo extends StatelessWidget {
ListViewSeperatedDemo({super.key});
final List<Widget> products = List.generate(
20,
(index) => ListTile(
leading: Image.asset('images/1.jpg'),
title: Text('商品标题 $index'),
subtitle: const Text('子标题'),
trailing: const Icon(Icons.keyboard_arrow_right),
));
@override
Widget build(BuildContext context) {
Widget dividerOdd = const Divider(
color: Colors.blue,
thickness: 2,
);
Widget dividerEven = const Divider(
color: Colors.red,
thickness: 2,
);
return Column(
children: [
const ListTile(
title: Text('商品列表'),
),
Container(
height: 200,
color: Colors.white,
child: ListView.separated(
itemBuilder: (context, index) {
return products[index];
},
itemCount: products.length,
// 分割器的构造器
separatorBuilder: (context, index) {
return index % 2 == 0 ? dividerEven : dividerOdd;
},
),
),
],
);
}
}
2.5.3. GridView
GridView(网格布局)children(子组件)scrollDirection(滚动方向).gridDelegateSliverGridDelegateWithFixedCrossAxisCount(指定列数 - 子组件宽度自适应)SliverGridDelegateWithMaxCrossAxisExtent(指定子组件宽度 - 列数自适应)
GridView.count(列数固定)GridView.extend(子组件宽度固定)GridView.builder(动态网格布局)
class GridViewDemo extends StatelessWidget {
const GridViewDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10),
color: Colors.grey[200],
child:
// 固定列数
// GridView(
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 2, // 指定列数
// mainAxisSpacing: 20, // 主轴方向的间距
// crossAxisSpacing: 20, // 交叉轴方向的间距
// childAspectRatio: 1.5, // 子组件的宽高比例
// ),
// children: [
// Container(color: Colors.cyan),
// Container(color: Colors.blueAccent),
// Container(color: Colors.purpleAccent),
// Container(color: Colors.purple),
// Container(color: Colors.yellow),
// Container(color: Colors.redAccent),
// Container(color: Colors.purpleAccent),
// Container(color: Colors.purple),
// Container(color: Colors.yellow),
// Container(color: Colors.redAccent),
// Container(color: Colors.purpleAccent),
// Container(color: Colors.purple),
// Container(color: Colors.yellow),
// Container(color: Colors.redAccent),
// Container(color: Colors.purpleAccent),
// Container(color: Colors.purple),
// Container(color: Colors.yellow),
// Container(color: Colors.redAccent),
// ],
// ),
// 子元素固定宽度
GridView(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200, // 子组件的宽度
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 0.8,
),
children: [
Container(color: Colors.cyan),
Container(color: Colors.blueAccent),
Container(color: Colors.purpleAccent),
Container(color: Colors.purple),
Container(color: Colors.yellow),
Container(color: Colors.redAccent),
Container(color: Colors.purpleAccent),
Container(color: Colors.purple),
Container(color: Colors.yellow),
Container(color: Colors.redAccent),
Container(color: Colors.purpleAccent),
Container(color: Colors.purple),
Container(color: Colors.yellow),
Container(color: Colors.redAccent),
Container(color: Colors.purpleAccent),
Container(color: Colors.purple),
Container(color: Colors.yellow),
Container(color: Colors.redAccent),
],
),
);
}
}
ScrollPhysics physics(确定可滚动控件的物理特性)BouncingScrollPhysics(允许超出边界 - 反弹效果)ClampingScrollPhysics(防止超出边界 - 夹住效果)AlwaysScrollableScrollPhysics(始终响应滚动)NeverScrollableScrollPhysics(不响应滚动)
class GridViewCountDemo extends StatelessWidget {
const GridViewCountDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey[100],
child: GridView.count(
crossAxisCount: 2, // 指定列数
mainAxisSpacing: 20,
crossAxisSpacing: 20,
// childAspectRatio: 1.5,
padding: const EdgeInsets.symmetric(horizontal: 20),
children: List.generate(10, (index) => Image.asset('images/1.jpg')),
),
);
}
}
class GridViewExtendDemo extends StatelessWidget {
const GridViewExtendDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey[100],
child: GridView.extent(
maxCrossAxisExtent: 200, // 子元素宽度
mainAxisSpacing: 20,
crossAxisSpacing: 20,
padding: const EdgeInsets.symmetric(horizontal: 20),
children: List.generate(10, (index) => Image.asset('images/1.jpg')),
),
);
}
}
class GridViewBuilderDemo extends StatelessWidget {
GridViewBuilderDemo({super.key});
final List<dynamic> _titles = [
Container(color: Colors.cyan),
Container(color: Colors.blueAccent),
Container(color: Colors.purpleAccent),
Container(color: Colors.purple),
Container(color: Colors.yellow),
Container(color: Colors.redAccent),
Container(color: Colors.purpleAccent),
Container(color: Colors.purple),
Container(color: Colors.yellow),
Container(color: Colors.redAccent),
Container(color: Colors.purpleAccent),
Container(color: Colors.purple),
Container(color: Colors.yellow),
Container(color: Colors.redAccent),
Container(color: Colors.purpleAccent),
Container(color: Colors.purple),
Container(color: Colors.yellow),
Container(color: Colors.redAccent),
];
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey[100],
padding: const EdgeInsets.all(20),
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
childAspectRatio: 1.0,
),
itemCount: _titles.length,
itemBuilder: (context, index) {
return _titles[index];
},
physics: const BouncingScrollPhysics(), // 反弹效果
// physics: const ClampingScrollPhysics(), // 夹住的效果
// physics: const AlwaysScrollableScrollPhysics(), // 滚动
// physics: const NeverScrollableScrollPhysics(), // 禁止滚动
),
);
}
}
2.6. 其他
2.6.1. Cupertino
- Material
- 安卓风格的组件
import 'package:flutter/material.dart'
- Cupertino
- ios 风格的组件
import 'package:flutter/cupertino.dart'- flutter.dev/docs/develo…
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:io';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('Cupertino'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const MyBody(),
);
}
}
class MyBody extends StatelessWidget {
const MyBody({super.key});
@override
Widget build(BuildContext context) {
dynamic dialogBox;
// 判断当前平台的信息
if (Platform.isIOS) {
// 加载IOS风格的组件
dialogBox = const CupertinoDemo();
} else if (Platform.isAndroid) {
// 加载安卓风格的组件
dialogBox = const MaterialDemo();
}
return Container(
color: Colors.grey[100],
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
dialogBox,
// 安卓风格的组件
const Text('Material - 安卓风格'),
const MaterialDemo(),
// IOS风格的组件
const Text('Cupertino - IOS风格'),
const CupertinoDemo(),
],
),
);
}
}
class MaterialDemo extends StatelessWidget {
const MaterialDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
child: AlertDialog(
title: const Text('提示'),
content: const Text('您确定要删除吗?'),
actions: [
TextButton(
child: const Text('取消'),
onPressed: () {
print('取消的逻辑');
},
),
TextButton(
child: const Text('确认'),
onPressed: () {
print('确认的逻辑');
},
),
],
),
);
}
}
class CupertinoDemo extends StatelessWidget {
const CupertinoDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: CupertinoAlertDialog(
title: const Text('提示'),
content: const Text('您确定要删除吗?'),
actions: [
CupertinoDialogAction(
child: const Text('取消'),
onPressed: () {
print('取消的逻辑');
},
),
CupertinoDialogAction(
child: const Text('确认'),
onPressed: () {
print('确认的逻辑');
},
),
],
),
);
}
}
2.6.2. SafeArea
SafeArea可以有效解决异形屏的问题 (刘海屏)
3. 第三方组件
3.1. dio
- dio 是一个强大的 Dart Http 请求库 (类似 axios)
- 使用步骤
- 在
pubsepc.yaml中添加 dio 依赖 - 安装依赖 (
pub get|flutter packages get| VS Code 中保存配置,自动下载) - 引入
import 'package:dio/dio.dart' - 使用: pub-web.flutter-io.cn/packages/di…
- 在
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('Dio'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const DioDemo(),
);
}
}
class DioDemo extends StatelessWidget {
const DioDemo({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
child: const Text('点击发送请求'),
onPressed: () {
// 调用 HTTP 请求
getIpAdress();
},
),
);
}
void getIpAdress() async {
try {
const url = 'https://httpbin.org/ip';
Response response = await Dio().get(url);
String ip = response.data['origin'];
print(ip);
} catch (e) {
print(e);
}
}
}
3.2. flutter_swiper
- Flutter 中最好的轮播组件,适配 Android 和 ios
- 使用步骤
- 在
pubsepc.yaml中添加flutter _swiper依赖 - 安装依赖 (
pub get|flutter packages get| VS Code 中保存配置,自动下载) - 引入
import 'package:flutter_swiper/flutter_swiper.dart'; - 使用
- 在
import 'package:flutter/material.dart';
import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('Flutter Swiper'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: FlutterSwiperDemo(),
);
}
}
class FlutterSwiperDemo extends StatelessWidget {
FlutterSwiperDemo({super.key});
final List<String> imgs = [
'images/1.jpg',
'images/2.webp',
'images/客厅.png',
];
@override
Widget build(BuildContext context) {
return ListView(
children: [
Container(
height: 200,
color: Colors.grey[100],
child: Swiper(
itemCount: imgs.length,
pagination: const SwiperPagination(), // 轮播图的指示点
control: const SwiperControl(), // 左右箭头控制器
itemBuilder: (context, index) {
return Image.asset(imgs[index], fit: BoxFit.cover);
},
),
),
Container(
height: 200,
color: Colors.blue[100],
child: Swiper(
itemCount: imgs.length,
viewportFraction: 0.7,
scale: 0.7,
itemBuilder: (context, index) {
return Image.asset(imgs[index], fit: BoxFit.cover);
},
),
),
Container(
height: 200,
color: Colors.red[100],
child: Swiper(
itemCount: imgs.length,
itemWidth: 300,
layout: SwiperLayout.STACK,
itemBuilder: (context, index) {
return Image.asset(imgs[index], fit: BoxFit.cover);
},
),
),
Container(
height: 200,
color: Colors.yellow[100],
child: Swiper(
itemCount: imgs.length,
itemWidth: 300,
itemHeight: 200,
layout: SwiperLayout.TINDER,
itemBuilder: (context, index) {
return Image.asset(imgs[index], fit: BoxFit.cover);
},
),
),
],
);
}
}
3.3. shared_preferences
-
shared_preferences是一个本地数据缓存库 (类似 AsyncStorage) -
使用步骤
- 在
pubsepc.yaml中添加shared _preferences依赖 - 安装依赖 (
pub get|flutter packages get| VS Code 中保存配置,自动下载) - 引入
- import
'package:shared_preferences/shared_preferences.dart'; - 使用
SharedPreferences prefs = await SharedPreferences.getlnstance();
- 在
-
增
setString(key, value)
-
删
remove(key) | clear()
-
改
- 更改就是重新设置数据
setString(key, value)
-
查
getString(key)
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('shared_preferences'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: SharedPreferencesDemo(),
);
}
}
class SharedPreferencesDemo extends StatelessWidget {
const SharedPreferencesDemo({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Colors.black,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _incrementCounter,
child: const Text('递增'),
),
ElevatedButton(
onPressed: _decrementCounter,
child: const Text('递减'),
),
ElevatedButton(
onPressed: _removeCounter,
child: const Text('删除'),
),
ElevatedButton(
onPressed: _addMyContent,
child: const Text('设置字符串'),
),
ElevatedButton(
onPressed: _getMyContent,
child: const Text('获取字符串'),
),
ElevatedButton(
onPressed: _clearContent,
child: const Text('清空'),
),
],
),
);
}
_incrementCounter() async {
// 获取保存实例
final SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed: $counter times.');
await prefs.setInt('counter', counter);
}
_decrementCounter() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = prefs.getInt('counter') ?? 0;
if (counter > 0) {
counter--;
}
print('Pressed: $counter times.');
await prefs.setInt('counter', counter);
}
_removeCounter() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove('counter');
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed: $counter times.');
}
_addMyContent() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('name', '张三');
String name = prefs.getString('name') ?? '';
print('设置的name: $name');
}
_getMyContent() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String name = prefs.getString('name') ?? '';
print('获取的name: $name');
}
_clearContent() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.clear();
}
}
4. 状态管理
4.1. StatefulWidget
- Flutter 中的组件,按状态划分
StatelessWidget(无状态组件)Statefulwidget(状态组件)
- 按状态作用域划分
- 组件内私有状态(
StatefulWidget) - 跨组件状态共享 (
InheritedWidget、Provider) - 全局状态 (
Redux、fish-redux、Mobx.....)
- 组件内私有状态(
- 状态组件的组成
StatefulWidget(组件本身不可变 -@immutable)state(将变化的状态放到state中维护)
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('StatefulWidget'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const MyState(),
);
}
}
class MyState extends StatefulWidget {
const MyState({super.key});
@override
State<MyState> createState() => _MyStateState();
}
class _MyStateState extends State<MyState> {
int _num = 0;
void _increment() {
setState(() {
_num++;
});
}
void _decrement() {
setState(() {
_num--;
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: _decrement,
child: const Icon(Icons.exposure_minus_1),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Text('$_num'),
),
ElevatedButton(
onPressed: _increment,
child: const Icon(Icons.add),
),
],
),
);
}
}
4.2. DataTable
DataTable是 Flutter 中的表格columns(声明表头列表)DataColumn(表头单元格)
rows(声明数据列表)DataRow(一行数据)DataCell(数据单元格)
- 其他属性
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('DataTable'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const UserListDemo(),
);
}
}
class User {
String name;
int age;
bool selected;
User(this.name, this.age, {this.selected = false});
}
class UserListDemo extends StatefulWidget {
const UserListDemo({super.key});
@override
State<UserListDemo> createState() => _UserListDemoState();
}
class _UserListDemoState extends State<UserListDemo> {
List<User> data = [
User('张三', 18),
User('张三丰', 180, selected: true),
User('张翠山', 30),
User('张无忌', 40),
];
var _sortAscending = true;
List<DataRow> _getUserRows() {
List<DataRow> dataRows = [];
for (int i = 0; i < data.length; i++) {
dataRows.add(DataRow(
selected: data[i].selected,
onSelectChanged: (selected) {
setState(() {
data[i].selected = selected!;
});
},
cells: [
DataCell(Text(data[i].name)),
DataCell(Text('${data[i].age}')),
const DataCell(Text('男')),
const DataCell(Text('---')),
],
));
}
return dataRows;
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
color: Colors.pink[100],
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
// 对那一列进行排序
sortColumnIndex: 1,
sortAscending: _sortAscending,
// 行高
dataRowHeight: 100,
// 水平方向外边距
horizontalMargin: 20,
// 列间距
columnSpacing: 100,
columns: [
const DataColumn(label: Text('姓名')),
DataColumn(
label: const Text('年龄'),
// 可排序
numeric: true,
onSort: (int columnIndex, bool asscending) {
setState(() {
_sortAscending = asscending;
if (asscending) {
data.sort((a, b) => a.age.compareTo(b.age));
} else {
data.sort((a, b) => b.age.compareTo(a.age));
}
});
},
),
const DataColumn(label: Text('性别')),
const DataColumn(label: Text('简介')),
],
rows: _getUserRows(),
// const [
// DataRow(
// cells: [
// DataCell(Text('张三')),
// DataCell(Text('20')),
// DataCell(Text('男')),
// DataCell(Text('无敌是多么的寂寞')),
// ],
// ),
// DataRow(
// cells: [
// DataCell(Text('李四')),
// DataCell(Text('18')),
// DataCell(Text('男')),
// DataCell(Text('无敌')),
// ],
// ),
// ],
),
),
);
}
}
4.3. InheritedWidget
- what: 提供了沿树向下,共享数据的功能
- 即子组件可以获取父组件(Inheritedwidget 的子类)的数据
- Why:
- 依赖构造函数传递数据的方式不能满足业务需求
- 所以,需要一个新的,更好的跨组件数据传输方案
- How:
BuildContext.dependOnlnheritedWidgetOfExactType<MylnheritedWidget>()
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('StatefulWidget'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const MyState(),
);
}
}
class MyState extends StatefulWidget {
const MyState({super.key});
@override
State<MyState> createState() => _MyStateState();
}
class _MyStateState extends State<MyState> {
int _num = 0;
void _increment() {
setState(() {
_num++;
});
}
void _decrement() {
setState(() {
_num--;
});
}
@override
Widget build(BuildContext context) {
return ShareDataWidget(
num: _num,
child: Center(
child: Column(
children: [
ElevatedButton(
onPressed: _decrement,
child: const Icon(Icons.exposure_minus_1),
),
const Padding(
padding: EdgeInsets.all(20.0),
// child: Text('$_num'),
// 跨组件访问数据
child: MyCounter(),
),
ElevatedButton(
onPressed: _increment,
child: const Icon(Icons.add),
),
],
),
),
);
}
}
class MyCounter extends StatefulWidget {
const MyCounter({super.key});
@override
State<MyCounter> createState() => _MyCounterState();
}
class _MyCounterState extends State<MyCounter> {
@override
Widget build(BuildContext context) {
// 获取共享数据
return Text('${ShareDataWidget.of(context)!.num}');
}
}
// 创建数据共享组件
class ShareDataWidget extends InheritedWidget {
final int num;
final Widget child;
const ShareDataWidget({super.key, required this.child, required this.num})
: super(child: child);
static ShareDataWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
@override
bool updateShouldNotify(ShareDataWidget oldWidget) {
return true;
}
}
4.4. 生命周期
initState()组件对象插入到元素树中时didchangeDependencies()当前状态对象的依赖改变时build()组件渲染时setState()组件对象的内部状态变更时didUpdateWidget()组件配置更新时deactivate()组件对象在元素树中暂时移除时dispose()组件对象在元素树中永远移除时
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('Life Cycle'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const MyState(),
);
}
}
class MyState extends StatefulWidget {
const MyState({super.key});
@override
State<MyState> createState() => _MyStateState();
}
class _MyStateState extends State<MyState> {
var _num;
// 状态初始化时调用,通常根据后台接口的返回数据对状态进行初始化
@override
void initState() {
super.initState();
print('initState');
_num = 1;
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print('didChangeDependencies');
}
@override
void didUpdateWidget(covariant MyState oldWidget) {
// TODO: implement didUpdateWidget
super.didUpdateWidget(oldWidget);
print('didUpdateWidget');
}
@override
void deactivate() {
// TODO: implement deactivate
super.deactivate();
print('deactivate');
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
print('dispose');
}
void _increment() {
setState(() {
print('setState');
_num++;
});
}
void _decrement() {
setState(() {
print('setState');
_num--;
});
}
@override
Widget build(BuildContext context) {
print('build');
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: _decrement,
child: const Icon(Icons.exposure_minus_1),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Text('$_num'),
),
ElevatedButton(
onPressed: _increment,
child: const Icon(Icons.add),
),
],
),
);
}
}
4.5. Provider
Provider是对Inheritedwidget的封装- 优点:
- 简化资源的分配与处置
- 懒加载
- Provider 的实现原理?
使用
- 安装 Provider (第三方库)
- 创建数据模型 (
T extends ChangeNotifier) - 创建 Provider(注册数据模型)
Provider() // 不会被要求随着变动而变动ChangeNotifierProvider() // 随着某些数据改变而被通知更新
- 获取数据模型并更新 UI
- 通过上下文 (
BuildContext) - 通过静态方法 (
Provider.of<T>(context))
- 通过上下文 (
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
// 2. 创建 Provider (注册数据模型)
return ChangeNotifierProvider(
create: (BuildContext context) => LikesModel(),
child: Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('Provider'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: MyHomePage(),
),
);
}
}
// 1. 创建数据模型
class LikesModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
incrementCounter() {
// 累加
_count++;
// 通过 UI 更新
notifyListeners();
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 100),
color: Colors.blue[100],
child: Column(
children: [
// 3. 在子组件中使用数据模型
Text('${context.watch<LikesModel>().count}'),
TextButton(
// 3. 在子组件中使用数据模型
onPressed: Provider.of<LikesModel>(context).incrementCounter,
child: const Icon(Icons.thumb_up),
),
],
),
);
}
}
5. 路由与导航
5.1. 路由简介
Route- 一个路由是一个屏幕或页面的抽象
Navigator- 管理路由的组件。Navigator 可以通过路由入栈和出栈来实现页面之间的跳转
- 常用属性
initialRoute:初始路由,即默认页面onGenerateRoute:动态路由 (根据规则,匹配动态路由)onUnknownRoute: 未知路由,也就是 404routes: 路由集合
5.2. 匿名路由
Navigatorpush(跳转到指定组件)
Navigator.push(
context,
MaterialPageRoute(builder:(context) => 组件名称())
)
pop(回退)Navigator.pop(context)
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('匿名路由'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Product()),
);
},
child: const Text('跳转到商品页面'),
),
),
);
}
}
class Product extends StatelessWidget {
const Product({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('商品页面'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Container(
child: Center(
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
),
),
);
}
}
5.3. 命名路由
- 声明路由
routes路由表 (Map 类型)initialRoute(初始路由)onUnknownRoute(未知路由 - 404)
- 跳转到命名路由
NavigatorpushNamed(context,路由名称');
// main.dart
import 'package:flutter/material.dart';
import '09_navigation/02_namedRoute.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '学习 Flutter',
// home: const Home(),
// 声明路由
routes: {
'home': (context) => const Home(),
'product': (context) => const Product(),
},
// 默认加载的路由
initialRoute: 'home',
onUnknownRoute: (RouteSettings settings) => MaterialPageRoute(
builder: (context) => const UnkonwPage(),
),
// 设置全局主题
theme: ThemeData(
// 自定义文本样式
// fontFamily: 'CustomFamily',
),
// 是否显示调试标记
debugShowCheckedModeBanner: false,
);
}
}
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('首页'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, 'product'),
child: const Text('跳转'),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, 'user'),
child: const Text('未知路由'),
),
],
),
),
);
}
}
class Product extends StatelessWidget {
const Product({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('商品页面'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
],
),
),
);
}
}
class UnkonwPage extends StatelessWidget {
const UnkonwPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('404'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
],
),
),
);
}
}
5.4. 动态路由
- 动态路由是指,通过
onGenerateRoute属性指定的路由
// main.dart
import 'package:flutter/material.dart';
import '09_navigation/03_onGenerateRoute.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '学习 Flutter',
// home: const Home(),
// 声明命名路由
// routes: {
// 'home': (context) => const Home(),
// 'product': (context) => const Product(),
// },
// // 默认加载的路由
// initialRoute: 'home',
// onUnknownRoute: (RouteSettings settings) => MaterialPageRoute(
// builder: (context) => const UnkonwPage(),
// ),
onGenerateRoute: (RouteSettings setting) {
print(setting);
// 匹配首页 /
if (setting.name == '/') {
return MaterialPageRoute(builder: (context) => const Home());
}
if (setting.name == '/product') {
return MaterialPageRoute(builder: (context) => const Product());
}
// 匹配 /product/:id
var uri = Uri.parse(setting.name!);
print(uri.pathSegments);
if (uri.pathSegments.length == 2 && uri.pathSegments[0] == 'product') {
String id = uri.pathSegments[1];
return MaterialPageRoute(
builder: (context) => ProductDetail(id: id),
);
}
return MaterialPageRoute(builder: (context) => const UnkonwPage());
},
// 设置全局主题
theme: ThemeData(
// 自定义文本样式
// fontFamily: 'CustomFamily',
),
// 是否显示调试标记
debugShowCheckedModeBanner: false,
);
}
}
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('首页'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, '/product'),
child: const Text('跳转'),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, '/product/1'),
child: const Text('商品1'),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, '/product/2'),
child: const Text('商品2'),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, 'user'),
child: const Text('未知路由'),
),
],
),
),
);
}
}
class Product extends StatelessWidget {
const Product({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('商品页面'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
],
),
),
);
}
}
class ProductDetail extends StatelessWidget {
// /product/1
final String id;
const ProductDetail({super.key, required this.id});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('商品详情页面'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
Text('当前商品的id是:$id'),
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
],
),
),
);
}
}
class UnkonwPage extends StatelessWidget {
const UnkonwPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('404'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
],
),
),
);
}
}
5.5. 路由传参
5.5.1. 路由传参 -匿名路由
- 路由中声明参数
Navigator.push
- 组件中接收参数
5.5.2. 路由传参-命名路由
- 路由中声明参数
Navigator.pushNamed(context, routename, (arguments))
- 组件中接收参数
ModalRoute.of(context).settings.arguments
// main.dart
import 'package:flutter/material.dart';
import '09_navigation/04_arguments.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '学习 Flutter',
// home: const Home(),
// 声明命名路由
routes: {
'home': (context) => const Home(),
'product': (context) => const Product(),
'productDetail': (context) => const ProductDetail(),
},
// 默认加载的路由
initialRoute: 'home',
onUnknownRoute: (RouteSettings settings) => MaterialPageRoute(
builder: (context) => const UnkonwPage(),
),
// onGenerateRoute: (RouteSettings setting) {
// print(setting);
// // 匹配首页 /
// if (setting.name == '/') {
// return MaterialPageRoute(builder: (context) => const Home());
// }
// if (setting.name == '/product') {
// return MaterialPageRoute(builder: (context) => const Product());
// }
// // 匹配 /product/:id
// var uri = Uri.parse(setting.name!);
// print(uri.pathSegments);
// if (uri.pathSegments.length == 2 && uri.pathSegments[0] == 'product') {
// String id = uri.pathSegments[1];
// return MaterialPageRoute(
// builder: (context) => ProductDetail(id: id),
// );
// }
// return MaterialPageRoute(builder: (context) => const UnkonwPage());
// },
// 设置全局主题
theme: ThemeData(
// 自定义文本样式
// fontFamily: 'CustomFamily',
),
// 是否显示调试标记
debugShowCheckedModeBanner: false,
);
}
}
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('首页'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pushNamed(
context,
'product',
arguments: {
'title': '我是主页传过来的参数',
},
),
child: const Text('跳转到商品页面'),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(
context,
'productDetail',
arguments: {
'id': 1,
},
),
child: const Text('商品1'),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(
context,
'productDetail',
arguments: {
'id': 2,
},
),
child: const Text('商品2'),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, 'user'),
child: const Text('未知路由'),
),
],
),
),
);
}
}
class Product extends StatelessWidget {
const Product({super.key});
@override
Widget build(BuildContext context) {
final Map arguments = ModalRoute.of(context)?.settings.arguments as Map;
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('商品页面'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
Text('接收的参数是:${arguments['title']}'),
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
],
),
),
);
}
}
class ProductDetail extends StatelessWidget {
const ProductDetail({super.key});
@override
Widget build(BuildContext context) {
final Map arguments = ModalRoute.of(context)?.settings.arguments as Map;
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('商品详情页面'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
Text('当前商品的id是:${arguments['id']}'),
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
],
),
),
);
}
}
class UnkonwPage extends StatelessWidget {
const UnkonwPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('404'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('返回'),
),
],
),
),
);
}
}
5.6. Drawer 导航
Scaffolddrawer(左侧抽菜单)endDrawer(右侧抽菜单)
UserAccountsDrawerHeader- 抽屉菜单头部组件
AboutListTile- 关于弹窗
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('Drawer'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
body: const HomePage(),
drawer: const DrawerList(),
endDrawer: const DrawerList(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Container(
child: const Center(
child: Text('Home'),
),
);
}
}
class DrawerList extends StatelessWidget {
const DrawerList({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
// 取消内边距
padding: const EdgeInsets.all(0),
children: [
const UserAccountsDrawerHeader(
accountName: Text('小笑残虹'),
accountEmail: Text('2504862082@qq.com'),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/2.webp'),
fit: BoxFit.cover,
),
),
// 用户头像
currentAccountPicture: CircleAvatar(
backgroundImage: AssetImage('images/1.jpg'),
),
),
const ListTile(
leading: Icon(Icons.settings),
title: Text('设置'),
trailing: Icon(Icons.arrow_forward_ios),
),
const Divider(
thickness: 1,
),
const ListTile(
leading: Icon(Icons.account_balance_wallet),
title: Text('余额'),
trailing: Icon(Icons.arrow_forward_ios),
),
const Divider(
thickness: 1,
),
const ListTile(
leading: Icon(Icons.person),
title: Text('我的'),
trailing: Icon(Icons.arrow_forward_ios),
),
const Divider(
thickness: 1,
),
ListTile(
leading: const Icon(Icons.keyboard_return),
title: const Text('回退'),
onTap: () => Navigator.pop(context),
trailing: const Icon(Icons.arrow_forward_ios),
),
AboutListTile(
applicationName: '我的APP',
applicationIcon: Image.asset(
'images/1.jpg',
width: 50,
height: 50,
),
applicationVersion: '1.0.0',
applicationLegalese: '应用法律条例',
aboutBoxChildren: const [
Text('条例一:XXXX'),
Text('条例二:XXXX'),
],
icon: const CircleAvatar(
child: Text('aaa'),
),
child: const Text('关于'),
),
// Text('Drawer'),
],
),
);
}
}
5.7. BottomNavigationBar 导航
items- 包含导航 (
BottomNavigationBarltem) 的列表
- 包含导航 (
currentIndex- 当前导航索引
type- 导航类型(
BottomNavigationBarType)
- 导航类型(
onTap()- 导航的点击事件(一般会更新导航索引)
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final List<BottomNavigationBarItem> bottomNavItems = [
const BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
backgroundColor: Colors.blue,
),
const BottomNavigationBarItem(
icon: Icon(Icons.message),
label: '消息',
backgroundColor: Colors.green,
),
const BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
label: '购物车',
backgroundColor: Colors.amber,
),
const BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '我',
backgroundColor: Colors.red,
),
];
final List<Widget> pages = [
const Center(
child: Text(
'首页',
style: TextStyle(fontSize: 50),
),
),
const Center(
child: Text(
'消息',
style: TextStyle(fontSize: 50),
),
),
const Center(
child: Text(
'购物车',
style: TextStyle(fontSize: 50),
),
),
const Center(
child: Text(
'我的',
style: TextStyle(fontSize: 50),
),
),
];
late int currentIndex;
@override
void initState() {
// TODO: implement initState
super.initState();
currentIndex = 0;
}
@override
Widget build(BuildContext context) {
return Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('底部导航'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
),
bottomNavigationBar: BottomNavigationBar(
items: bottomNavItems,
currentIndex: currentIndex,
// type: BottomNavigationBarType.fixed,
type: BottomNavigationBarType.shifting,
onTap: (index) {
_changePage(index);
},
),
body: pages[currentIndex],
);
}
void _changePage(int index) {
if (index != currentIndex) {
setState(() {
currentIndex = index;
});
}
}
}
5.8. Tab 导航
DefaultTabController(整个 Tab 导航的容器)length(声明导航数量)child(指定子组件)
TabBar(导航菜单)tabs(导航菜单数组)
TabBarView(导航页面)children(多个导航页面内容)
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
Home({super.key});
// 菜单数组
final List<Widget> _tabs = [
const Tab(
text: '首页',
icon: Icon(Icons.home),
),
const Tab(
text: '添加',
icon: Icon(Icons.add),
),
const Tab(
text: '搜索',
icon: Icon(Icons.search),
),
];
// 页面数组
final List<Widget> _tabViews = [
const Icon(
Icons.home,
size: 120,
color: Colors.red,
),
const Icon(
Icons.add,
size: 120,
color: Colors.green,
),
const Icon(
Icons.search,
size: 120,
color: Colors.black,
),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tabs.length,
child: Scaffold(
// 头部导航条
appBar: AppBar(
// 标题
title: const Text('Tab'),
// 左侧插槽
leading: const Icon(Icons.menu),
// 右侧插槽
actions: const [
Icon(Icons.add_circle_outline),
],
// 设置下边阴影
elevation: 0.0,
// 设置标题居中
centerTitle: true,
bottom: TabBar(
tabs: _tabs,
labelColor: Colors.yellow,
unselectedLabelColor: Colors.black45,
indicatorSize: TabBarIndicatorSize.tab,
indicatorColor: Colors.yellow,
indicatorWeight: 10,
),
),
body: TabBarView(
children: _tabViews,
),
bottomNavigationBar: TabBar(
tabs: _tabs,
labelColor: Colors.blue,
unselectedLabelColor: Colors.black45,
),
),
);
}
}