字节笔记本
2026年2月22日
Flutter 基础 Widget 完全指南
本文介绍 Flutter 基础 Widget 的使用方法,包括文本 Widget、按钮 Widget、图片 Widget、字体图标 Widget 和表单 Widget 的详细用法和代码示例。
一、文本 Widget
1、纯文本(Text)
Text 控件常见属性:
- textAlign: 文本对齐,默认是左对齐,可以根据实际情况修改为居中(
TextAlign.center)或右对齐(TextAlign.right) - maxLines: 文字最多显示多少行,通常与
overflow搭配使用 - overflow: 内容溢出显示效果,可以设置显示省略号(
TextOverflow.ellipsis) - textScaleFactor: 缩放因子,默认是 1
- style: 文本样式,直接使用
TextStyle():fontSize: 文字大小color: 文字颜色fontWeight: 文字粗细
/// Text的使用Demo
class TextWidgetDemo extends StatelessWidget {
final textContent = "《定风波》 苏轼 \n"
"莫听穿林打叶声,何妨吟啸且徐行。\n"
"竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生。\n";
@override
Widget build(BuildContext context) {
return Text(
textContent,
// 默认情况下,Text是包裹文字的,文字内容太少时可能看不出效果
textAlign: TextAlign.center,
// 文字最多显示2行
maxLines: 3,
// 文字超过2行时,显示...
overflow: TextOverflow.ellipsis,
// 缩放因子,默认是1
textScaleFactor: 1,
style: TextStyle(
fontSize: 30,
color: Colors.red,
fontWeight: FontWeight.bold, // 字体加粗
),
);
}
}注意:Text 并不是最终渲染的 Widget,最终渲染的是
RichText。Text 的父类是 StatelessWidget,最终渲染的 Widget 是 build()方法创建出来的RenderObjectWidget,即RichText。
2、富文本(Text.rich())
Text.rich() 有一个必须参数 InlineSpan textSpan,InlineSpan 是抽象类且无工厂构造函数,无法直接创建,故需要使用其子类:
TextSpan: 用于构建纯文本的 SpanWidgetSpan: 用于构建内嵌 Widget 的 Span (比如:Icon)
/// Text.rich()的使用Demo
class TextRichDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text.rich(
TextSpan(
children: [
TextSpan(text: "Hello World", style: TextStyle(color: Colors.red)),
TextSpan(text: "Hello Flutter", style: TextStyle(color: Colors.green)),
WidgetSpan(child: Icon(Icons.favorite, color: Colors.red)),
TextSpan(text: "Hello Dart", style: TextStyle(color: Colors.blue)),
],
),
style: TextStyle(fontSize: 26),
);
}
}二、按钮 Widget
1、常见 Button
- RaisedButton: 突出的 Button(从 v1.25.0 过时,推荐使用 ElevatedButton)
- FlatButton: 扁平的 Button(从 v1.25.0 过时,推荐使用 TextButton)
- OutlineButton: 边框 Button(从 v1.25.0 过时,推荐使用 OutlinedButton)
- FloatingActionButton: 浮动按钮,简称 FAB,一般用在 Scaffold 中
floatingActionButtonLocation: 可指定 FAB 在界面中的位置,比如底部居中:FloatingActionButtonLocation.centerFloat
在使用这些常见 Widget 时,经常会看到构造方法中有两类"必传"参数:
- 必传参数: 指的是 Dart 语法中方法的必传参数,这种参数不传一定报错(编译不通过)
- required 参数: 使用 @required(或 required 关键字)修饰的可选参数,这种不传编译可以通过,但是会报警告
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基础Widget")),
body: HomeContent(),
// 4. FloatingActionButton
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => print("FloatingActionButton Click"),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
class ButtonDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// 1. 突出的Button(从 v1.25.0 过时,推荐使用 ElevatedButton)
RaisedButton(
child: Text("RaisedButton"),
textColor: Colors.red,
color: Colors.blue,
onPressed: () => print("RaisedButton Click"),
),
// 2. 扁平的Button(从 v1.25.0 过时,推荐使用 TextButton)
FlatButton(
child: Text("FlatButton"),
color: Colors.orange,
onPressed: () => print("FlatButton Click"),
),
// 3. 边框Button(从 v1.25.0 过时,推荐使用 OutlinedButton)
OutlineButton(
child: Text("OutlineButton"),
onPressed: () => print("OutlineButton Click"),
),
// 5. 自定义Button:图标-文字-背景-圆角
FlatButton(
color: Colors.amberAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.favorite, color: Colors.red),
Text("喜欢作者"),
],
),
onPressed: () => print("自定义Button"),
)
],
);
}
}2、定制 Button
- 默认间隔: 默认情况下 Button 上下有一定有间隔,可以指定
materialTapTargetSize来修改MaterialTapTargetSize.padded: (默认值) 当按钮宽(或高)不足 48px 时,就把宽(或高)扩展到 48pxMaterialTapTargetSize.shrinkWrap: 紧缩包裹,可以去除上下的间隔
- 最小宽度:
ButtonTheme(也是个 Widget,包裹 Button) 或minWidth(Button 的一个属性) - 内间距: 修改
padding属性值
class ButtonExtensionDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
FlatButton(
color: Colors.red,
child: Text("Flat Button1"),
textColor: Colors.white,
onPressed: () {},
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
FlatButton(
minWidth: 30,
height: 30,
color: Colors.red,
child: Text(""),
onPressed: () {},
),
ButtonTheme(
minWidth: 30,
height: 30,
child: FlatButton(
color: Colors.red,
child: Text(""),
onPressed: () {},
),
),
FlatButton(
padding: EdgeInsets.all(0),
color: Colors.red,
child: Text("Float Button3"),
textColor: Colors.white,
onPressed: () {},
),
],
);
}
}三、图片 Widget
Image 控件需要一个必传参数 ImageProvider image,常见子类如下:
- NetworkImage: 用于加载网络图片
- 简单写法:
Image.network('http://lqr.com/FSA_QR.png')
- 简单写法:
- AssetImage: 用于加载 app 包内图片
- 简单写法:
Image.asset('assets/images/FSA_QR.png')
- 简单写法:
1、NetworkImage
常见属性:
- fit: 图片填充方式
BoxFit.fill: 拉伸BoxFit.contain: 内容缩放至最长的一边贴边BoxFit.cover: 内容缩放至最短的一边贴边BoxFit.fitWidth: 宽度一定,高度自适应BoxFit.fitHeight: 高度一定,宽度自适应
- alignment:
Alignment.bottomCenter: 底部居中Alignment.center: 居中Alignment(x, y): 左上角是(-1, -1),右下角是(1, 1)
- color: color 不是背景色,而是用于图像混入的颜色,配合
colorBlendMode使用 - repeat: 重复模式,比如纵向重复
ImageRepeat.repeatY
class ImageDemo01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisSpacing: 8,
mainAxisSpacing: 8,
crossAxisCount: 3,
children: [
Image(image: NetworkImage(imageUrl)),
Image.network(imageUrl),
Image.network(imageUrl, fit: BoxFit.fill), // 拉伸
Image.network(imageUrl, fit: BoxFit.contain), // 内容缩放至最长的一边贴边
Image.network(imageUrl, fit: BoxFit.cover), // 内容缩放至最短的一边贴边
Image.network(imageUrl, fit: BoxFit.fitWidth), // 宽度一定,高度自适应
Image.network(imageUrl, fit: BoxFit.fitHeight), // 高度一定,宽度自适应
Image.network(imageUrl, alignment: Alignment.bottomCenter),
Image.network(imageUrl, alignment: Alignment.center),
Image.network(imageUrl, alignment: Alignment(0, -1)),
Image.network(imageUrl, color: Colors.green, colorBlendMode: BlendMode.colorDodge),
Image.network(imageUrl, repeat: ImageRepeat.repeatY),
],
);
}
}2、AssetImage
使用 AssetImage 加载包内图片步骤如下:
- 在 Flutter 项目中创建一个文件夹目录(比如
assets/image),存储图片 - 在
pubspec.yaml进行配置
assets:
- assets/images/- 使用图片
class ImageDemo02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 简单写法
// Image.asset('assets/images/FSA_QR.png');
return Image(
image: AssetImage('assets/images/FSA_QR.png'),
);
}
}3、占位图(placeHolder)
在网络图片未加载出来之前显示的图片称为占位图,可以使用 FadeInImage 实现占位图功能:
class ImageExtensionDemo extends StatelessWidget {
final imageUrl = "https://up.enterdesk.com/edpic_source/ab/a0/40/aba040ce2daa32fa9cb0cc624b385c0a.jpg";
@override
Widget build(BuildContext context) {
return FadeInImage(
fadeInDuration: Duration(milliseconds: 1),
fadeOutDuration: Duration(milliseconds: 1),
placeholder: AssetImage("assets/images/FSA_QR.png"),
image: NetworkImage(imageUrl),
);
}
}Flutter 会自动进行图片缓存(默认最多缓存 1000 张,缓存空间最多 100m)
四、字体图标 Widget
Icon 字体图标和图片图标对比:
- 字体图标是矢量图(放大的时候不会失真)
- 字体图标可以设置颜色
- 图标很多时,字体图标占据空间更小
Icon 控件接收一个必传参数 IconData icon,Icons 中配备了大量常用 icon (如 Icons.pets),可以使用 Icons.xxx 或 IconData(编码,字体) 这 2 种方式来得到 IconData 对象。另外,IconData 的本质就是字体,因此也可以使用 Text 来显示字体图标:
class IconDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// return Icon(Icons.pets, size: 300, color: Colors.orange);
// return Icon(IconData(0xe90e, fontFamily: 'MaterialIcons'), size: 300, color: Colors.orange);
// 使用Text显示字体图标时,需要将字体编码 -> unicode编码
// 设置对应的字体fontFamily
return Text(
"\ue90e",
style: TextStyle(
fontSize: 100,
color: Colors.orange,
fontFamily: 'MaterialIcons',
),
);
}
}五、表单 Widget
1、TextField 配置
- decoration: 用于自定义输入框样式,
InputDecoration():labelText: 输入框上的 label 文字icon: 输入框左侧的 iconhintText: 输入框中的提示文字border: 边框,通常使用OutlineInputBorder()filled: 是否使用填充色,默认为 falsefillColor: 填充色(可以理解为输入框的背景色)
- obscureText: 是否模糊文字,默认为 false,密文模式设置设置为 true
- onChanged: 监听文字内容变化
- onSubmitted: 监听提交事件
class TextFieldDemo1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(),
TextField(
decoration: InputDecoration(
labelText: 'username(labelText)',
icon: Icon(Icons.people),
hintText: '请输入用户名(hintText)',
),
),
TextField(
decoration: InputDecoration(
labelText: 'OutlineInputBorder',
icon: Icon(Icons.people),
border: OutlineInputBorder(),
),
),
TextField(
decoration: InputDecoration(
labelText: 'fillColor',
icon: Icon(Icons.people),
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.red[100],
),
),
TextField(
decoration: InputDecoration(hintText: '监听事件'),
onChanged: (value) => print("onChange:$value"),
onSubmitted: (value) => print("onSubmitted:$value"),
),
TextField(
obscureText: true,
decoration: InputDecoration(
labelText: 'password',
icon: Icon(Icons.lock),
border: OutlineInputBorder(),
),
)
],
);
}
}2、TextField 内容获取
Flutter 是声明式 UI,也没有提供 ref 之类的方式去获取到 TextField 控件,因此无法通过 TextField 对象来获取输入框中的内容,不过,可以为 TextField 指定 controller,通过 controller 来获取对应输入框中的内容:
题外话:Vue 和 React 可以为控件指定 ref 值,用于在 js 或 jsx 中来获取到控件。
class TextFieldDemo2 extends StatelessWidget {
final usernameTextEditController = TextEditingController();
final passwordTextEditController = TextEditingController();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
TextField(
controller: usernameTextEditController,
decoration: InputDecoration(
labelText: 'username',
icon: Icon(Icons.people),
hintText: '请输入用户名',
border: OutlineInputBorder(),
),
),
SizedBox(height: 8),
TextField(
controller: passwordTextEditController,
obscureText: true,
decoration: InputDecoration(
labelText: 'password',
hintText: '请输入密码',
icon: Icon(Icons.lock),
border: OutlineInputBorder(),
),
),
SizedBox(height: 8),
Container(
width: double.infinity,
height: 40,
child: FlatButton(
color: Colors.blue,
textColor: Colors.white,
child: Text("登录"),
onPressed: () {
final username = usernameTextEditController.text;
final password = passwordTextEditController.text;
print("账号:$username , 密码:$password");
},
),
),
],
),
);
}
}原文链接:https://fullstackaction.com/pages/0f3155/ 作者:GitLqr 来源:FSA全栈行动