5.2 装饰容器(DecoratedBox)
📚 章节概览
本节学习 DecoratedBox 装饰容器,它可以在子组件绘制前后添加各种装饰效果:
- DecoratedBox - 装饰容器组件
- BoxDecoration - 装饰器实现类
- color - 背景颜色
- border - 边框
- borderRadius - 圆角
- boxShadow - 阴影
- gradient - 渐变(线性、径向、扫描)
- image - 背景图片
- shape - 形状
- position - 绘制位置
🎯 核心知识点
DecoratedBox vs Container
// DecoratedBox:专门用于装饰
DecoratedBox(
decoration: BoxDecoration(...),
child: Widget(),
)
// Container:功能更全面(包含decoration)
Container(
decoration: BoxDecoration(...),
child: Widget(),
)
区别:
DecoratedBox:单一职责,只负责装饰Container:多功能容器,包含padding、margin、constraints等
1️⃣ DecoratedBox(装饰容器)
1.1 构造函数
const DecoratedBox({
Key? key,
required Decoration decoration,
DecorationPosition position = DecorationPosition.background,
Widget? child,
})
1.2 主要属性
| 属性 | 类型 | 说明 |
|---|---|---|
decoration | Decoration | 装饰器(必需) |
position | DecorationPosition | 绘制位置(background/foreground) |
child | Widget? | 子组件 |
1.3 DecorationPosition
enum DecorationPosition {
background, // 在子组件之后绘制(默认)
foreground, // 在子组件之上绘制
}
示例:
// background:装饰在背后
DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
position: DecorationPosition.background,
child: Text('Text'), // 文字在前,装饰在后
)
// foreground:装饰在前面
DecoratedBox(
decoration: BoxDecoration(color: Colors.red.withOpacity(0.5)),
position: DecorationPosition.foreground,
child: Text('Text'), // 装饰在前,文字在后(会被遮挡)
)
2️⃣ BoxDecoration(装饰器)
2.1 构造函数
BoxDecoration({
Color? color, // 颜色
DecorationImage? image, // 图片
BoxBorder? border, // 边框
BorderRadiusGeometry? borderRadius, // 圆角
List<BoxShadow>? boxShadow, // 阴影(可多个)
Gradient? gradient, // 渐变
BlendMode? backgroundBlendMode, // 背景混合模式
BoxShape shape = BoxShape.rectangle, // 形状
})
2.2 属性详解
1. color - 背景颜色
DecoratedBox(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Container(height: 100),
)
2. border - 边框
// 全边框
BoxDecoration(
border: Border.all(
color: Colors.red,
width: 2,
),
)
// 指定某些边
BoxDecoration(
border: Border(
top: BorderSide(color: Colors.blue, width: 2),
bottom: BorderSide(color: Colors.green, width: 2),
),
)
3. borderRadius - 圆角
// 统一圆角
BoxDecoration(
borderRadius: BorderRadius.circular(8),
)
// 指定某些角
BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
)
⚠️ 注意: borderRadius 和 shape: BoxShape.circle 不能同时使用!
4. boxShadow - 阴影
BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
offset: Offset(2, 2), // 偏移量
blurRadius: 4, // 模糊半径
spreadRadius: 1, // 扩散半径
),
],
)
BoxShadow属性:
color:阴影颜色offset:偏移量(x, y)blurRadius:模糊半径(越大越模糊)spreadRadius:扩散半径(阴影大小)
多个阴影:
boxShadow: [
BoxShadow(
color: Colors.red.withOpacity(0.3),
offset: Offset(-2, -2),
blurRadius: 4,
),
BoxShadow(
color: Colors.blue.withOpacity(0.3),
offset: Offset(2, 2),
blurRadius: 4,
),
]
5. gradient - 渐变
Flutter支持三种渐变:
a) LinearGradient(线性渐变)
BoxDecoration(
gradient: LinearGradient(
colors: [Colors.red, Colors.orange],
begin: Alignment.topLeft, // 开始位置
end: Alignment.bottomRight, // 结束位置
),
)
常用方向:
// 水平:左→右(默认)
begin: Alignment.centerLeft,
end: Alignment.centerRight,
// 垂直:上→下
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
// 对角:左上→右下
begin: Alignment.topLeft,
end: Alignment.bottomRight,
b) RadialGradient(径向渐变)
BoxDecoration(
gradient: RadialGradient(
colors: [Colors.yellow, Colors.orange, Colors.red],
center: Alignment.center, // 中心点
radius: 1.0, // 半径
),
)
c) SweepGradient(扫描渐变)
BoxDecoration(
gradient: SweepGradient(
colors: [
Colors.red,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.purple,
Colors.red,
],
),
)
6. image - 背景图片
BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://example.com/image.jpg'),
fit: BoxFit.cover,
),
)
BoxFit选项:
fill:拉伸填充cover:覆盖(保持比例,可能裁剪)contain:包含(保持比例,完整显示)fitWidth:宽度填充fitHeight:高度填充
7. shape - 形状
// 矩形(默认)
BoxDecoration(
shape: BoxShape.rectangle,
)
// 圆形
BoxDecoration(
shape: BoxShape.circle,
)
⚠️ 注意: 圆形需要宽高相等!
3️⃣ 实战示例
示例1:渐变按钮(书中示例)
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.red, Colors.orange.shade700],
),
borderRadius: BorderRadius.circular(3.0),
boxShadow: [
BoxShadow(
color: Colors.black54,
offset: Offset(2.0, 2.0),
blurRadius: 4.0,
),
],
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 80.0, vertical: 18.0),
child: Text(
'Login',
style: TextStyle(color: Colors.white),
),
),
)
效果:
- 红色到橙色的渐变背景
- 3像素圆角
- 右下方黑色阴影
示例2:卡片效果
DecoratedBox(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
offset: Offset(0, 2),
blurRadius: 8,
spreadRadius: 1,
),
],
),
child: Padding(
padding: EdgeInsets.all(16),
child: Text('卡片内容'),
),
)
示例3:头像
DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.blue, width: 3),
image: DecorationImage(
image: NetworkImage('https://example.com/avatar.jpg'),
fit: BoxFit.cover,
),
),
child: Container(
width: 80,
height: 80,
),
)
示例4:渐变卡片
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.purple, Colors.blue, Colors.teal],
),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white, width: 3),
boxShadow: [
BoxShadow(
color: Colors.purple.withOpacity(0.5),
offset: Offset(0, 4),
blurRadius: 12,
spreadRadius: 2,
),
],
),
child: Container(
padding: EdgeInsets.all(24),
child: Column(
children: [
Icon(Icons.star, size: 40, color: Colors.white),
SizedBox(height: 8),
Text(
'组合装饰',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
),
)
🤔 常见问题(FAQ)
Q1: BoxDecoration和Container有什么区别?
A:
| 组件 | 特点 | 使用场景 |
|---|---|---|
| DecoratedBox | 单一职责,只做装饰 | 只需要装饰效果时 |
| Container | 多功能,包含多种属性 | 需要padding、margin等时 |
// DecoratedBox:只能装饰
DecoratedBox(
decoration: BoxDecoration(color: Colors.blue),
child: Widget(),
)
// Container:功能更多
Container(
decoration: BoxDecoration(color: Colors.blue),
padding: EdgeInsets.all(16), // DecoratedBox没有
margin: EdgeInsets.all(8), // DecoratedBox没有
constraints: BoxConstraints(...), // DecoratedBox没有
child: Widget(),
)
Q2: 为什么圆角不生效?
A: 可能的原因:
- shape设置为circle
// ❌ 错误:circle不能使用borderRadius
BoxDecoration(
shape: BoxShape.circle,
borderRadius: BorderRadius.circular(8), // 无效
)
// ✅ 正确:使用rectangle
BoxDecoration(
shape: BoxShape.rectangle, // 或者不设置(默认)
borderRadius: BorderRadius.circular(8),
)
- 子组件超出边界
// ❌ 子组件可能超出圆角区域
DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
child: Image.network('...'), // 图片可能超出
)
// ✅ 使用ClipRRect裁剪
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
child: Image.network('...'),
),
)
Q3: 如何实现虚线边框?
A: Flutter的BoxDecoration不直接支持虚线,需要自定义:
// 方法1:使用第三方包
// dotted_border: ^2.0.0
// 方法2:使用CustomPaint自绘
CustomPaint(
painter: DashedBorderPainter(),
child: Container(...),
)
Q4: gradient和color可以同时使用吗?
A: 不可以!两者互斥
// ❌ 错误:只有gradient生效
BoxDecoration(
color: Colors.blue, // 无效
gradient: LinearGradient(...), // 生效
)
// ✅ 正确:只用一个
BoxDecoration(
gradient: LinearGradient(...),
)
Q5: 如何实现图片上的渐变遮罩?
A: 嵌套两个DecoratedBox
DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage('...'),
fit: BoxFit.cover,
),
),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black.withOpacity(0.7),
],
),
),
child: Container(
padding: EdgeInsets.all(16),
child: Text('文字'),
),
),
)
🎯 跟着做练习
练习1:Material Design卡片
目标: 实现一个符合Material Design的卡片效果
要求:
- 白色背景
- 12像素圆角
- 轻微阴影(elevation 2)
- 16像素内边距
💡 查看答案
DecoratedBox(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: Offset(0, 2),
blurRadius: 4,
spreadRadius: 0,
),
],
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'标题',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text('这是卡片的内容描述...'),
],
),
),
)
练习2:Instagram风格渐变按钮
目标: 实现Instagram风格的渐变按钮
要求:
- 紫色到粉色到橙色的渐变
- 圆角按钮
- 白色文字
💡 查看答案
GestureDetector(
onTap: () {
print('按钮点击');
},
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color(0xFF833AB4), // 紫色
Color(0xFFC13584), // 粉色
Color(0xFFE1306C), // 红色
Color(0xFFFD1D1D), // 橙红色
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(
color: Colors.pink.withOpacity(0.3),
offset: Offset(0, 4),
blurRadius: 8,
),
],
),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 12),
child: Text(
'Follow',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
)
练习3:带头像的用户卡片
目标: 实现一个带头像和渐变背景的用户卡片
要求:
- 渐变背景
- 圆形头像
- 阴影效果
💡 查看答案
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.blue, Colors.purple],
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.4),
offset: Offset(0, 4),
blurRadius: 12,
spreadRadius: 2,
),
],
),
child: Padding(
padding: EdgeInsets.all(20),
child: Row(
children: [
// 头像
DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 3),
image: DecorationImage(
image: NetworkImage('https://picsum.photos/100'),
fit: BoxFit.cover,
),
),
child: Container(
width: 60,
height: 60,
),
),
SizedBox(width: 16),
// 信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'John Doe',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
'Flutter Developer',
style: TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
],
),
),
],
),
),
)
📊 属性对比表
BoxShadow vs Material Elevation
| 属性 | BoxShadow | Material Elevation |
|---|---|---|
| 灵活性 | 高(完全自定义) | 低(预定义) |
| 颜色 | 自定义 | 固定黑色 |
| 偏移 | 自定义 | 固定向下 |
| 多阴影 | 支持 | 不支持 |
| 使用 | BoxDecoration | Material组件 |
三种渐变对比
| 渐变类型 | 效果 | 适用场景 |
|---|---|---|
| LinearGradient | 线性(直线) | 按钮、背景 |
| RadialGradient | 径向(圆形扩散) | 聚光灯效果 |
| SweepGradient | 扫描(圆形旋转) | 进度环、色轮 |
📋 小结
核心概念
DecoratedBox(
decoration: BoxDecoration(
// 颜色(与gradient互斥)
color: Colors.blue,
// 边框
border: Border.all(color: Colors.red, width: 2),
// 圆角(与circle互斥)
borderRadius: BorderRadius.circular(8),
// 阴影(可多个)
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
offset: Offset(2, 2),
blurRadius: 4,
),
],
// 渐变(与color互斥)
gradient: LinearGradient(
colors: [Colors.red, Colors.orange],
),
// 背景图片
image: DecorationImage(
image: NetworkImage('...'),
fit: BoxFit.cover,
),
// 形状
shape: BoxShape.rectangle, // 或 BoxShape.circle
),
position: DecorationPosition.background, // 或 foreground
child: Widget(),
)
记忆技巧
- BoxDecoration = Box(盒子) + Decoration(装饰)
- 渐变顺序:Linear(线性)→ Radial(径向)→ Sweep(扫描)
- 阴影参数:Color(颜色)→ Offset(偏移)→ Blur(模糊)→ Spread(扩散)
- 互斥属性:color vs gradient,borderRadius vs circle