包含 Flutter 常用的组件、布局、方法等。初学者的完整快速参考
完整教程请参阅 Flutter 中文社区的 安装和环境配置
$ sudo softwareupdate --install-rosetta --agree-to-license
在 Apple 芯片的 Mac 电脑 上,还需要安装 Rosetta 2 环境因为 一些辅助工具 仍然需要,通过手动运行上面的命令来安装
-
安装包来获取最新的 stable Flutter SDK:
-
Apple 芯片
flutter_macos_arm64_3.22.2-stable.zip
想要获取到其他版本的安装包,请参阅 SDK 版本列表 页面
-
将文件解压到目标路径, 比如:
$ cd ~/development $ unzip ~/Downloads/flutter_macos_3.22.2-stable.zip
-
配置
flutter
的 PATH 环境变量:$ export PATH="$PATH:`pwd`/flutter/bin"
-
运行
flutter doctor
命令
完整教程请参阅 Flutter 中文社区的 安装和环境配置
- 点击下方的安装包,获取 stable 发行通道的 Flutter SDK 最新版本:
- flutter_windows_3.22.2-stable.zip
- 要查看其他发行通道和以往的版本,请参阅 SDK 版本列表 页面
- 将压缩包解压,然后把其中的
flutter
目录整个放在你想放置Flutter SDK
的路径中(例如C:\src\flutter
) - 更新
path
环境变量,在开始菜单的搜索功能键入「env」
,然后选择编辑系统环境变量
。在用户变量
一栏中,检查是否有Path
这个条目:- 如果存在这个条目,以
;
分隔已有的内容,加入flutter\bin
目录的完整路径。 - 如果不存在的话,在用户环境变量中创建一个新的 Path 变量,然后将
flutter\bin
所在的完整路径作为新变量的值
- 如果存在这个条目,以
如果你不想安装指定版本的安装包。可以忽略步骤 1
和 2
。从 GitHub
上的 Flutter repo
获取源代码,并根据需要,切换到指定的分支或标签
C:\src>git clone https://github.com/flutter/flutter.git -b stable
Text("Hello world",
textAlign: TextAlign.left,
);
Text("Hello world! I'm Jack. "*4,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
Text("Hello world",
textScaleFactor: 1.5,
);
Text("Hello world",
style: TextStyle(
color: Colors.blue,
fontSize: 18.0,
height: 1.2,
fontFamily: "Courier",
background: Paint()..color=Colors.yellow,
decoration:TextDecoration.underline,
decorationStyle: TextDecorationStyle.dashed
),
);
Text.rich(TextSpan(
children: [
TextSpan(
text: "Home: "
),
TextSpan(
text: "https://flutter.dev",
style: TextStyle(
color: Colors.blue
),
recognizer: _tapRecognizer
),
]
))
DefaultTextStyle(
// 1.设置文本默认样式
style: TextStyle(
color:Colors.red,
fontSize: 20.0,
),
textAlign: TextAlign.start,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("hello world"),
Text("I am Jack"),
Text("I am Jack",
style: TextStyle(
inherit: false, //2.不继承默认样式
color: Colors.grey
),
),
],
),
);
-
在 asset 中声明,要先在
pubspec.yaml
中声明它flutter: fonts: - family: Raleway fonts: - asset: assets/fonts/Raleway-Regular.ttf - asset: assets/fonts/Raleway-Medium.ttf weight: 500 - asset: assets/fonts/Raleway-SemiBold.ttf weight: 600 - family: AbrilFatface fonts: - asset: assets/fonts/abrilfatface/AbrilFatface-Regular.ttf
-
将字体文件复制到在
pubspec.yaml
中指定的位置 -
使用字体
// 声明文本样式 const textStyle = const TextStyle( fontFamily: 'Raleway', ); // 使用文本样式 var buttonText = const Text( "Use the font for this text", style: textStyle, );
ElevatedButton(
child: Text("normal"),
onPressed: () {},
);
TextButton(
child: Text("normal"),
onPressed: () {},
)
OutlineButton(
child: Text("normal"),
onPressed: () {},
)
IconButton(
icon: Icon(Icons.thumb_up),
onPressed: () {},
)
ElevatedButton.icon(
icon: Icon(Icons.send),
label: Text("发送"),
onPressed: _onPressed,
),
OutlineButton.icon(
icon: Icon(Icons.add),
label: Text("添加"),
onPressed: _onPressed,
),
TextButton.icon(
icon: Icon(Icons.info),
label: Text("详情"),
onPressed: _onPressed,
),
-
在工程根目录下创建一个
images目录
,并将图片aaa.png
拷贝到该目录。 -
在
pubspec.yaml
中的flutter
部分添加如下内容:assets: - images/aaa.png
注意: 由于 yaml 文件对缩进严格,所以必须严格按照每一层两个空格的方式进行缩进,此处 assets 前面应有两个空格。
-
加载该图片
Image( image: AssetImage("images/aaa.png"), width: 100.0 );
Image 也提供了一个快捷的构造函数
Image.asset
用于从asset
中加载、显示图片:Image.asset("images/aaa.png", width: 100.0, )
Image(
image: NetworkImage(
"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4"),
width: 100.0,
)
Image 也提供了一个快捷的构造函数 Image.network
用于从网络加载、显示图片:
Image.network(
"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4",
width: 100.0,
)
const Image({
...
this.width, // 图片的宽
this.height, // 图片高度
this.color, // 图片的混合色值
this.colorBlendMode, // 混合模式
this.fit,// 缩放模式
// 对齐方式
this.alignment = Alignment.center,
// 重复方式
this.repeat = ImageRepeat.noRepeat,
...
})
Switch(
value: true,//当前状态
onChanged:(value){
// 重新构建页面
},
),
Checkbox(
value: true,
// 选中时的颜色
activeColor: Colors.red,
onChanged:(value){
// ...
},
)
TextField(
autofocus: true,
onChanged: (v) {
print("onChange: $v");
}
)
模糊进度条(会执行一个动画)
LinearProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
),
进度条显示 50%
LinearProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .5,
)
模糊进度条(会执行一个旋转动画)
CircularProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
),
进度条显示 50%
,会显示一个半圆
CircularProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .5,
),
线性进度条高度指定为 3
SizedBox(
height: 3,
child: LinearProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .5,
),
),
圆形进度条直径指定为 100
SizedBox(
height: 100,
width: 100,
child: CircularProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .7,
),
),
在实际开发中,Container常常用于对一个组件进行包装修饰。
Container(
width: 100,
height: 100,
color: Colors.blue,
alignment: Alignment.center,
child: Text('Hello world'),
),
将 Contianer
大小固定为 100 * 100
, 背景色为蓝色。把 Text
包裹在 Container
中,并将其居中
列布局(Column),可以将多个子组件沿着垂直的方向摆放(竖的摆放)
// 将container 和 button 摆放到同一列。
Column(
children: [
Container(
width: 100,
height: 100,
color: Colors.blue,
alignment: Alignment.center,
child: Text('Hello world'),
),
ElevatedButton(
onPressed: () {},
child: Text('Button'),
),
],
),
行布局(Row),可以将多个组件沿水平的方向摆放。
Row(
children: [
ElevatedButton(
onPressed: () {},
child: const Text('1'),
),
ElevatedButton(
onPressed: () {},
child: const Text('2'),
),
ElevatedButton(
onPressed: () {},
child: const Text('3'),
),
],
),
在同一行摆放 3 个 Button
将子组件从左到右依次排列,当空间不足时自动换行。
Wrap(
children: [
FlutterLogo(),
FlutterLogo(),
FlutterLogo(),
FlutterLogo(),
FlutterLogo(),
FlutterLogo(),
],
),
显示多个 Flutter
的 logo
并自动换行
Stack 可以将一多个子组件叠在一起显示。堆叠顺序按照children属性中的列表依次堆叠摆放,默认将子控件沿左上角对齐。
需要控制子控件位置可以嵌套Positoned
控件。
Stack(
children: [
Container(
height: 300,
width: 300,
color: Colors.blue,
),
Container(
height: 200,
width: 200,
color: Colors.black,
),
Container(
height: 100,
width: 100,
color: Colors.yellow,
),
],
),
依次堆叠 300*300
的蓝色色块、200*200
的黑色色块、100*100
的黄色色块
若需要控制Stack中子控件的位置,则可以嵌套改控件。
Stack(
children: [
// 默认摆放在左上位置
Container(
height: 300,
width: 300,
color: Colors.blue,
),
// 距离左边40个、距离上面40个逻辑像素的位置
Positioned(
left: 40,
top: 40,
child: Container(
height: 200,
width: 200,
color: Colors.black,
),
),
// 距离左边80个、距离上面80个逻辑像素的位置
Positioned(
left: 80,
top: 80,
child: Container(
height: 100,
width: 100,
color: Colors.yellow,
),
),
],
),
Align组件用于决定子组件对齐方式
// 使用Align将Button 居中在Container中
Container(
width: 100,
height: 100,
color: Colors.green,
child: Align(
alignment: Alignment.center,
child: ElevatedButton(
onPressed: () {},
child: Text('Center'),
),
),
),
Center 组件实际上继承于Align。用于专门剧中。
//与 Align中代码效果一致
Container(
width: 100,
height: 100,
color: Colors.green,
child: Center(
child: ElevatedButton(
onPressed: () {},
child: Text('Center'),
),
),
),
Flex 的用法与 Row
或 Column
类似,但只需要额外传入 direction
参数
Row
和Column
组件都继承Flex
组件- 设置
direction
为Axis.horizontal
表示水平方向(Row
),为Axis.vertical
则为垂直方向(Column
)
垂直方向依次摆放3个flutter logo
Flex(
direction: Axis.vertiacl,
children: [
FlutterLogo(),
FlutterLogo(),
FlutterLogo(),
],
),
水平方向依次摆放 3 个 flutter logo
Flex(
direction: Axis.horizontal,
children: [
FlutterLogo(),
FlutterLogo(),
FlutterLogo(),
],
),
Expanded 用于扩张一个子组件。可以通过 flex
属性,用于表示该组件相对其他弹性组件放大的倍数(可以理解为一个权重)。
// Container 会占满剩余的全部空用空间
Row(
children: [
FlutterLogo(),
Expanded(
child: Container(
child: FlutterLogo(),
color: Colors.green,
),
),
FlutterLogo(),
],
),
// 按照1:2 的比例分配一整行的空间
Row(
children: [
Expanded(
flex: 1,
child: Container(
child: FlutterLogo(),
color: Colors.green,
),
),
Expanded(
flex: 2,
child: Container(
child: FlutterLogo(),
color: Colors.blue,
),
),
],
),
Flexible
是 Expanded
组件的父类。
与 Expanded
不同的是,Flexible
可以通过 fit
属性设置子控件是否必须占满 Flexibal
扩展的空间。而 Expaned
默认子控件必须占满
// 如果将fit设置为tight,
// 则绿色Container 和蓝色Container大小一样。
// 如果将fit设置为loose,
// 则两个Flexible扩展的空间大小是一样的,
// 但绿色Container并不会填充整个扩展的空间。
Row(
children: [
Flexible(
flex: 2,
// fit: FlexFit.tight,
child: Container(
child: FlutterLogo(),
color: Colors.green,
),
),
Expanded(
flex: 2,
child: Container(
child: FlutterLogo(),
color: Colors.blue,
),
),
],
),
将 Flexible
的 fit
属性设置为 tingt
,就等价于使用 Expanded
Spacer 用于在布局中留白
Row(
children: [
Text('Item'),
Spacer(),
FlutterLogo(),
],
),
例如,需要文本和图标位于一个行的两端,而中间留白时。就可以使用 Spacer
ListView
是一个支持滚动的列表组件。该组件默认支持上下滑动。
ListView
的默认构造函数,会立即初始化children
中的所有子widget
,无法动态加载。
ListView(
children: [
Text('1'),
Text('2'),
Text('3'),
Text('4'),
],
),
需要动态加载,则可以使用 ListView.builder()
命名构函数。
// 动态生成4个Text
ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Text('$index');
},
itemCount: 4,
),
需要在对ListView
中的Item
添加分割线,则可以借助ListView.separated()
。
// separatorBuilder 函数用于在元素之间插入分割线。
// 也可以返回其他widget。该widget会被插入各个元素之间。
ListView.separated(
itemBuilder: (BuildContext context, int index) {
return Text('$index');
},
itemCount: 4,
separatorBuilder: (BuildContext context, int index) {
// 使用Divider widget 画一条粗为5,颜色为红色的线
return const Divider(
height: 5,
thickness: 5,
color: Colors.red,
);
},
),
GridView
可将元素显示为二维网格状的列表组件,并支持主轴方向滚动。
使用GridView() 构造函数,需要传入gridDelegate和children。Flutter中已经提供了两种实现方式,分别是:
SliverGridDelegateWithFixedCrossAxisCount()
用于交叉轴方向固定数。SliverGridDelegateWithMaxCrossAxisExtent()
用于交叉轴方向限制最大长度。
// 使用SliverGridDelegateWithFixedCrossAxisCount
GridView(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
children: List.generate(
8,
(index) => Container(
color: Colors.red[index % 8 * 100],
child: Text("index $index"),
)),
),
// 使用SliverGridDelegateWithMaxCrossAxisExtent
GridView(
gridDelegate:
SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 200),
children: List.generate(
8,
(index) => Container(
color: Colors.red[index % 8 * 100],
child: Text("index $index"),
),
),
),
GridView.builder()
命名构造可以实现元素的动态加载,与ListView.builder()
类似
GridView.builder(
itemCount: 8,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4),
itemBuilder: (context, index) => Container(
color: Colors.red[index % 8 * 100],
child: Text("index $index"),
),
),
Gridview.count()
一个简单的构造函数,只需要传入crossAxisCount
(交叉轴元素的个数)和children
即可。
GridView.count(
crossAxisCount: 4, // 每行固定为4个
children: List.generate(
8,
(index) => Container(
color: Colors.red[index % 8 * 100],
child: Text("index $index"),
)),
),
GridView.extent()
用于设定每个元素在交叉轴方向的最大尺寸。当程序运行在较大屏幕时通常能看到更多的元素,而不是少量元素的放大版。通过传入maxCrossAxisExtent
,Gridview
会根据屏幕尺寸自动选择合适的行数量。
GridView.extent(
maxCrossAxisExtent: 200,
children: List.generate(
8,
(index) => Container(
color: Colors.red[index % 8 * 100],
child: Text("index $index"),
)),
),
GridView.count()
和GridView.extent()`可以看作GridView的语法糖。
使用PageView
可以实现整屏页面滚动,默认为水平方向翻页。与ListView
类似。
pageSnapping
参数可以设置滑动时Page
停留在任意位置。scrollDirection
参数设置滚动方向(默认为水平方向)。
PageView(
pageSnapping: false, // 取消页面固定
scrollDirection: Axis.vertical, // 设置为垂直方向滚动
children: [
for (int i = 0; i < 4; i++)
Container(
color: Colors.red[i % 4 * 100],
)
],
),
使用PageView.builder()
命名构造,可以动态加载页面。与ListView.builder()
类似。
PageView.builder(
pageSnapping: false,
scrollDirection: Axis.vertical,
itemBuilder: (BuildContext context, int index) => Container(
color: Colors.red[index % 4 * 100],
),
),
在动画组件内,直接配置curve和duration属性
使用AnimatedContainer组件,配置curve曲线过渡和duration过渡时间
class HomeState extends StatefulWidget{
const HomeState({Key? key}) : super(key:key);
@override
State<HomeState> createState()=>_HomeState();
}
class _HomeState extends State<HomeState>{
bool press = false; //设置动画触发的条件
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton:FloatingActionButton(onPressed: (){
setState(() {
press = true; //点击FloatingActionButton进行动画效果
});
}
,child: const Icon(Icons.add),) ,
appBar: AppBar(
title: const Text("测试"),
),
body: Center(
child: AnimatedContainer(
curve: Curves.ease, //曲线
duration: const Duration(seconds: 1), //延时
width: press ? 200 : 300,
height: 200,
color:Colors.yellow,
transform: press ? Matrix4.translationValues(0, 0, 0) :
Matrix4.translationValues(100, 100, 0)
),
)
)
);
}
}
通过配置padding值的改变,引起组件的移动动画效果,同样支持curve和duration的配置
class _HomeState extends State<HomeState>{
bool press = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton:FloatingActionButton(onPressed: (){
setState(() {
press = true;
});
}
,child: const Icon(Icons.add),) ,
appBar: AppBar(
title: const Text("测试"),
),
body: Center(
child: AnimatedPadding(
padding: EdgeInsets.fromLTRB(10, press ? 10 : 400, 0, 0), //配置边距值
curve: Curves.ease, //曲线
duration: const Duration(seconds: 1), //延时
child: Container(
width: 200,
height: 200,
color:Colors.yellow,
),
),
)
)
);
}
}
通过配置alignment值的改变,引起组件的对齐动画效果,同样支持curve和duration的配置
class _HomeState extends State<HomeState>{
bool press = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton:FloatingActionButton(onPressed: (){
setState(() {
press = true;
});
}
,child: const Icon(Icons.add),) ,
appBar: AppBar(
title: const Text("测试"),
),
body: Center(
child: AnimatedAlign(
alignment: press ? Alignment.center : Alignment.topCenter,
curve: Curves.ease, //曲线
duration: const Duration(seconds: 1), //延时
child: Container(
width: 200,
height: 200,
color:Colors.yellow,
),
),
)
)
);
}
}
通过配置opacity值的改变,引起组件的透明度变化动画效果,同样支持curve和duration的配置
class _HomeState extends State<HomeState>{
bool press = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton:FloatingActionButton(onPressed: (){
setState(() {
press = true;
});
}
,child: const Icon(Icons.add),) ,
appBar: AppBar(
title: const Text("测试"),
),
body: Center(
child: AnimatedOpacity(
opacity: press ? 1 : 0.1,
curve: Curves.ease, //曲线
duration: const Duration(seconds: 1), //延时
child: Container(
width: 200,
height: 200,
color:Colors.yellow,
),
),
)
)
);
}
}
通过配置top,left,right,bottom值的改变,引起组件的距离变化动画效果,同样支持curve和duration的配置
class _HomeState extends State<HomeState>{
bool press = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton:FloatingActionButton(onPressed: (){
setState(() {
press = true;
});
}
,child: const Icon(Icons.add),) ,
appBar: AppBar(
title: const Text("测试"),
),
body:Stack(
children: [
AnimatedPositioned(
top: press ? 0 : 100,
left:press ? 0 : 100,
curve: Curves.ease, //曲线
duration: const Duration(seconds: 1), //延时
child: Container(
width: 200,
height: 200,
color:Colors.yellow,
),
),
],
)
)
);
}
}
使用显示动画时,定义 AnimationController
,并在组件中使用 SingleTickerProviderStateMixin
。
RotationTransition
实现旋转动画,turns
为 AnimationController
。在 initState
中设置 vsync
和 duration
,使用 ..repeat()
实现动画循环。
class _Boxed extends State<Boxed> with SingleTickerProviderStateMixin{
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1)
)..repeat(); // 让程序和手机的刷新频率统一
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
width: 100,
child: RotationTransition(turns: _controller,
child: const FlutterLogo(size: 60),
)
);
}
}
class _HomeState extends State<HomeState> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
// 让程序和手机的刷新频率统一
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton:FloatingActionButton(onPressed: () {
_controller.repeat(); //重复播放
},child:const Icon(Icons.add),) ,
appBar: AppBar(
title: const Text("测试"),
),
body: Center(
child: Column(
children: [
RotationTransition(
turns: _controller,
child: const FlutterLogo(size: 60),
),
ElevatedButton(onPressed: (){
_controller.forward(); // 👈 播放一次
}, child:const Icon(Icons.refresh)),
ElevatedButton(onPressed: (){
_controller.reverse(); // 👈 倒序播放
}, child:const Icon(Icons.refresh)),
ElevatedButton(onPressed: (){
_controller.stop(); // 👈 停止
}, child:const Icon(Icons.refresh)),
ElevatedButton(onPressed: (){
_controller.reset(); // 👈 重置
}, child:const Icon(Icons.refresh)),
]
)
)
)
);
}
}
FadeTransition
实现透明度变化,opacity
为 AnimationController
。可以通过 reverse()
实现从实体逐渐变透明。
class _HomeState extends State<HomeState> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
//让程序和手机的刷新频率统一
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton:FloatingActionButton(onPressed: (){
_controller.repeat(); //重复播放
},child:const Icon(Icons.add),) ,
appBar: AppBar(
title: const Text("测试"),
),
body: Center(
child: FadeTransition(opacity: _controller,
child: const FlutterLogo(size: 60,),
)
)
)
);
}
}
也可以通过 lowerBound 和 upperBound 来配置 controller 的最低和最高值
ScaleTransition
实现缩放动画,scale
为 AnimationController
,可以通过 reverse()
实现从大到小的动画效果。
class _HomeState extends State<HomeState> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
// 让程序和手机的刷新频率统一
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton:FloatingActionButton(onPressed: (){
_controller.repeat(); //重复播放
},child:const Icon(Icons.add),) ,
appBar: AppBar(
title: const Text("测试"),
),
body: Center(
child: ScaleTransition(scale: _controller,
child: const FlutterLogo(size: 60,),
)
)
)
);
}
}
- Dart 备忘清单 (jaywcjlove.github.io)
- flutter 官网 (flutter.dev)
- flutter 中文社区官网 (flutter.cn)
- flutter 中文开发者社区 (flutterchina.club)