Make a transition when reordering pages in Navigator.pages

Viewed 40

I have a list of pages in Navigator.pages. When I push a page with a duplicate key, I want to bring the old page on top. The problem is, the page shows without animation, it just appears on top instantly. Meanwhile the default animation works alright when pushing a new page for the first time. How do I animate the page if it is brought on top?

enter image description here

Here is the code:

import 'package:flutter/material.dart';

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

final _notifier = ChangeNotifier();
final _pages = <Page>[Page1()];

class Page1 extends MaterialPage {
  Page1()
      : super(
          key: const ValueKey('Page1'),
          child: Scaffold(
            appBar: AppBar(title: const Text('Page1')),
            body: const Center(
              child: ElevatedButton(
                onPressed: _showPage2,
                child: Text('Show Page2'),
              ),
            ),
          ),
        );
}

class Page2 extends MaterialPage {
  Page2()
      : super(
          key: const ValueKey('Page2'),
          child: Scaffold(
            appBar: AppBar(title: const Text('Page2')),
            body: const Center(
              child: ElevatedButton(
                onPressed: _showPage1,
                child: Text('Show Page1'),
              ),
            ),
          ),
        );
}

void _showPage1() {
  for (final page in _pages) {
    if (page.key == const ValueKey('Page1')) {
      _pages.remove(page);
      break;
    }
  }
  _pages.add(Page1());
  _notifier.notifyListeners();
}

void _showPage2() {
  for (final page in _pages) {
    if (page.key == const ValueKey('Page2')) {
      _pages.remove(page);
      break;
    }
  }
  _pages.add(Page2());
  _notifier.notifyListeners();
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _notifier,
      builder: (_, __) {
        return Navigator(
          pages: [..._pages],
          transitionDelegate: const MyTransitionDelegate(),
          onPopPage: (route, result) {
            if (!route.didPop(result)) return false;

            return true;
          },
        );
      },
    );
  }
}

What I tried:

  1. Navigator.transitionDelegate seemed like the property to handle this. But it is only invoked when a new route is pushed and not when the order is changed.

  2. To dispose the old route before bringing the page on top in hope that it will be re-created by Flutter and that could somehow trigger the animation. But I just get an exception saying the route cannot be used after disposal.

  3. To let the navigator rebuild without the old page with _changeNotifier.notifyListeners(); await Future.delayed(Duration.zero); before re-adding the page. But the zero duration did not work.

The workarounds so far are:

  1. To add a non-zero duration between removing and re-adding a page. But this is noticeable.

  2. To change the key of the new page. But I need keys to track some things in the app.

0 Answers
Related