tomgilder / routemaster

Easy-to-use Navigator 2.0 router for web, mobile and desktop. URL-based routing, simple navigation of tabs and nested routes.
https://pub.dev/packages/routemaster
MIT License
327 stars 61 forks source link

Questions: Use cases and questions when trying Routemaster with a custom adaptive and responsive scaffold #88

Open rydmike opened 3 years ago

rydmike commented 3 years ago

Hi @tomgilder, I'm really enjoying exploring Routemaster. So impressed by it!

While trying to use Routemaster on the demo application for my still unpublished and not yet fully ready Flexfold adaptive and responsive scaffold package (info here https://rydmike.com/) and live older version of the the demo app here https://rydmike.com/demoflexfold.

I ran into some use case issues or maybe more like questions. However, in order to present them I first need to explain a bit what Flexfold is and how it works.

Flexfold info

Flexfold works a lot like the Navigation Rail in Flutter SDK, but it handles also bottom navigation bar, drawer, rail and side menu, plus a sidebar. And it has a lot of design features and behavior parameters. Also when using Flexfold on variable size canvas, the transition between the layouts for the different navigation types are animated, it just looks cooler than a sudden jump to the new responsive layout view.

The setup of navigation destinations is similar to the Navigation Rail, you just define a bunch of destinations, with labels, icons, tooltips and also path based named routes if you like. Then you give this as config data to Flexfold. When you click on destinations in the Flexfold scaffold it just returns via a callback what the destination was. You handle the actual routing yourself, so it is totally agnostic to what kind of routing you use, as it should be.

Flexfold Destinations

With Flexfold you just define destinations very much like for the navigation rail, but it has a few more options. For the the live demo app it looks like this:

image

This will when used with the the Flexfold scaffold result in scaffold that looks like below in the main different responsive use cases:

Flexfold phone size view

flexfold-phone3

Flexfold tab size view

flexfold-tablet1

Flexfold desktop size view with side bar

flexfold-desktop1

Flexfold destination info.

In the demo app you can see the type of info it returns based on what mode was used to go to a new destination: image

You get an overall menu index, bottom index and the named path route you defined for the destinations. Plus an enum indicating what was used: bottom, drawer, rail, menu, plus direction reverse: false/true (moving to lower or higher index), both these are intended to be used as info for a way to vary the transition based on where user clicked to go to the destination. The reverse info can be used to have a slight left/right type of slide on bottom bar or up/down on rail/menu, if so desired.

image

What you do and how you navigate based on the 'onDestination' info is up to whatever used navigation implementation.

Challenges with Routemaster and Flexfold demo app

I had on purpose left out all fancy Web based URL navigation from the Flexfold web demo. Simply because I had no simple and quick way of doing it. I now wanted to try and see if I can solve it it with your wonderful Routemaster package.

Mostly I think I can, and if I can't it is perhaps just because I'm not yet 100% familiar with all its possibilities. It certainly has much more features than I need when it comes to web routing, at least in this simple demo. I might even add some more features to the demo later to show off some more capabilities that your router solves beautifully.

Here are some of the challenges and question I have stumbled on so far:

A) General State Storing Navigator - partially solved - but web URL entry does not navigate

In the demo (and another more real use case too) I want to store and keep the state of all the destinations that are available as direct "top" destinations in the Flexfold scaffold.

The CupertinoTabScaffold, CupertinoTabBar, CupertinoTabPage and the StackNavigator setup, like you do in the mobile_app demo is a great way of doing this. But of course we cannot use a CupertinoTabScaffold in this use case, and we also have no CupertinoTabBar, (even if one might be baked into the Flexfold scaffold, if that is used as bottom UI widget), when using the Flexfold scaffold.

For the Flexfold demo app, using the above shown destinations, I made this simple test setup to try routemaster:

image

and for buildRouteMap this worked well enough for the simple demo app:

image

As an experiment I used CupertinoTabPage as my root route. The child LayoutShellWrapper is a Widget the has the Flexfold scaffold in it, so no CupertinoTabScaffold. For the body it just uses a variant of what the CupertinoTabScaffold uses internally to keep the pages in memory off stage when so needed. For this I just copied the implementation detail, namely the _TabSwitchingView and made a PageSwitchingView version of it. It is identical to it, only public and some props renamed a bit.

With it, in the Flexfold scaffold's build I can still do:

image

In its onDestination we can just set:

image

The Flexfold has a body property like a Material scaffold, so we can then do this in its body:

image

This all actually works beautifully and state is kept when navigating between all "top" destinations in the Flexfold scaffold and they all have their own stack as well.

I noticed that in the onDestination I can instead of setting the controller index, also navigate to the page with the returned path by doing a:

Routemaster.of(context).push(destination.route);

This is nice because it means it will easily even support my "modules" need for Flexfold destinations.

This is just a simple idea where destinations could also have module ID and you can swap out shown destinations in Flexfold by selecting the module. The modules can of course share some routes, like home, help, about etc... if so desired.

This just means that this setup could be used to always navigate to the right page based on the returned path from the destination and just ignore the indexes. The indexes are just used to hold and store all the pages in the for the keeping state when navigating to different destinations even between modules. This consumes more ram for sure, but for my particular use case it is what is needed on desktop and web. No idea how well it will perform yet. If it becomes to memory hungry I can also go back to the idea that only navigation within a module keeps state. The app implementation would just keep which index was last used in each module, so you are at least back on same page where you where when switched to another module. But if it performs OK while keeping state of all pages, that is even nicer UX wise.

This all seemed to work very well, but...

