第5章:容器类组件 —— 5.2 装饰容器(DecoratedBox)

51 阅读6分钟

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 主要属性

属性类型说明
decorationDecoration装饰器(必需)
positionDecorationPosition绘制位置(background/foreground)
childWidget?子组件

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),
  ),
)

⚠️ 注意: borderRadiusshape: 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: 可能的原因:

  1. shape设置为circle
// ❌ 错误:circle不能使用borderRadius
BoxDecoration(
  shape: BoxShape.circle,
  borderRadius: BorderRadius.circular(8),  // 无效
)

// ✅ 正确:使用rectangle
BoxDecoration(
  shape: BoxShape.rectangle,  // 或者不设置(默认)
  borderRadius: BorderRadius.circular(8),
)
  1. 子组件超出边界
// ❌ 子组件可能超出圆角区域
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

属性BoxShadowMaterial Elevation
灵活性高(完全自定义)低(预定义)
颜色自定义固定黑色
偏移自定义固定向下
多阴影支持不支持
使用BoxDecorationMaterial组件

三种渐变对比

渐变类型效果适用场景
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(),
)

记忆技巧

  1. BoxDecoration = Box(盒子) + Decoration(装饰)
  2. 渐变顺序:Linear(线性)→ Radial(径向)→ Sweep(扫描)
  3. 阴影参数:Color(颜色)→ Offset(偏移)→ Blur(模糊)→ Spread(扩散)
  4. 互斥属性:color vs gradient,borderRadius vs circle

🔗 相关资源