Thursday, December 9, 2021

Flutter in Action Chapter 8 "Flutter State Management"

Completed taking notes on "Flutter in Action" chapter 8 "Flutter State Management".

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

The book lists three closely related sub-chapters, but there's actually four subchapters.

Ch08a "StatefulWidget":

This is a pretty good explanation of the StatefulWidget lifecycle and how the widget tree, element tree, and state object work together over time.  Its clearer than the explanation on api.flutter.dev.

There are two small issues with this chapter.  It glosses over the big picture "why", and it avoids talking about keys and why you need them in lists.

The big picture "why" is that the Flutter element tree is, very simplistically, a cache between the programmer controlled widget tree and the pixels on the screen from the render tree, intended to make it faster, or to use that speed to make it cheaper to manipulate the widget tree as much as the programmer feels like.  So when you change the behavior of the app as the programmer, by changing the widget tree and causing a rebuild, the app renders faster because most of it was unchanged and remains in the element and render trees.  The cache-like nature has its issues; sometimes change flows from the widget tree to the element tree like when something new is created, sometimes change flows from the element tree to the widget tree (kinda) like when something is deleted from the widget tree and the element tree needs to garbage collect and discover its pointing to something that no longer exists and needs deletion.

That deletion situation is the reason for the mysterious keys that are skipped over in this sub-chapter of the book.  For something like a list where somebody might store a little state in the widget (probably a bad idea) like the stereotypical example from the Academind video series of using a random color as a background color for artistic reasons, after a widget tree rebuild the process that reconnects old elements to new widgets can get confused if you delete, for example, the 7th of 10 list items and your state may properly delete the 7th state object, but the remaining widgets might see the 10th background color removed instead of the 7th background colored widget because the re-linker has trouble figuring out which pairs of widgets and elements are now broken by that deletion.  So for that, we can give the widget a unique key to remember forever and share with the element tree and then when it "checks backwards" it can very trivially match keys very quickly.  See, if you were displaying an ebook one line at a time, and there's nothing special about any given line widget, then deleting any old random state object wouldn't matter how the remaining unchanged elements pair up with the identical widgets.

Could add a paragraph around the start of the subchapter something like: Very handwavy, Flutter is fast because it has layers of caches between the programmer generated widgets and the screen pixels, one layer of cache being the Element tree, and caches always have cache coherency problems, so keys are sometimes needed in unusual situations to help old elements find their matching new widgets after the widget tree rebuilds.

Its not really a technical complaint in that if you read the rest of the book this topic makes sense, but in a literary sense I feel technical book chapters should be like self contained essays with some attempt to answer the "why" in each and every chapter.  Yet I can already imagine the authors argument, that would cost a couple pages, and its already a 340 page book.

Ch08b "InheritedWidget"

There's an issue in the Flutter ecosystem where people get confused about small s state as in generic program state vs capital letter S State which is a specific object in Flutter.

There's some leakage between subchapter b and subchapter c where b discusses three state management paradigms and subchapter c discusses what the author considers the most useful "serious" state management paradigm.

One state management paradigm in Flutter is "just kinda pass stuff around to your neighbors and hope for the best".  On very small projects that works great, but is the spaghetti code equivalent of 1980s BASIC having goto statements randomly laying all over.  It often works the first time you write it, but any changes later will cause headaches.  I'm not sure this paradigm has a formal Flutter ecosystem name other than "please don't do this".

A somewhat better state management paradigm is "vanilla lifting state up" which does a disservice to the good name and flavor of vanilla in that it's the second worst way to handle app state.  All state is put in the root or top or peak of the tree and everything that could ever be useful needs to be passed down manually thru every layer then thru to every leaf.  This opens up hilarious debugging problems where there's a problem in layer 12 because layer 6 is intermittently not passing to layer 7, or even worse, layer 6 accidentally on purpose modifies whatever its passing down instead of pushing changes to the top and letting them percolate back down.

The third state management paradigm is the focus of most of chapt 8 subchapter b, which is using Flutter's own InheritedWidget system, which replicates how Theme, Mediaquery, and Scaffolds work.  Essentially instead of complicated business state flowing thru the tree, the complication is focused at the top where there's a three (or four) part process where the child widget gets passed into the container stateful widget, passed into the State object via widget.child, then passed via build method into the private container, then the private container passes it to its superclass, all without touching the actual child widget.  The agony at initial setup seems worth it as now the entire tree can access state by something similar to AppState.of(context).yourMethodHere(); which is relatively clean and its not necessarily global in that you could have multiple "near the top of the tree" states, but then you'll just mix up which data is in which place, so in practice it amounts to a global state solution.  It has a cost in that changing anything with any of the setState methods will cause a rebuild of "all" the StatefulWidget underneath, but that's supposed to be very fast in Flutter and that's why we have the Element Tree as a middle cache to speed it up, etc.  At least its no longer necessary to copy everything correctly from layer 6 to layer 7 to make layer 12 work, layer 12 simply looks its way up the tree until it finds the top and the data it needs.  Everything has a tradeoff.

I have to compliment the author, this "subchapter b" for InheritedWidgets is possibly the clearest explanation I've ever read of how they work and how to implement this state management paradigm / pattern.  Where do I get my $49.99 worth out of this book, well just point right here.

Ch08c "State Mgmt focusing on Google BLoC"

Before getting started with BLoC, the author points out there are sixteen examples of state management alternatives in Flutter at:

https://fluttersamples.com/

So subchapter c is all about the BLoC aka "business logic components".  As a side note, DartConf 2018 is available on youtube, about a quarter of the videos seem to discuss state management in some form, and its all worth watching at:

https://www.youtube.com/playlist?list=PLOU2XLYxmsIIJr3vjxggY7yGcGO7i9BK5

I would describe BLoC theory as separate UI and logic as much as possible and minimize the code in UI and connect the two with streams.

The book explains the good idea of creating a helper class that holds all your BLoCs and then implement something looking kinda like InheritedWidgets where you'll gain access to your BLoCs downstream with something like _someBloc=AppStateContainer.of(context).blocProvider.someBloc;

Actually running streams in and out of the BLoCs is explained in the forth and final subchapter of the book.

Ch08d "Intro to Streams with an emphasis on BLoCs"

This is a short, straightforward, minimal, theory free intro with examples of using streams to implement the flow of data between BLoCs and UI code.  As heard many times thru the book, wait until chapter 9 async, then everything will make more sense.  If you just want data to flow in streams as minimally as possible, these couple pages very clearly explain how to do so, enough to implement BLoC style state management paradigm.

There's about a dozen other ways to do flutter app state management that are not mentioned in the book.  But the book is already 350 pages long, and this chapter is a good intro to the general problem and some simpler solutions.

Next up, chapter 9, "Async Dart and Infinite Scrolling".

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