How Does Flutter’s UI/Platform Thread Merge Impact Platform API Calls?

Hi Flutter community!

With Flutter 3.29 merging the UI and platform threads (as discussed in #150525), how does this change the way we interact with platform APIs (e.g., Android/iOS)?

Could someone provide a minimal example comparing the “old” platform channel approach with the new thread-merged paradigm?

Thanks

1 Like

In today’s world, Flutter has a UI thread to produce frames and a platform thread to interact with the native platform. But what happens if the app needs state from the platform? For example, what if an app needs to know which keys the user is currently pressing?

To communicate between threads, you either need to use locks or asynchronous messaging. But since Flutter’s frame pipeline is synchronous, we can’t actually do this inside of a frame. Instead, Flutter’s internals are built like a distributed system that replicates state across the UI and platform threads in an eventually consistent way. This is seriously complex and error-prone.

In the future, merged threads will allow us to dramatically simplify Flutter’s internals. Instead of replicating state across threads, we’ll have a single thread. If Flutter needs state from the native platform while producing a frame, it’ll be able to just call native APIs directly! This will improve the team’s engineering velocity.

For app developers and plugin authors, you’ll be able to replace platform channels with Dart FFI.

However, there are performance drawbacks to merged threads. Previously, there was a thread dedicated to just the UI. Now, your app will drop frames if your plugins are hogging the UI thread by doing things like mining bitcoin. You’ll need to move expensive logic to a background thread. But this is how all other UI frameworks work, so it’s a well-understood problem.

3 Likes

Finally, Xamarin done right. Can’t wait for this feature to hit stable.

1 Like

Just to understand it a bit better - are threads already merged in 3.29 and the above behavior can occur now compared to earlier versions of flutter? According to the announcement post it’s now available, but I’m not sure if we can observe its effects.

We’re rolling out merged threads gradually:

  1. In 3.29, merged threads are on by default on iOS and Android
  2. In the next release, merged threads will be available but off by default on Windows and macOS.
  3. The next release after that should have merged threads on Linux available but off by default, and merged threads on by default on macOS and Windows.
  4. At some point we’ll remove the ability to opt out of merged threads. Only then can we simplify Flutter’s internals.
2 Likes

Thanks! So if I do a blocking operation in plugin e.g. database read it may cause frame drops in flutter now?

Yup that’s correct

Another behavior change that we noticed because of the increased battery usage is following. We use iOS state restoration on iOS i.e. app is woken up when the BLE accessory appears in range. This wakes the app with a specific key in the AppDelegate. In a previous setup it would mean that the Flutter app would not render the first frame until the app is in foreground, i.e.

  1. App in foreground
  2. In background
  3. Gets suspended
  4. BLE accessory appears
  5. App woken up, but in background
  6. Wait…
  7. Bring back to foreground
  8. runApp is executed

From what we can see the behavior now is different and runApp as well as initState of the top-level widget is run in point 5. of the above list. This means that our network requests, websockets, database operations etc. start even when the app is in the background.

We can see this since the version that uses new Flutter version and we confirmed the behavior change with network debugging. In our case the app may be woken up like that hundreds of times in a day.

Previous behavior would mean it’s just the native part of the iOS app that gets triggered, whereas Flutter would wait for coming back to foreground to run its first frame.

We know our use case is quite niche, but nonetheless platform thread merge wasn’t ever announced as a breaking change (Breaking changes | Flutter). Lesson learned for us :slight_smile:

3 Likes