装饰器模式 - Flutter中的组件化妆师,运行时动态添加超能力!
);@override。
·
痛点场景:为按钮添加多重效果
假设你需要一个按钮,要求支持:
- 基础样式
- 可选圆角
- 可选阴影
- 可选边框
- 点击缩放动画
- 按下变色效果
- 加载状态
传统继承方案:
class BasicButton extends StatelessWidget {...}
class RoundedButton extends BasicButton {...}
class ShadowButton extends RoundedButton {...}
class AnimatedShadowButton extends ShadowButton {...}
// 类爆炸!
问题爆发点:
- 💥 组合爆炸(n种效果 → 2^n个子类)
- 🔧 难以动态添加/移除功能
- 🔄 无法在运行时切换效果
- 📦 功能无法独立复用
装饰器模式解决方案
核心思想: 动态地给一个对象添加额外的职责,相比继承更加灵活。
四个关键角色:
- 组件接口(Component): 定义原始对象和装饰器的共同接口
- 具体组件(ConcreteComponent): 原始对象
- 装饰器基类(Decorator): 持有组件引用并实现组件接口
- 具体装饰器(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),
)
装饰器模式最佳实践
-
何时使用装饰器模式:
- 需要动态/透明地添加职责
- 需要撤销功能(通过移除装饰器)
- 继承不现实(类爆炸或final类)
- 需要组合多种功能
-
Flutter特化技巧:
// 链式调用装饰器 Widget buildButton() { return scaleDecorator( borderDecorator( shadowDecorator( roundedDecorator( BasicButton(...) ) ) ) ); } // 使用扩展方法 extension WidgetDecoration on Widget { Widget withShadow() => shadowDecorator(this); Widget withBorder() => borderDecorator(this); } // 使用 BasicButton(...).withShadow().withBorder() -
性能优化:
// 缓存装饰结果 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); } } -
测试策略:
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需要"穿搭"多种功能时,装饰器模式就是你的"智能衣柜",让你自由搭配不重样!
更多推荐


所有评论(0)