痛点场景:为按钮添加多重效果

假设你需要一个按钮,要求支持:

  • 基础样式
  • 可选圆角
  • 可选阴影
  • 可选边框
  • 点击缩放动画
  • 按下变色效果
  • 加载状态

传统继承方案:

class BasicButton extends StatelessWidget {...}
class RoundedButton extends BasicButton {...}
class ShadowButton extends RoundedButton {...} 
class AnimatedShadowButton extends ShadowButton {...}
// 类爆炸!

问题爆发点:

  • 💥 组合爆炸(n种效果 → 2^n个子类)
  • 🔧 难以动态添加/移除功能
  • 🔄 无法在运行时切换效果
  • 📦 功能无法独立复用

装饰器模式解决方案

核心思想: 动态地给一个对象添加额外的职责,相比继承更加灵活。

四个关键角色:

  1. 组件接口(Component): 定义原始对象和装饰器的共同接口
  2. 具体组件(ConcreteComponent): 原始对象
  3. 装饰器基类(Decorator): 持有组件引用并实现组件接口
  4. 具体装饰器(ConcreteDecorator): 添加具体功能

Flutter按钮装饰器实现

1. 定义基础按钮(被装饰对象)
class BasicButton extends StatelessWidget {
  final VoidCallback onPressed;
  final String text;
  
  const BasicButton({
    required this.onPressed,
    required this.text,
  });
  
  
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      child: Text(text),
    );
  }
}
2. 创建装饰器基类
abstract class ButtonDecorator extends StatelessWidget {
  final Widget child;
  
  const ButtonDecorator({required this.child});
}

// 更Flutter风格的实现:使用Widget包裹
class ButtonDecoration extends StatelessWidget {
  final Widget child;
  final List<Widget Function(Widget)> decorators;
  
  const ButtonDecoration({
    required this.child,
    required this.decorators,
  });
  
  
  Widget build(BuildContext context) {
    return _recursiveDecorate(child, decorators);
  }
  
  Widget _recursiveDecorate(Widget child, List<Widget Function(Widget)> decorators) {
    if (decorators.isEmpty) return child;
    return decorators.first(
      _recursiveDecorate(child, decorators.sublist(1))
    );
  }
}
3. 实现具体装饰器
// 圆角装饰器
Widget roundedDecorator(Widget child) {
  return ClipRRect(
    borderRadius: BorderRadius.circular(20),
    child: child,
  );
}

// 阴影装饰器
Widget shadowDecorator(Widget child) {
  return Container(
    decoration: BoxDecoration(
      boxShadow: [
        BoxShadow(color: Colors.black38, blurRadius: 10),
      ],
    ),
    child: child,
  );
}

// 点击缩放装饰器
Widget scaleDecorator(Widget child, VoidCallback onPressed) {
  return TweenAnimationBuilder<double>(
    tween: Tween(begin: 1.0, end: _isPressed ? 0.95 : 1.0),
    duration: Duration(milliseconds: 200),
    builder: (_, scale, child) {
      return Transform.scale(
        scale: scale,
        child: child,
      );
    },
    child: GestureDetector(
      onTapDown: (_) => setState(() => _isPressed = true),
      onTapUp: (_) => setState(() => _isPressed = false),
      onTapCancel: () => setState(() => _isPressed = false),
      onTap: onPressed,
      child: child,
    ),
  );
}

// 边框装饰器
Widget borderDecorator(Widget child) {
  return Container(
    decoration: BoxDecoration(
      border: Border.all(color: Colors.blue, width: 2),
    ),
    child: child,
  );
}
4. 动态组合装饰器
class DecoratedButton extends StatefulWidget {
  final bool hasRoundCorner;
  final bool hasShadow;
  final bool hasScaleAnimation;
  final bool hasBorder;
  final VoidCallback onPressed;
  final String text;

  
  _DecoratedButtonState createState() => _DecoratedButtonState();
}

class _DecoratedButtonState extends State<DecoratedButton> {
  bool _isPressed = false;
  
  
  Widget build(BuildContext context) {
    final basicButton = BasicButton(
      onPressed: widget.onPressed,
      text: widget.text,
    );
    
    final decorators = <Widget Function(Widget)>[];
    
    if (widget.hasRoundCorner) {
      decorators.add(roundedDecorator);
    }
    
    if (widget.hasShadow) {
      decorators.add(shadowDecorator);
    }
    
    if (widget.hasBorder) {
      decorators.add(borderDecorator);
    }
    
    if (widget.hasScaleAnimation) {
      decorators.add((child) => scaleDecorator(child, widget.onPressed));
    }
    
    return ButtonDecoration(
      child: basicButton,
      decorators: decorators,
    );
  }
}

// 使用示例
DecoratedButton(
  hasRoundCorner: true,
  hasShadow: true,
  hasScaleAnimation: true,
  onPressed: () => print('Button pressed'),
  text: '动态装饰按钮',
)

Flutter中的实际应用场景

场景1:带加载状态的按钮
Widget loadingDecorator(Widget child, bool isLoading) {
  return Stack(
    alignment: Alignment.center,
    children: [
      IgnorePointer(
        ignoring: isLoading,
        child: Opacity(
          opacity: isLoading ? 0.5 : 1,
          child: child,
        ),
      ),
      if (isLoading) CircularProgressIndicator(),
    ],
  );
}

