Milad-Akarie / auto_route_library

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

Nested route inside of Modal Bottom Sheet #675

Closed SpaceWaffles closed 2 years ago

SpaceWaffles commented 3 years ago

Hello, thanks for this great library. I'm enjoying it but am stuck with this:

I'd like to have a flow sitting inside of a bottom sheet. I'm using this library: https://github.com/jamesblasco/modal_bottom_sheet which essentially improves on the basic showBottomSheet method.

I'm having a hard time figuring out how to set up my nested routes and also am unsure how to navigate to this bottom sheet.

I am using the AutoTabsScaffold and on one of my tabs I'd like to be able to open a bottom sheet and then navigate around within this sheet.

Here's my route setup for the tab in question:

const marketTab = AutoRoute(
  page: EmptyRouterPage,
  name: 'MarketTab',
  children: [
    AutoRoute(path: '', name: 'MarketRoute', page: MarketPage, children: [
      CustomModalRoute(path: 'bottom-sheet', name: "BottomRoute", page: EmptyRouterPage, children: [
        AutoRoute(name: "Coin1Route", path: '', page: CoinBottomSheet1),
        AutoRoute(name: "Coin2Route", path: 'coin2', page: CoinBottomSheet2)
      ])
    ])
  ]
);

Notice I'm using CustomModalRoute which is a CustomRoute and my attempt to create a bottom sheet which I can navigate to.

Here is that class:

import 'package:auto_route/auto_route.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';

class CustomModalRoute<T> extends CustomRoute<T> {
  const CustomModalRoute({
    bool initial = false,
    bool fullscreenDialog = false,
    bool maintainState = true,
    String? name,
    String? path,
    bool fullMatch = false,
    Type page = NoObserverPage,
    List<Type>? guards,
    bool usesPathAsKey = false,
    List<AutoRoute>? children,
    String? barrierLabel,
    Function? transitionsBuilder,
    int? durationInMilliseconds,
    int? reverseDurationInMilliseconds,
    bool opaque = true,
    bool barrierDismissible = false,
  }) : super(
    initial: initial,
    fullscreenDialog: fullscreenDialog,
    maintainState: maintainState,
    name: name,
    path: path,
    fullMatch: fullMatch,
    page: page,
    guards: guards,
    usesPathAsKey: usesPathAsKey,
    children: children,
    customRouteBuilder: ModalBuilder.modal,
    barrierLabel: barrierLabel,
    transitionsBuilder: transitionsBuilder,
    durationInMilliseconds: durationInMilliseconds,
    reverseDurationInMilliseconds: reverseDurationInMilliseconds,
    opaque: opaque,
    barrierDismissible: barrierDismissible,
  );
}

/// FORK from https://pub.dev/packages/modal_bottom_sheet
class _CupertinoBottomSheetContainer extends StatelessWidget {
  final Widget child;

  const _CupertinoBottomSheetContainer({
    Key? key,
    required this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final Radius topRadius = const Radius.circular(12);
    final BoxShadow shadow = const BoxShadow(blurRadius: 10, color: Colors.black12, spreadRadius: 5);
    final topSafeAreaPadding = MediaQuery.of(context).padding.top;
    final topPadding = 10 + topSafeAreaPadding;
    final _backgroundColor = CupertinoTheme.of(context).scaffoldBackgroundColor;

    return Padding(
      padding: EdgeInsets.only(top: topPadding),
      child: ClipRRect(
        borderRadius: BorderRadius.vertical(top: topRadius),
        child: Container(
          decoration: BoxDecoration(color: _backgroundColor, boxShadow: [shadow]),
          width: double.infinity,
          child: MediaQuery.removePadding(
            context: context,
            removeTop: true, //Remove top Safe Area
            child: child,
          ),
        ),
      ),
    );
  }
}

class ModalBuilder<T> {
  static const modal = _modal;

  static Route<T> _modal<T>(BuildContext context, Widget child, CustomPage page) {
    return CupertinoModalBottomSheetRoute(
      builder: (BuildContext context) {
        return _CupertinoBottomSheetContainer(
          child: child,
        );
      },
      settings: page,
      expanded: false,
      modalBarrierColor: Colors.black.withOpacity(0.5),
    );
  }
}

class NoObserverPage extends AutoRouter {
  NoObserverPage({Key? key}) : super(key: key, navigatorObservers: () => [], inheritNavigatorObservers: false);
}

When I try to open the sheet from within this tab by calling context.pushRoute(BottomRoute(children: [Coin1Route()]));

I get Unhandled Exception: [MarketTab Router] Router can not navigate to bottom-sheet/coin1

If I change CustomModalRoute to a regular AutoRoute and try to navigate there by opening a bottom sheet like this:

  showMaterialModalBottomSheet(context: context, builder: (context) {
    return AutoRouter();
  });

I get

MarketTab does not have children
'package:auto_route/src/matcher/route_matcher.dart':
Failed assertion: line 28 pos 12: 'this[key]?.children != null'

So my questions are: How should I be setting up my nested routes and what is the correct way to launch a bottom sheet which displays a nested route?

Thank you!

Milad-Akarie commented 3 years ago

@SpaceWaffles Thank for liking the package,

The following setup should work for you.

const marketTap = AutoRoute(
  name: 'MarketTab',
  page: EmptyRouterPage,
  children: [
    // assuming this is the page that will launch the bottom sheet
    // inside of MarketPage simply call 
    // context.pushRoute(BottomSheetRouter());
    AutoRoute(page: MarketPage, initial: true), 
    CustomRoute(
        name: 'BottomSheetRouter',
        page: EmptyRouterPage,
        customRouteBuilder: modalSheetBuilder,
        children: [
          AutoRoute(page: Coin1Route ,initial: true),
          AutoRoute(page, Coin1Route,)
        ]),
  ],
);

Route<T> modalSheetBuilder<T>(BuildContext context, Widget child, CustomPage<T> page) {
  return ModalBottomSheetRoute(
    settings: page,
    builder: (context) => child,
    expanded: true,
  );
}
pontus-andersson commented 3 years ago

@Milad-Akarie Where do you get the ModalBottomSheetRoute component? It does not seem to be defined in the auto_route library.

Error message:

The function 'ModalBottomSheetRoute' isn't defined.
Milad-Akarie commented 3 years ago

@pontus-andersson It's from the modal bottom sheet package mentioned above

SpaceWaffles commented 3 years ago

This worked, thanks very much for the response!

fabcall commented 3 years ago

@Milad-Akarie sorry for bringing this back, but I tried this approach and although it worked, I couldn't figure out how to make the bottom sheet not expand to full height. This: Navigator.of(context).push(ModalBottomSheetRoute(builder: (context) => Material(child: LoginScreen()), expanded: false)) seems to work correctly, while modalSheetBuilder (with expanded: false) does not.

P.S.: Great lib, btw! <3

SpaceWaffles commented 3 years ago

I'm also seeing this behavior. The bottom sheet refuses to "wrap" the content. Unsure how to fix this.

soeltz7fold commented 2 years ago

just temporary solution for me with the expand, just specify the "containerBuilder"

  1. set the/custom height ModalBottomSheetRoute( settings: page, containerBuilder: (context, animation, child) => SizedBox(height: 500, child: child), builder: (context) => child, expanded: false);

    1. also try wrap the child with wrap widget (tested with column widget) containerBuilder: (context, animation, child) => Wrap(children: [child]),
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

dastein1 commented 2 years ago

Did somebody find a solution that does not involve setting a fixed height?

mrzanirato commented 1 year ago

Hello, I'm trying to replicate whats @SpaceWaffles has done but I'm missing something... Can someone or @Milad-Akarie post a very simple example where:

Thanks! Marco

itsmeabhi12 commented 1 year ago

@pontus-andersson It's from the modal bottom sheet package mentioned above

what if we don't want to use that package? for eg : with showModalBottomSheet , is there any way?

kentnek commented 1 year ago

Did somebody find a solution that does not involve setting a fixed height?

According to this issue, AutoRouter() will always expand to fill its parent. To make it wrap its contents, put it inside an IntrinsicHeight:

child: IntrinsicHeight(child: AutoRouter())
ondanplatform commented 1 year ago

How to push modal bottom sheet as an auto route #1292

did you find any solution?

emri99 commented 7 months ago

If you don't want to use extra dependencies like the modal_bottom_sheet package, check this gist.

Also contains some helpers to get the scroll controller inside the modal content to take profit of DraggableScrollableSheet easily.