flutter

Flutter and the Law of Leaky Abstractions

animal avian beak bird

Photo by Pixabay on Pexels.com

A few weeks ago I started a project to rewrite my app Pray On The Go using Flutter.

First I implemented the persistence methods as this would make testing the rest of the app easier. Even though this is local file storage, I used json for persistence anticipating that I will leverage the same format when I eventually start serving up prayer requests from a server.

The json encoding and decoding libraries for Dart (the language used with Flutter) have undergone a lot of change in recent years and it turned out to be some work to figure out the best approach for handling decoding. Much of what is written about json decoding for Dart is outdated because of the changes that have taken (or are taking) place.

The code for creating my classes from json turned out to be simple once I properly understood the model that could be used for parsing json and reviving classes. I just created a fromJson constructor for my primary object class and then created a revivor method that could be used on the list of objects, like this:

dynamic revivePlaybackList(dynamic key, dynamic value) {
  if (key == "playbackElement") {
    PlaybackElement pbe = 
                new PlaybackElement.fromJson(new Map.from(value));
    pbe.index = this._playbackList.length;
    this._playbackList.add(pbe);

    return pbe;
  }

  if (key == "listName") {
    assert(listName == value);
    return value;
  }

  return value;
}

The rather straightforward task of turning the file into a list of objects can then be handled like this:

  _jsonCodec = new JsonCodec.withReviver(revivePlaybackListList);
  String jsonData = await _file.readAsStringSync();
  await _jsonCodec.decode(jsonData);

Happy with my progress on implementing persistence, I wasn’t quite prepared for the difficulty that would come next as I tried to get the screen to update with my changes as the file loaded.

Since I was new to Flutter, I had started off with a simple list example app and then was modifying it both to learn and to implement the functionality I needed. But with my superficial understanding of the model for updating a screen of widgets, I quickly met with frustration as all of the things I tried seemed to have no effect. My mental model wasn’t only superficial – it was broken.

I had to take time out and really understand the stateless and stateful widget tree model that the creators of Flutter designed. In addition to understanding this model, there are nearly 150 widgets defined with about 500 classes that can be combined in all kinds of helpful and unhelpful ways. So the examples I found to help with this process were key. I ended up spending a couple of weeks learning Flutter and trying my hand at turning a simple sample app into something useful.

Once I had that behind me, I started fresh, with better understanding and with more appropriate examples. I’m nearing completion and have put in 80 hours since this fresh start building my app.

As I look over those 80 hours, I see that 24 of them were spent tracking down and solving just one problem; recording on the physical iPhone refused to work. I pursued a lot of dead-end leads trying to track this one down. Ultimately, I ended up debugging the iOS specific code used to implement one of the libraries I had included in my project.

I think Flutter does a beautiful job of creating a UI abstraction that works well in both Android and iOS. Also, the growing library of packages to accomplish other things (such as audio playback and recording in my case) nicely abstracts these functions so that you don’t have to deal with the nuances of each underlying operating system… until you do.

 

“All non-trivial abstractions,
to some degree, are leaky.”
– Joel Spolsky

 

Joel Spolsky popularized the Law of Leaky Abstractions which every developer before and since has had to wrestle with to get anything useful done. And this one bit me good. The problem I spent 24 hours working on over a week and a half is written up in detail here. The solution turned out to be rather simple. But getting to the actual cause of the problem required digging beneath that nice abstraction to figure out what was going on in Objective C with the iOS implementation. And further complicating the situation for me, the problem never manifested in the simulator!

In the end, having understood the problem in enough detail, I was able to fork the repository for the stereo audio playback library that was causing interference and make a code change that eliminated the problem altogether. The author of the library quickly accepted my changes so I was happy to have contributed to a more reliable ecosystem of Flutter packages even if it cost me the equivalent of 3 full days.

Many more leaks in the Flutter abstraction layer will be found as new packages are created and as people start to use them. And this will definitely take some time. For those who decide to build their app now in Flutter, participation in abstraction leak finding and patching is almost necessarily a requirement.

Given the relative young age of Flutter, I’m actually surprised that I was able to create this app without writing any platform specific code (with the exception of fixing the stereo library of course). I’m very happy with app performance and responsiveness as well as the flexibility I had in fulfilling my design intentions. Flutter will be a leading candidate when I’m ready to create the next app I need – along with the expectation of handling a few leaky abstractions.

 

 

 

Flutter – My Unlikely Choice For Building An App

Early in my career, I needed to build a Windows app and thought we would eventually need to build the same app for Mac as well. To facilitate this direction I chose a framework called XVT to build the app on Windows. In theory, we would leverage this work for a Mac version when the time was right.

I was convinced for a year and a half after I made that decision that it was the right way to go. However, eventually two things made it clear that it was the wrong way to go for our project.

  1. We never built the Mac app. It turned out there wasn’t any demand for it and the effort to do it, even with a framework that was built to support Mac, made the effort not worth it. In those days, the Mac was declining in popularity.
  2. XVT could not keep up with the pace of change in Windows and for that reason, our app always felt a bit behind the times. We ended up rewriting the app without XVT so that we could take advantage of the new features and design elements of Windows as they became available to the general community.

How long do you operate on information gathered from a prior experience? For instance, if you try out a particular type of salsa and don’t like the taste, you may never buy it again your whole life. It makes no difference if the manufacturer changes the ingredients in the meantime. You don’t even know that change happened because you have moved on. You only have so much time and energy to test things out and make your discoveries. We make thousands of conclusions like this. So it’s generally worth relying on those discoveries made years ago – unless there is a compelling reason to re-evaluate.

Primarily through my experience with this one decision to use XVT, for 25 years I have been down on frameworks that advertise developing once for multiple completely disparate platforms. A couple of times I have dabbled with new frameworks like Xamarin. But I was very hesitant and it didn’t seem that the fundamental issues I ran into with XVT had been solved.

Why did I reconsider when I heard about Flutter?

Firstly, my experience with the cost of having separate developers work on building Android and iPhone apps to do the same task keeps begging for a better solution.

Secondly, in my current experimentation mode, I have the need to create an app that I want to work on both iPhone and Android. I have some experience building iPhone apps, but none on Android. And I don’t quite have the time to write this app twice.

Thirdly, Dean Chalk’s article – Why I’m Giving Up Everything for Flutter. My current need, and Dean’s compelling arguments convinced me to read more and to jump in.

So… for the past few days I have been building my proof of concept with Flutter. And I’ve been surprised at how quickly things have progressed. I took a couple of days to learn the basics of the Dart programming language. I’ve found beta and alpha libraries that are quite capable even in their early stage (audio, text to speech, etc.). And I love the Material Design framework and their approach to building clean UIs that look great on both platforms. Even for targeting a single platform, I’ve found it much quicker to get a clean-looking interface than working directly with XCode. Some of the other benefits I’ve discovered are already written about in this Wm Leler post: What’s Revolutionary About Flutter.

Circling back to that original question – to use a framework or build an app twice on native platforms…

With my history, I know that whatever decision I make, whether it was the *right* way to go or not may not materialize until a few years down the road. But early indicators give me hope for Flutter as a viable way to develop many apps.