本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

隐式动画组件是 Flutter 中一组特殊的 Widget,它们能够自动处理动画过渡。只需要设置目标值,组件就会自动创建并管理动画过程,无需手动创建 AnimationController 或 Animation 对象。

核心特点:

  • 声明式:只需声明最终状态

  • 自动管理:自动处理动画时长、曲线和控制器

  • 性能优化:内置性能优化

  • 易于使用:代码简洁,学习曲线低

二、常用隐式动画组件分类

2.1 容器和布局动画组件

1. AnimatedContainer - 多功能容器动画

最常用的隐式动画组件,可以同时动画化多个属性。

AnimatedContainer(
  duration: Duration(seconds: 1),      // 动画时长
  curve: Curves.easeInOut,            // 动画曲线
  width: _isExpanded ? 200 : 100,     // 宽度变化
  height: _isExpanded ? 200 : 100,    // 高度变化
  color: _isSelected ? Colors.blue : Colors.grey,  // 颜色变化
  padding: _hasPadding ? EdgeInsets.all(20) : EdgeInsets.zero, // 内边距
  margin: EdgeInsets.all(10),         // 外边距
  alignment: Alignment.center,        // 对齐方式
  transform: Matrix4.rotationZ(_rotate ? pi/4 : 0), // 变换
  decoration: BoxDecoration(          // 装饰
    borderRadius: BorderRadius.circular(_round ? 20 : 0),
    border: Border.all(
      color: Colors.black,
      width: _hasBorder ? 2 : 0,
    ),
    boxShadow: _hasShadow ? [
      BoxShadow(
        color: Colors.black26,
        blurRadius: 10,
        offset: Offset(0, 5),
      )
    ] : [],
  ),
  child: Text('Animated Container'),
)

支持的属性变化:

  • width、height

  • color、decoration

  • padding、margin、alignment

  • constraints

  • transform

  • clipBehavior

2. AnimatedPadding - 内边距动画

专门用于内边距变化的动画。

AnimatedPadding(
  duration: Duration(milliseconds: 500),
  padding: _isExpanded ? 
    EdgeInsets.all(30) : 
    EdgeInsets.symmetric(horizontal: 10, vertical: 5),
  curve: Curves.easeOut,
  child: Container(
    color: Colors.blue,
    child: Text('Padding Animation'),
  ),
)
3. AnimatedAlign - 对齐动画

改变子组件对齐方式的动画。

AnimatedAlign(
  duration: Duration(seconds: 1),
  alignment: _alignment,  // 可以是 Alignment.topLeft、Alignment.center 等
  curve: Curves.bounceOut,
  child: Container(
    width: 100,
    height: 100,
    color: Colors.green,
    child: Center(child: Text('Aligned')),
  ),
)

// 控制对齐方式
Alignment _alignment = Alignment.topLeft;

void toggleAlignment() {
  setState(() {
    _alignment = _alignment == Alignment.topLeft 
        ? Alignment.bottomRight 
        : Alignment.topLeft;
  });
}
4. AnimatedPositioned - 定位动画(只能在 Stack 中使用)

用于在 Stack 中改变位置的动画。

Stack(
  children: [
    // 背景
    Container(color: Colors.grey[200]),
    
    AnimatedPositioned(
      duration: Duration(seconds: 1),
      left: _isRight ? 200 : 0,
      top: _isBottom ? 200 : 0,
      width: _isLarge ? 150 : 100,
      height: _isLarge ? 150 : 100,
      child: Container(
        color: Colors.red,
        child: Center(child: Text('Positioned')),
      ),
    ),
  ],
)
5. AnimatedCrossFade - 交叉淡入淡出动画

在两个子组件之间切换,带有交叉淡入淡出效果。

AnimatedCrossFade(
  duration: Duration(milliseconds: 500),
  firstChild: Container(
    width: 200,
    height: 100,
    color: Colors.blue,
    child: Center(child: Text('First Widget')),
  ),
  secondChild: Container(
    width: 200,
    height: 150,
    color: Colors.green,
    child: Center(child: Text('Second Widget')),
  ),
  crossFadeState: _showFirst 
      ? CrossFadeState.showFirst 
      : CrossFadeState.showSecond,
  firstCurve: Curves.easeIn,
  secondCurve: Curves.easeOut,
  sizeCurve: Curves.easeInOut,  // 尺寸变化的曲线
  alignment: Alignment.center,  // 对齐方式
)

2.2 视觉外观动画组件

