984 字
5 分钟
Flutter Day02: Image 与 TextField 组件

Flutter Day02: Image 与 TextField 组件#

本文整理 Flutter 中常用的图片组件 Image 和输入组件 TextField,包含核心属性与实用示例。

1. Image 组件详解#

Image 用于从不同来源(网络、本地资源、文件系统、内存)加载并显示图片。

1.1 常见构造函数(图片来源)#

构造函数描述示例
Image.asset()从项目资源目录(在 pubspec.yaml 中配置)加载图片Image.asset('assets/images/logo.png')
Image.network()从网络 URL 加载图片,支持缓存Image.network('https://picsum.photos/300/200')
Image.file()从设备本地文件系统加载图片Image.file(File('/path/to/image.png'))
Image.memory()从内存字节数组 Uint8List 加载图片Image.memory(bytes)

1.2 核心属性#

属性说明常见值/建议
fit控制图片如何适应容器(最重要)BoxFit.cover(常用)、containfillfitWidthfitHeight
width / height图片显示宽高(逻辑像素)width: 120, height: 120
alignment图片在容器内对齐方式默认 Alignment.center
color + colorBlendMode给图片着色常用于单色图标或蒙层效果
repeat图片小于容器时平铺ImageRepeat.repeatrepeatXrepeatY
frameBuilder自定义加载过程(如渐显)可配合动画提高体验

1.3 fit 快速理解#

  • BoxFit.cover: 等比缩放并填满容器,可能裁剪。
  • BoxFit.contain: 等比缩放完整显示,可能留白。
  • BoxFit.fill: 强制填满,可能拉伸变形。
  • BoxFit.fitWidth: 宽度撑满,高度等比缩放。
  • BoxFit.fitHeight: 高度撑满,宽度等比缩放。

1.4 示例: 常见图片加载与占位#

import 'dart:typed_data';
import 'package:flutter/material.dart';
class ImageDemoPage extends StatelessWidget {
const ImageDemoPage({super.key, required this.memoryBytes});
final Uint8List memoryBytes;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Image Demo')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
const Text('1) Asset 图片'),
Image.asset(
'assets/images/logo.png',
height: 120,
fit: BoxFit.contain,
),
const SizedBox(height: 16),
const Text('2) 网络图片(带加载中提示和错误兜底)'),
Image.network(
'https://picsum.photos/400/200',
height: 120,
fit: BoxFit.cover,
loadingBuilder: (context, child, progress) {
if (progress == null) return child;
return const SizedBox(
height: 120,
child: Center(child: CircularProgressIndicator()),
);
},
errorBuilder: (context, error, stackTrace) => const SizedBox(
height: 120,
child: Center(child: Text('图片加载失败')),
),
),
const SizedBox(height: 16),
const Text('3) 内存图片'),
Image.memory(
memoryBytes,
height: 120,
fit: BoxFit.cover,
),
],
),
);
}
}

1.5 实战建议#

  • 网络图片尽量加 loadingBuildererrorBuilder,避免白屏。
  • 头像、卡片封面常用 BoxFit.cover
  • 需要渐显效果可使用 FadeInImage
  • Flutter 默认有内存缓存;需要磁盘持久化缓存可用 cached_network_image

2. TextField 组件详解#

TextField 是 Material Design 的文本输入框,支持丰富装饰、输入控制和事件回调。

2.1 核心属性#

属性描述示例 / 备注
controller绑定 TextEditingController,用于读写文本大多数业务场景必备
decoration输入框外观配置InputDecoration(...)
keyboardType键盘类型TextInputType.emailAddressnumber
textInputAction键盘右下角动作按钮TextInputAction.searchdonenext
obscureText是否隐藏输入内容密码输入设为 true
onChanged文本变化回调实时搜索、校验
onSubmitted点击动作按钮回调提交表单、搜索

2.2 InputDecoration 常见字段#

  • labelText: 浮动标签。
  • hintText: 占位提示。
  • border: 边框样式(如 OutlineInputBorder())。
  • prefixIcon / suffixIcon: 前后图标。
  • errorText: 错误提示(非 null 时边框变红)。

2.3 示例: 登录表单(含焦点切换与基础校验)#

import 'package:flutter/material.dart';
class LoginFormPage extends StatefulWidget {
const LoginFormPage({super.key});
@override
State<LoginFormPage> createState() => _LoginFormPageState();
}
class _LoginFormPageState extends State<LoginFormPage> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _passwordFocus = FocusNode();
String? _emailError;
String? _passwordError;
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_passwordFocus.dispose();
super.dispose();
}
void _submit() {
final email = _emailController.text.trim();
final password = _passwordController.text;
setState(() {
_emailError = email.contains('@') ? null : '请输入正确的邮箱';
_passwordError = password.length >= 6 ? null : '密码至少 6 位';
});
if (_emailError == null && _passwordError == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('登录请求已提交')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('TextField Demo')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
onSubmitted: (_) => _passwordFocus.requestFocus(),
decoration: InputDecoration(
labelText: '邮箱',
hintText: '请输入邮箱地址',
prefixIcon: const Icon(Icons.email_outlined),
border: const OutlineInputBorder(),
errorText: _emailError,
),
),
const SizedBox(height: 12),
TextField(
controller: _passwordController,
focusNode: _passwordFocus,
obscureText: true,
textInputAction: TextInputAction.done,
onSubmitted: (_) => _submit(),
decoration: InputDecoration(
labelText: '密码',
hintText: '请输入密码',
prefixIcon: const Icon(Icons.lock_outline),
border: const OutlineInputBorder(),
errorText: _passwordError,
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: _submit,
child: const Text('登录'),
),
),
],
),
),
);
}
}

2.4 实战建议#

  • 优先使用 TextEditingController 获取输入值。
  • StatefulWidget 中记得 dispose() 控制器和 FocusNode
  • 表单较复杂时建议配合 Form + TextFormField + validator
  • 搜索框可用 onChanged,提交行为可用 onSubmitted
Flutter Day02: Image 与 TextField 组件
https://zgq1008.github.io/posts/flutter/flutter_day02/
作者
Telus
发布于
2026-04-06
许可协议
CC BY-NC-SA 4.0
Flutter 系列导航