字节笔记本

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: 文字粗细
dart
/// 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: 用于构建纯文本的 Span
  • WidgetSpan: 用于构建内嵌 Widget 的 Span (比如:Icon)
dart
/// 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 关键字)修饰的可选参数,这种不传编译可以通过,但是会报警告
dart
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 时,就把宽(或高)扩展到 48px
    • MaterialTapTargetSize.shrinkWrap: 紧缩包裹,可以去除上下的间隔
  • 最小宽度: ButtonTheme(也是个 Widget,包裹 Button) 或 minWidth(Button 的一个属性)
  • 内间距: 修改 padding 属性值
dart
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
dart
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 加载包内图片步骤如下:

  1. 在 Flutter 项目中创建一个文件夹目录(比如 assets/image),存储图片
  2. pubspec.yaml 进行配置
yaml
assets:
  - assets/images/
  1. 使用图片
dart
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 实现占位图功能:

dart
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.xxxIconData(编码,字体) 这 2 种方式来得到 IconData 对象。另外,IconData 的本质就是字体,因此也可以使用 Text 来显示字体图标:

dart
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: 输入框左侧的 icon
    • hintText: 输入框中的提示文字
    • border: 边框,通常使用 OutlineInputBorder()
    • filled: 是否使用填充色,默认为 false
    • fillColor: 填充色(可以理解为输入框的背景色)
  • obscureText: 是否模糊文字,默认为 false,密文模式设置设置为 true
  • onChanged: 监听文字内容变化
  • onSubmitted: 监听提交事件
dart
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 中来获取到控件。

dart
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全栈行动

分享: