Appearance
Flutter 组件
Container
容器组件
属性名 | 类型 | 说明 | 示例 |
---|---|---|---|
alignment | AlignmentGeometry | 子元素对齐方式 | topCenter /topLeft /topRight center /centerLeft /centerRight bottomCenter /bottomLeft /bottomRight |
decoration | BoxDecoration | 容器装饰样式(背景/边框/阴影等) | dart<br>BoxDecoration(<br> color: Colors.blue,<br> border: Border.all(<br> color: Colors.red,<br> width: 2.0<br> ),<br> borderRadius: BorderRadius.circular(8),<br> boxShadow: [BoxShadow(<br> color: Colors.black54,<br> offset: Offset(2.0, 2.0),<br> blurRadius: 10.0<br> )],<br> gradient: LinearGradient( // 或 RadialGradient<br> colors: [Colors.red, Colors.orange]<br> )<br>)<br> |
margin | EdgeInsetsGeometry | 容器外间距(与外部组件的距离) | EdgeInsets.all(20.0) EdgeInsets.only(top: 10) |
padding | EdgeInsetsGeometry | 容器内边距(边缘与子元素的间距) | EdgeInsets.all(10.0) EdgeInsets.symmetric(horizontal: 15) |
transform | Matrix4 | 矩阵变换(旋转/缩放/平移等) | Matrix4.rotationZ(0.2) Matrix4.translationValues(10, 0, 0) |
height | double | 容器高度 | height: 100.0 |
width | double | 容器宽度 | width: 200.0 |
child | Widget | 子元素内容 | child: Text("Hello") child: Icon(Icons.star) |
关键说明:
alignment 使用
Alignment
类的静态常量控制子元素位置,如:dartalignment: Alignment.topCenter // 顶部居中
decoration 支持嵌套多种样式效果:
color
:背景色(与gradient
冲突)border
:边框样式borderRadius
:圆角半径boxShadow
:阴影效果gradient
:线性/径向渐变背景
margin & padding 使用
EdgeInsets
类定义间距,常用构造方法:dartEdgeInsets.all(20) // 全方向 EdgeInsets.only(left:10) // 指定方向 EdgeInsets.symmetric(vertical: 10) // 对称方向
transform 通过 4x4 矩阵实现 3D 变换,常用操作:
dartMatrix4.rotationZ(angle) // Z轴旋转 Matrix4.translationValues(x, y, z) // 平移
Text
组件属性详解
属性名 | 类型 | 说明 | 可选值/示例 |
---|---|---|---|
textAlign | TextAlign | 文本水平对齐方式 | TextAlign.center (居中)TextAlign.left (左对齐)TextAlign.right (右对齐)TextAlign.justify (两端对齐) |
textDirection | TextDirection | 文本阅读方向 | TextDirection.ltr (从左至右)TextDirection.rtl (从右至左) |
overflow | TextOverflow | 文本溢出处理方式 | TextOverflow.clip (裁剪)TextOverflow.fade (渐隐)TextOverflow.ellipsis (省略号) |
textScaleFactor | double | 字体缩放比例(基于系统字体大小) | 1.0 (默认大小)1.2 (放大 20%)0.8 (缩小 20%) |
maxLines | int | 最大显示行数 | 1 (单行显示)3 (最多 3 行)null (无限制) |
style | TextStyle | 文本样式设置(支持嵌套多种样式) | dart<br>TextStyle(<br> color: Colors.red,<br> fontSize: 16.0,<br> fontWeight: FontWeight.bold,<br> fontStyle: FontStyle.italic,<br> letterSpacing: 1.5,<br> decoration: TextDecoration.underline<br>) |
关键属性详解:
textAlign
控制文本在水平方向的对齐方式:dartText('Hello World', textAlign: TextAlign.center)
textDirection
影响对齐方向和溢出行为:dartText('مرحبا', textDirection: TextDirection.rtl) // 阿拉伯语从右向左
overflow 与 maxLines 配合使用
常见组合:dartText('长文本内容...', maxLines: 1, overflow: TextOverflow.ellipsis // 单行省略 )
textScaleFactor
响应系统字体设置:dartText('可缩放的文本', textScaleFactor: 1.2)
style 常用样式属性:
样式属性 类型/值 说明 color
Color 文字颜色 fontSize
double 字体大小 fontWeight
FontWeight.w100-w900 字体粗细 fontStyle
FontStyle.normal/italic 正常/斜体 letterSpacing
double 字符间距 decoration
TextDecoration.underline/lineTrough 下划线/删除线 fontFamily
String 自定义字体
💡 最佳实践:
- 多语言文本务必设置正确的
textDirection
- 长文本推荐组合使用
maxLines
和overflow
- 使用
textScaleFactor
保持 UI 可访问性- 通过
style
的copyWith()
复用文本样式:dartTextStyle baseStyle = TextStyle(fontSize: 16); Text('标题', style: baseStyle.copyWith(fontWeight: FontWeight.bold))
Image
组件属性详解
- Image.asset, 本地图片
- Image.network 远程图片 | 属性名 | 类型 | 说明 | 可选值/示例 | | ------------------------------- | ------------------ | ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | | alignment | Alignment | 图片在容器内的对齐方式 |
Alignment.topCenter
Alignment.bottomLeft
Alignment(-0.5, 0.5)
| | color
colorBlendMode | Color
BlendMode | 背景色与混合模式(需配合使用) |dart<br>color: Colors.green,<br>colorBlendMode: BlendMode.difference
| | fit | BoxFit | 图片填充方式 |BoxFit.fill
BoxFit.contain
BoxFit.cover
BoxFit.fitWidth
BoxFit.fitHeight
BoxFit.scaleDown
| | repeat | ImageRepeat | 图片平铺方式 |ImageRepeat.repeat
ImageRepeat.repeatX
ImageRepeat.repeatY
ImageRepeat.noRepeat
| | width | double | 图片宽度(常配合圆形裁剪使用) |width: 120.0
| | height | double | 图片高度(常配合圆形裁剪使用) |height: 120.0
|
关键属性详解:
1. color & colorBlendMode
dart
Image.asset('assets/icon.png',
color: Colors.green,
colorBlendMode: BlendMode.difference
)
- 常用混合模式:
BlendMode.difference
:反色效果BlendMode.multiply
:叠加效果BlendMode.saturation
:饱和度混合
2. fit(图片填充方式)
值 | 效果说明 | 视觉表现 |
---|---|---|
BoxFit.fill | 拉伸充满容器(可能变形) | ⬛ 全填充(变形) |
BoxFit.contain | 保持比例全显示(可能有留白) | ⬛ 完整显示(带留白) |
BoxFit.cover | 保持比例充满(可能裁剪) | ⬛ 充满(裁剪边缘) |
BoxFit.fitWidth | 宽度充满(高度自适应) | ↔️ 宽度撑满 |
BoxFit.fitHeight | 高度充满(宽度自适应) | ↕️ 高度撑满 |
BoxFit.scaleDown | 保持比例但不放大(contain 变体) | ⬛ 完整显示(不放大) |
3. repeat(平铺模式)
dart
Image.asset('pattern.png',
repeat: ImageRepeat.repeatX,
width: double.infinity
)
- 应用场景:
- 背景纹理平铺
- 创建连续图案
- 无缝拼接大图
4. width/height(尺寸控制)
dart
ClipOval(
child: Image.network('url',
width: 100,
height: 100,
fit: BoxFit.cover,
)
)
最佳实践:
混合模式选择:
- 使用
color
属性实现单色图标 - 通过
colorBlendMode
创建特殊滤镜效果
- 使用
响应式图片处理:
dartLayoutBuilder( builder: (context, constraints) { return Image.asset('bg.jpg', width: constraints.maxWidth, fit: BoxFit.cover ); } )
圆形头像实现:
dartClipOval( child: Image.asset('avatar.jpg', width: 80, height: 80, fit: BoxFit.cover, // 关键:确保图片居中裁剪 ), )
平铺背景优化:
dartContainer( decoration: BoxDecoration( image: DecorationImage( image: AssetImage('tile.png'), repeat: ImageRepeat.repeat, scale: 0.5 // 控制图案密度 ) ) )
💡 性能提示:
- 网络图片使用
cacheHeight
/cacheWidth
减少内存占用- 需要圆角效果优先考虑
ClipRRect
而非borderRadius
- 平铺小图替代大图可显著降低内存消耗
Flutter 实现圆形图片的 5 种方法
方法 1:使用 ClipOval
(最常用)
dart
ClipOval(
child: Image.network(
'https://example.com/avatar.jpg',
width: 100,
height: 100,
fit: BoxFit.cover, // 关键:确保图片填充裁剪区域
),
)
方法 2:使用 CircleAvatar
(头像专用)
dart
CircleAvatar(
radius: 60, // 控制圆形大小
backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
child: Text('AB'), // 可添加备用文本
)
方法 3:使用 Container
+ BoxDecoration
dart
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
shape: BoxShape.circle,
// borderRadius: BorderRadius.circular(75),
image: DecorationImage(
image: NetworkImage('https://example.com/avatar.jpg'),
fit: BoxFit.cover,
),
),
)
方法 4:使用 ClipRRect
+ 圆角(可调整圆角程度)
dart
ClipRRect(
borderRadius: BorderRadius.circular(100), // 设置为宽高的一半
child: Image.asset(
'assets/profile.jpg',
width: 100,
height: 100,
fit: BoxFit.cover,
),
)
方法 5:使用 PhysicalModel
(带物理效果)
dart
PhysicalModel(
color: Colors.transparent,
shape: BoxShape.circle,
elevation: 5, // 添加阴影深度
child: Image.network(
'https://example.com/avatar.jpg',
width: 100,
height: 100,
fit: BoxFit.cover,
),
)
最佳实践建议:
尺寸控制:
- 宽高必须相等才能得到正圆
- 使用
LayoutBuilder
实现响应式圆形:
dartLayoutBuilder( builder: (context, constraints) { double size = constraints.maxWidth; return ClipOval( child: Image.asset('avatar.jpg', width: size, height: size, fit: BoxFit.cover, ), ); } )
占位与错误处理:
dartClipOval( child: CachedNetworkImage( imageUrl: 'https://example.com/avatar.jpg', width: 100, height: 100, fit: BoxFit.cover, placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), ), )
添加边框:
dartContainer( decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: Colors.blue, width: 3), ), child: ClipOval( child: Image.asset(...), ), )
带点击效果的圆形图片:
dartInkWell( onTap: () {}, // 点击事件 borderRadius: BorderRadius.circular(50), child: ClipOval( child: Image.asset(...), ), )
💡 性能优化:
- 网络图片使用
cached_network_image
包缓存- 本地图片使用
Image.asset
并指定精确尺寸- 对于小图标,优先使用
Icon
而不是图片资源
官方图标组件
- Material Design 所有图标可以在其官网查看:https://material.io/tools/icons/
dart
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: const [
Icon(Icons.search, color: Colors.red, size: 40),
SizedBox(height: 10),
Icon(Icons.home),
SizedBox(height: 10),
Icon(Icons.category),
SizedBox(height: 10),
Icon(Icons.shop),
SizedBox(height: 10),
],
)
);
}
}
集成阿里巴巴图标库自定义字体图标指南
完整实现步骤
dart
// 步骤1:定义图标映射类
class Iconfont {
static const String _family = 'iconfont'; // 与pubspec.yaml中定义一致
Iconfont._();
// 使用Unicode码点定义图标
static const IconData home = IconData(0xe600, fontFamily: _family);
static const IconData search = IconData(0xe601, fontFamily: _family);
static const IconData cart = IconData(0xe602, fontFamily: _family);
static const IconData user = IconData(0xe603, fontFamily: _family);
static const IconData settings = IconData(0xe604, fontFamily: _family);
}
class IconfontDemo extends StatelessWidget {
const IconfontDemo({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('阿里巴巴图标库示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 步骤2:使用自定义图标
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildIcon(Iconfont.home, '首页', Colors.blue),
_buildIcon(Iconfont.search, '搜索', Colors.red),
_buildIcon(Iconfont.cart, '购物车', Colors.orange),
],
),
const SizedBox(height: 40),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildIcon(Iconfont.user, '用户', Colors.green),
_buildIcon(Iconfont.settings, '设置', Colors.purple),
],
),
const SizedBox(height: 40),
// 使用Text显示图标
Text(
String.fromCharCode(Iconfont.home.codePoint),
style: TextStyle(
fontFamily: Iconfont._family,
fontSize: 40,
color: Colors.blue,
),
),
],
),
),
);
}
Widget _buildIcon(IconData icon, String label, Color color) {
return Column(
children: [
Icon(icon, size: 36, color: color),
const SizedBox(height: 8),
Text(label, style: TextStyle(color: color)),
],
);
}
}
配置步骤详解
步骤 1:获取图标文件
- 访问阿里巴巴图标库
- 创建项目并添加所需图标
- 下载图标包(选择
Font class
方式) - 解压后得到:
iconfont.ttf
(字体文件)iconfont.css
(包含 Unicode 编码)
步骤 2:配置 Flutter 项目
- 在项目根目录创建
assets/fonts
文件夹 - 将
iconfont.ttf
复制到此文件夹 - 修改
pubspec.yaml
添加字体配置:
yaml
flutter:
uses-material-design: true
assets:
- assets/images/ # 如果有其他资源
fonts:
- family: iconfont # 自定义字体名称
fonts:
- asset: assets/fonts/iconfont.ttf
高级用法:自动生成映射类
安装代码生成工具:
bashflutter pub global activate iconfont_builder
使用命令生成 Dart 类:
bashflutter pub global run iconfont_builder --from path/to/iconfont.css --to lib/iconfont.dart
常见问题解决
图标显示为方块 □
- 检查字体文件路径是否正确
- 确保
pubspec.yaml
缩进正确 - 执行
flutter clean
后重新运行
图标颜色不生效
- 确认图标不是多色图标(多色图标需要使用 SVG)
- 检查是否在父组件中覆盖了图标颜色
图标大小异常
- 使用
size
参数明确指定尺寸 - 避免在父容器中使用固定宽高限制
- 使用
图标偏移问题
dartIcon( Iconfont.cart, size: 24, textDirection: TextDirection.ltr, // 解决方向问题 )
最佳实践建议
图标管理
- 创建单独的
iconfont.dart
文件管理所有图标 - 使用脚本自动更新图标映射类
- 为图标添加详细注释说明用途
- 创建单独的
性能优化
- 使用
const
修饰图标组件 - 避免在动画中频繁改变图标
- 复杂图标优先考虑 SVG 格式
- 使用
多主题适配
dartIcon( Iconfont.home, color: Theme.of(context).iconTheme.color, )
图标命名规范
dartclass AppIcons { static const IconData home = IconData(0xe600, fontFamily: 'iconfont'); static const IconData search = IconData(0xe601, fontFamily: 'iconfont'); // 按功能模块分组 }
提示:对于多色图标或需要动态变化的图标,建议使用
flutter_svg
包加载 SVG 格式图标
ListView 列表组件
列表类型及核心属性
列表类型 | 实现方式 | 特点描述 |
---|---|---|
垂直列表 | ListView | 默认垂直滚动,适合单列数据展示 |
垂直图文列表 | ListView + ListTile | 图文混合布局,适合商品列表、新闻列表等复杂内容 |
水平列表 | ListView(scrollDirection: Axis.horizontal) | 横向滚动,适合轮播图、分类标签等 |
动态列表 | ListView.builder | 按需构建列表项,性能最优,适合大数据集 |
核心属性详解
属性名 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | 滚动方向:Axis.vertical (垂直,默认),Axis.horizontal (水平) |
padding | EdgeInsetsGeometry | 列表内边距,如:EdgeInsets.all(10) |
reverse | bool | 是否反向显示列表(从下往上/从右往左) |
children | List<Widget> | 直接子组件列表(静态列表使用) |
itemCount | int | 动态列表项数量(仅builder 使用) |
itemBuilder | IndexedWidgetBuilder | 动态列表项构建函数((context, index) => Widget ) |
1. 基础垂直列表
dart
ListView(
padding: const EdgeInsets.all(16),
children: const [
ListTile(title: Text('选项1')),
ListTile(title: Text('选项2')),
ListTile(title: Text('选项3')),
ListTile(title: Text('选项4')),
],
)
2. 垂直图文列表
dart
ListView(
children: [
ListTile(
leading: Image.network('https://example.com/img1.jpg', width: 50),
title: const Text('商品标题1'),
subtitle: const Text('商品描述信息'),
trailing: const Icon(Icons.arrow_forward),
),
ListTile(
leading: Image.network('https://example.com/img2.jpg', width: 50),
title: const Text('商品标题2'),
subtitle: const Text('商品描述信息'),
trailing: const Icon(Icons.arrow_forward),
),
// 更多列表项...
],
)
3. 水平列表
dart
SizedBox(
height: 120, // 必须指定高度
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 12),
children: [
_buildCategory('美食', Icons.restaurant),
_buildCategory('购物', Icons.shopping_cart),
_buildCategory('旅行', Icons.flight),
_buildCategory('娱乐', Icons.movie),
_buildCategory('学习', Icons.school),
],
),
)
Widget _buildCategory(String name, IconData icon) {
return Container(
width: 100,
margin: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 40),
const SizedBox(height: 8),
Text(name),
],
),
);
}
4. 动态列表(性能优化)
dart
final List<String> items = List.generate(100, (i) => '列表项 ${i + 1}');
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(child: Text('${index + 1}')),
title: Text(items[index]),
subtitle: Text('索引: $index'),
trailing: Icon(
index % 3 == 0 ? Icons.star : Icons.star_border,
color: Colors.amber,
),
);
},
)
高级列表技巧
1. 分隔线控制
dart
ListView.separated(
itemCount: 50,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) => ListTile(title: Text('项目 $index')),
)
2. 滚动控制器
dart
final ScrollController _controller = ScrollController();
@override
void initState() {
super.initState();
_controller.addListener(() {
print('滚动位置: ${_controller.offset}');
});
}
ListView.builder(
controller: _controller,
// ...
)
3. 无限滚动加载
dart
ListView.builder(
itemCount: items.length + 1,
itemBuilder: (context, index) {
if (index == items.length) {
return _buildLoadingIndicator();
}
return ListTile(title: Text(items[index]));
},
)
Widget _buildLoadingIndicator() {
return const Padding(
padding: EdgeInsets.all(16),
child: Center(child: CircularProgressIndicator()),
);
}
4. 吸顶头部
dart
CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
title: Text('吸顶标题'),
background: Image.network('https://example.com/header.jpg', fit: BoxFit.cover),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('内容 $index')),
childCount: 50,
),
),
],
)
性能优化建议
静态列表:
dartListView( children: const [ // 使用const ItemWidget(), // 组件也使用const ItemWidget(), ], )
动态列表黄金法则:
- 始终使用
ListView.builder
- 设置精确的
itemCount
- 避免在
itemBuilder
中创建新函数
- 始终使用
懒加载图片:
dartListView.builder( itemBuilder: (context, index) => CachedNetworkImage( imageUrl: images[index], placeholder: (_, __) => LoadingWidget(), ), )
列表项保持单一职责:
dart// ❌ 避免 itemBuilder: (c, i) => Column( children: [ if (i == 0) Header(), Item(data[i]) ] ) // ✅ 正确 Column( children: [ Header(), Expanded(child: ListView.builder(...)) ] )
使用
RepaintBoundary
:dartListView.builder( itemBuilder: (c, i) => RepaintBoundary( child: ComplexItemWidget(data[i]) ) )
常见问题解决方案
嵌套滚动冲突:
dartListView( physics: const ClampingScrollPhysics(), // 解决嵌套滚动 // ... )
列表高度错误:
dart// 在Column中包裹ListView Expanded( child: ListView(...) )
滚动位置保持:
dartclass _MyListState extends State<MyList> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(context) { super.build(context); return ListView(...); } }
动态更新优化:
dart// 使用Key强制重建特定项 ListView.builder( itemBuilder: (c, i) => ItemWidget( key: ValueKey(items[i].id), // 唯一Key data: items[i], ) )
GridView 网格组件
核心属性总览
属性名 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | 滚动方向:Axis.vertical (垂直,默认),Axis.horizontal (水平) |
padding | EdgeInsetsGeometry | 内边距 |
reverse | bool | 是否反向排序(默认 false) |
crossAxisSpacing | double | 水平子 Widget 之间的间距 |
mainAxisSpacing | double | 垂直子 Widget 之间的间距 |
crossAxisCount | int | 在GridView.count 中使用,指定一行中的网格数量 |
maxCrossAxisExtent | double | 在GridView.extent 中使用,指定横轴子元素的最大长度 |
childAspectRatio | double | 子 Widget 宽高比例(宽度/高度) |
children | List<Widget> | 子组件列表(用于静态网格) |
gridDelegate | SliverGridDelegate | 布局控制器(用于动态网格),有两种类型:SliverGridDelegateWithFixedCrossAxisCount SliverGridDelegateWithMaxCrossAxisExtent |
三种创建方式详解
1. GridView.count - 固定数量网格
dart
GridView.count(
crossAxisCount: 3, // 每行3个元素
crossAxisSpacing: 8, // 水平间距
mainAxisSpacing: 12, // 垂直间距
childAspectRatio: 0.8, // 宽高比(宽/高)
padding: const EdgeInsets.all(16),
children: List.generate(12, (index) {
return Container(
color: Colors.blue[100 * (index % 9)],
child: Center(child: Text('Item $index')),
);
}),
)
2. GridView.extent - 最大宽度网格
dart
GridView.extent(
maxCrossAxisExtent: 150, // 每个网格最大宽度150
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 1, // 正方形网格
children: List.generate(20, (index) {
return Card(
elevation: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.star, size: 40, color: Colors.amber),
Text('商品 $index')
],
),
);
}),
)
3. GridView.builder - 动态网格(推荐)
dart
final List<Product> products = [
Product(name: '手机', icon: Icons.phone_iphone),
Product(name: '电脑', icon: Icons.computer),
// ...更多数据
];
GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, // 每行4个
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 0.9,
),
itemCount: products.length,
itemBuilder: (context, index) {
return ProductItem(product: products[index]);
},
)
高级网格布局技巧
1. 响应式网格布局
dart
LayoutBuilder(
builder: (context, constraints) {
int crossAxisCount;
if (constraints.maxWidth > 600) {
crossAxisCount = 4; // 大屏幕4列
} else if (constraints.maxWidth > 400) {
crossAxisCount = 3; // 中等屏幕3列
} else {
crossAxisCount = 2; // 小屏幕2列
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
// ...
),
// ...
);
},
)
2. 网格与列表混合布局
dart
CustomScrollView(
slivers: [
SliverGrid(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 0.8,
),
delegate: SliverChildBuilderDelegate(
(context, index) => ProductCard(products[index]),
childCount: products.length,
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('推荐商品 $index')),
childCount: 10,
),
),
],
)
3. 网格头部吸顶效果
dart
CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
title: Text('商品分类'),
background: Image.network('header.jpg', fit: BoxFit.cover),
),
),
SliverGrid(
// ...网格配置
),
],
)
4. 网格项动画效果
dart
GridView.builder(
gridDelegate: /*...*/,
itemBuilder: (context, index) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
child: /*...*/,
);
},
)
网格布局最佳实践
1. 性能优化方案
dart
GridView.builder( // 始终使用builder
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: items.length,
itemBuilder: (context, index) {
return ProductCard( // 提取为独立组件
key: ValueKey(items[index].id), // 唯一键值
product: items[index],
);
},
)
2. 网格卡片组件示例
dart
class ProductCard extends StatelessWidget {
final Product product;
const ProductCard({super.key, required this.product});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
child: Image.network(product.imageUrl, fit: BoxFit.cover),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(product.name, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 4),
Text('¥${product.price}', style: const TextStyle(color: Colors.red)),
],
),
),
],
),
);
}
}
3. 网格布局常见问题解决
问题:网格高度不正确
dart
// 解决方案:使用Expanded包裹
Scaffold(
body: Column(
children: [
Expanded( // 关键
child: GridView.count(
crossAxisCount: 3,
// ...
),
),
],
),
)
问题:滚动冲突
dart
GridView.builder(
physics: const ClampingScrollPhysics(), // 解决嵌套滚动
// ...
)
问题:网格项大小不一致
dart
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
mainAxisExtent: 180, // 固定高度
// ...
)
网格布局使用场景示例
电商商品网格
dart
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 0.75,
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
itemCount: products.length,
itemBuilder: (context, index) {
return ProductGridItem(product: products[index]);
},
)
照片墙布局
dart
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
mainAxisSpacing: 4,
crossAxisSpacing: 4,
),
itemCount: images.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () => _openFullScreen(index),
child: Hero(
tag: 'image-$index',
child: Image.network(images[index], fit: BoxFit.cover),
),
);
},
)
设置选项网格
dart
GridView.count(
crossAxisCount: 4,
padding: const EdgeInsets.all(24),
children: [
_buildSettingItem(Icons.wifi, '网络'),
_buildSettingItem(Icons.bluetooth, '蓝牙'),
_buildSettingItem(Icons.battery_full, '电池'),
_buildSettingItem(Icons.storage, '存储'),
// ...更多设置项
],
)
Widget _buildSettingItem(IconData icon, String label) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 32),
const SizedBox(height: 8),
Text(label),
],
);
}
性能提示:
- 对于大型网格使用
GridView.builder
+SliverGridDelegate
- 使用
const
构造函数优化静态网格项- 为网格项添加
RepaintBoundary
减少重绘- 使用
cacheExtent
预加载网格区域提高滚动流畅度
Padding 组件
属性名 | 类型 | 说明 | 示例 |
---|---|---|---|
padding | EdgeInsetsGeometry | 设置内边距(核心属性) | EdgeInsets.all(16) EdgeInsets.only(top: 20) EdgeInsets.symmetric(horizontal: 10) |
child | Widget | 需要添加内边距的子组件 | child: Text('Hello') child: Icon(Icons.star) |
1. 统一内边距
dart
Padding(
padding: const EdgeInsets.all(20), // 所有方向20像素内边距
child: Container(
color: Colors.blue,
child: Text('统一内边距'),
),
)
2. 非对称内边距
dart
Padding(
padding: const EdgeInsets.only(
top: 30,
left: 15,
right: 15
), // 仅顶部/左侧/右侧有内边距
child: Card(
child: ListTile(
title: Text('非对称内边距示例'),
subtitle: Text('底部无内边距'),
),
),
)
3. 对称内边距
dart
Padding(
padding: const EdgeInsets.symmetric(
vertical: 10, // 垂直方向10px
horizontal: 20 // 水平方向20px
),
child: ElevatedButton(
onPressed: () {},
child: Text('对称内边距按钮'),
),
)
4. 组合内边距
dart
Padding(
padding: const EdgeInsets.fromLTRB(15, 10, 15, 5), // 左15, 上10, 右15, 下5
child: Text('自定义组合内边距'),
)
线性布局(Row 和 Column)
属性名 | Row(水平布局) | Column(垂直布局) | 说明 |
---|---|---|---|
主轴方向 | 水平(从左到右) | 垂直(从上到下) | 布局的主轴方向 |
主轴对齐 | MainAxisAlignment | MainAxisAlignment | 控制子组件在主轴上的排列方式 |
交叉轴对齐 | CrossAxisAlignment | CrossAxisAlignment | 控制子组件在交叉轴上的排列方式 |
主轴尺寸 | MainAxisSize | MainAxisSize | 主轴方向占用的空间(max 或 min) |
文本方向 | TextDirection | - | 控制水平方向子组件的排列顺序 |
垂直方向 | - | VerticalDirection | 控制垂直方向子组件的排列顺序 |
子组件列表 | children | children | 包含的子组件 |
1. 主轴对齐(MainAxisAlignment)
dart
enum MainAxisAlignment {
start, // 从起始位置开始排列
end, // 从结束位置开始排列
center, // 居中排列
spaceBetween, // 两端对齐,子组件之间间距相等
spaceAround, // 每个子组件两侧间距相等
spaceEvenly, // 所有间距相等(包括两端)
}
2. 交叉轴对齐(CrossAxisAlignment)
dart
enum CrossAxisAlignment {
start, // 沿交叉轴起始位置对齐
end, // 沿交叉轴结束位置对齐
center, // 居中对齐(默认)
stretch, // 拉伸子组件填满交叉轴
baseline, // 按文本基线对齐
}
3. 主轴尺寸(MainAxisSize)
dart
enum MainAxisSize {
min, // 占用最小空间
max, // 占用最大可用空间(默认)
}
基础用法示例
1. 基本水平布局(Row)
dart
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.star, color: Colors.amber),
Text('评分:4.8', style: TextStyle(fontSize: 16)),
ElevatedButton(
onPressed: () {},
child: Text('购买'),
),
],
)
2. 基本垂直布局(Column)
dart
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('标题', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text('副标题', style: TextStyle(fontSize: 16, color: Colors.grey)),
SizedBox(height: 16),
Text('详细内容...' * 5),
],
)
高级布局技巧
1. 灵活空间分配(Expanded)
dart
Row(
children: [
Expanded(
flex: 2, // 占2份
child: Container(height: 50, color: Colors.red),
),
Expanded(
flex: 3, // 占3份
child: Container(height: 50, color: Colors.blue),
),
],
)
2. 相对空间分配(Flexible)
dart
Row(
children: [
Flexible(
fit: FlexFit.loose, // 宽松分配
child: Container(width: 150, color: Colors.green),
),
Flexible(
fit: FlexFit.tight, // 紧密分配
child: Container(color: Colors.yellow),
),
],
)
3. 基线对齐(跨轴)
dart
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
'24',
style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
),
Text(
'°C',
style: TextStyle(fontSize: 20),
),
],
)
4. 嵌套布局
dart
Column(
children: [
// 顶部导航栏
Row(
children: [
IconButton(icon: Icon(Icons.menu),
Expanded(child: TextField()),
IconButton(icon: Icon(Icons.search)),
],
),
// 内容区域
Expanded(
child: ListView(/*...*/),
),
// 底部导航
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IconButton(icon: Icon(Icons.home)),
IconButton(icon: Icon(Icons.shopping_cart)),
IconButton(icon: Icon(Icons.person)),
],
),
],
)
最佳实践与解决方案
1. 处理溢出问题
dart
Row(
children: [
Expanded( // 使用Expanded防止溢出
child: Text('这是一个非常长的文本内容' * 5),
),
IconButton(icon: Icon(Icons.more_vert)),
],
)
2. 响应式布局
dart
LayoutBuilder(
builder: (context, constraints) {
bool isWide = constraints.maxWidth > 600;
return isWide
? Row(children: [NavigationRail(), ContentArea()]) // 宽屏布局
: Column(children: [AppBar(), ContentArea()]); // 窄屏布局
},
)
3. 动态列表构建
dart
Column(
children: List.generate(10, (index) =>
ListTile(
leading: CircleAvatar(child: Text('${index+1}')),
title: Text('项目 $index'),
),
),
)
4. 复杂对齐场景
dart
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 左侧头像
CircleAvatar(radius: 30),
// 右侧内容(使用Column)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text('用户名', style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(width: 8),
Text('@userhandle', style: TextStyle(color: Colors.grey)),
],
),
SizedBox(height: 4),
Text('内容文本...'),
],
),
),
],
)
性能优化指南
使用 const 构造函数:
dartconst Row( children: [ Icon(Icons.star), Text('固定内容'), ], )
提取子组件:
dart// 提取为独立组件 class RatingBar extends StatelessWidget { const RatingBar({super.key}); @override Widget build(BuildContext context) { return Row(/*...*/); } }
避免深层嵌套:
dart// ❌ 不推荐(嵌套过深) Row(children: [Column(children: [Row(children: [...]])]) // ✅ 推荐(提取子组件) CustomComplexWidget()
使用 IntrinsicHeight(谨慎使用):
dartIntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 等高子组件 ], ), )
常见布局模式
1. 居中图标+文字按钮
dart
ElevatedButton(
onPressed: () {},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.download),
SizedBox(width: 8),
Text('下载文件'),
],
),
)
2. 标签+数值展示
dart
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('温度', style: TextStyle(color: Colors.grey)),
Text('24°C', style: TextStyle(fontSize: 20)),
],
)
3. 带图标的输入框
dart
Row(
children: [
Icon(Icons.search, color: Colors.grey),
SizedBox(width: 8),
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: '搜索...',
border: InputBorder.none,
),
),
),
],
)
4. 卡片底部操作栏
dart
Card(
child: Column(
children: [
Image.network('image.jpg'),
Padding(
padding: const EdgeInsets.all(16),
child: Text('描述内容'),
),
const Divider(height: 1),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(onPressed: () {}, child: Text('取消')),
TextButton(onPressed: () {}, child: Text('确定')),
],
),
],
),
)
提示:
- 使用
Spacer()
在布局中创建灵活空间- 结合
Wrap
组件实现自动换行- 在需要滚动时使用
SingleChildScrollView
包裹 Row/Column- 使用
LayoutBuilder
获取父容器约束实现响应式布局
层叠布局(Stack, Align, Positioned)
组件 | 主要功能 | 核心特点 |
---|---|---|
Stack | 层叠布局容器,允许子组件重叠显示 | 支持相对定位和绝对定位 |
Align | 控制子组件在父容器中的对齐位置 | 支持精确的分数定位(FractionalOffset) |
Positioned | 在 Stack 中精确控制子组件的位置 | 可设置上下左右四个方向的偏移值 |
Stack 属性
属性名 | 类型 | 说明 |
---|---|---|
alignment | AlignmentDirectional | 默认子组件对齐方式(默认左上角) |
textDirection | TextDirection | 文本方向(影响对齐方向) |
fit | StackFit | 未定位子组件的尺寸调整方式(loose/expand/passthrough) |
clipBehavior | Clip | 内容溢出处理方式(默认为 Clip.hardEdge) |
children | List<Widget> | 子组件列表 |
Align 属性
属性名 | 类型 | 说明 |
---|---|---|
alignment | Alignment | 对齐方式(如 Alignment.center) |
widthFactor | double | 宽度因子(相对于子组件宽度) |
heightFactor | double | 高度因子(相对于子组件高度) |
child | Widget | 子组件 |
Positioned 属性
属性名 | 类型 | 说明 |
---|---|---|
left | double | 距离 Stack 左边的距离 |
top | double | 距离 Stack 顶部的距离 |
right | double | 距离 Stack 右边的距离 |
bottom | double | 距离 Stack 底部的距离 |
width | double | 指定宽度(非必须) |
height | double | 指定高度(非必须) |
child | Widget | 子组件 |
基础用法示例
1. 基础层叠布局
dart
Stack(
children: [
// 底层背景
Container(color: Colors.blue, height: 200, width: double.infinity),
// 居中对齐的文本
const Align(
alignment: Alignment.center,
child: Text('居中文本', style: TextStyle(color: Colors.white, fontSize: 24)),
// 右上角图标
const Positioned(
top: 10,
right: 10,
child: Icon(Icons.star, color: Colors.amber, size: 30),
),
// 左下角按钮
Positioned(
bottom: 20,
left: 20,
child: ElevatedButton(
onPressed: () {},
child: const Text('点击'),
),
),
],
)
2. 相对定位(FractionalOffset)
dart
Align(
alignment: const FractionalOffset(0.7, 0.3), // 70%宽度,30%高度处
child: Container(
width: 50,
height: 50,
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
)
3. 部分定位
dart
Positioned(
left: 20,
right: 20, // 左右定位,宽度自适应
bottom: 40,
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(10),
),
child: const Center(child: Text('底部横幅')),
),
)
高级应用技巧
1. 响应式层叠布局
dart
LayoutBuilder(
builder: (context, constraints) {
return Stack(
children: [
// 背景图片
Image.network('background.jpg',
width: constraints.maxWidth,
height: constraints.maxHeight,
fit: BoxFit.cover),
// 根据屏幕尺寸调整位置
Positioned(
top: constraints.maxHeight * 0.1,
left: constraints.maxWidth * 0.15,
child: const Text('响应式标题', style: TextStyle(fontSize: 24)),
),
],
);
},
)
2. 动画浮动按钮
dart
AnimatedPositioned(
duration: const Duration(milliseconds: 300),
bottom: _expanded ? 100 : 20,
right: _expanded ? 100 : 20,
child: FloatingActionButton(
onPressed: () => setState(() => _expanded = !_expanded),
child: const Icon(Icons.add),
),
)
3. 自定义对话框
dart
Stack(
children: [
// 半透明遮罩
Positioned.fill(
child: Container(color: Colors.black54),
),
// 居中对话框
Center(
child: Container(
width: 300,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('确认删除?', style: TextStyle(fontSize: 18)),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TextButton(onPressed: () {}, child: const Text('取消')),
TextButton(onPressed: () {}, child: const Text('确定')),
],
)
],
),
),
),
],
)
4. 图片水印效果
dart
Stack(
children: [
// 主图片
Image.network('photo.jpg'),
// 水印文本(重复排列)
Positioned.fill(
child: Column(
children: List.generate(5, (row) => Expanded(
child: Row(
children: List.generate(4, (col) => Expanded(
child: Center(
child: Transform.rotate(
angle: -0.2,
child: const Text('机密',
style: TextStyle(
color: Colors.white30,
fontSize: 24,
fontWeight: FontWeight.bold
),
),
),
),
)),
),
)),
),
),
],
)
最佳实践与解决方案
1. 处理溢出问题
dart
Stack(
clipBehavior: Clip.none, // 允许内容溢出
children: [
Container(color: Colors.blue, width: 100, height: 100),
Positioned(
top: 80, // 部分溢出容器
left: 80,
child: Container(width: 50, height: 50, color: Colors.red),
),
],
)
2. 等比例缩放元素
dart
Align(
alignment: Alignment.center,
widthFactor: 0.8, // 容器宽度为子组件的80%
heightFactor: 0.6, // 容器高度为子组件的60%
child: Container(
color: Colors.green,
child: const Center(child: Text('等比例缩放')),
),
3. 复杂定位系统
dart
Stack(
children: [
// 使用Align定位
const Align(
alignment: Alignment(0.8, -0.8), // 右上区域
child: Icon(Icons.star, size: 40),
),
// 使用Positioned定位
Positioned(
top: MediaQuery.of(context).size.height * 0.4,
left: MediaQuery.of(context).size.width * 0.2,
child: const Icon(Icons.circle, size: 40),
),
// 使用FractionalTranslation
FractionalTranslation(
translation: const Offset(0.5, 0.5), // 中心点
child: const Icon(Icons.add, size: 60),
),
],
)
4. 性能优化
dart
// 使用const避免重绘
const Stack(
children: [
Positioned(top: 10, child: Icon(Icons.star)),
Align(alignment: Alignment.center, child: Text('固定内容')),
],
)
// 提取独立组件
class Badge extends StatelessWidget {
const Badge({super.key});
@override
Widget build(BuildContext context) {
return const Positioned(
top: 5,
right: 5,
child: CircleAvatar(radius: 10, backgroundColor: Colors.red),
);
}
}
常见使用场景
1. 带徽章的头像
dart
Stack(
clipBehavior: Clip.none, // 允许徽章溢出
children: [
const CircleAvatar(radius: 30, backgroundImage: NetworkImage('avatar.jpg')),
Positioned(
top: -5,
right: -5,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Text('3', style: TextStyle(color: Colors.white)),
),
),
],
)
2. 卡片重叠效果
dart
Stack(
children: [
Positioned( // 底层卡片
top: 20,
left: 20,
right: 20,
child: Card(
elevation: 4,
child: Container(height: 150),
),
),
Positioned( // 中层卡片
top: 40,
left: 40,
right: 40,
child: Card(
elevation: 8,
child: Container(height: 150),
),
),
Positioned( // 顶层卡片
top: 60,
left: 60,
right: 60,
child: Card(
elevation: 12,
child: Container(height: 150),
),
),
],
)
3. 视频播放器控制层
dart
Stack(
children: [
// 视频内容
VideoPlayer(controller: _controller),
// 控制层(半透明背景)
Positioned.fill(
child: Container(
color: Colors.black45,
child: Column(
children: [
// 顶部标题栏
AppBar(title: const Text('视频标题'), backgroundColor: Colors.transparent),
// 中间播放按钮
Expanded(
child: Center(
child: IconButton(
icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow),
iconSize: 50,
color: Colors.white,
onPressed: _togglePlay,
),
),
),
// 底部进度条
VideoProgressIndicator(_controller, allowScrubbing: true),
],
),
),
),
],
)
4. 地图标记点
dart
Stack(
children: [
// 地图组件
GoogleMap(/*...*/),
// 标记点
Positioned(
left: 100,
top: 200,
child: Column(
children: [
const Icon(Icons.location_pin, color: Colors.red, size: 40),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
child: const Text('位置A'),
),
],
),
),
],
)
提示:
- 优先使用
Positioned
进行精确控制,使用Align
进行相对定位- 使用
LayoutBuilder
实现响应式层叠布局- 对于复杂布局,组合使用
Stack
+Transform
+Opacity
- 使用
Clip.none
允许内容溢出容器边界- 避免在
Stack
中嵌套过多子组件(超过 5 个考虑重构)
Card 组件全面指南
Card 是 Flutter 中用于创建卡片式布局的核心组件,具有圆角、阴影和立体效果,非常适合展示内容区块。以下是 Card 组件的详细解析:
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
margin | EdgeInsetsGeometry | null | 卡片外部的边距 |
child | Widget | 必填 | 卡片内容区域 |
elevation | double | 1.0 | 阴影深度(值越大阴影越明显) |
color | Color | Theme.cardColor | 卡片背景颜色 |
shadowColor | Color | Theme.shadowColor | 阴影颜色 |
shape | ShapeBorder | RoundedRectangleBorder(borderRadius: BorderRadius.circular(4.0)) | 卡片形状(可自定义圆角) |
clipBehavior | Clip | Clip.none | 内容裁剪方式(none/antiAlias/antiAliasWithSaveLayer/hardEdge) |
borderOnForeground | bool | true | 是否在子组件前绘制边框 |
surfaceTintColor | Color | null | 材料表面色调颜色(Material 3) |
基础用法示例
1. 基础卡片
dart
Card(
child: ListTile(
leading: Icon(Icons.person),
title: Text('用户姓名'),
subtitle: Text('用户描述信息'),
trailing: Icon(Icons.arrow_forward),
),
)
2. 自定义卡片
dart
Card(
elevation: 8, // 更强的阴影效果
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), // 更大的圆角
),
color: Colors.blue[50], // 自定义背景色
margin: EdgeInsets.all(16), // 外边距
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('卡片标题', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text('这里是卡片的内容区域,可以放置各种组件...'),
SizedBox(height: 16),
Align(
alignment: Alignment.centerRight,
child: ElevatedButton(
onPressed: () {},
child: Text('操作按钮'),
),
),
],
),
),
)
3. 图文卡片
dart
Card(
clipBehavior: Clip.antiAlias, // 平滑裁剪
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Image.network(
'https://example.com/image.jpg',
height: 150,
fit: BoxFit.cover,
),
Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('产品名称', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text('¥299', style: TextStyle(color: Colors.red, fontSize: 20)),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: List.generate(5, (i) =>
Icon(Icons.star, size: 16, color: i < 4 ? Colors.amber : Colors.grey)
),
),
Text('128条评价', style: TextStyle(color: Colors.grey)),
],
),
],
),
),
],
),
)
高级用法与技巧
1. 响应式卡片设计
dart
LayoutBuilder(
builder: (context, constraints) {
bool isWide = constraints.maxWidth > 600;
return Card(
margin: EdgeInsets.symmetric(
vertical: 16,
horizontal: isWide ? 40 : 16,
),
child: Flex(
direction: isWide ? Axis.horizontal : Axis.vertical,
children: [
if (isWide) Expanded(
flex: 2,
child: Image.asset('product.jpg', fit: BoxFit.cover),
),
Expanded(
flex: 3,
child: Padding(
padding: EdgeInsets.all(20),
child: /* 内容区域 */
),
),
],
),
);
},
)
2. 动画卡片
dart
AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
child: Card(
elevation: _isHovered ? 12 : 4, // 悬停时提升阴影
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(_isHovered ? 20 : 10),
),
child: InkWell(
onTap: () {},
onHover: (hover) => setState(() => _isHovered = hover),
child: /* 卡片内容 */
),
),
)
3. 卡片堆叠效果
dart
Stack(
children: [
Positioned(
top: 20,
left: 20,
right: 20,
child: Card(
elevation: 2,
color: Colors.grey[200],
child: Container(height: 120),
),
),
Positioned(
top: 40,
left: 40,
right: 40,
child: Card(
elevation: 6,
color: Colors.grey[300],
child: Container(height: 120),
),
),
Card(
elevation: 12,
margin: EdgeInsets.symmetric(horizontal: 60),
child: Container(
height: 120,
padding: EdgeInsets.all(16),
child: Center(child: Text('顶层卡片')),
),
),
],
)
4. 卡片式表单
dart
Card(
margin: EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('登录账户', style: Theme.of(context).textTheme.titleLarge),
SizedBox(height: 24),
TextFormField(
decoration: InputDecoration(labelText: '用户名'),
validator: (value) => value!.isEmpty ? '请输入用户名' : null,
),
SizedBox(height: 16),
TextFormField(
obscureText: true,
decoration: InputDecoration(labelText: '密码'),
validator: (value) => value!.isEmpty ? '请输入密码' : null,
),
SizedBox(height: 24),
ElevatedButton(
onPressed: _submitForm,
child: Text('登录'),
),
],
),
),
),
)
最佳实践指南
1. 内容组织原则
dart
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 1. 标题区域
Padding(
padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text('卡片标题', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
),
// 2. 分隔线(可选)
Divider(height: 1, thickness: 1),
// 3. 内容主体
Padding(
padding: EdgeInsets.all(16),
child: Text('这里是卡片的主要内容区域...'),
),
// 4. 操作区域
ButtonBar(
alignment: MainAxisAlignment.end,
children: [
TextButton(onPressed: () {}, child: Text('取消')),
ElevatedButton(onPressed: () {}, child: Text('确定')),
],
),
],
),
)
2. 性能优化
dart
// 使用const减少重建
const Card(
child: ListTile(
title: Text('固定内容'),
),
)
// 复杂卡片提取为独立组件
class ProductCard extends StatelessWidget {
const ProductCard({super.key});
@override
Widget build(BuildContext context) {
return Card(/* ... */);
}
}
3. 主题一致性
dart
MaterialApp(
theme: ThemeData(
cardTheme: CardTheme(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
margin: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
),
),
home: Scaffold(/* ... */),
)
4. 无障碍支持
dart
Card(
semanticContainer: true, // 将卡片作为语义容器
child: MergeSemantics(
child: ListTile(
title: Text('无障碍卡片'),
subtitle: Text('屏幕阅读器会朗读整个卡片内容'),
onTap: () {},
),
),
)
常见问题解决方案
1. 阴影效果不明显
dart
Card(
elevation: 8, // 增加阴影深度
shadowColor: Colors.black.withOpacity(0.5), // 加深阴影颜色
// ...
)
2. 圆角不生效
dart
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), // 设置圆角半径
),
// ...
)
3. 内容超出边界
dart
Card(
clipBehavior: Clip.antiAlias, // 裁剪超出内容
child: Column(
children: [
Image.asset('large-image.jpg'), // 可能超出边界
// ...
],
),
)
4. 响应式边距
dart
Card(
margin: EdgeInsets.symmetric(
vertical: 16,
horizontal: MediaQuery.of(context).size.width > 600 ? 40 : 16,
),
// ...
)
实际应用场景
1. 设置项卡片
dart
Card(
child: Column(
children: [
SwitchListTile(
title: Text('通知设置'),
value: _notificationsEnabled,
onChanged: (v) => setState(() => _notificationsEnabled = v),
),
Divider(height: 1),
SwitchListTile(
title: Text('夜间模式'),
value: _darkMode,
onChanged: (v) => setState(() => _darkMode = v),
),
],
),
)
2. 数据展示卡片
dart
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('今日数据', style: TextStyle(fontSize: 18)),
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildDataItem(Icons.visibility, '访问量', '1,248'),
_buildDataItem(Icons.shopping_cart, '订单', '86'),
_buildDataItem(Icons.attach_money, '收入', '¥5,280'),
],
),
],
),
),
)
Widget _buildDataItem(IconData icon, String label, String value) {
return Column(
children: [
Icon(icon, size: 30, color: Colors.blue),
SizedBox(height: 8),
Text(label, style: TextStyle(color: Colors.grey)),
SizedBox(height: 4),
Text(value, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
],
);
}
3. 用户资料卡片
dart
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
radius: 40,
backgroundImage: NetworkImage('avatar.jpg'),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('张小明', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Text('高级UI设计师', style: TextStyle(color: Colors.grey)),
SizedBox(height: 8),
Wrap(
spacing: 8,
children: [
Chip(label: Text('UI设计')),
Chip(label: Text('插画')),
Chip(label: Text('动效')),
],
),
],
),
),
],
),
),
)
4. 天气信息卡片
dart
Card(
color: Colors.blue[700],
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('武汉市', style: TextStyle(fontSize: 24, color: Colors.white)),
Text('2023-07-15', style: TextStyle(color: Colors.white70)),
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
Text('28°', style: TextStyle(fontSize: 48, color: Colors.white)),
Text('多云', style: TextStyle(color: Colors.white)),
],
),
WeatherIcon(weather: 'cloudy', size: 80),
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('湿度: 65%', style: TextStyle(color: Colors.white)),
Text('风速: 3km/h', style: TextStyle(color: Colors.white)),
Text('气压: 1013hPa', style: TextStyle(color: Colors.white)),
],
),
],
),
),
)
提示:
- 使用
elevation
控制卡片层次感,但避免过度使用(建议 1-12 范围)- 结合
ClipRRect
实现图片圆角效果更自然- 使用
InkWell
或InkResponse
为卡片添加水波纹效果- 在暗色模式下调整卡片颜色:
color: Theme.of(context).cardColor
- 使用
Card
嵌套Card
创建分层设计效果- 遵循 Material Design 指南,卡片间距至少 8dp,内容内边距至少 16dp
CircleAvatar 组件
CircleAvatar 是一个圆形头像组件,用于展示用户头像、图标或文字,支持图片、图标、文字等多种内容形式,是用户界面中展示个人信息的核心组件。
核心属性表
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
backgroundColor | Color | 主题默认色 | 圆形背景颜色(当没有背景图片时) |
backgroundImage | ImageProvider | null | 背景图片(如 NetworkImage、AssetImage) |
foregroundImage | ImageProvider | null | 前景图片(覆盖在背景之上) |
child | Widget | null | 子组件(通常是文字或图标) |
radius | double | 20 | 圆形半径(定义大小) |
minRadius | double | null | 最小半径 |
maxRadius | double | null | 最大半径 |
onBackgroundImageError | Function | null | 背景图片加载失败的回调函数 |
onForegroundImageError | Function | null | 前景图片加载失败的回调函数 |
基础用法示例
1. 文字头像
dart
CircleAvatar(
radius: 30,
backgroundColor: Colors.blue,
child: Text(
'AB',
style: TextStyle(fontSize: 20, color: Colors.white),
),
)
2. 图片头像
dart
CircleAvatar(
radius: 40,
backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
)
3. 图标头像
dart
CircleAvatar(
radius: 30,
backgroundColor: Colors.amber,
child: Icon(Icons.person, color: Colors.white),
)
4. 带边框的头像
dart
CircleAvatar(
radius: 40,
backgroundColor: Colors.white, // 边框颜色
child: CircleAvatar(
radius: 38,
backgroundImage: AssetImage('assets/avatar.jpg'),
),
)
高级用法与技巧
1. 加载失败处理
dart
CircleAvatar(
radius: 40,
backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
onBackgroundImageError: (exception, stackTrace) {
// 图片加载失败时显示默认头像
debugPrint('头像加载失败: $exception');
},
child: Text('加载失败', style: TextStyle(fontSize: 12)), // 备用显示
)
2. 动态头像状态
dart
Stack(
clipBehavior: Clip.none,
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(user.avatarUrl),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
padding: EdgeInsets.all(3),
decoration: BoxDecoration(
color: user.isOnline ? Colors.green : Colors.grey,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
),
width: 12,
height: 12,
),
),
],
)
3. 头像组合
dart
Stack(
children: [
CircleAvatar(
radius: 25,
backgroundColor: Colors.grey[300],
child: Text('+5', style: TextStyle(color: Colors.black)),
),
Positioned(
left: 15,
child: CircleAvatar(
radius: 25,
backgroundColor: Colors.blue[300],
child: Text('+4'),
),
),
Positioned(
left: 30,
child: CircleAvatar(
radius: 25,
backgroundColor: Colors.green[300],
child: Text('+3'),
),
),
// 更多头像...
],
)
4. 头像动画效果
dart
AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _isExpanded ? 100 : 50,
height: _isExpanded ? 100 : 50,
child: CircleAvatar(
backgroundImage: NetworkImage('avatar.jpg'),
),
)
最佳实践指南
1. 统一头像尺寸
dart
// 创建统一尺寸的头像组件
class AppAvatar extends StatelessWidget {
final String? imageUrl;
final String? text;
const AppAvatar({this.imageUrl, this.text});
@override
Widget build(BuildContext context) {
return CircleAvatar(
radius: 24,
backgroundColor: Theme.of(context).primaryColor,
backgroundImage: imageUrl != null ? NetworkImage(imageUrl!) : null,
child: text != null
? Text(text!.substring(0, 2),
: Icon(Icons.person),
);
}
}
2. 性能优化
dart
// 使用const减少重建
const CircleAvatar(
radius: 30,
child: Icon(Icons.person),
)
// 本地资源使用AssetImage
CircleAvatar(
backgroundImage: AssetImage('assets/avatar.png'),
)
// 网络图片使用缓存
CachedNetworkImage(
imageUrl: 'https://example.com/avatar.jpg',
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
),
placeholder: (context, url) => CircleAvatar(
backgroundColor: Colors.grey[200],
child: CircularProgressIndicator(),
),
errorWidget: (context, url, error) => CircleAvatar(
backgroundColor: Colors.grey[200],
child: Icon(Icons.error),
),
)
3. 主题一致性
dart
MaterialApp(
theme: ThemeData(
avatarTheme: AvatarThemeData(
backgroundColor: Colors.blue[800],
radius: 22,
),
),
)
4. 无障碍支持
dart
CircleAvatar(
backgroundImage: NetworkImage('avatar.jpg'),
child: ExcludeSemantics( // 隐藏文本内容
child: Text(user.name.substring(0, 2)),
),
semanticLabel: '${user.name}的头像', // 屏幕阅读器会朗读此内容
)
实际应用场景
1. 用户列表项
dart
ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(user.avatarUrl),
),
title: Text(user.name),
subtitle: Text(user.email),
trailing: Icon(Icons.arrow_forward),
)
2. 聊天界面
dart
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
radius: 20,
backgroundImage: NetworkImage(message.sender.avatar),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(message.sender.name, style: TextStyle(fontWeight: FontWeight.bold)),
Text(message.content),
],
),
),
],
)
3. 个人资料页
dart
Column(
children: [
CircleAvatar(
radius: 60,
backgroundImage: NetworkImage(profile.avatar),
),
SizedBox(height: 16),
Text(profile.name, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
Text(profile.title, style: TextStyle(color: Colors.grey)),
],
)
4. 评论组件
dart
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(radius: 20, backgroundImage: NetworkImage(comment.user.avatar)),
SizedBox(width: 12),
Text(comment.user.name, style: TextStyle(fontWeight: FontWeight.bold)),
],
),
SizedBox(height: 12),
Text(comment.content),
SizedBox(height: 12),
Text(comment.timeAgo, style: TextStyle(color: Colors.grey, fontSize: 12)),
],
),
),
)
5. 头像选择器
dart
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: avatarOptions.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () => _selectAvatar(avatarOptions[index]),
child: CircleAvatar(
radius: 40,
backgroundColor: _selectedIndex == index
? Colors.blue.withOpacity(0.3)
: Colors.transparent,
child: CircleAvatar(
radius: 36,
backgroundImage: AssetImage(avatarOptions[index]),
),
),
);
},
)
常见问题解决方案
1. 图片变形问题
dart
CircleAvatar(
backgroundImage: NetworkImage('avatar.jpg'),
child: ClipOval( // 确保圆形裁剪
child: Image.network(
'avatar.jpg',
fit: BoxFit.cover,
width: 80, // 需要指定宽高
height: 80,
),
),
)
2. 加载状态处理
dart
CircleAvatar(
radius: 30,
child: FutureBuilder(
future: _loadAvatar(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Icon(Icons.error);
}
return Image.memory(snapshot.data!);
},
),
)
3. 头像大小自适应
dart
LayoutBuilder(
builder: (context, constraints) {
double avatarSize = constraints.maxWidth > 600 ? 80 : 50;
return CircleAvatar(
radius: avatarSize,
backgroundImage: NetworkImage('avatar.jpg'),
);
},
)
4. 文字自动缩放
dart
CircleAvatar(
radius: 40,
backgroundColor: Colors.blue,
child: FittedBox( // 文字自适应缩放
fit: BoxFit.scaleDown,
child: Text(
user.initials,
style: TextStyle(fontSize: 40), // 初始大字体
),
),
)
提示:
- 使用
CachedNetworkImage
优化网络头像加载性能- 对于未知用户,使用姓名首字母 + 随机背景色方案
- 使用
Hero
动画实现头像详情页的平滑过渡- 在暗色模式下调整背景色:
backgroundColor: Theme.of(context).colorScheme.surfaceVariant
- 使用
Badge
组件添加通知徽章- 组合
Tooltip
提供用户信息提示- 重要用户头像添加
BoxShadow
提升视觉层次
Button 按钮组件
Flutter 提供了丰富的按钮组件体系,满足不同场景下的交互需求。以下是完整的按钮组件分类与详解:
按钮类型对比表
按钮类型 | 视觉风格 | 适用场景 | 核心组件 |
---|---|---|---|
凸起按钮 | 有阴影和背景色 | 主要操作、强调操作 | ElevatedButton |
文本按钮 | 无背景、仅文字 | 次要操作、对话框操作 | TextButton |
描边按钮 | 带边框、无填充 | 中等重要性操作、取消操作 | OutlinedButton |
图标按钮 | 仅图标 | 工具栏、紧凑空间操作 | IconButton |
悬浮按钮 | 圆形悬浮按钮 | 主屏幕主要操作 | FloatingActionButton |
下拉按钮 | 带下拉菜单 | 选项选择 | DropdownButton |
弹出菜单按钮 | 三点菜单 | 更多操作菜单 | PopupMenuButton |
自定义按钮 | 完全自定义样式 | 品牌化设计、特殊交互 | InkWell/GestureDetector |
一、基础按钮组件
1. ElevatedButton (凸起按钮)
dart
ElevatedButton(
onPressed: () {
print('按钮被点击');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // 背景色
foregroundColor: Colors.white, // 文字/图标颜色
elevation: 5, // 阴影深度
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), // 内边距
shape: RoundedRectangleBorder( // 形状
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('确认提交'),
)
2. TextButton (文本按钮)
dart
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
padding: EdgeInsets.all(12),
),
child: const Text('取消操作'),
)
3. OutlinedButton (描边按钮)
dart
OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: BorderSide(color: Colors.blue, width: 2), // 边框样式
shape: StadiumBorder(), // 圆角形状
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
child: const Text('查看详情'),
)
4. IconButton (图标按钮)
dart
IconButton(
onPressed: () {},
icon: Icon(Icons.favorite),
iconSize: 30,
color: Colors.red,
tooltip: '收藏', // 长按提示
)
5. FloatingActionButton (悬浮按钮)
dart
FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
elevation: 6,
shape: CircleBorder(), // 圆形
child: Icon(Icons.add),
)
二、高级按钮组件
1. DropdownButton (下拉按钮)
dart
String? _selectedValue = '选项1';
DropdownButton<String>(
value: _selectedValue,
onChanged: (newValue) {
setState(() => _selectedValue = newValue);
},
items: <String>['选项1', '选项2', '选项3']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
2. PopupMenuButton (弹出菜单按钮)
dart
PopupMenuButton<String>(
icon: Icon(Icons.more_vert),
itemBuilder: (context) => [
PopupMenuItem(value: 'edit', child: Text('编辑')),
PopupMenuItem(value: 'delete', child: Text('删除')),
],
onSelected: (value) {
if (value == 'delete') _deleteItem();
},
)
3. ButtonBar (按钮组)
dart
ButtonBar(
alignment: MainAxisAlignment.end, // 对齐方式
children: [
TextButton(onPressed: () {}, child: Text('取消')),
ElevatedButton(onPressed: () {}, child: Text('确定')),
],
)
三、按钮状态管理
1. 禁用状态
dart
ElevatedButton(
onPressed: isLoading ? null : _submitForm, // null表示禁用
child: isLoading
? CircularProgressIndicator(color: Colors.white)
: Text('提交'),
)
2. 加载状态
dart
bool _isLoading = false;
ElevatedButton(
onPressed: _isLoading ? null : _fetchData,
child: _isLoading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text('加载数据'),
)
3. 按钮状态样式
dart
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return Colors.grey; // 禁用状态
}
if (states.contains(MaterialState.pressed)) {
return Colors.blue[700]!; // 按下状态
}
return Colors.blue; // 默认状态
},
),
),
// ...
)
四、自定义按钮
1. 使用 InkWell 创建自定义按钮
dart
InkWell(
onTap: () {},
borderRadius: BorderRadius.circular(8),
splashColor: Colors.blue[100], // 水波纹颜色
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue),
),
child: Text('自定义按钮'),
),
)
2. 渐变按钮
dart
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero, // 清除默认内边距
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
onPressed: () {},
child: Ink(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(10),
),
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
child: Text('渐变按钮', style: TextStyle(color: Colors.white)),
),
)
3. 图标+文字按钮
dart
ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.cloud_upload),
label: Text('上传文件'),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
),
)
五、最佳实践指南
1. 按钮尺寸规范
dart
// 小尺寸按钮
TextButton(
style: TextButton.styleFrom(padding: EdgeInsets.all(8)),
child: Text('小按钮'),
)
// 中尺寸按钮(默认)
ElevatedButton(child: Text('中按钮'),)
// 大尺寸按钮
ElevatedButton(
style: ElevatedButton.styleFrom(padding: EdgeInsets.all(16)),
child: Text('大按钮'),
)
2. 响应式按钮设计
dart
LayoutBuilder(
builder: (context, constraints) {
bool isMobile = constraints.maxWidth < 600;
return isMobile
? FloatingActionButton(onPressed: () {}, child: Icon(Icons.add))
: ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.add),
label: Text('添加新项目'),
);
},
)
3. 按钮主题统一
dart
MaterialApp(
theme: ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
),
),
),
)
4. 按钮无障碍优化
dart
ElevatedButton(
onPressed: () {},
child: Text('确认'),
autofocus: true, // 自动获取焦点
focusNode: _focusNode, // 焦点控制
onFocusChange: (hasFocus) {
if (hasFocus) _announceAccessibility('确认按钮已聚焦');
},
)
六、常见问题解决方案
1. 按钮点击无响应
dart
// 原因:onPressed为null
ElevatedButton(
onPressed: _isEnabled ? _handlePress : null, // 正确管理状态
// ...
)
2. 按钮样式不生效
dart
// 解决方案:使用styleFrom代替已弃用属性
ElevatedButton(
style: ElevatedButton.styleFrom( // 正确方式
backgroundColor: Colors.red,
),
// ...
)
3. 按钮尺寸异常
dart
// 解决方案:包裹约束容器
SizedBox(
width: double.infinity, // 撑满宽度
height: 50, // 固定高度
child: ElevatedButton(child: Text('登录')),
)
4. 按钮水波纹效果超出边界
dart
Material(
borderRadius: BorderRadius.circular(10), // 设置裁剪边界
child: InkWell(
borderRadius: BorderRadius.circular(10), // 与Material一致
onTap: () {},
child: Container(
padding: EdgeInsets.all(16),
child: Text('自定义按钮'),
),
),
)
七、实际应用场景
1. 表单提交按钮组
dart
Column(
children: [
// 表单字段...
ButtonBar(
alignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('取消'),
),
ElevatedButton(
onPressed: _submitForm,
child: Text('保存设置'),
),
],
),
],
)
2. 购物车操作按钮
dart
Row(
children: [
IconButton(
icon: Icon(Icons.favorite_border),
onPressed: _toggleFavorite,
),
SizedBox(width: 16),
Expanded(
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16),
),
icon: Icon(Icons.shopping_cart),
label: Text('加入购物车'),
onPressed: _addToCart,
),
),
],
)
3. 社交登录按钮组
dart
Column(
children: [
OutlinedButton.icon(
icon: Image.asset('assets/google.png', width: 24),
label: Text('使用Google登录'),
onPressed: _signInWithGoogle,
),
SizedBox(height: 12),
OutlinedButton.icon(
icon: Image.asset('assets/facebook.png', width: 24),
label: Text('使用Facebook登录'),
onPressed: _signInWithFacebook,
),
],
)
4. 多功能悬浮按钮
dart
FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
child: Icon(Icons.add),
heroTag: 'main_fab', // 唯一标识
)
// 次级悬浮按钮
ScaleTransition(
scale: CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
child: FloatingActionButton(
heroTag: 'fab1', // 不同heroTag
mini: true, // 小型按钮
onPressed: () {},
child: Icon(Icons.camera),
),
)
提示:
- 使用
ButtonStyle
替代已弃用的按钮样式属性- 对于重要操作,使用
ElevatedButton
提高视觉优先级- 在长列表中使用
NotificationListener
优化滚动时按钮性能- 使用
Focus
和FocusTraversalGroup
管理按钮焦点顺序- 通过
MaterialStateProperty
创建响应不同状态的按钮样式- 在暗色模式下使用
ThemeData
自动适配按钮颜色- 为图标按钮添加
tooltip
属性提升无障碍访问体验
Wrap 组件
Flutter 的 Wrap
组件是一种流式布局(Flow Layout)工具,用于在空间不足时自动换行排列子组件,特别适合标签云、动态列表等场景。以下是其核心特性和使用指南:
属性 | 说明 | 默认值 |
---|---|---|
direction | 主轴方向(Axis.horizontal 水平 / Axis.vertical 垂直) | Axis.horizontal |
alignment | 主轴对齐方式(如 WrapAlignment.start 、.center 、.spaceEvenly ) | WrapAlignment.start |
spacing | 主轴方向子组件的间距 | 0.0 |
runSpacing | 交叉轴方向行/列的间距(控制换行后的间隔) | 0.0 |
runAlignment | 交叉轴的行/列对齐方式(需父容器高度足够才生效) | WrapAlignment.start |
crossAxisAlignment | 子组件在交叉轴的对齐方式(如 WrapCrossAlignment.start ) | WrapCrossAlignment.start |
children | 子组件列表 | 必填 |
💡 关键特性:
当主轴空间不足时,Wrap
会自动换行(水平布局换行,垂直布局换列),而Row
/Column
会溢出报错。
🛠️ 二、基本用法示例
水平标签布局(自动换行)
dart
Wrap(
spacing: 10.0, // 水平间距
runSpacing: 15.0, // 行间距
alignment: WrapAlignment.spaceEvenly,
children: List.generate(10, (index) =>
Chip(
label: Text("标签${index + 1}"),
backgroundColor: Colors.blue[100],
),
),
)
垂直流式布局
dart
Wrap(
direction: Axis.vertical, // 垂直主轴
runSpacing: 20.0,
children: [
Icon(Icons.star),
Icon(Icons.favorite),
// 更多子组件...
],
)
⚙️ 三、高级技巧
1. 响应式布局
根据屏幕宽度动态切换方向:
dart
Wrap(
direction: MediaQuery.of(context).size.width > 600
? Axis.horizontal
: Axis.vertical,
children: [/*...*/],
)
2. 嵌套组合
嵌套 Wrap
实现复杂流式结构:
dart
Wrap(
children: [
Wrap( // 内层垂直Wrap
direction: Axis.vertical,
children: [Chip(label: Text("A")), Chip(label: Text("B"))],
),
Chip(label: Text("主内容区")),
],
)
3. 与状态管理结合
动态添加/删除标签:
dart
List<String> tags = ["Flutter", "Dart"];
Wrap(
children: tags.map((tag) =>
InputChip(
label: Text(tag),
onDeleted: () => setState(() => tags.remove(tag)),
),
).toList(),
)
⚠️ 四、常见问题解决
1. 换行后行对齐不生效
原因:runAlignment
需父容器有额外空间才能生效。
方案:为父容器设置足够高度:
dart
Container(
height: 300, // 明确高度
child: Wrap(runAlignment: WrapAlignment.end, children: [...]),
)
2. 子组件间距异常
- 使用
spacing
控制主轴间距,runSpacing
控制行间距。 - 避免子组件自身外边距(如
Padding
),优先用Wrap
的间距属性。
3. 性能优化
- 子组件数量过大时,用
Wrap
替代嵌套Row/Column
避免布局溢出。 - 复杂子组件提取为
const
或独立Widget
减少重建。
🎯 五、应用场景
1. 标签选择器
dart
Wrap(
spacing: 8.0,
children: ["喜剧", "动作", "科幻"].map((genre) =>
FilterChip(
label: Text(genre),
selected: _selectedGenre == genre,
onSelected: (v) => setState(() => _selectedGenre = genre),
),
).toList(),
)
2. 自适应按钮组
dart
Wrap(
runSpacing: 10.0,
children: [
ElevatedButton(onPressed: () {}, child: Text("保存")),
OutlinedButton(onPressed: () {}, child: Text("取消")),
// 更多按钮...
],
)
3. 瀑布流图片布局
dart
Wrap(
spacing: 5.0,
runSpacing: 5.0,
children: imageUrls.map((url) =>
Image.network(url, width: 100, height: 100, fit: BoxFit.cover),
).toList(),
)
🔍 六、对比其他布局
组件 | 特点 | 适用场景 |
---|---|---|
Wrap | 自动换行,简单易用 | 标签、动态内容流 |
Flow | 精确控制子组件位置,性能更高但代码复杂 | 自定义复杂流式布局 |
Row/Column | 单行/列布局,溢出报错 | 线性固定内容 |
💡 提示:
对于大多数场景,Wrap
的简洁性和自动换行特性已足够;需要像素级控制时(如重叠布局),再考虑Flow
。
💎 综合示例
dart
Wrap(
spacing: 12,
runSpacing: 16,
alignment: WrapAlignment.center,
children: [
_buildCategory("音乐", Icons.music_note),
_buildCategory("电影", Icons.movie),
// 添加更多分类...
],
)
Widget _buildCategory(String name, IconData icon) {
return Column(
children: [
Icon(icon, size: 36),
SizedBox(height: 8),
Text(name),
],
);
}
通过灵活组合 Wrap
的属性,可轻松实现各类自适应布局,尤其适合内容动态生成的场景(如搜索关键词、用户兴趣标签等)。