routemaster
routemaster copied to clipboard
Questions: Use cases and questions when trying Routemaster with a custom adaptive and responsive scaffold
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:
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
- Destinations defined to be in the bottom bar are shown in a bottom navigation bar at phone size.
- Bottom bar is either Material or Cupertino (experimental, now also MaterialYou). It can even be defined to be platform adaptive. The one used above is Material, but it has style parameters set that makes it mimic the nice frosted glass look of the Cupertino one, just an extra feature that can be used if so desired.
- The bottom bar can hide when scrolling, does not have to, but often useful.
- Destinations that were not defined to be in the bottom bar, show up in the drawer. The drawer can also accept a few custom top, bottom and footer widgets if so desired.
- In the demo when you navigate, the transition is different when using bottom nav, than compared to tablet rail mode, or desktop size mode. This is just an implementation in the the demo app, it uses return info from Flexfold destination callback to determine what responsive state was navigated from and the used navigator uses a different transition based on that info. The demo app uses slight slide action in bottom nav bar mode. I need to pass this info somehow to routemaster and setup custom transitions there too based on where the navigation was started from.
- The destinations relegated to the drawer in the phone view are pushed on top when navigated to from the drawer as a new screen and have a back button. It does not have to do so, the home option in the demo actually does not do so. If you use a destination that does not push the screen on top, but is not a member of the bottom bar destinations, then if you open the drawer, the bottom destinations will be present in the drawer as well. If you open the drawer when a bottom destination is selected, the bottom destinations are not included in the drawer. It is also possible to define a behavior where bottom destinations are always present in the drawer as well, but then you have two ways to navigate to same destination, some might find that confusing UX, so it is not the default, even if I like it.
- The live demo keeps the scroll state of the tab bar page destination when you are on that page, but it is not kept for the bottom ones in the demo, but I will add that as well in the next update to the live demo.
Flexfold tab size view
- In the tablet size view, all the destinations are shown in a navigation rail style UI (It is not using the SDK rail though).
- The labels are used as tooltips here. In the bottom navbar case when labels are shown, they are not duplicated as tooltips.
- You can also add tooltips that are different from the labels, if so they are always shown. Unless Flexfold flag to totally disable tooltips is set.
- If the Flexfold settings has been configured to allow it, the user can toggle between rail/bottom bar view in tablet size (shown in the gif). You can also set it up so that using a drawer is toggleable as well, this not shown in the above GIF. The use case is of course that if you on a larger canvas want to really maximize its size for the shown content, the user can do so.
- The navbar can show all destinations that we defined in the setup above. The destinations that in bottom nav bar mode were in the drawer and pushed on top, are now instead always shown in the body content area, not pushed on top as new screen with back button.
- The transition is now different than when we navigated from bottom bar, it uses a slight zoom fade through in this demo. This is again just implemented in the demo app's current navigator based on where Flexfold told us it we navigated from.
- In the demo app we also want the transition to ONLY affect the content area. On a large canvas it looks distracting if the entire page uses a transition, but if only the content transitions, it is quite OK. So the rail, and app bar remain fixed, only the content is transitioned when navigating. The page we navigate to is actually just the body part, and the navigator is nested in Flexfold scaffold's body part. This is actually also the case for the bottom nav bar mode in the phone view, if you look closely you can notice it there too, only the content transitions there as well, but the bottom navbar an app bar stay in place, as I want it.
Flexfold desktop size view with side bar
- This view is pretty equivalent to the tablet view with the following additions.
- Destinations can, but does not have to, have a sidebar.
- Destinations are shown as a side menu with labels.
- If allowed in Flexfold settings, the user can still toggle the menu from menu size, to rail, and even to drawer. And also close the side bar and hide it as an end-drawer.
- In this case when navigating in desktop mode, there is no transition at all. This is just a design choice in the demo app, I might ad some minor subtle very quick fade through later to the demo, just to show you can make decent transitions for larger surfaces too. Again the transition is a part of the demo app and its current routing, where the router gets as info what Flexfold mode was used to select the new destination.
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:
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.
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:
- General state storing navigator - partially solved - but web URL entry does not navigate.
- A Tab or Cupertino Page content as a full page pushed on top of root "/" route.
- Transition selection.
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:
and for buildRouteMap
this worked well enough for the simple demo app:
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:
In its onDestination
we can just set:
The Flexfold
has a body
property like a Material scaffold, so we can then do this in its body:
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.
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 theCupertinoTabScaffold
does internally. I don't actually need the root to be aCupertinoTabPage
I can just as well use anIndexedPage
, 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.
- Tried a custom Page extending Page that gets a parameter with the info of what transition it should do, and has different transitions based on it. But, I get no transitions at all.
- Tried another approach, using a PageTransitionSwitcher from the Flutter Animations package, this baked into the PageSwitchingView, that has PageTranstionSwitcher for the StackNavigator for the root "/" route, but nope no transitions with that either. Page switches worked though, just no transitions with that setup either.
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.
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.
- Standard Material
Scaffold
used as a "home" page for root "/". - Its body contains the main Navigator, but there is lower hidden root too.
- Destinations similar to
NavigatioRail
are used to describe top level routes, they also have "/page" style named routes. - On a phone, the main top level destinations are in a
BottomNavigationBar
, but some that do not fit (max 5 can fit) are defined to not be shown in the bottom navigation bar, they will only show up in a Drawer (without bottom destination) or Rail/Sidemenu that also contains the bottom destinations. - There is no controller for the selected bottom nav bar destination (BottomNavigationBar and NavigationRail do not have a controller, like eg TabView or CupertinoTabBar), nor is there one for the Drawer destinations. They all just use a current index and callback for what was clicked. It is up to the app and its state management to keep track on where you are, indexes and current named route.
- When you navigate with bottom nav bar, the routes (page) widgets only contain the body content for the Scaffold body. Only the body is rebuilt and transitions. The BottomNavBar and even the AppBar is NOT a part of page transition, but sure their content change via the current page state update, just no transition on them.
- The bottom nav bar transition can be a custom one, fade through or slight left/right slide+fade effect depending on lower/higher index selected.
- When you select destinations that did not fit in the bottom nav bar, ie those in the Drawer in this view, they get pushed on top of the entire "root" Scaffold and its Navigator in its body. The real "root" navigator is used for this. When such a route is popped, the Scaffold remains where it was, as it is under screen that was pushed on top of it. This transition typically uses device platform transition for pushing a new page on top, and for popping it away, as it is the most appropriate and expected one.
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.
- The rail or full width menu, now contains ALL the top level destinations. The ones that were in bottom nav bar, and also the ones that did not fit and were in a Drawer before. They all fit in the Rail or Side menu, they both scroll if needed.
- The ones that were in the Drawer, now when they are in the Rail or Side menu and you navigate to them, now also show their content in the Scaffold body part, instead of pushing it on top of the root Scaffold, which they did from the Drawer in phone view.
- Only body part has page transition, and even different ones if so desired if navigating in Rail or Side menu mode.
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.