Skip to content

[go_router] Expose Navigator.clipBehavior to ShellRoute #131836

Closed
flutter/packages
#12646

Description

@JakesMD

Is there an existing issue for this?

Steps to reproduce

When adding padding to the builder of a MaterialApp, you can see that the router clips its children. This is demonstrated best by creating a GoRoute with a big box shadow.

This gets problematic when using a ShellRoute, where a child route has a container with a box shadow. The box shadow gets clipped, looks off and does not fulfil the requirements of the design team.

To experience this behavior firsthand, you can test the code sample provided below.

Expected results

The box shadow should spread in all directions.

Actual results

The box shadow only spreads left and top and is clipped right and bottom.

Code sample

Basic code sample
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      builder: (context, child) => Padding(
        padding: const EdgeInsets.all(32),
        child: child,
      ),
      routerConfig: GoRouter(
        routes: [
          GoRoute(
            path: "/",
            builder: (context, state) => Container(
              margin: const EdgeInsets.only(left: 100, top: 100),
              decoration: const BoxDecoration(
                color: Colors.red,
                boxShadow: [
                  BoxShadow(
                    blurRadius: 50,
                    offset: (Offset(20, 20)),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
Code sample in the context of a ShellRoute
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      routerConfig: GoRouter(
        routes: [
          ShellRoute(
            builder: (context, state, child) => MyHomePage(child: child),
            routes: [
              GoRoute(
                path: "/",
                builder: (context, state) => const MySubPage(),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final Widget child;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          Container(
            width: 100,
            color: Colors.green,
          ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(32),
              child: child,
            ),
          ),
        ],
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(top: 100),
      decoration: const BoxDecoration(
        color: Colors.red,
        boxShadow: [
          BoxShadow(
            blurRadius: 50,
            offset: (Offset(20, 20)),
          ),
        ],
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration Screenshot 2023-08-03 at 11 05 50

Logs

Logs

log.txt

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.10.5, on macOS 13.3.1 22E772610a darwin-arm64, locale en-DE)
    • Flutter version 3.10.5 on channel stable at /Users/[user]/Development/src/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 796c8ef792 (7 weeks ago), 2023-06-13 15:51:02 -0700
    • Engine revision 45f6e00911
    • Dart version 3.0.5
    • DevTools version 2.23.1

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    • Android SDK at /Users/[user]/Library/Android/sdk
    • Platform android-33, build-tools 33.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14E300c
    • CocoaPods version 1.12.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] VS Code (version 1.80.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.70.0

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-arm64   • macOS 13.3.1 22E772610a darwin-arm64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 115.0.5790.114

[✓] Network resources
    • All expected network resources are available.

• No issues found!

Activity

  1. added
    in triagePresently being triaged by the triage team
    on Aug 3, 2023
  2. huycozy commented on Aug 3, 2023

    @huycozy
    Member

    Hi @JakesMD
    I see the same result in case no padding:

    has padding no padding

    Instead, if you set margin for right and bottom of the Container, you will have the expected result, for eg:

  3. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Aug 3, 2023
  4. JakesMD commented on Aug 3, 2023

    @JakesMD
    Author

    Thats still not the expected result. Without the router I get the expected result which looks like this:

    expected result actual result
    Screenshot 2023-08-03 at 16 15 04 Screenshot 2023-08-03 at 11 05 50
    Code for generating expected result
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          home: Padding(
            padding: const EdgeInsets.all(32),
            child: Container(
              margin: const EdgeInsets.only(left: 100, top: 100),
              decoration: const BoxDecoration(
                color: Colors.red,
                boxShadow: [
                  BoxShadow(
                    blurRadius: 50,
                    offset: (Offset(20, 20)),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }

    The shadow should have no flat or sharp edges. But don't forget that the shadow was just an example for a widget that overflows. You could have a stack with a positioned widget, for example:

    expected result actual result
    Screenshot 2023-08-03 at 16 25 51 Screenshot 2023-08-03 at 16 25 26
    Code for generating expected result
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          home: Padding(
            padding: const EdgeInsets.all(100),
            child: Container(
              color: Colors.red,
              child: Stack(
                clipBehavior: Clip.none,
                children: [
                  Positioned(
                    top: -50,
                    left: -50,
                    child: Container(
                      width: 100,
                      height: 100,
                      color: Colors.blue,
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    Code for generating actual result
    import 'package:flutter/material.dart';
    import 'package:go_router/go_router.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          title: 'Flutter Demo',
          builder: (context, child) => Padding(
            padding: const EdgeInsets.all(100),
            child: child,
          ),
          routerConfig: GoRouter(
            routes: [
              GoRoute(
                path: "/",
                builder: (context, state) => Container(
                  color: Colors.red,
                  child: Stack(
                    clipBehavior: Clip.none,
                    children: [
                      Positioned(
                        top: -50,
                        left: -50,
                        child: Container(
                          width: 100,
                          height: 100,
                          color: Colors.blue,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
  5. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Aug 3, 2023
  6. huycozy commented on Aug 4, 2023

    @huycozy
    Member

    Thanks for your update with more examples. I also check this with default route usage (without using go_router package), and see it outputs the expected result. The issue seems to be go_router's issue.

    Sample code using default route (expected result)
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          builder: (context, child) => Padding(
            padding: const EdgeInsets.all(32),
            child: child,
          ),
          routes: {
            // When navigating to the "/" route, build the FirstScreen widget.
            '/': (context) => const FirstScreen(),
            // When navigating to the "/second" route, build the SecondScreen widget.
            '/second': (context) => const SecondScreen(),
          },
        );
      }
    }
    
    class FirstScreen extends StatelessWidget {
      const FirstScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: const EdgeInsets.only(left: 100, top: 100),
          decoration: const BoxDecoration(
            color: Colors.red,
            boxShadow: [
              BoxShadow(
                blurRadius: 50,
                offset: (Offset(20, 20)),
              ),
            ],
          ),
        );
      }
    }
    
    class SecondScreen extends StatelessWidget {
      const SecondScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Second Screen'),
          ),
          body: Center(
            child: ElevatedButton(
              // Within the SecondScreen widget
              onPressed: () {
                // Navigate back to the first screen by popping the current route
                // off the stack.
                Navigator.pop(context);
              },
              child: const Text('Go back!'),
            ),
          ),
        );
      }
    }
    Demo default route (expected result)

    Screenshot 2023-08-04 at 11 58 55

    Reproduced the issue with reproduction samples above (#131836 (comment), #131836 (comment))

    flutter doctor -v (stable and master)
    [✓] Flutter (Channel stable, 3.10.6, on macOS 13.5 22G74 darwin-x64, locale en-VN)
        • Flutter version 3.10.6 on channel stable at /Users/huynq/Documents/GitHub/flutter
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision f468f3366c (3 weeks ago), 2023-07-12 15:19:05 -0700
        • Engine revision cdbeda788a
        • Dart version 3.0.6
        • DevTools version 2.23.1
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 32.0.0)
        • Android SDK at /Users/huynq/Library/Android/sdk
        • Platform android-33, build-tools 32.0.0
        • ANDROID_HOME = /Users/huynq/Library/Android/sdk
        • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 15.0)
        • Xcode at /Applications/Xcode-beta.app/Contents/Developer
        • Build 15A5195m
        • CocoaPods version 1.11.3
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 2022.2)
        • Android Studio at /Applications/Android Studio.app/Contents
        • Flutter plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/9212-flutter
        • Dart plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/6351-dart
        • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
    
    [✓] VS Code (version 1.80.2)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.68.0
    
    [✓] Connected device (2 available)
        • macOS (desktop) • macos  • darwin-x64     • macOS 13.5 22G74 darwin-x64
        • Chrome (web)    • chrome • web-javascript • Google Chrome 115.0.5790.114
    
    [✓] Network resources
        • All expected network resources are available.
    
    • No issues found!
    [!] Flutter (Channel master, 3.13.0-17.0.pre.41, on macOS 13.5 22G74 darwin-x64, locale en-VN)
        • Flutter version 3.13.0-17.0.pre.41 on channel master at /Users/huynq/Documents/GitHub/flutter_master
        ! Warning: `flutter` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path.
        ! Warning: `dart` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision f7b68919ac (42 minutes ago), 2023-08-03 22:37:30 -0400
        • Engine revision ca8d852edd
        • Dart version 3.2.0 (build 3.2.0-34.0.dev)
        • DevTools version 2.26.1
        • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 32.0.0)
        • Android SDK at /Users/huynq/Library/Android/sdk
        • Platform android-33, build-tools 32.0.0
        • ANDROID_HOME = /Users/huynq/Library/Android/sdk
        • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 14.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Build 14E222b
        • CocoaPods version 1.11.3
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 2022.2)
        • Android Studio at /Applications/Android Studio.app/Contents
        • Flutter plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/9212-flutter
        • Dart plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/6351-dart
        • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
    
    [✓] VS Code (version 1.80.2)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.70.0
    
    [✓] Connected device (3 available)
        • RMX2001 (mobile) • EUYTFEUSQSRGDA6D • android-arm64  • Android 11 (API 30)
        • macOS (desktop)  • macos            • darwin-x64     • macOS 13.5 22G74 darwin-x64
        • Chrome (web)     • chrome           • web-javascript • Google Chrome 115.0.5790.170
    
    [✓] Network resources
        • All expected network resources are available.
    
    ! Doctor found issues in 1 category.
  7. added
    packageflutter/packages repository. See also p: labels.
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    and removed
    in triagePresently being triaged by the triage team
    on Aug 4, 2023
  8. chunhtai commented on Aug 10, 2023

    @chunhtai
    Contributor

    Shellroute will clip the child; otherwise, the route transition like material route transition may overflow the box. If you are suggesting the box shadow shouldn't be clip, I think then this is more of a framework issue

  9. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Aug 10, 2023
  10. 12 remaining items

  11. changed the title [-][go_router] Routes get clipped[/-] [+][go_router] Expose Navigator.clipBehavior to ShellRoute[/+] on Aug 17, 2023
  12. JakesMD commented on Aug 19, 2023

    @JakesMD
    Author

    It is either clipping like what we have now, or no clipping at all. We could add a flag for shellroute to not clip the sub route, but then the material page transition will look pretty broken if the flag is set to not clip. So I am hesitate for adding this feature.

    I tried it out for my self by adding clipBehavior: Clip.none to the Navigator and the page transition didn't look broken. The only difference it makes is that the sub route animates over the shell route, which is not a problem in our case. And definitely a sacrifice worth paying in order have no horribly clipped shadows and animated widgets.

  13. Borlay commented on Mar 7, 2024

    @Borlay

    Any news on this one? I'm having same problem.
    Don't want to add margin to every child page when i could add padding inside shell page only once.

  14. JakesMD commented on Mar 9, 2024

    @JakesMD
    Author

    Back then we simply edited the package code locally. It has changed a lot since then but you can set the clip behavior here. If required, you can pass the clip behaviour up the RouteBuilder and GoRouterDelegate into the GoRouter.

  15. sampaioletti commented on May 27, 2025

    @sampaioletti

    I believe this also changes the expected behavior of DropdownButtonFormField

    Shell Route Regular Route (Expected)
    Image Image
    import 'package:flutter/material.dart';
    import 'package:go_router/go_router.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    final _router = GoRouter(
      routes: [
        //This works as expected
        // GoRoute(
        //   path: '/',
        //   builder: (context, state) => Scaffold(
        //     body: Column(
        //       children: [
        //         DropdownButtonFormField(
        //           items: [
        //             DropdownMenuItem(value: "1", child: Text("1")),
        //             DropdownMenuItem(value: "2", child: Text("2")),
        //             DropdownMenuItem(value: "3", child: Text("3")),
        //           ],
        //           onChanged: (_) {},
        //         ),
        //       ],
        //     ),
        //   ),
        // ),
        ShellRoute(
          routes: [
            GoRoute(
              path: "/",
              pageBuilder: (context, state) => NoTransitionPage(
                child: DropdownButtonFormField(
                  items: [
                    DropdownMenuItem(value: "1", child: Text("1")),
                    DropdownMenuItem(value: "2", child: Text("2")),
                    DropdownMenuItem(value: "3", child: Text("3")),
                  ],
                  onChanged: (_) {},
                ),
              ),
            ),
          ],
          pageBuilder: (context, state, child) => NoTransitionPage(
            child: Scaffold(body: Column(children: [child])),
          ),
        ),
      ],
    );
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(routerConfig: _router);
      }
    }
    

    In our application we have a DropDown in a list and it causes it to scroll for options even though there is enough room for the full list

    Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectfound in release: 3.10Found to occur in 3.10found in release: 3.13Found to occur in 3.13has 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-ecosystemOwned by Ecosystem teamtriaged-ecosystemTriaged by Ecosystem team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions