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

如何使用flutter_animate为Navigator.pop()添加反向淡出动画?

实现全屏覆盖层的淡入/淡出双向动画(flutter_animate)

要实现关闭时的反向淡出动画,无需单独处理pop逻辑,只需把动画整合到PageRouteBuilder的transitionsBuilder中,让路由系统自动管理正向(打开)和反向(关闭)的动画流程。

修改后的完整代码

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Animated Transition',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context).push(
            PageRouteBuilder(
              opaque: false,
              transitionDuration: const Duration(milliseconds: 300),
              reverseTransitionDuration: const Duration(milliseconds: 300),
              pageBuilder: (BuildContext context, _, __) => const AddOverlay(),
              // 核心:通过transitionsBuilder绑定路由动画
              transitionsBuilder: (context, animation, secondaryAnimation, child) {
                return Animate(
                  adapter: AnimationAdapter(animation),
                  effects: const [FadeEffect()],
                  child: child,
                );
              },
            ),
          );
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black.withOpacity(0.85),
      body: BackdropFilter(
        filter: ImageFilter.blur(
          sigmaX: 5.0,
          sigmaY: 5.0,
        ),
        child: Center(
            child: ElevatedButton(
                onPressed: () {
                  Navigator.of(context).pop(); // 直接pop,路由自动触发反向动画
                },
                child: const Text("Close"))),
      ),
    );
  }
}

关键修改说明

  • 将动画从pageBuilder的子组件移到transitionsBuilder中,通过AnimationAdapter(animation)绑定路由自带的动画对象。路由打开时animation正向执行(0→1)触发淡入,关闭时自动反向执行(1→0)触发淡出。
  • 设置transitionDuration和reverseTransitionDuration统一动画时长(可选,使用默认值也可)。
  • 关闭时只需正常调用Navigator.of(context).pop(),无需额外处理动画逻辑,路由系统会自动反向播放过渡动画。

备选方案(手动控制动画)

如果需要在动画过程中添加自定义逻辑,可以给AddOverlay添加AnimationController,先播放反向动画再执行pop:

class AddOverlay extends StatefulWidget {
  const AddOverlay({super.key});

  @override
  State<AddOverlay> createState() => _AddOverlayState();
}

class _AddOverlayState extends State<AddOverlay> with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    )..forward(); // 初始化时播放淡入动画
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _closeOverlay() {
    _controller.reverse().then((_) {
      if (mounted) Navigator.of(context).pop();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black.withOpacity(0.85),
      body: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
        child: Animate(
          controller: _controller,
          effects: const [FadeEffect()],
          child: Center(
            child: ElevatedButton(
              onPressed: _closeOverlay,
              child: const Text("Close"),
            ),
          ),
        ),
      ),
    );
  }
}

该方案适合需要额外动画逻辑的场景,但第一种结合路由过渡的方式更简洁,符合Flutter路由动画的设计规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 13:43:13