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

Saturday, October 23, 2021

Upgraded three servers from Devuan Beowulf OS version to Devuan Chimaera OS version

Upgraded three servers from Devuan Beowulf version to Devuan Chimaera version today.

A gold OS template for deployment, and two DNS servers.

The procedure to turn a gold OS image into a DNS server is handled entirely by some Ansible scripts I wrote; I used to use Puppet a long time ago, but tired of restarting Puppet agents to resolve misconfigured systems, and I prefer the "push" configuration of Ansible over "pull" technique of Puppet.  I can deploy a template in VMware, change its name and IP address, reboot it, connect it to the SSH web of trust and the Active Directory web of trust, run Ansible against it, and it turns into a fully featured DNS server in a couple minutes.  I could have downed the old version servers and brought up new, but the upgrade process was so flawless and fast on the template that I upgraded the pair of DNS servers instead of making new and reload; it only took minutes either way and I was interested to see what happens (given that nearly instant rollback is possible with VMware, and I'm alone on a Saturday morning, its not like there's any risk LOL)

First, in VMware vSphere, my back out plan, in case the upgrade went poorly, was to shut down the images, make a duplicate, and upgrade the dupes, and keep the untouched originals around in case something went wrong.  I've seen performance problem due to forgetting VMware snapshots were left up; less headache and "shut down the new one and start up the old one" is faster than VMware snapshot rollbacks, and I only use FOSS software on these servers, so there are no licensing issues like windows would require.  I can leave untouched images running and connect/disconnect image network interfaces in mere seconds...

These are resolution DNS servers not authoritative DNS servers, so a simpler plan is a better plan.  If they were authoritative I'd spin up new servers and test using 'dig' that they work properly.  But I'm the only person using these resolution servers on a Saturday morning, so its pretty safe.  The simplest plan that gets the job done is the plan most likely to be successful.  I would have to allocate two more routable IP addresses to run both test and production images simultaneously; its not really worth it to log into NetBox and justify the allocation.

After the VMware work, I upgraded the Devuan Beowulf packages to the latest/last versions.  The usual "apt-get update" "apt-get dist-upgrade" "apt-get autoremove" finally "apt-get clean" then run the ansible-playbook for each server against it, and test everything works.  Not much happened in the upgrades (I generally maintain each server every two months)

I do not store or configure major version configurations like Apt 'list' files in Ansible as the chance of a "surprise upgrade" is not worth the risk.  I only had a couple servers to upgrade so I removed the old /etc/apt/sources.list.d/beowulf.list file and set up a new /etc/apt/sources.list.d/chimaera.list file along the lines of the Devuan suggested file. 

The "apt-get upgrade" and "apt-get dist-upgrade" as per the Devuan suggested upgrade path was completely uneventful.  I have apt-listchanges configured to send changelogs and news to me via email.  I will save those emails for later reference in case any problem develops, but usually those upgrade logs end up deleted after a couple months.

According to those upgrade emails, recently Exim, the main transport agent, has undergone a substantial major upgrade possibly requiring configuration changes, and Gnupg now no longer uses ~/.gnupg/options file in favor of ~/.gnupg/gpg.conf.  For me, everything is fine, others may find those changes more relevant.

I ran "apt-get autoremove" and "apt-get clean" to clean up the upgrade.  Interesting to see Devuan no longer uses Python version 2 (although it is installable) so I had to update my Ansible configuration system inventory to specify Devuan based operating systems have a python path of "ansible_python_interpreter=/usr/bin/python3" instead of "ansible_python_interpreter=/usr/bin/python" for legacy python2.  I keep my Ansible scripts in a Git repository so I committed documented and uploaded my small change.

I ran my Ansible configuration script on the DNS server.  Aside from the previously mentioned upgrade from python2 to python3, it was uneventful.

I did a server reboot (technically un-necessary) to verify everything starts up correctly after a reboot, which it did.

I verified everything working on the DNS servers (note, I have a cluster and did one server at a time).  They both do forward and reverse for ipv4 and ipv6, and also forward a subdomain to an Active Directory domain controller cluster I also maintain, that's based on Samba, and it all works quite well.

I cleared any alerts in Zabbix, a LAN server monitoring system.  I run Zabbix using Docker images; it works well and alerts me to any server failures (such as reboots).  I could set a maintenance interval in Zabbix to silence alerting, but I believe it counterproductive; if the software upgrade fails and DNS queries no longer resolve, I want to know immediately rather than at the end of a scheduled maintenance interval...  Zabbix caught the server reboot, and also automatically opened a problem ticket "Operating system description has changed".  I acknowledged and closed that automatically opened problem ticket.

After the servers ran for an hour I checked the Zabbix performance graphs and there's no substantial change in performance.  Much less granular VMware monitoring more or less matched what I saw in Zabbix.  Always worrisome if CPU use or disk space go wildly higher OR lower after an upgrade.  Everything seems to be working normally.

Finally I updated the three runbooks I maintain in Todoist, an online web and mobile app for to do lists.  I set the next date to check up on the servers for two months from now, as usual for these servers, documented the upgrade in the server log, let the users know I'm done and how to reach me if necessary, etc.

