Skip to content

[go_router] ShellRoute transition is not working properly v2 #137034

@Velkamhell97

Description

@Velkamhell97

Is there an existing issue for this?

What package does this bug report belong to?

go_router

What target platforms are you seeing this bug on?

Android

Have you already upgraded your packages?

Yes

Dependency versions

pubspec.lock
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
  async:
    dependency: transitive
    description:
      name: async
      sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
      url: "https://pub.dev"
    source: hosted
    version: "2.11.0"
  boolean_selector:
    dependency: transitive
    description:
      name: boolean_selector
      sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.1"
  characters:
    dependency: transitive
    description:
      name: characters
      sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
      url: "https://pub.dev"
    source: hosted
    version: "1.3.0"
  clock:
    dependency: transitive
    description:
      name: clock
      sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
      url: "https://pub.dev"
    source: hosted
    version: "1.1.1"
  collection:
    dependency: transitive
    description:
      name: collection
      sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
      url: "https://pub.dev"
    source: hosted
    version: "1.17.2"
  cupertino_icons:
    dependency: "direct main"
    description:
      name: cupertino_icons
      sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
      url: "https://pub.dev"
    source: hosted
    version: "1.0.6"
  fake_async:
    dependency: transitive
    description:
      name: fake_async
      sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
      url: "https://pub.dev"
    source: hosted
    version: "1.3.1"
  flutter:
    dependency: "direct main"
    description: flutter
    source: sdk
    version: "0.0.0"
  flutter_lints:
    dependency: "direct dev"
    description:
      name: flutter_lints
      sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
      url: "https://pub.dev"
    source: hosted
    version: "2.0.3"
  flutter_test:
    dependency: "direct dev"
    description: flutter
    source: sdk
    version: "0.0.0"
  flutter_web_plugins:
    dependency: transitive
    description: flutter
    source: sdk
    version: "0.0.0"
  go_router:
    dependency: "direct main"
    description:
      name: go_router
      sha256: "2ccd74480706e0a70a0e0dfa9543dede41bc11d0fe3b146a6ad7b7686f6b4407"
      url: "https://pub.dev"
    source: hosted
    version: "11.1.4"
  lints:
    dependency: transitive
    description:
      name: lints
      sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.1"
  logging:
    dependency: transitive
    description:
      name: logging
      sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
      url: "https://pub.dev"
    source: hosted
    version: "1.2.0"
  matcher:
    dependency: transitive
    description:
      name: matcher
      sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
      url: "https://pub.dev"
    source: hosted
    version: "0.12.16"
  material_color_utilities:
    dependency: transitive
    description:
      name: material_color_utilities
      sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
      url: "https://pub.dev"
    source: hosted
    version: "0.5.0"
  meta:
    dependency: transitive
    description:
      name: meta
      sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
      url: "https://pub.dev"
    source: hosted
    version: "1.9.1"
  path:
    dependency: transitive
    description:
      name: path
      sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
      url: "https://pub.dev"
    source: hosted
    version: "1.8.3"
  sky_engine:
    dependency: transitive
    description: flutter
    source: sdk
    version: "0.0.99"
  source_span:
    dependency: transitive
    description:
      name: source_span
      sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
      url: "https://pub.dev"
    source: hosted
    version: "1.10.0"
  stack_trace:
    dependency: transitive
    description:
      name: stack_trace
      sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
      url: "https://pub.dev"
    source: hosted
    version: "1.11.0"
  stream_channel:
    dependency: transitive
    description:
      name: stream_channel
      sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.1"
  string_scanner:
    dependency: transitive
    description:
      name: string_scanner
      sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
      url: "https://pub.dev"
    source: hosted
    version: "1.2.0"
  term_glyph:
    dependency: transitive
    description:
      name: term_glyph
      sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
      url: "https://pub.dev"
    source: hosted
    version: "1.2.1"
  test_api:
    dependency: transitive
    description:
      name: test_api
      sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
      url: "https://pub.dev"
    source: hosted
    version: "0.6.0"
  vector_math:
    dependency: transitive
    description:
      name: vector_math
      sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.4"
  web:
    dependency: transitive
    description:
      name: web
      sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
      url: "https://pub.dev"
    source: hosted
    version: "0.1.4-beta"
sdks:
  dart: ">=3.1.3 <4.0.0"
  flutter: ">=3.7.0"

Steps to reproduce

  1. Execute flutter run on the code sample
  2. Navigate between pages and you should be able to see the issue.

Expected results

The push transition and the pop transition should be animated in the same way.

Actual results

The transitions only works when you push a new route, the popped route is not animated. I followed the steps mentioned by @chunhtai in the previous issue, copying and modifying the _ZoomPageTransition in flutter/lib/src/material/page_transitions_theme.dart. But the transitions still don't work correctly even with using secondaryAnimation

Code sample

Code sample
// ignore_for_file: unused_element

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final router = GoRouter(
  initialLocation: '/page1',
  routes: [
    ShellRoute(
      builder: (context, state, child) {
        return HomePage(child: child);
      },
      routes: [
        GoRoute(
          path: '/page1',
          name: "page1",
          // builder: (context, state) => const Page1(),
          pageBuilder: (context, state) {
            return FadeTransitionPage(key: state.pageKey, child: const Page1());
          },
        ),

        GoRoute(
          path: '/page2',
          name: "page2",
          // builder: (context, state) => const Page2(),
          pageBuilder: (context, state) {
            return FadeTransitionPage(key: state.pageKey, child: const Page2());
          },
        ),

        GoRoute(
          path: '/page3',
          name: "page3",
          // builder: (context, state) => const Page3(),
          pageBuilder: (context, state) {
            return FadeTransitionPage(key: state.pageKey, child: const Page3());
          },
        )
      ], 
    ),
  ]
);

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(useMaterial3: true),
      routerConfig: router,
    );
  }
}