6. AnimatedOpacity - 透明度动画

改变透明度的动画。

AnimatedOpacity(
  duration: Duration(milliseconds: 300),
  opacity: _isVisible ? 1.0 : 0.0,  // 0.0 完全透明,1.0 完全不透明
  curve: Curves.easeInOut,
  // 当 opacity 为 0 时是否占用空间
  alwaysIncludeSemantics: false,  // 辅助功能相关
  child: Container(
    width: 200,
    height: 200,
    color: Colors.blue,
    child: Center(
      child: Text(
        'Fade Animation',
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
    ),
  ),
)
7. AnimatedDefaultTextStyle - 文本样式动画

动态改变文本样式的动画。

AnimatedDefaultTextStyle(
  duration: Duration(milliseconds: 500),
  style: _isLargeText
      ? TextStyle(
          fontSize: 30,
          fontWeight: FontWeight.bold,
          color: Colors.red,
          fontStyle: FontStyle.italic,
          letterSpacing: 2,
        )
      : TextStyle(
          fontSize: 16,
          fontWeight: FontWeight.normal,
          color: Colors.black,
        ),
  textAlign: TextAlign.center,
  softWrap: true,
  overflow: TextOverflow.ellipsis,
  maxLines: 2,
  child: Text('Text Style Animation'),
)
8. AnimatedPhysicalModel - 物理模型动画

模拟物理效果的动画,如海拔、阴影等。

AnimatedPhysicalModel(
  duration: Duration(milliseconds: 500),
  curve: Curves.easeInOut,
  elevation: _isElevated ? 12 : 2,          // 海拔/阴影深度
  shape: BoxShape.rectangle,               // 形状
  borderRadius: BorderRadius.circular(_isRound ? 20 : 8),
  color: Colors.blue,
  shadowColor: Colors.black54,
  child: Container(
    width: 150,
    height: 150,
    child: Center(
      child: Text(
        'Physical Model',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
)
9. AnimatedTheme - 主题动画

动态改变主题的动画。

AnimatedTheme(
  duration: Duration(seconds: 1),
  data: _isDarkTheme 
      ? ThemeData.dark().copyWith(
          primaryColor: Colors.blue,
          accentColor: Colors.orange,
        )
      : ThemeData.light().copyWith(
          primaryColor: Colors.green,
          accentColor: Colors.purple,
        ),
  curve: Curves.easeInOut,
  child: Container(
    padding: EdgeInsets.all(20),
    color: Theme.of(context).primaryColor,
    child: Text(
      'Theme Animation',
      style: TextStyle(
        color: Theme.of(context).colorScheme.onPrimary,
      ),
    ),
  ),
)

2.3 变换动画组件

10. AnimatedScale - 缩放动画(3.0+)

专门用于缩放的动画组件。

AnimatedScale(
  duration: Duration(milliseconds: 500),
  scale: _isScaled ? 1.5 : 1.0,
  curve: Curves.elasticOut,  // 弹性效果
  child: Container(
    width: 100,
    height: 100,
    color: Colors.orange,
    child: Center(child: Text('Scale')),
  ),
)
11. AnimatedRotation - 旋转动画(3.0+)

专门用于旋转的动画组件。

AnimatedRotation(
  duration: Duration(seconds: 1),
  turns: _turns,  // 旋转圈数,0.25 = 90度,1 = 360度
  curve: Curves.easeInOut,
  child: Container(
    width: 100,
    height: 100,
    color: Colors.purple,
    child: Center(child: Text('Rotate')),
  ),
)

double _turns = 0.0;

void rotate() {
  setState(() {
    _turns += 0.25;  // 每次旋转90度
  });
}

2.4 列表和网格动画组件

12. AnimatedList - 列表动画

列表项添加/删除时带有动画效果。

final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
List<String> _items = ['Item 1', 'Item 2', 'Item 3'];

AnimatedList(
  key: _listKey,
  initialItemCount: _items.length,
  itemBuilder: (context, index, animation) {
    return SizeTransition(
      sizeFactor: animation,
      child: ListTile(
        title: Text(_items[index]),
        trailing: IconButton(
          icon: Icon(Icons.delete),
          onPressed: () => _removeItem(index),
        ),
      ),
    );
  },
)

// 添加项目
void _addItem() {
  _items.add('Item ${_items.length + 1}');
  _listKey.currentState!.insertItem(_items.length - 1);
}

// 删除项目
void _removeItem(int index) {
  final removedItem = _items.removeAt(index);
  _listKey.currentState!.removeItem(
    index,
    (context, animation) => SizeTransition(
      sizeFactor: animation,
      child: ListTile(title: Text(removedItem)),
    ),
  );
}
13. AnimatedGrid - 网格动画(2.10+)

网格项添加/删除时带有动画效果。

final GlobalKey<AnimatedGridState> _gridKey = GlobalKey<AnimatedGridState>();
List<Widget> _gridItems = [
  _buildGridItem(Colors.red, '1'),
  _buildGridItem(Colors.blue, '2'),
  _buildGridItem(Colors.green, '3'),
];

AnimatedGrid(
  key: _gridKey,
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    crossAxisSpacing: 10,
    mainAxisSpacing: 10,
  ),
  initialItemCount: _gridItems.length,
  itemBuilder: (context, index, animation) {
    return ScaleTransition(
      scale: animation,
      child: _gridItems[index],
    );
  },
)

2.5 高级切换动画组件

14. AnimatedSwitcher - 通用切换动画

在子组件之间切换时显示动画。

int _currentIndex = 0;
final List<Widget> _pages = [
  Container(key: ValueKey(1), color: Colors.red, child: Center(child: Text('Page 1'))),
  Container(key: ValueKey(2), color: Colors.blue, child: Center(child: Text('Page 2'))),
  Container(key: ValueKey(3), color: Colors.green, child: Center(child: Text('Page 3'))),
];

AnimatedSwitcher(
  duration: Duration(milliseconds: 500),
  switchInCurve: Curves.easeIn,
  switchOutCurve: Curves.easeOut,
  transitionBuilder: (child, animation) {
    // 自定义过渡动画
    return ScaleTransition(
      scale: animation,
      child: FadeTransition(
        opacity: animation,
        child: child,
      ),
    );
  },
  layoutBuilder: (currentChild, previousChildren) {
    return Stack(
      alignment: Alignment.center,
      children: [
        ...previousChildren,
        if (currentChild != null) currentChild,
      ],
    );
  },
  child: _pages[_currentIndex],
)

// 切换页面
void nextPage() {
  setState(() {
    _currentIndex = (_currentIndex + 1) % _pages.length;
  });
}
15. AnimatedSize - 尺寸变化动画

自动动画化尺寸变化的组件。

AnimatedSize(
  duration: Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  alignment: Alignment.topCenter,  // 尺寸变化时的对齐方式
  vsync: this,  // 需要混入 SingleTickerProviderStateMixin
  child: Container(
    width: _isExpanded ? 200 : 100,
    height: _isExpanded ? 200 : 100,
    color: Colors.amber,
    child: Center(child: Text('Size Animation')),
  ),
)
16. AnimatedModalBarrier - 模态屏障动画

用于模态对话框的动画屏障。

bool _showBarrier = false;

Stack(
  children: [
    // 主要内容
    Center(child: Text('Main Content')),
    
    // 模态屏障
    if (_showBarrier)
      AnimatedModalBarrier(
        dismissible: true,
        color: Colors.black.withOpacity(0.5),
        duration: Duration(milliseconds: 300),
      ),
    
    // 模态内容
    if (_showBarrier)
      Center(
        child: Container(
          width: 200,
          height: 200,
          color: Colors.white,
          child: Center(child: Text('Modal Content')),
        ),
      ),
  ],
)

三、代码示例

3.1 加载状态动画

bool _isLoading = false;

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    AnimatedSwitcher(
      duration: Duration(milliseconds: 300),
      child: _isLoading
          ? CircularProgressIndicator()
          : Icon(Icons.check_circle, color: Colors.green, size: 50),
    ),
    
    SizedBox(height: 20),
    
    AnimatedOpacity(
      duration: Duration(milliseconds: 300),
      opacity: _isLoading ? 0.5 : 1,
      child: ElevatedButton(
        onPressed: _isLoading ? null : () {
          setState(() {
            _isLoading = true;
          });
          // 模拟加载过程
          Future.delayed(Duration(seconds: 2), () {
            setState(() {
              _isLoading = false;
            });
          });
        },
        child: AnimatedDefaultTextStyle(
          duration: Duration(milliseconds: 300),
          style: TextStyle(
            fontSize: _isLoading ? 14 : 16,
            color: _isLoading ? Colors.grey : Colors.white,
          ),
          child: Text(_isLoading ? '加载中...' : '开始加载'),
        ),
      ),
    ),
  ],
)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