Thursday, November 18, 2021

Flutter in Action Chapter 7 "Flutter Routing in Depth"

Completed taking notes on "Flutter in Action" chapter 7 "Flutter Routing in Depth".

https://www.manning.com/books/flutter-in-action

Routing is a large collection of several small simple problems.

A section of the chapter is missing, in the sense that routing needs a history to make sense of what app routing is, where it is now, and where its going.

More than a quarter century ago, routing was done online via anchor href html entities.  This made sense for web pages that were online information or processes performed online, such as reading online books or order entry for selling physical books online.  Even from the beginning, browsers had forward and backward buttons to navigate the historical path, handy for references and process steps.  Admittedly forward and backward navigation at a page level doesn't make any sense for online games or online applications such as a "KVM in a web browser tab".  However, it mostly worked well enough.

Around twenty or so years ago devs started to apply Apache web server regular expression rewrite rules to very creatively convert URLs from /something/somenumber to a CGI parameter expression going to a particular cgi script, probably a Perl script.  I'd argue this was the origin of the split between front end and back end web development.  Back end devs were told to make sure something is there at a particular URL regardless if static file or dynamically generated or cache the last 500 items accessed or whatever based on their systems administration judgment.  Meanwhile the front end devs were told to simply render the catalog detail image at a certain URL and not worry about where that file comes from.  This front end vs back end developer split has continued in some form to the present day.

Maybe fifteen years ago with the rise of enormous and elaborate frameworks, if people are no longer running a generic webserver in front of CGI scripts but are running the entire thing in, perhaps, Ruby on Rails on port 8080 or a Scala Play framework app on port 3000, the framework has some kind of routes file with lines similar to this (This example from Play framework docs):

GET   /clients/:id          controllers.Clients.show(id: Long)

Fairly intuitive that route means a HTML "get" command against a URL with /clients prefix and a numeric ID, should pass the client id number to a controller named Clients and its method named show, and the parameter of the method is the previously mentioned id number seen in the URL, and BTW for type safety its a long integer.

Of course the problem with "static routing" like that is someone invariably changes the code while forgetting to change the project-wide routes file or vice versa.

That lead to the idea of dynamic routing.  Which has nothing to do with networking or BGP or OSPF LSA types.  Using something like React, the source code has one routing entry and the routing table is built dynamically as the code runs.  One example of an interesting thing you can do with dynamic route generation, is make the entire app route completely differently on large vs small screens, or portrait vs landscape, perhaps one pane for small portrait screens and two pane design for large landscape screens.

Anyway around the time static vs dynamic routing was a hot debate topic, the first smart phones were becoming popular and originally iPhones and similar had the idea of only rendering web pages as 'apps' as opposed to native apps running on the phone as most apps are today.  And there has always been a lot of crossover between web frontend devs and mobile app devs, so the debate about static or dynamic routing naturally flowed over in to mobile app development.

https://material.io/design/navigation/understanding-navigation.html

Eventually, Google released the Material Design system which has a small part that defines some navigational concepts.  Lateral navigation is like a main menu to select processes, whereas forward and reverse navigation move thru those processes.  Where it gets tricky is if the user hopped between processes, perhaps from a search page to a detail page, what is the reverse navigation, the chronological search page or the data oriented list of items that was never accessed before but makes sense in logical hierarchy?  Material Design does not have a strong opinion but does imply the chronological reverse navigation only remembers to the immediate previous hop, depending on platform and app.

There are interesting process flow and real world interaction problems with reverse navigation.  If you see your bank balance, and transfer from one account to another account, then reverse button back to the balance, should you be able to see the old pre-transfer balance page?  Probably not, although it is literally the previously viewed page.  There are ways to modify your back button history in apps, but those are impossible to use online because of security concerns and spammers who would flood browser histories with advertisements people would have to click thru.

This brings us to Flutter which had/has two navigation systems, Flutter 1.0 with navigation 1.0 that is static imperative routing, and Flutter 2.0 with navigation 2.0 that allows both older imperative but additionally declarative / dynamic routing.

So in Flutter Navigation class 1.0, you Navigator.push to a newer page and Navigator.pop to return to an older page.  The pages are just like the main() classes you've always used on single page apps, just differently named.  Navigator also needs to know BuildContext but no point getting bogged down in details.  

Nav 1.0 also had named static routes, so you can change pages using string names instead of passing around page classes.  So you'd define a map in MaterialApp named routes and it would map a string name "otherPage" to a class.  Then anywhere in your code you could pass the string "otherPage" to Navigator.pushReplacementNamed and it would just work.   Cool.  The book covers named routes in section 7.2.1 although it immediately jumps into a best practice of passing around a class full of strings instead of passing around raw string values; sort of like an enum for strings; its harder to have a typo this way, or at least easier for the IDE to find typos.  Unfortunately sect 7.2.2 immediately explains how to use the named routes using an example of raw strings rather than the helper class, so that's confusing.

Its probably worth checking out the Flutter cookbook for navigation:

https://docs.flutter.dev/cookbook/navigation

At this point, the basics are covered above and I'd suggest returning to the book chapter for deeper coverage of the more advanced topics.  With this background it will make more sense what is going on with routing currently in Flutter, and where its likely to go in the future.

I have to note again that as typical for the entire book, a Navigator.pushNamed returns a Future so you need to "await" it.  Still not settled on if using async thru the entire book before defining it at the end is a good or bad idea; certainly by the time the official chapter is read the readers will have a pretty good intuitive idea how to actually use async.

I liked the clear explanation and walk thru of setting up a NavigatorObserver and RouteObserver to highlight the currently displayed menu page.

Most of the chapter is the little extra details of routing; strategies for handling string names of routes, suggestions for composing a large menu out of little easy to use widgets, or how to wrap a page with animation such that it just works.

The book is 342 pages long, so its tradition to mention what I'd add if it was 600 pages long.  I'd like to see coverage of fluro the wildcard router package and voyager the yaml based custom plugin code generator framework for routing.  I believe its generally possible to predict future development in mainline Flutter by looking at the more interesting libraries so I would not be surprised if some future Nav3.0 for Flutter looks similar to voyager, or maybe fluro.

Next up, chapter 8, "Flutter State Management".

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.