In the future I will clean up and remove the old stuff in VMware, assuming the new DNS servers work fine and there's no reason to roll back.  Nice to know I can rollback almost instantly although typically there's no need.

Hilariously the only problem I had with the entire major version upgrade was the spelling of Chimera has apparently changed since my Dungeons and Dragons days, and is now spelled Chimaera.

My primary reference for the project was:

https://www.devuan.org/os/documentation/install-guides/chimaera/upgrade-to-chimaera

And that, in summary, is how to spend about two hours painlessly upgrading three Devuan servers to the latest version.

Friday, October 22, 2021

Flutter in Action Chapter 4 "Flutter UI"

Completed taking notes on "Flutter in Action" chapter 4 "Flutter UI".

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

This chapter of the book quickly reviewed important Flutter UI widgets.

SystemChrome the native device controller was mentioned, along with the widgets WidgetsApp, MaterialApp, Scaffold, and some material design widgets like AppBar.

Other important widgets mentioned in the chapter were Stack (the foreground/background alpha blender), Table, TabBar, and ListView.

The complete Flutter theme process was explained.  Basically you pass a ThemeData object to MaterialApp.theme, although there are interesting details.  MediaQuery can read info such as absolute pixel heights and widths.

The explanation of how to use additional fonts in Flutter using the pubspec.yaml file to attach the asset and then apply in a theme, was very clear and easy to follow and implement.

Dart does named imports where you can set a custom (probably short) prefix to everything imported in a package.

Its hard to cover some of these UI widgets without discussing Async Dart techniques.  I'm not sure why async is covered in a later chapter; that topic should be something like chapter 2.5 not chapter 9.  Perhaps the author was trying to avoid too much Dart and not enough Flutter, in the first half of the book.

I'm a fan of the subject depth of this chapter.  Too much detail is wasted paper, simply check out the online Flutter Widget Catalog for infinite detail, but not enough detail and the readers won't know what is generally possible or what the overall outline of the system looks like.  I think the tightrope was walked nearly perfectly without tipping too far in either direction.

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

Next up, chapter 5, "User Interaction".

Tuesday, October 12, 2021

Flutter in Action Chapter 3 "Breaking into Flutter".

Completed taking notes on "Flutter in Action" chapter 3 "Breaking into Flutter".

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

Flutter is big; the minimum set of knowledge required to play along is very large so an intro that tosses you into Flutter is going to cover a lot of topics at levels from very high to very low that all have to be mastered, or at least somewhat understood, simultaneously.

To give a historical comparison, the entire K+R C language book that I learned C from in the 80s is smaller than this introduction chapter, and it followed a linear education path rather than requiring everything to be understood at once simultaneously.

So in one sense this chapter is scattered because one page is discussing the project design philosophy of preferring OO composition over OO inheritance, and the next is a deep dive into the concept of the widget tree and the next is a discussion of stateful vs stateless widgets and the next is an "in the weeds" detail about how to embed images into your apps in the pubspec.yaml file, then back to the theory of how the widget tree works with the element tree.  And much much more.

There are some rough spots not covered in the book but easy enough to figure out.  Flutter seems to be a language that's unwritable without an advanced IDE like Android Studio.  This is NOT Python or C.  And there are minor inconsistencies in the IDE that you're going to have to learn and get used to the hard way.  For example the Flutter Inspector is handy for debugging graphical layouts, but it only scans the source code and widget tree when you click the refresh icon inside the Flutter Inspector; it would be logical if it rescanned when you "C-s" hot reload, but it does not.  Meanwhile the Flutter Outline code navigator, which is a logical view of the widget tree, more or less, auto reloads live as you type in the editor, no need to hit "C-s" to hot reload.  Another minor irritation is as of the date of this post in late 2021, Flutter and the IDE deprecate using the older RaisedButton so on the fly you get to modify your code to use ElevatedButton.  Which is good workplace experience LOL but it shows how rapidly things develop in mobile app world when a book less than a year old is technically somewhat out of date.

Overall the chapter does an excellent job of tossing the future Flutter developer in the deep end to learn to swim.  I worked thru the example IRL as I read the chapter and everything eventually worked.

Next up, chapter 4, "Flutter UI".

Friday, August 20, 2021

My Flutter development environment

I experimented with Visual Studio Code editor with the Dart and Flutter plugins and it works fine, but I settled on the Android Studio experience because I liked how tightly integrated it is with respect to the editor, emulator, flutter inspector, and flutter performance tabs.  Also I was comfortable with Android Studio from past experience doing native Java development on Android.  I made some notes:


Daily Use (about two minutes from startup to running the demo counter app in the emulator):

"start" "android studio"

"tools" "avd manager" start my preconfigured emu (I used Android 10.0 aka API 29)

"file" "new" "new flutter project" it'll ask where my SDK is located and project name, etc.

"run" "run"


Testing and Notes:

Run "flutter doctor" from command line to verify installation

Managing AVDs  https://developer.android.com/studio/run/managing-avds


Setup:

https://flutter.dev/docs/get-started/install/windows

Note, I installed my flutter SDK in C:\Users\vince\Documents\flutter, your location will vary..

https://developer.android.com/studio

https://flutter.dev/docs/get-started/editor?tab=androidstudio