22

I am trying to use Hero Animations in my first flutter app, but there is already instagram like bottom navigation, that implemented using this approach, and I found that Hero Animations just doesn't work inside nested Navigators.

For example you can get Complete example from here and replace home: MainScreen(), from HeroApp class to

home: Navigator(onGenerateRoute: (_) {
  return MaterialPageRoute(builder: (_) => MainScreen());
}),

and hero animation will break.

Maybe there is some another approaches to implement bottom navigation with independent stacks for each tab, that don't use nested navigators, but I haven't found any.

So any advice much appreciated.

UPD: I just realized that answer should be in MaterialApp class and bingo!

There is naive solution that works:

home: Navigator(
  onGenerateRoute: (_) => MaterialPageRoute(builder: (_) => MainScreen()),
  observers: [HeroController()],
),

But in MaterialApp source code things are little more complicated, so maybe there are some hidden things, that broke my naive solution. Therefore question is still open.

Mikhail
  • 223
  • 2
  • 6
  • You shouldn't nest `Navigator` to begin with – Rémi Rousselet Aug 09 '18 at 08:15
  • 1
    @RemiRousselet hm, but [this](https://stackoverflow.com/a/46498543/10184844) answer (actually made by engineer from Flutter team) proposes `Navigator` nesting. How should I implement bottom navigation with independent stacks for each tab then? – Mikhail Aug 09 '18 at 08:55
  • `Navigator` doesn't support well nesting, as it breaks a few things such as Hero; which are directly associated to a navigator instance. That answer is quite old; depending on what you want there may be better way to achive your need. – Rémi Rousselet Aug 09 '18 at 09:00
  • Hi @RémiRousselet the Navigator class documentation has a section on 'Nesting Navigators'. Will Hero animations always break in this case? – juliusspencer Apr 12 '19 at 03:48
  • @Mikhail Is it working with your examples above? – GensaGames Dec 19 '19 at 21:46
  • @Mikhail: Also would like to know whether this solution works for you. It doesn't for me. – ThemBones Dec 25 '19 at 15:14
  • 1
    @Mikhail, THANK YOU So much!!! I have been wondering for a LONG period of time WHY Hero animation doesn't work in nested navigators. I have viewed a LOT of examples on multiple tabs running at the same time and truly all of them have their own navigators (to keep track of its own stacks) and your solution with a simple line of code simply fixes everything and animations work now! I would've given you 1000 pts for that. Thank you! – kovalyovi Mar 14 '20 at 04:58

2 Answers2

33

This is because Hero relies on a HeroController which the navigator in MaterialApp has but your custom one does not, to fix this just add the controller, like this.

import 'package:flutter/material.dart';

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  HeroController _heroController;

  @override
  void initState() {
    super.initState();
    _heroController = HeroController(createRectTween: _createRectTween);
  }

  @override
  Widget build(BuildContext context) {
    return Navigator(
      observers: [_heroController],
      onGenerateRoute: (settings) {
        return MaterialPageRoute(
            settings: settings, builder: (context) => Text(''));
      },
    );
  }

  RectTween _createRectTween(Rect begin, Rect end) {
    return MaterialRectArcTween(begin: begin, end: end);
  }
}
Votagus
  • 544
  • 6
  • 9
  • 4
    Consider refactoring this to use the [`HeroControllerScope`](https://flutter.dev/docs/release/breaking-changes/hero-controller-scope) widget. – Chance Snow Mar 29 '21 at 05:40
5

August 2022 Update:

A better solution to the accepted answer is to wrap each of your Navigator widgets in a HeroControllerScope widget as shown.

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      builder: (BuildContext context, Widget child) {
        // Builds two parallel navigators.
        return Stack(
          children: <Widget>[
            HeroControllerScope(
              controller: MaterialApp.createMaterialHeroController(),
              child: Navigator(
                onGenerateRoute: (RouteSettings settings) {
                  return MaterialPageRoute<void>(
                    settings: settings,
                    builder: (BuildContext context) {
                      return const Text('first Navigator');
                    }
                  );
                },
              ),
            ),
            HeroControllerScope(
              controller: MaterialApp.createMaterialHeroController(),
              child: Navigator(
                onGenerateRoute: (RouteSettings settings) {
                  return MaterialPageRoute<void>(
                    settings: settings,
                    builder: (BuildContext context) {
                      return const Text('second Navigator');
                    }
                  );
                },
              ),
            ),
          ],
        );
      }
    )
  );
}
Eric Su
  • 725
  • 6
  • 14