class HomePage extends StatefulWidget {
  final Widget child;

  const HomePage({
    super.key,
    required this.child
  });

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
  static const _routes = ["page1", "page2", "page3"];

  int _routeIndex = 0;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Go Router Issue'),
      ),
      body: widget.child,
      bottomNavigationBar: NavigationBar(
        selectedIndex: _routeIndex,
        onDestinationSelected: (index) {
          setState(() => _routeIndex = index);
          context.goNamed(_routes[index]);
        },
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.looks_one_rounded), 
            label: "Page 1"
          ),

          NavigationDestination(
            icon: Icon(Icons.looks_two_rounded), 
            label: "Page 2"
          ),

          NavigationDestination(
            icon: Icon(Icons.looks_3_rounded), 
            label: "Page 3"
          )
        ],        
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const Align(
      alignment: Alignment(0.0, -0.2),
      child: Text('PAGE 1', style: TextStyle(fontSize: 48)),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const Align(
      alignment: Alignment(0.0, 0.0),
      child: Text('PAGE 2', style: TextStyle(fontSize: 48)),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const Align(
      alignment: Alignment(0.0, 0.2),
      child: Text('PAGE 3', style: TextStyle(fontSize: 48)),
    );
  }
}

class FadeTransitionPage extends CustomTransitionPage {
  const FadeTransitionPage({
    super.key,
    required super.child, 
  }) : super(
    transitionsBuilder: _transitionBuilder,
    transitionDuration: const Duration(milliseconds: 1000),
    reverseTransitionDuration: const Duration(milliseconds: 1000)
  );

  static Widget _transitionBuilder(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
    return _FadePageTransition(animation: animation, secondaryAnimation: secondaryAnimation, child: child);
  }
}


class _FadePageTransition extends StatelessWidget {
  const _FadePageTransition({
    super.key,
    required this.animation,
    required this.secondaryAnimation,
    required this.child,
  });

  final Animation<double> animation;
  final Animation<double> secondaryAnimation;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    return DualTransitionBuilder(
      animation: animation,
      forwardBuilder: (
        BuildContext context,
        Animation<double> animation,
        Widget? child,
      ) {
        return _FadeEnterTransition(
          animation: animation,
          child: child,
        );
      },
      reverseBuilder: (
        BuildContext context,
        Animation<double> animation,
        Widget? child,
      ) {
        return _FadeExitTransition(
          animation: animation,
          reverse: true,
          child: child,
        );
      },
      child: DualTransitionBuilder(
        animation: ReverseAnimation(secondaryAnimation),
        forwardBuilder: (
          BuildContext context,
          Animation<double> animation,
          Widget? child,
        ) {
          return _FadeEnterTransition(
            animation: animation,
            reverse: true,
            child: child,
          );
        },
        reverseBuilder: (
          BuildContext context,
          Animation<double> animation,
          Widget? child,
        ) {
          return _FadeExitTransition(
            animation: animation,
            child: child,
          );
        },
        child: child,
      ),
    );
  }
}

class _FadeEnterTransition extends StatelessWidget {
  const _FadeEnterTransition({
    super.key,
    required this.animation,
    this.reverse = false,
    required this.child,
  });

  final Animation<double> animation;
  final Widget? child;
  final bool reverse;

  static final Animatable<double> _fadeInTransition = Tween<double>(begin: 0.0, end: 1.00);

  @override
  Widget build(BuildContext context) {
    final Animation<double> fadeTransition = reverse
      ? kAlwaysCompleteAnimation
      : _fadeInTransition.animate(animation);

    return FadeTransition(
      opacity: fadeTransition,
      child: child,
    );
  }
}

class _FadeExitTransition extends StatelessWidget {
  const _FadeExitTransition({
    super.key,
    required this.animation,
    this.reverse = false,
    required this.child,
  });

  final Animation<double> animation;
  final bool reverse;
  final Widget? child;

  static final Animatable<double> _fadeOutTransition = Tween<double>(begin: 1.0, end: 0.0);

  @override
  Widget build(BuildContext context) {
    final Animation<double> fadeTransition = reverse
      ? _fadeOutTransition.animate(animation)
      : kAlwaysCompleteAnimation;

    return FadeTransition(
      opacity: fadeTransition,
      child: child,
    );
  }
}

Screenshots or Videos

Screenshots / Video demonstration
qemu-system-x86_64_qmzDnC36QL.mp4

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.13.6, on Microsoft Windows [Versi¢n 10.0.23570.1000], locale es-MX)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.5.0)
[√] Android Studio (version 2022.3)
[√] VS Code (version 1.83.1)
[√] Connected device (4 available)
[√] Network resources

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listfound in release: 3.13Found to occur in 3.13found in release: 3.16Found to occur in 3.16has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: go_routerThe go_router packagepackageflutter/packages repository. See also p: labels.team-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions