Milad-Akarie / auto_route_library

Flutter route generator
MIT License
1.58k stars 400 forks source link

Hero animation not showing #418

Closed AscentionOne closed 2 years ago

AscentionOne commented 3 years ago

Hi, I am using Hero widget in my application. However, the hero animation is not showing. The Hero widget is set to the product image in the ShopPage and by clicking the image it will navigate to the ProductDetailPage with relative id. There should be a hero animation between page transitions. However, it is showing the slide in transition.

Another question is since this is a mobile app. Is setting up the path mandatory? I know in web applications we have to set up the path. Not sure if I have to in the mobile application, since we can not type the route manually. Thanks!

Below is my route setup example.

@MaterialAutoRouter(
  replaceInRouteName: 'Page,Route',
  routes: <AutoRoute>[
    AutoRoute(
      path: '/',
      page: HomePage,
      usesTabsRouter: true,
      children: [
        RedirectRoute(path: '', redirectTo: 'user'),
        AutoRoute(
          path: 'shop',
          page: EmptyRouterPage,
          name: 'ShopTab',
          children: [
            AutoRoute(
              path: '',
              page: ShopPage,
            ),
            AutoRoute(
              path: ':id',
              page: ProductDetailPage,
            ),
          ],
        ),
      ],
    ),
  ],
)
theweiweiway commented 3 years ago

To answer your second question, path is not mandatory for mobile apps.

Please see the bottom of this page: https://autoroute.vercel.app/basics/root_router

AscentionOne commented 3 years ago

Got it. Thank you!

WasserEsser commented 3 years ago

I have the same issue with Hero animations not showing.

Milad-Akarie commented 3 years ago

@AscentionOne @WasserEsser It seems HeroController is maintained inside the MaterialApp now, it's creating a HeroControllerScope but for a reason I still don't know nested Navigators can not access that scope although it seems at the top of the widgets tree, p.s it seems that it's old known problem of Nested Navigators

anyways.. for now a decent fix would be wrapping the Nested Router with our own HeroController scope, you could make a general Hero-Scoped EmptyRouterPage like follows

class HeroEmptyRouterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return HeroControllerScope(
      controller: HeroController(),
      child: AutoRouter(),
    );
  }
}

p.s you could also pass the HeroController as a Navigator Observer to AutoRouter() instead of scoping it.

const booksTab = AutoRoute(
  path: 'books',
  // use it instead of EmptyRouterPage
  page: HeroEmptyRouterPage,
  name: 'BooksTab',
  children: [
    AutoRoute(path: '', page: BookListPage),
    AutoRoute(path: ':id', page: BookDetailsPage),
  ],
);

if this turns out to be the best practice to do this, I'll handle it internally in future builds.

AscentionOne commented 3 years ago

@Milad-Akarie Thank you for the answer! Yes, I also did some research that this stackoverflow issue also recommend that you can put the HeroController in the observers. There seems no observers argument in the current AutoRouter correct? I'll try your method and see how it goes! Thank you! 😊

AscentionOne commented 3 years ago

@Milad-Akarie I try your method and it works! πŸŽ‰

Another question is, let's say if I have Hero widgets in multiple pages (ex: booksPage, userPage, moviesPage... etc) I have to wrap every route like what you did using HeroEmptyRouterPage or I can just wrap the root route (ex: homePage) and all the relative page can get the HeroController. Thank you.

Milad-Akarie commented 3 years ago

@AscentionOne it's called NavigatorObservers because it passes it to the navigator it wraps AutoRouter(navigatorObservers: [],)

Milad-Akarie commented 3 years ago

@AscentionOne I haven't tested it like that but technically yes wrapping the root should be enough. maybe let me know if you get the chance to try it?

I think you should stick the scoping approach if you want this to work though.

AscentionOne commented 3 years ago

@AscentionOne I haven't tested it like that but technically yes wrapping the root should be enough. maybe let me know if you get the chance to try it?

Okay, I will try it and let you know how it goes!

AscentionOne commented 3 years ago

@AscentionOne it's called NavigatorObservers because it passes it to the navigator it wraps AutoRouter(navigatorObservers: [],)

I saw it in AutoRouter but I haven't seen one in MaterialAutoRouter since I am using MaterialAutoRouter.

Milad-Akarie commented 3 years ago

@AscentionOne MaterialAutoRouter extends AutoRouterAnnotation, it's not the same as AutoRouter() the widget.

That's what I meant

class HeroEmptyRouterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AutoRouter(navigatorObservers: [HeroController()]);
  }
}
AscentionOne commented 3 years ago

@AscentionOne MaterialAutoRouter extends AutoRouterAnnotation, it's not the same as AutoRouter() the widget.

That's what I meant

class HeroEmptyRouterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AutoRouter(navigatorObservers: [HeroController()]);
  }
}

I understand now, thank you! Sorry for the brain delay πŸ˜‚

AscentionOne commented 3 years ago

@Milad-Akarie I also try wrapping the root widget with HeroControllerScope which has multiple Hero widgets in different pages. Apparently, it doesn't work. We have to wrap every pageRoute with their own HeroControllerScope. I checked it work.

Below is the error message I got when I wrap the root route with HeroControllerScope.


════════ Exception caught by widget library ════════════════════════════════════
The following assertion was thrown:
A HeroController can not be shared by multiple Navigators. The Navigators that share the same HeroController are:

- NavigatorState#d4bef(tickers: tracking 1 ticker)
- NavigatorState#b3a7d(tickers: tracking 1 ticker)
Please create a HeroControllerScope for each Navigator or use a HeroControllerScope.none to prevent subtree from receiving a HeroController.
When the exception was thrown, this was the stack
#0      NavigatorState._updateHeroController.<anonymous closure>.<anonymous closure>
package:flutter/…/widgets/navigator.dart:3501
#1      SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1144
#2      SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1090
#3      SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:998
#7      _invoke (dart:ui/hooks.dart:161:10)
...
════════════════════════════════════════════════════════════════════════════════
Milad-Akarie commented 3 years ago

@AscentionOne I see, I guess that makes it easier for me to take the decision of weather to build-in the HeroScope inside of AutoRouterWidget

WasserEsser commented 3 years ago

p.s you could also pass the HeroController as a Navigator Observer to AutoRouter() instead of scoping it.

This fixes the issue nicely.

britannio commented 3 years ago

@Milad-Akarie I'm placing a HeroController inside the navigatorObservers of AutoRouter.declarative but it's not working.

cacianokroth commented 3 years ago

Hello! I tried the ways suggested above and the problem persists, is there anything else I can do?

nullbytesoftware commented 3 years ago

AutoRouteNavigator is already wrapped with HeroControllerScope now but its not working. Im on v2.2.0

llorenzo commented 3 years ago

auto_route: ^2.3.2

class HeroEmptyRouterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return HeroControllerScope(
      controller: HeroController(),
      child: AutoRouter(),
    );
  }
}

Does not seem to work. The Hero is not animated.

Also the compiler dose not like this:

  Widget build(BuildContext context) {
    return AutoRouter(navigatorObservers: [HeroController()]);
  }

The argument type 'List<HeroController>' can't be assigned to the parameter type 'List<NavigatorObserver> Function()'.

What is the current solution to this?

wildCherryTO commented 2 years ago

auto_route: ^ 2.3.2

class HeroEmptyRouterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return HeroControllerScope(
      controller: HeroController(),
      child: AutoRouter(),
    );
  }
}

ΠŸΠΎΡ…ΠΎΠΆΠ΅, Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚. Π“Π΅Ρ€ΠΎΠΉ Π½Π΅ Π°Π½ΠΈΠΌΠΈΡ€ΠΎΠ²Π°Π½.

Π’Π°ΠΊΠΆΠ΅ Π΄ΠΎΠ·Π° компилятора Π½Π΅ такая:

  Widget build(BuildContext context) {
    return AutoRouter(navigatorObservers: [HeroController()]);
  }

The argument type 'List<HeroController>' can't be assigned to the parameter type 'List<NavigatorObserver> Function()'.

Каково Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ этой ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹?

Hello! you figured out this problem?

agordeev commented 2 years ago

Wrapping with HeroControllerScope didn't work. But adding HeroController to navigator observers worked for me:

AutoTabsScaffold(
  navigatorObservers:() => [HeroController()]
  ...
)
SashaKryzh commented 2 years ago

I am adding HeroController(), but still the animation doesn't work.

Material app

routerDelegate: getIt<AppRouter>().delegate(
          placeholder: (_) => const SplashPage(),
          navigatorObservers: () => [HeroController()],
        ),
class RootWrapperPage extends StatelessWidget {
  const RootWrapperPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider.value(
      value: getIt<AuthBloc>(),
      child: BlocBuilder<AuthBloc, AuthState>(
        buildWhen: (previous, current) =>
            previous.runtimeType != current.runtimeType,
        builder: (_, state) => AutoRouter.declarative(
          routes: (_) => [
            const SplashRoute(),
            if (state is Unauthenticated) const AuthWrapperRoute(),
            if (state is Authenticated) const HomeRoute(),
          ],
        ),
      ),
    );
  }
}
class AuthWrapperPage extends StatelessWidget {
  const AuthWrapperPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const AutoRouter();
  }
}
@MaterialAutoRouter(
  replaceInRouteName: 'Page,Route',
  routes: <AutoRoute>[
    AutoRoute(
      initial: true,
      path: '',
      page: RootWrapperPage,
      children: [
        CustomRoute(page: SplashPage),
        CustomRoute(
          page: AuthWrapperPage,
          durationInMilliseconds: splashTransitionMilliseconds,
          children: [
            CustomRoute(
              page: MainAuthPage,
              initial: true,
            ),
            CustomRoute(
              page: LoginPage,
              durationInMilliseconds: splashTransitionMilliseconds,
            ),
          ],
        ),
        AutoRoute(page: HomePage),
      ],
    ),
  ],
)
class AppRouter extends _$AppRouter {}

Edit I ended up duplicating SplashPage in AuthWrapperPage and showing it for 1 frame and then navigating to MainAuthPage.

github-actions[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions

dhiyaaulauliyaa commented 8 months ago

ppe

has you solve this problem? mine also cannot add HeroController to List

edit: should use this syntax :

 AutoRouter(
     navigatorObservers: ()=> [HeroController()],
),

instead of

 AutoRouter(
     navigatorObservers: [HeroController()],
 ),