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

Flutter:保持HomeScreen为StatelessWidget实现AppBar同步图标旋转动画

在StatelessWidget中实现AppBar图标按钮的旋转动画

要在保持HomeScreen为StatelessWidget的前提下实现IconButton的旋转动画,可以用ValueNotifier配合ValueListenableBuilder来做,不用把组件改成Stateful。具体实现方式如下:

步骤1:用ValueNotifier管理动画状态

我们用一个ValueNotifier<bool>来标记当前是否正在执行数据库操作,以此控制动画的启停:

final ValueNotifier<bool> _isRefreshing = ValueNotifier(false);

步骤2:替换IconButton为带旋转动画的组件

把原来的IconButton替换成监听状态变化的动画组件,通过RotationTransition实现旋转效果:

ValueListenableBuilder<bool>(
  valueListenable: _isRefreshing,
  builder: (context, isRefreshing, child) {
    final animationController = AnimationController(
      vsync: Navigator.of(context),
      duration: const Duration(milliseconds: 500),
    );

    // 根据状态控制动画启停
    if (isRefreshing) {
      animationController.repeat();
    } else {
      animationController.stop();
    }

    return IconButton(
      onPressed: () async {
        _isRefreshing.value = true; // 启动动画
        try {
          await DatabaseService().signInAutomatically(); // 执行数据库操作
        } finally {
          _isRefreshing.value = false; // 确保动画停止
        }
      },
      tooltip: 'Refresh connection status',
      icon: RotationTransition(
        turns: Tween(begin: 0.0, end: 1.0).animate(
          CurvedAnimation(
            parent: animationController,
            curve: Curves.linear,
          ),
        ),
        child: const Icon(Icons.sync, color: Colors.white),
      ),
    );
  },
)

完整的HomeScreen代码

整合后的完整代码如下:

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});
  final ValueNotifier<bool> _isRefreshing = ValueNotifier(false);

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: SafeArea(
        child: Scaffold(
          appBar: AppBar(
            actions: <Widget>[
              ValueListenableBuilder<bool>(
                valueListenable: _isRefreshing,
                builder: (context, isRefreshing, child) {
                  final animationController = AnimationController(
                    vsync: Navigator.of(context),
                    duration: const Duration(milliseconds: 500),
                  );

                  if (isRefreshing) {
                    animationController.repeat();
                  } else {
                    animationController.stop();
                  }

                  return IconButton(
                    onPressed: () async {
                      _isRefreshing.value = true;
                      try {
                        await DatabaseService().signInAutomatically();
                      } finally {
                        _isRefreshing.value = false;
                      }
                    },
                    tooltip: 'Refresh connection status',
                    icon: RotationTransition(
                      turns: Tween(begin: 0.0, end: 1.0).animate(
                        CurvedAnimation(
                          parent: animationController,
                          curve: Curves.linear,
                        ),
                      ),
                      child: const Icon(Icons.sync, color: Colors.white),
                    ),
                  );
                },
              )
            ],
            title: Constants.appname,
            flexibleSpace: Container(
              decoration: BoxDecoration(
                gradient: LinearGradient(colors: [
                  Theme.of(context).colorScheme.primary,
                  Theme.of(context).colorScheme.secondary
                ], begin: Alignment.topCenter, end: Alignment.bottomCenter),
              ),
            ),
            toolbarHeight: 110,
            centerTitle: true,
            automaticallyImplyLeading: false,
            bottom: const TabBar(
              indicatorColor: Colors.white,
              tabs: [
                Tab(
                  icon: Icon(Icons.work, color: Colors.white),
                ),
                Tab(
                  icon: Icon(Icons.home_work, color: Colors.white),
                )
              ],
            ),
          ),
          body: TabBarView(
            children: [
              Container(
                alignment: Alignment.center,
                child: const StampInFromOffice(),
              ),
              Container(
                alignment: Alignment.center,
                child: const StampInFromHome(),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

补充说明

  • ValueNotifier是轻量级状态管理工具,适合这种单一状态的场景,完全不需要将StatelessWidget改为StatefulWidget
  • 用try/finally包裹数据库操作,确保无论操作成功或失败,动画都会停止,避免无限旋转
  • 动画控制器的vsync使用Navigator.of(context)获取TickerProvider,无需额外实现接口

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 02:27:46