You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:切换主题亮度时禁用AnimatedContainer的额外动画

如何禁用AnimatedContainer在主题切换时的颜色额外动画

当AnimatedContainer使用依赖于应用主题亮度的颜色(例如Theme.of(context).colorScheme.onBackground)时,切换主题亮度会触发AnimatedContainer以预设的Duration执行颜色过渡动画,但我们希望主题切换时颜色能像普通Container一样瞬间变化,只保留AnimatedContainer在其他属性变化时的动画。

效果对比:
效果动图
左侧为AnimatedContainer(主题切换时有颜色动画),右侧为普通Container(主题切换时颜色瞬间变化)

问题复现代码

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

ValueNotifier<ThemeData> _theme = ValueNotifier(
  ThemeData(brightness: Brightness.dark, backgroundColor: Colors.white),
);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<ThemeData>(
      valueListenable: _theme,
      builder: (_, theme, __) => MaterialApp(
        theme: theme,
        title: 'Material App',
        home: _Scaffold(),
      ),
    );
  }
}

class _Scaffold extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Material App Bar'),
        backgroundColor: Theme.of(context).backgroundColor,
      ),
      backgroundColor: Colors.red,
      body: Center(
        child: Column(
          children: <Widget>[
            Row(
              children: <Widget>[
                AnimatedContainer(
                  curve: Curves.easeInOutCubic,
                  duration: const Duration(milliseconds: 175),
                  color: Theme.of(context).backgroundColor,
                  width: 50.0,
                  height: 50.0,
                  margin: const EdgeInsets.all(10.0),
                ),
                Container(
                  color: Theme.of(context).backgroundColor,
                  width: 50.0,
                  height: 50.0,
                ),
              ],
            ),
            ElevatedButton(
              onPressed: () {
                final Brightness newBrightness =
                    _theme.value.brightness == Brightness.dark
                        ? Brightness.light
                        : Brightness.dark;
                _theme.value = _theme.value.copyWith(
                  brightness: newBrightness,
                  backgroundColor: newBrightness == Brightness.dark
                      ? Colors.white
                      : Colors.black,
                );
              },
              child: const Text('switch brightness'),
            ),
          ],
        ),
      ),
    );
  }
}

解决方案1:动态控制AnimatedContainer的Duration

核心思路:在主题切换的瞬间,将AnimatedContainer的duration设为Duration.zero跳过颜色动画,动画结束后恢复原有时长,确保其他属性(如宽高)的正常动画。

修改后的代码:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

ValueNotifier<ThemeData> _theme = ValueNotifier(
  ThemeData(brightness: Brightness.dark, backgroundColor: Colors.white),
);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<ThemeData>(
      valueListenable: _theme,
      builder: (_, theme, __) => MaterialApp(
        theme: theme,
        title: 'Material App',
        home: const _Scaffold(),
      ),
    );
  }
}

class _Scaffold extends StatefulWidget {
  const _Scaffold();

  @override
  State<_Scaffold> createState() => _ScaffoldState();
}

class _ScaffoldState extends State<_Scaffold> {
  bool _isSwitchingTheme = false;
  final Duration _normalDuration = const Duration(milliseconds: 175);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Material App Bar'),
        backgroundColor: Theme.of(context).backgroundColor,
      ),
      backgroundColor: Colors.red,
      body: Center(
        child: Column(
          children: <Widget>[
            Row(
              children: <Widget>[
                AnimatedContainer(
                  curve: Curves.easeInOutCubic,
                  duration: _isSwitchingTheme ? Duration.zero : _normalDuration,
                  color: Theme.of(context).backgroundColor,
                  width: 50.0,
                  height: 50.0,
                  margin: const EdgeInsets.all(10.0),
                  onEnd: () {
                    if (_isSwitchingTheme) {
                      setState(() => _isSwitchingTheme = false);
                    }
                  },
                ),
                Container(
                  color: Theme.of(context).backgroundColor,
                  width: 50.0,
                  height: 50.0,
                ),
              ],
            ),
            ElevatedButton(
              onPressed: () {
                setState(() => _isSwitchingTheme = true);
                final Brightness newBrightness =
                    _theme.value.brightness == Brightness.dark
                        ? Brightness.light
                        : Brightness.dark;
                _theme.value = _theme.value.copyWith(
                  brightness: newBrightness,
                  backgroundColor: newBrightness == Brightness.dark
                      ? Colors.white
                      : Colors.black,
                );
              },
              child: const Text('switch brightness'),
            ),
          ],
        ),
      ),
    );
  }
}

解决方案2:分离动画属性与主题颜色

如果仅需要AnimatedContainer对非主题相关属性(如宽高、margin)执行动画,可以将颜色部分用普通Container包裹,让AnimatedContainer只处理指定属性的动画:

// 示例:仅对width执行动画,颜色随主题瞬间变化
AnimatedContainer(
  curve: Curves.easeInOutCubic,
  duration: const Duration(milliseconds: 175),
  width: _someAnimatedWidth, // 假设这是需要动画的属性
  height: 50.0,
  margin: const EdgeInsets.all(10.0),
  child: Container(
    color: Theme.of(context).backgroundColor,
    width: double.infinity,
    height: double.infinity,
  ),
)

内容的提问来源于stack exchange,提问作者Kirill

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 10:30:49