Except, this setup seem like it has no hooks into handling the URL entry from the browser (or back button). The browser URL updates when navigating from the UI fine, but because I'm not using a CupertinoTabScaffold I am of course not getting all the nice browser URL entry based navigation you baked into routemaster when using CupertinoTabScaffold. I have not yet figured out how I can get it to also update it based on browser URL entry when using routemaster and my PageSwitchingView.

It now keeps the state of top destinations, like I want it to, and also the tabs. The tab sub views on the tabs destination is just a normal routemaster tab page view, the sub pages for the tabs page are not part of the Flexfold destination, only the tab page is, so for that those pages the URL entry based update of the tabs, and browser back, even works OK.

Here is a slowish debug build (with low GIF framerate o make it small so it fits on GH) showing the Flexfold demo using routemaster with the URL updating OK when navigating, with state of all main destinations kept, and URL based navigation entry even working on the tabs page too.

flexfold-routemaster4

However on the main destinations the URL entry does not work, since there is no built-in support for it. I wonder if there is some hook I could tie into in order to add it? Or if this is a case where support for something like the PageSwitchingView() in routemaster is needed?

Since I am using my custom PageSwitchingView() to store the page states like the CupertinoTabScaffold does internally. I don't actually need the root to be a CupertinoTabPage I can just as well use an IndexedPage, but it did not help with the URL parsing. The mobile_app examples uses the Cupertino thing again for the IndexedPage, so there was no guidance there. Perhaps there is something I'm missing in the setup, hmm...

B) A Tab or Cupertino Page content as a full page pushed on top of root.

The other case I'm wondering about. How I can when the onDestination returned destination has destination.useModal set to true, how can I then instead of using the PageSwitchingView view in the body, instead push it as new screen on top of the entire LayoutShellWrapper while using (showing) the same route path as it has in the wrapper view? A need to wrap the content in a Scaffold with an AppBar might be there, although in the live demo in its nested nav1 I could actually even avoid that. With nav 1, I just used another navigator, well the root one and not the one nested into the body of Flexfold, and pushed it on top of the stack with that covering the Flexfold wrapper and its "body" nested navigator.

EDIT (28.5.2021): Tried a bunch of things, but nothing really produces the correct result.

I feel to do this I must have access to a lower root navigator, than the first IndexedPage tied to root "/" route with its PageStackNavigator to be able to push a page on top of it. Also when I trigger that one, I need to prevent the navigator tied to the root "/" from navigating, ie showing the same page inside the scaffold that it normally wants to do in another layout scenario, instead in this scenario I want to push the page on top of everything and the page stack below must remain where it is, so when the page pushed on top of it is popped, it remains where it was. Hmm... I will experiment more I guess. Feel a bit stuck though.

C) Transition selection

Based on the examples and docs, it does seems like I should be able to grab the Flexfold destination.source (drawer, bottom, rail, menu) and pass it along to a page transition builder, so that when navigation happens I can switch there for the different desired transition styles, just like I'm doing in the live version of demo app with navigator 1 already.

EDIT (28.5.2021): I tried two different setups with the config described in A) and B) above, but I cannot get any page transitions with it regardless what I try.

Maybe it is custom PageSwitchingView that kills them, it contains some no off stages transition thing. If that is it, I should see the same issue with CupertinoTabScaffold that uses it as well, I will check that out.


Sorry this was a super long questions posts, but I like to put things in context so the use case and scenarios can be properly understood. Not sure I managed to do so, I tend to be so darn verbose, hehe πŸ˜„

So far I think routemaster rocks πŸ’ͺ🏻 πŸš€ , and if I can get the above cases to work, I have no reason to build anything from scratch with Nav2 for any use case.

rydmike commented 3 years ago

Hi @tomgilder I edited the above long Flexfold demo based case study, that contains all the use cases I have been unable to solve with Routemaster.

The issues are still the same as day 1 when I first tried to use Routemaster for my custom Flexfold scaffold (which is based on SDK Scaffold) and get it to navigate as desired. Perhaps I'm trying to get Routmaster to do things it cannot or perhaps I'm going about it the wrong way.

The explanations above are a bit long, not difficult, just verbose. Here is another way to describe it all, based on a fictive setup with Flutter SDK widgets.

If the app is on a tablet, web or desktop and wide enough, then we get into rail/menu territory.

If we are changing to this view via a responsive layout change on web/desktop, the state of all pages from phone sized view are kept, and I mean all, nested stacked ones and whatever. The view changes with animation, bottom navbar slides away (down), rail or side menu slides in from left or right (if RTL). Pages keep their state, the shown current one, but also all the other destinations that we might have visited already via the bottom navigation bar.

That pages keep the state when transitioning between the responsive views is a requirement. So we must keep them all in a PageSwitchingView like construct, that I borrowed from the CupertinoTabScaffold. But we must show pages from its index based view storage, with different transitions and even different Navigators, depending on what responsive layout view we are in.

I have a semi working setup with Nav1 using nested navs for al this, but I need Nav2 for the URL crap, URL entry navigation, back button(s), deep links, URL query parameters and all this web crap as well, hehe.

I have not really tried getting into trying to build it from scratch using Nav2, I have been hoping that some package like yours would come along and spare me from the trouble.

Almost there, but not quite... πŸ˜ƒ

Like I said, perhaps I'm going about it the wrong way when trying to use Routemaster for it, or maybe it is not possible to use it for it, not sure yet.

I should probably setup a "simple" (well it cannot really be super simple, but simpler than Flexfold anyway) example based on the above description using SDK widgets, because, if that example can be solved, then so can my Flexfold setup.