Sunday, December 19, 2021

Node-RED development

A long time ago, I developed some Node-RED nodes for "STEM" and "Amateur Radio" hardware, written in node.js and Python, under a FOSS license for the general public to use.

A couple months ago I noticed a popular electronic hobbyist magazine linked to my code for the relay board.  Which is really cool, the more people using Node-RED to automate and control using flow based programming technologies, the better.

I wrote that old code for my own personal use; but released it to the general public, may as well let everyone play along.  However, if people are interested, I could apply real resources and real effort toward building better Node-RED nodes.  And people DO seem interested.

I decided to set up a project, Patreon funded, to fund further Node-RED node development for "STEM" and "Amateur Radio" related hardware, mostly on Raspberry Pi type computer hardware.  I intend to produce nodes that follow all the standards in full, are well documented in written and video format, are very easy to use, FOSS licensed, and generally of the highest possible quality.  

And so far, so good!  I've cleaned up the relay board a bit, added several example flows, and recorded some pretty good Youtube documentation videos.

The link to the project main page is:

https://www.springcitysolutions.com/nodered

The link to the project Patreon page is:

https://www.patreon.com/springcitysolutions_nodered

I intend to post detailed daily logs of Node-RED project related work on the Patreon page along with downloads and special posts for Patrons and similar add-ons.

I'll only very occasionally post major news about the project on the main company blog.

Thank You in advance for your support!

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