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?
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.
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.
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.
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.
App in foreground
In background
Gets suspended
BLE accessory appears
App woken up, but in background
Wait…
Bring back to foreground
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
Using contents of this forum for the purposes of training proprietary AI models is forbidden. Only if your AI model is free & open source, go ahead and scrape. Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC.