Flutter V1 vs V2 Embedding, Deprecated Android APIs, and Colliding Kotlin Versions
Oct 30, 2021
To recap my sound null safety blog post: when you have forty package dependencies and you must move to sound null safety due to transitive dependencies, you might have to convert a couple of plugins that lag behind on your own. Converting a codebase and a few plugins is a sizable amount of work. However, there are a few more points in this module and technology stack that you have to keep an eye out for. Most of these are related to the herd of plugins you depend on, and I realized with that many packages I am basically a developer shepherd watching over my stock. Are any sheep sick? Are all of them doing well? Are any of them dragging their feet?
I’ve read blog posts which warned against depending on too many plugins, arguing that usually you only use a fraction of the feature offerings, and you might as well develop code on your own or borrow the needed pieces. But as soon as you borrow code, you lose the advantage of a community. When you depend on a package, a whole community is using it, finding issues and bugs, and contributing fixes. If you tear out some code, you won’t have that power of the community anymore and the code could become stale versus a more actively maintained one within a package. Of course, by the statistical law of large numbers, with many plugins there will be a few that lag behind. But overall, I’m a huge fan of not reinventing the wheel, and I rely heavily on packages anyway.
An exception to borrowing code could be if it is purely in Dart land (so you don’t have to fiddle with native Android or iOS code) and you have a decent amount of customization and features added to it. Even in that case, I constantly try to work towards contributing back to the original code so that one day I can rely on a dependency again. For example, I still hope that in the case of the Fab Circular Menu, the debugging of a significant issue and my solution to it will be merged one day. Unfortunately, in the case of the Strava and TCX modules, I had way too many changes in the API to support in-memory files, so I don’t have a PR yet to contribute back.
So, back to the possible issues arising beyond sound null safety tasks:
- Plugins which still implement the Flutter V1 embedding API only.
- Plugins which use deprecated Android APIs.
- Kotlin version collision between dependencies.
Let’s look at those one-by-one.
Flutter V1 API vs V2 API
When you write a Flutter package there’s a well-defined way to interface with the rest of the Flutter technology stack, and that’s basically the Flutter embedding API. The V2 embedding API was introduced a long time ago with Flutter 1.12, and although the V1 API is still supported, it’s in a deprecated state, and who knows when it will become unsupported. As of Q2 2020, only 26% of plugins were using V1, and the ecosystem stopped letting in V1 packages after that. For a good while, I was dealing with a mixed set of V1 and V2 embeddings among my forty dependencies. There were reports that the initialization of any V1 plugins before V2 plugins could cause problems. I saw the related code inside the Flutter SDK. The scaffolding didn’t pay attention to which version a plugin was and it was also lacking any exception handling, so if any plugin craps itself, the initialization sequence is interrupted and the application probably crashes upon startup.
Take a look at this proposed patch within a Flutter issue. I started to use that patch over the Flutter SDK to make sure that the generated scaffolding orders the V1 plugin initializations after the V2 ones. I had to stash that patch every time before I upgraded my Flutter SDK, then unstash it and recompile the SDK. This worked all the way until Flutter 2.5 came out, at which point the related code area was refactored so much that the patch was not applicable anymore. Since I constantly kept upgrading my plugins, at that point only two of them used V1 embedding.
One of them was the bluetooth_enable plugin, and fortunately I found a fork of Alystrasz with a PR already upgraded to V2 embedding. Now, there is a migration guide; however, the hard part of the work is up to you and is not necessarily intuitive. I felt a little bit in the dark, but I finally upgraded the File and Screenshot Share Widget and it was quite a ride; I needed to dig deep like this StackOverflow issue. Unfortunately, I accidentally merged in a screenshot feature deletion, so I scrambled to push another PR which brought the screenshot back. I felt awful, but provided a quick recovery, hopefully redeeming myself. The plugin maintainer released the refreshed version, so I could remove one reliance on a custom fork from my codebase and I’m back relying on the pub.dev package. Speaking of plugins dragging their feet, the bluetooth_enable plugin is another story: even though someone else already did the work, the plugin maintainer still hasn’t merged the PR.
Plugins which use deprecated Android APIs
I have about half a dozen such plugins. Issues can be really nagging if they affect any permissions. The permission system got stricter over the last 2-3 major Android versions. In my case, this involves file saving, file import, and Bluetooth permissions. If anything gets out of alignment, users may not be able to use my application, and I’d get bad reviews. Right now I keep deferring dealing with them, since I just cannot carry all of them on my back. I hope that their respective maintainers will keep updating them. I purposefully haven’t updated the permission checker plugin, for example. I have already contributed to more than half a dozen plugins along my journey.
Kotlin version collision between dependencies
Fortunately, I have avoided this so far; however, it could happen if your Kotlin version or any of the plugins’ Kotlin versions are too far apart. See this, this, or this Stack Overflow issue. Kotlin is a first-class citizen Android language and it goes through a pretty good pace of evolution. Along the way, there can be some colliding changes; I won’t go into more detail here.
These were a few areas to watch out for with any Flutter project that is bigger than Hello World. Essentially, time will solve most of these issues, because V1 embedding will reach EOL (End of Life) at some point, as will the various deprecated Android APIs. Let’s hope for the best, and that the community and the plugin maintainers will keep the momentum going.