// 使用
ButtonDecoration(
  child: BasicButton(...),
  decorators: [
    (child) => loadingDecorator(child, true),
  ],
)
场景2:条件性装饰
Widget conditionalDecorator(
  Widget child, 
  bool condition, 
  Widget Function(Widget) decorator,
) {
  return condition ? decorator(child) : child;
}

// 使用
ButtonDecoration(
  child: BasicButton(...),
  decorators: [
    (child) => conditionalDecorator(
      child, 
      isPremiumUser, 
      (w) => glowDecorator(w),
    ),
  ],
)
场景3:主题装饰器
Widget themeDecorator(Widget child, ThemeData theme) {
  return Theme(
    data: theme,
    child: DefaultTextStyle(
      style: theme.textTheme.button!,
      child: child,
    ),
  );
}

// 使用
ButtonDecoration(
  child: BasicButton(...),
  decorators: [
    (child) => themeDecorator(child, darkTheme),
  ],
)

装饰器模式与Flutter组合

Flutter本身大量使用装饰器模式思想:

1. DecoratedBox 组件
DecoratedBox(
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(10),
  ),
  child: Text('装饰文本'),
)
2. Padding 组件
Padding(
  padding: EdgeInsets.all(10),
  child: Icon(Icons.star),
)
3. Transform 组件
Transform.rotate(
  angle: 0.1,
  child: Icon(Icons.navigation),
)

装饰器模式最佳实践

  1. 何时使用装饰器模式:

    • 需要动态/透明地添加职责
    • 需要撤销功能(通过移除装饰器)
    • 继承不现实(类爆炸或final类)
    • 需要组合多种功能
  2. Flutter特化技巧:

    // 链式调用装饰器
    Widget buildButton() {
      return scaleDecorator(
        borderDecorator(
          shadowDecorator(
            roundedDecorator(
              BasicButton(...)
            )
          )
        )
      );
    }
    
    // 使用扩展方法
    extension WidgetDecoration on Widget {
      Widget withShadow() => shadowDecorator(this);
      Widget withBorder() => borderDecorator(this);
    }
    
    // 使用
    BasicButton(...).withShadow().withBorder()
    
  3. 性能优化:

    // 缓存装饰结果
    class CachedDecorator extends StatelessWidget {
      final Widget child;
      final Widget Function(Widget) decorator;
      
      const CachedDecorator({required this.child, required this.decorator});
      
      
      Widget build(BuildContext context) {
        return decorator(child);
      }
    }
    
  4. 测试策略:

    test('阴影装饰器应添加BoxShadow', () {
      final decorated = shadowDecorator(Container());
      final boxDecor = (decorated as Container).decoration as BoxDecoration;
      expect(boxDecor.boxShadow, isNotEmpty);
    });
    

装饰器模式 vs 组合模式

特性 装饰器模式 组合模式
目的 动态添加职责 构造部分-整体层次结构
关系 装饰器与被装饰对象类型一致 组件与容器可以是不同类型
典型应用 添加视觉效果/行为 树形菜单/嵌套布局
Flutter应用 Widget装饰 Widget树构建

装饰器模式的高级变体

1. 可配置装饰器工厂
class DecoratorFactory {
  static Widget Function(Widget) createRounded(double radius) {
    return (child) => ClipRRect(
      borderRadius: BorderRadius.circular(radius),
      child: child,
    );
  }
  
  static Widget Function(Widget) createGlow(Color color) {
    return (child) => Container(
      decoration: BoxDecoration(
        boxShadow: [
          BoxShadow(color: color, blurRadius: 10),
        ],
      ),
      child: child,
    );
  }
}

// 使用
ButtonDecoration(
  decorators: [
    DecoratorFactory.createRounded(10),
    DecoratorFactory.createGlow(Colors.blue),
  ],
)
2. 状态感知装饰器
Widget statefulDecorator(Widget child) {
  return StatefulBuilder(
    builder: (context, setState) {
      return GestureDetector(
        onTap: () => setState(() {}),
        child: Container(
          color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
          child: child,
        ),
      );
    },
  );
}
3. 动画序列装饰器
Widget animationSequenceDecorator(Widget child) {
  return SequenceAnimationBuilder(
    animations: {
      'scale': Tween(begin: 0.5, end: 1.0),
      'opacity': Tween(begin: 0.0, end: 1.0),
    },
    builder: (context, animation, _) {
      return Opacity(
        opacity: animation['opacity']!.value,
        child: Transform.scale(
          scale: animation['scale']!.value,
          child: child,
        ),
      );
    },
  );
}

总结:装饰器模式是你的Widget多功能外套

  • 核心价值: 动态扩展对象功能,避免类爆炸
  • Flutter优势:
    • 灵活组合Widget效果
    • 保持代码开闭原则
    • 功能模块化,易于复用
    • 与Flutter组件体系完美契合
  • 适用场景: 动态样式、条件包裹、动画效果、主题切换

🎨 设计启示: 当你的Widget需要"穿搭"多种功能时,装饰器模式就是你的"智能衣柜",让你自由搭配不重样!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