Wait for page transition to finish
Asked Answered
S

4

6

How can I detect if a page transition was finished in flutter?

Background: When I pop to a page, I want to start a function after the page transition has finished.

Sosthina answered 14/8, 2019 at 20:26 Comment(0)
W
5

You can register a callback when you push your new route that can also contain data from the popped route, if you need, for example, to pass data from it.

Navigator.of(context).push(/*Some route*/).then((data) {
 // You can call your function here. data will be null if nothing is passed from the popped route
});

If you want to pass data from the popped route, just do it like so

Navigator.of(context).pop(someData);

Edited

If you want to know when the transition actually ends, you can register a callback listener for it as well

  Navigator.of(context).push(MaterialPageRoute(
          builder: (BuildContext context) => RouteWidget())
                   ..completed.then((_) {print('transition completed');},
          ),
  );
Wilder answered 14/8, 2019 at 20:38 Comment(1)
But the callback of .then fires immediately after the page was popped. It does not wait for the transition to complete.Sosthina
B
4

Its also possible to wait for the push transition if you keep a reference to the route and using the didPush() TickerFuture

MaterialPageRoute route = MaterialPageRoute(builder: (context) => MyPage());
Navigator.of(context).push(route);
await route.didPush(); // you could also use then instead of await


// ROUTE FINISHED TRANSITION HERE 

This can be used if you have multiple stacked navigators and you want smoother transitions between them. Like wait for Navigator 1 to finish the push before you pop the currently active Navigator 2 to show the page of Navigator 1

Blanchblancha answered 21/8, 2019 at 11:16 Comment(0)
L
2

Another solution

is to await the animation to finish on the second page (this is usefull if you want to update the page after the animation transition)

first page:

...
Navigator.push(
        context,
        PageRouteBuilder(
            transitionDuration: Duration(milliseconds: 3000), //optional but useful for testing
            pageBuilder: (_, animation, ___) => SecondPage(
                  pageBuilderAnimation: animation,
                )));
...

second page:

class SecondPage extends StatefulWidget {
  const SecondPage ({Key? key, this.pageBuilderAnimation});

  final Animation<double>? pageBuilderAnimation;

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

class _SecondPageState extends State<SecondPage> {

  bool isTransitioning = true;


  @override
  void initState() {
    super.initState();

    if (widget.pageBuilderAnimation != null)
      widget.pageBuilderAnimation!.addListener(() {
        if (widget.pageBuilderAnimation!.isCompleted)
          setState(() => isTransitioning = false);
      });
    else
      isTransitioning = false;
  }
  ...
}


Linguiform answered 13/4, 2021 at 12:51 Comment(0)
P
0

If you need callbacks for the completion of push and pop animations, just use the route's animation to attach a status listener.

short:

route.animation?.addStatusListener((AnimationStatus status) {
  if (status.isCompleted) {
    print('push animation finished');
  } else if (status.isDismissed) {
    print('pop animation finished');
  }
});

flutter route animations callbacks

full code:

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;

void main() {
  timeDilation = 10.0; // slow down any animation
  runApp(DemoApp());
}

class DemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        textTheme: const TextTheme(
          bodyMedium: TextStyle(fontSize: 20),
        ),
        elevatedButtonTheme: const ElevatedButtonThemeData(
          style: ButtonStyle(
            textStyle: WidgetStatePropertyAll(TextStyle(fontSize: 20)),
          ),
        ),
      ),
      home: PageOne(),
    );
  }
}

class PageOne extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Page one'),
            ElevatedButton(
              child: const Text('Open page two'),
              onPressed: () async {
                print('--------- open tap ---------');

                // 1. build route
                final route = MaterialPageRoute(builder: (_) => PageTwo());

                // 2. push route
                final routeFuture = Navigator.of(context).push(route);

                // 3. attach animation status listener
                route.animation?.addStatusListener((AnimationStatus status) {
                  if (status.isCompleted) {
                    print('push animation finished');
                  } else if (status.isDismissed) {
                    print('pop animation finished');
                  }
                });

                // 4. If you awaiting something from page two
                final result = await routeFuture;
                print('result: $result');
              },
            )
          ],
        ),
      ),
    );
  }
}

class PageTwo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Page two'),
            ElevatedButton(
              onPressed: () {
                Navigator.of(context).pop('Some data from page two');
              },
              child: const Text('Return result'),
            ),
          ],
        ),
      ),
    );
  }
}

Pressurize answered 30/9 at 10:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.