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 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.
- 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.