DecoratedBoxTransition
专门用于在Decoration属性之间实现平滑动画效果的组件
DecoratedBoxTransition是Flutter中的一个动画过渡组件,专门用于在装饰(Decoration)属性之间实现平滑的动画效果。它继承自ImplicitlyAnimatedWidget,通过隐式动画机制自动处理装饰变化,无需手
动控制动画控制器。主要用途包括动态改变容器的背景、边框、阴影等视觉属性,适用于需要渐变或动态UI效果的场景。
使用场景
- 主题切换: 在亮暗模式切换时,平滑过渡背景颜色或边框样式。
- 交互反馈: 当用户悬停或点击按钮时,动态改变装饰效果(如阴影放大)。
- 状态指示: 在加载或进度更新时,通过装饰动画增强视觉提示。
示例
基础颜色过渡
import 'package:flutter/material.dart';
class BasicColorTransition extends StatefulWidget {
_BasicColorTransitionState createState() => _BasicColorTransitionState();
}
class _BasicColorTransitionState extends State<BasicColorTransition> {
bool _isActive = false;
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _isActive = !_isActive),
child: DecoratedBoxTransition(
decoration: DecorationTween(
begin: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
end: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(20),
),
).animate(CurvedAnimation(
parent: ModalRoute.of(context)!.animation!,
curve: Curves.easeInOut,
)),
child: Container(
width: 100,
height: 100,
child: Center(child: Text(_isActive ? 'Active' : 'Inactive')),
),
),
);
}
}
交互式阴影动画
import 'package:flutter/material.dart';
class ShadowAnimationDemo extends StatefulWidget {
_ShadowAnimationDemoState createState() => _ShadowAnimationDemoState();
}
class _ShadowAnimationDemoState extends State<ShadowAnimationDemo> {
bool _isPressed = false;
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTapDown: (_) => setState(() => _isPressed = true),
onTapUp: (_) => setState(() => _isPressed = false),
child: DecoratedBoxTransition(
decoration: DecorationTween(
begin: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(10),
boxShadow: [BoxShadow(blurRadius: 5, color: Colors.black26)],
),
end: BoxDecoration(
color: Colors.greenAccent,
borderRadius: BorderRadius.circular(30),
boxShadow: [BoxShadow(blurRadius: 15, color: Colors.black45)],
),
).animate(CurvedAnimation(
parent: ModalRoute.of(context)!.animation!,
curve: _isPressed ? Curves.bounceOut : Curves.bounceIn,
)),
child: Container(
width: 120,
height: 50,
child: Center(child: Text('Press Me')),
),
),
),
),
);
}
}
主题适配动画
import 'package:flutter/material.dart';
class ThemeAdaptationDemo extends StatefulWidget {
_ThemeAdaptationDemoState createState() => _ThemeAdaptationDemoState();
}
class _ThemeAdaptationDemoState extends State<ThemeAdaptationDemo> {
bool _isDarkMode = false;
Widget build(BuildContext context) {
return MaterialApp(
theme: _isDarkMode ? ThemeData.dark() : ThemeData.light(),
home: Scaffold(
appBar: AppBar(
title: Text('Theme Demo'),
actions: [
IconButton(
icon: Icon(Icons.brightness_6),
onPressed: () => setState(() => _isDarkMode = !_isDarkMode),
),
],
),
body: Center(
child: DecoratedBoxTransition(
decoration: DecorationTween(
begin: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border.all(color: Colors.grey),
),
end: BoxDecoration(
color: Theme.of(context).backgroundColor,
border: Border.all(color: Colors.blue),
),
).animate(CurvedAnimation(
parent: ModalRoute.of(context)!.animation!,
curve: Curves.ease,
)),
child: Container(
width: 200,
height: 100,
child: Center(child: Text('Theme Adapting')),
),
),
),
),
);
}
}
注意点
- 性能考虑: 避免在频繁重建的组件(如列表项)中使用
DecoratedBoxTransition,可能导致动画卡顿。优先使用AnimatedContainer简化场景。 - 兼容性: 确保父级动画(如
ModalRoute.of(context)!.animation)有效,否则动画无法触发。在无路由上下文的部件中,需自定义AnimationController。 - 复杂装饰: 渐变或形状复杂的
Decoration可能增加渲染开销,测试时关注帧率表现。 - 最佳实践: 使用
CurvedAnimation指定缓动曲线,让过渡更自然;通过Duration控制动画时间,避免过长影响交互。
构造函数
const DecoratedBoxTransition({
Key? key,
required Animation<Decoration> decoration, // 必需参数,控制装饰动画
Widget? child, // 可选子组件
})
属性
| 属性名 | 属性类型 | 说明 |
|---|---|---|
| decoration | Animation<Decoration> | 必需,定义装饰属性的动画过渡,通常由 DecorationTween 创建。 |
| child | Widget? | 可选,被装饰的子组件,如果为空则使用 SizedBox.shrink() 作为默认。 |
关键属性解释
decoration: 核心属性,接受一个Animation<Decoration>对象。必须通过DecorationTween设置起始(begin)和结束(end)状态,动画会自动插值过渡。例如,颜色、边框半径或阴影的变化都通过此属性控制。性能上,简单装饰(如纯色)比复杂装饰(如多重渐变)更高效。child: 通常用于包裹实际内容(如文本或图标)。如果未指定,组件会使用最小空间,可能导致布局异常,建议始终显式设置child。