Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update URL with showDialog #670

Closed
SSebigo opened this issue Aug 30, 2021 · 10 comments
Closed

Update URL with showDialog #670

SSebigo opened this issue Aug 30, 2021 · 10 comments

Comments

@SSebigo
Copy link

SSebigo commented Aug 30, 2021

Is it possible to show a dialog and update the URL at the same type?

For example: In Android or IOS I would navigate to the login page but on the web, I would like to display a dialog instead and still update the URL.

@nolandg
Copy link

nolandg commented Sep 2, 2021

auto_route seems to break the Flutter built-in dialog...but I'm not sure. showDialog doesn't modify the URL like it's supposed to and pressing the back button doesn't close the dialog, it goes back to the previous page before the dialog was opened. Not good UX.

Eg: this code has no effect on the URL

showDialog(
      context: context,
      barrierDismissible: true,
      useRootNavigator: true,
      builder: (BuildContext context) {
        return SimpleDialog(
          title: Text('Edit Job'),
          children: <Widget>[
            JobUpdateForm(job),
          ],
        );
      },
    );

@Milad-Akarie
Copy link
Owner

@nolandg @SSebigo
sorry for the delay, if you want your dialogs to be routable simply add them as routes.
using a custom route builder we can build a dialog route instead of material or cupertino route.
e.g

// CustomRoute is coming from auto_route
class DialogModalRoute<T> extends CustomRoute<T> {
  const DialogModalRoute({required Type page, String? path})
      : super(
          page: page,
          path: path,
          customRouteBuilder: dialogRouteBuilder,
        );

  // must be static and public
  static Route<T> dialogRouteBuilder<T>(
    BuildContext context,
    Widget child,
    CustomPage<T> page,
  ) {
    // DialogRoute is coming from flutter material
    return DialogRoute<T>(
      context: context,
      settings: page, // must assign page to settings
      builder: (context) => child,
    );
  }
}

Then use your custom dialog route like this

@MaterialAutoRouter(
  replaceInRouteName: 'Page|Dialog,Route',
  routes: <AutoRoute>[
    AutoRoute<String>(path: '/', page: HomePage),
     // our custom route
    DialogModalRoute(page: LoginDialog, path: '/login'),
  ],
)
class $RootRouter {}

now just push it as a regular page route

router.push(LoginRoute()),
// or
router.navigate(LoginRoute()),
//or use .show method to make it feel like you're showing a dialog
LoginRoute().show(context);
auto_route_dialog_demo.mp4

@nolandg
Copy link

nolandg commented Sep 6, 2021

Thanks for the detailed response @Milad-Akarie! I've realized the problem is not with the route of the dialog but the fact that the browser/OS back button is handled very differently from pop. I've tried all the approaches listed here: flutter/samples#2018

And the conclusion so far is that Flutter Navigator 2.0 is incapable of properly responding to the browser/OS back button. I really hope I'm missing something simple. "Are you sure you want to lose all your unsaved work" is, so far, impossible with Flutter :-(

In the video you so awesomely recorded, is there any possible way to click your browser's back button and confirm the route change before navigating back? If I just filled out a long form and accidentally swiped back on my phone, I lose it all. I can't find a way around that.

@SSebigo
Copy link
Author

SSebigo commented Sep 6, 2021

@Milad-Akarie Thanks for the sample but now the /login path is used for LoginDialog, right?

How do you do to distinguish the LoginDialog for web and the LoginPage for mobile inside the router since as I said previously the dialog would be used only for the web version and the mobile version would be a full on page navigation.

@Milad-Akarie
Copy link
Owner

@nolandg indeed the native pop button and the browser's back button behave differently, the native pop button simply calls mayPop on the navigator state, the browser's back button pushes a new path to the router delegate which is the entry before last in the browser's history, auto_route tries to update existing routes instead of pushing new ones that's what makes it feel like you're popping the last page.

unfortunately there's no way to handle ( BeforeExist ) operation in the time being but now I'm intrigued to find one.

@Milad-Akarie
Copy link
Owner

@SSebigo you can have your custom route builder return a different route type based on current platform

import 'package:flutter/foundation.dart' show kIsWeb;

  if (kIsWeb) {
      return DialogRoute<T>(
        context: context,
        settings: page,
        builder: (context) => child,
      );
    } else {
      return MaterialPageRoute<T>(
        settings: page,
        builder: (context) => child,
      );
    }

@SSebigo
Copy link
Author

SSebigo commented Sep 6, 2021

I'm an idiot... @Milad-Akarie thank you so much

@SSebigo SSebigo closed this as completed Sep 6, 2021
@nolandg
Copy link

nolandg commented Sep 6, 2021

@Milad-Akarie Thanks for being intrigued! If you find a solution you'll have beat all the Fiverr "experts" we hired to solve this problem.

Also, I posted a bounty for this on Stackoverflow: https://stackoverflow.com/questions/67325662/web-control-browser-back-button that you could claim.

And I just bought you 5 coffees :-)

@moshOntong-IT
Copy link

Can I ask that is this

@nolandg @SSebigo sorry for the delay, if you want your dialogs to be routable simply add them as routes. using a custom route builder we can build a dialog route instead of material or cupertino route. e.g

// CustomRoute is coming from auto_route
class DialogModalRoute<T> extends CustomRoute<T> {
  const DialogModalRoute({required Type page, String? path})
      : super(
          page: page,
          path: path,
          customRouteBuilder: dialogRouteBuilder,
        );

  // must be static and public
  static Route<T> dialogRouteBuilder<T>(
    BuildContext context,
    Widget child,
    CustomPage<T> page,
  ) {
    // DialogRoute is coming from flutter material
    return DialogRoute<T>(
      context: context,
      settings: page, // must assign page to settings
      builder: (context) => child,
    );
  }
}

Then use your custom dialog route like this

@MaterialAutoRouter(
  replaceInRouteName: 'Page|Dialog,Route',
  routes: <AutoRoute>[
    AutoRoute<String>(path: '/', page: HomePage),
     // our custom route
    DialogModalRoute(page: LoginDialog, path: '/login'),
  ],
)
class $RootRouter {}

now just push it as a regular page route

router.push(LoginRoute()),
// or
router.navigate(LoginRoute()),
//or use .show method to make it feel like you're showing a dialog
LoginRoute().show(context);

auto_route_dialog_demo.mp4

It is 2023, and I think the current version has Breaking changes like @MaterialAutoRouter is not exist anymore. So I want to clarify if this code is still the solution on how to build a dialog?

@moshOntong-IT
Copy link

@Milad-Akarie

        CustomRoute(
          fullscreenDialog: false,
          page: RoleFormRoute.page,
          path: '/roles/add',
          customRouteBuilder:
              <T>(BuildContext context, Widget child, AutoRoutePage<T> page) {
            return MaterialPageRoute<T>(
              settings: page,
              builder: (context) =>
                  SizedBox(height: 300, width: 300, child: child),
            );
          },
        ),
        ```
        
        
        seems not working

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants