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

Wednesday, November 10, 2021

Flutter in Action Chapter 6 "Flutter Animations and Using the Canvas"

Completed taking notes on "Flutter in Action" chapter 6 "Flutter Animations and Using the Canvas".

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

This was unofficially a three part chapter.

The first, main part, was animations.

Flutter has several good web documentation pages for animations:

https://flutter.dev/docs/development/ui/animations

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

First of all, as with many topics, Flutter has a lot of built ins at multiple layers.  Material Design widgets have built in animations by default.  At a slightly deeper layer there's the built in SlideTransition, ScaleTransition, SizeTransition, and FadeTransition if you extend AnimatedWidget.

If you write your own complicated widget animations you can sequence them using TweenSequence and TweenSequenceItem.

In general to create an animation, you need an AnimationController, some kind of tween to change, one of many built in curves (of rate of change of the animation), and a ticker to move it along in real time.  Note you usually don't deal directly with the ticker, its a part of the widget when you extend a State class from StatefulWidget with TickerProviderMixin.  The controller connects the tween to the widget, it returns an Animation object for the widget.

A side diversion in the chapter was the second part, the CustomPaint widget allowing you to draw arbitrary graphics on the screen.  And animate them, as this is the animation chapter.  The general scheme of things in Flutter for custom paints, is you put a CustomPaint widget in the widget hierarchy as usual.  However its passed a Size object which results in a sized "child" which is a Canvas object.  Then you override the CustomPaint's paint and shouldRepaint methods and fill it with canvas object methods like drawRect until your custom image is drawn.  The book doesn't go into much detail about shouldRepaint.  From my own experience, shouldRepaint is a way to advise Flutter if, aside from state changes, the widget needs repainting, in the sense of maybe some part of the render involves random numbers.  Its an "OR" concept where the widget will repaint because of resizing or similar reasons other widgets repaint, "OR" it'll also repaint if the shouldRepaint ever returns a true.  So imagine plotting ten sparkly point dots like stars, you can force a repaint of the sparkly stars if you want if nothing changes in the UI using a timer or something.  I suppose you could make an animation that's similar to a clock or a moving progress bar using shouldRepaint methods in a creative manner.

The third part of the chapter is briefly going over the concept of Listenable objects in Dart and how they async emit new values to the objects that listen to them, like for example as seen in animations.

The book lacks links to the Flutter docs although readers should be able to figure that out by this point; I added some good links above.  Could have used a little more coverage of shouldRepaint, although the book cannot be double.infinity pages long (that double.infinity is supposed to be a Flutter joke...)  There is the problem that Flutter animations in general are a huge topic and none of it makes any sense until all of it makes sense simultaneously.  Which is actually a pretty big problem in Flutter and mobile dev in general.  Also the classic problem of where does the instructor explain async Dart, too early it'll appear abstract and not applicable, too late and so much of Flutter is async that its hard to explain how Flutter works.

Sometimes while reading the book I get the strange feeling that a later edition of the book would have all Flutter and practical stuff on the left hand side pages and all Dart and async and theoretical stuff along the right hand side pages.

Next up, chapter 7, "Flutter Routing in Depth".  This also begins part 3 of the book "State management and asynchronous Dart".

I'm really enjoying this book!

Wednesday, November 3, 2021

Flutter in Action Chapter 5 "User Interaction"

Completed taking notes on "Flutter in Action" chapter 5 "User Interaction".

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

This chapter is named "User Interaction", but its really the user INPUT chapter as it only covers one direction of UI.  The chapter covers gestures, dismissible widgets, forms, and very briefly discusses keys.

Gestures are pretty simple in flutter, and the GestureDetector wraps its children in the widget tree providing around 30 optional callback functions (onLongPress, etc).  The more interesting gestures have an object passed back about the specific gesture (start or end coordinates, duration, etc).  Like everyone else who discovers gestures I have the immediate desire to code something up that somehow uses all possible gestures.  Its a late 2010s thing to go a little overboard with unpredictable gesture behavior in mobile apps, LOL.  Gestures are ... a little overdone in mobile apps, too confusing, no standard, no way to prompt the user to use them...  Anyway Flutter makes them easy to use... easy for the programmer anyway LOL.

Dismissibles are covered in the sense of mentioning their Flutter peculiarities.  They need  a child widget, but also need a background to show while dismissing, and a onDismissed callback that gets told which direction it was swiped away, and it needs a key.  The book glosses over it, but its easy for Flutter to translate a widget tree into a displayable element tree.  But how to push a notification backwards that an item was dismissed from the element to the appropriate widget in the widget tree, in fact how does an individual element know which widget it belongs to in general?  The Flutter-wide solution is passing a key value in the downward direction in some widgets, such as Dismissibles.  The book is a bit UI-centric working from outside in, more coverage from backend-centric working from inside the phone outwards would help understand keys in Flutter.  Maybe later in the book?

Forms occupy the majority of the chapter.  Typical Flutter style of a big Form widget wrapping many items inside it.  Forms "need" a global key of type FormState to use analogous to a controller from other widgets, it gives global access to see valid and save state of the entire form.  The book mentions forms as the only good reason to use global keys, but as per above the book focus is on the outside in rather than inside out.  The book needs more coverage of keys!  Anyway, inside the form, Flutter has three field widgets, one for wrapping any old thing (checkboxes, anything really), and two for special form fields, a TextFormfield with all the special material design features, and a DropdownButtonFormField for those scrollable dropdown lists that look so nice in a UI for postal states or similar.  An InputDecoration object gives form specific styling like those Material Design specific "optional" form tags and similar to a TestFormfield.  Aside from fields, Flutter has a FocusNode so the app can decide custom UI flow between items in a form, if you don't want default top down focus as you fill the form out.  The chapter shows some really useful standards, tricks, and ideas like not autovalidating until the item has been typed into, and grayout "hiding" the save button until validation passes for the entire form (remember the global _formKey.currentState.validate() method?)  Flutter forms can also pop a regret popup using onWillPop ("Are you sure you want to leave and lose all your unsaved data?")

The chapter does a great job of explaining multiple Flutter user input elements and explains interesting real world techniques.  I wish for more coverage of the concept of keys in Flutter.

Next up, chapter 6, "Flutter animations and using the canvas".