skip to Main Content

When push with FadeTransition and Hero animation, the icon will frozen in the screen when the page too fast to push again. Anyone has idea to solve this bug?

This is the full code example to reproduce. I am push a Hero widget to another screen, it is work to push to another screen but it is still freeze in the screen if push again before the hero widget fade out.

import 'package:flutter/material.dart';

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

class AppRoutes {
  static MaterialPageRoute? getRoute(RouteSettings settings) {
    switch (settings.name) {
      case "/":
        return MainRoute(
          MyHomePage(),
          settings: settings,
        );
    }
  }
}

class MainRoute<T> extends MaterialPageRoute<T> {
  MainRoute(
    Widget widget, {
    RouteSettings? settings,
  }) : super(builder: (_) => widget, settings: settings);
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      onGenerateRoute: (RouteSettings settings) => AppRoutes.getRoute(
        settings,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (context) => TestScreen(),
            ),
          );
        },
        child: const Icon(Icons.route),
      ),
    );
  }
}

class TestScreen extends StatefulWidget {
  const TestScreen({
    super.key,
    this.isSuccess = false,
  });

  final bool isSuccess;

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  @override
  void initState() {
    super.initState();
    _redirectToDetailPage();
  }

  void _redirectToDetailPage() async {
    Future.delayed(const Duration(seconds: 2), () {
      Navigator.push(
        context,
        PageRouteBuilder(
          maintainState: false,
          opaque: true,
          pageBuilder: (_, Animation<double> start, Animation<double> end) {
            return TestUnsuccess(
              test: end,
            );
          },
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
            return FadeTransition(
              opacity: animation,
              child: child,
            );
          },
          transitionDuration: Duration(milliseconds: 2000),
        ),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PopScope(
        canPop: false,
        child: SafeArea(
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Hero(
                  tag: 'payment_icon',
                  child: Icon(
                    Icons.abc,
                    size: 40,
                  ),
                ),
                SizedBox(
                  height: 32.0,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class TestUnsuccess extends StatefulWidget {
  final Animation<double> test;

  TestUnsuccess({
    required this.test,
  });

  @override
  TestUnsuccessState createState() => TestUnsuccessState();
}

class TestUnsuccessState extends State<TestUnsuccess> {
  @override
  void initState() {
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PopScope(
        canPop: false,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Hero(
                tag: 'payment_icon',
                child: Icon(
                  Icons.abc,
                  size: 40,
                ),
              ),
              SizedBox(
                height: 16.0,
              ),
              ElevatedButton(
                child: Icon(
                  Icons.home_filled,
                ),
                onPressed: () {
                  Navigator.of(context).pushNamedAndRemoveUntil(
                    "/",
                    ModalRoute.withName('/'),
                  );

                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => TestAnother(),
                    ),
                  );
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class TestAnother extends StatefulWidget {
  @override
  State<TestAnother> createState() => _TestAnotherState();
}

class _TestAnotherState extends State<TestAnother> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
            ],
          ),
        ),
      ),
    );
  }
}

enter image description here

2

Answers


  1. Use PageRouteBuilder with opaque: true: Set the opaque property of
    PageRouteBuilder to true. This ensures that the destination page
    covers the entire screen during the transition, preventing any frozen
    elements from being visible.

     Navigator.push(
      context,
      PageRouteBuilder(
        maintainState: false,
        opaque: true, // Ensure that the destination page covers the entire screen
        pageBuilder: (_, Animation<double> start, Animation<double> end) {
          return TestUnsuccess(
            test: end,
          );
        },
        transitionsBuilder:
            (context, animation, secondaryAnimation, child) {
          return FadeTransition(
            opacity: animation,
            child: Hero(
              tag: 'unique_tag', // Ensure each Hero has a unique tag
              child: child,
            ),
          );
        },
        transitionDuration: Duration(milliseconds: 1000),
      ),
    );
    

    Ensure that the Hero widgets in both the source and destination pages have the same unique tag. This should help prevent the icon from freezing on the screen during fast navigation.

    Login or Signup to reply.
  2. Try this one,

     import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class AppRoutes {
      static MaterialPageRoute? getRoute(RouteSettings settings) {
        switch (settings.name) {
          case "/":
            return MainRoute(
              MyHomePage(),
              settings: settings,
            );
        }
      }
    }
    
    class MainRoute<T> extends MaterialPageRoute<T> {
      MainRoute(
        Widget widget, {
        RouteSettings? settings,
      }) : super(builder: (_) => widget, settings: settings);
    }
    
    class MyApp extends StatelessWidget {
      const MyApp();
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          onGenerateRoute: (RouteSettings settings) => AppRoutes.getRoute(
            settings,
          ),
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage();
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.primary,
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (context) => TestScreen(),
                ),
              );
            },
            child: const Icon(Icons.route),
          ),
        );
      }
    }
    
    class TestScreen extends StatefulWidget {
      const TestScreen({
        this.isSuccess = false,
      });
    
      final bool isSuccess;
    
      @override
      State<TestScreen> createState() => _TestScreenState();
    }
    
    class _TestScreenState extends State<TestScreen> {
      @override
      void initState() {
        super.initState();
        _redirectToDetailPage();
      }
    
      void _redirectToDetailPage() async {
        Future.delayed(const Duration(seconds: 2), () {
          Navigator.push(
            context,
            PageRouteBuilder(
              maintainState: false,
              opaque: true,
              pageBuilder: (_, Animation<double> start, Animation<double> end) {
                return TestUnsuccess(
                  test: end,
                );
              },
              transitionsBuilder: (context, animation, secondaryAnimation, child) {
                return FadeTransition(
                  opacity: animation,
                  child: child,
                );
              },
              transitionDuration: Duration(milliseconds: 2000),
            ),
          );
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: WillPopScope(
            onWillPop: () async {
              return false;
            },
            child: SafeArea(
              child: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Hero(
                      tag: 'payment_icon',
                      child: Icon(
                        Icons.ac_unit,
                        size: 40,
                      ),
                    ),
                    SizedBox(
                      height: 32.0,
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      }
    }
    
    class TestUnsuccess extends StatefulWidget {
      final Animation<double> test;
    
      TestUnsuccess({
        required this.test,
      });
    
      @override
      TestUnsuccessState createState() => TestUnsuccessState();
    }
    
    class TestUnsuccessState extends State<TestUnsuccess> {
      @override
      void initState() {
        super.initState();
      }
    
      @override
      void dispose() {
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: WillPopScope(
            onWillPop: () async {
              return false;
            },
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Hero(
                    tag: 'payment_icon',
                    child: Icon(
                      Icons.ac_unit,
                      size: 40,
                    ),
                  ),
                  SizedBox(
                    height: 16.0,
                  ),
                  ElevatedButton(
                    onPressed: () {
                      Navigator.of(context).pushNamedAndRemoveUntil(
                        "/",
                        ModalRoute.withName('/'),
                      );
    
                      Navigator.of(context).push(
                        MaterialPageRoute(
                          builder: (context) => TestAnother(),
                        ),
                      );
                    },
                    child: Icon(
                      Icons.home_filled,
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    
    class TestAnother extends StatefulWidget {
      @override
      State<TestAnother> createState() => _TestAnotherState();
    }
    
    class _TestAnotherState extends State<TestAnother> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'You have pushed the button this many times:',
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search