— Flutter, Android, Plugins, Migration, Mobile Development — 1 min read
So Flutter 3.29 has finally dropped, and with it comes a critical change for plugin developers - the complete removal of the old PluginRegistry.Registrar
API. If you've got old plugins that haven't been updated yet, they're about to break. Let's fix that.
If your plugin code still looks like this, you're in trouble:
1class DeviceScannerPlugin(private val registrar: Registrar) : MethodCallHandler {2 3 companion object {4 @JvmStatic5 fun registerWith(registrar: Registrar) {6 val channel = MethodChannel(registrar.messenger(), "device_scanner")7 channel.setMethodCallHandler(DeviceScannerPlugin(registrar))8 }9 }10 11 override fun onMethodCall(call: MethodCall, result: Result) {12 // Method handling code...13 }14}
The above approach is completely dead as of Flutter 3.29 - time to move on.
Here's how your plugin should look now, using the FlutterPlugin
interface:
1class DeviceScannerPlugin : FlutterPlugin, MethodCallHandler {2 private lateinit var channel: MethodChannel3 4 override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {5 channel = MethodChannel(binding.binaryMessenger, "device_scanner")6 channel.setMethodCallHandler(this)7 }8 9 override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {10 channel.setMethodCallHandler(null)11 }12 13 override fun onMethodCall(call: MethodCall, result: Result) {14 // Method handling code...15 }16}
This pattern properly supports the modern Flutter embedding API and handles lifecycle events correctly.
Let's look at a complete transformation of a real plugin.
1package com.example.hardware_monitor2
3import io.flutter.plugin.common.MethodCall4import io.flutter.plugin.common.MethodChannel5import io.flutter.plugin.common.PluginRegistry.Registrar6
7class HardwareMonitorPlugin : MethodChannel.MethodCallHandler {8 companion object {9 @JvmStatic10 fun registerWith(registrar: Registrar) {11 val channel = MethodChannel(registrar.messenger(), "hardware_monitor")12 channel.setMethodCallHandler(HardwareMonitorPlugin())13 }14 }15 16 override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {17 // Method handling code...18 }19}
1package com.example.hardware_monitor2
3import io.flutter.embedding.engine.plugins.FlutterPlugin4import io.flutter.plugin.common.MethodCall5import io.flutter.plugin.common.MethodChannel6
7class HardwareMonitorPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {8 private lateinit var channel: MethodChannel9 10 override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {11 channel = MethodChannel(flutterPluginBinding.binaryMessenger, "hardware_monitor")12 channel.setMethodCallHandler(this)13 }14 15 override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {16 // Method handling code...17 }18 19 override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {20 channel.setMethodCallHandler(null)21 }22}
Here's what you need to watch for:
registerWith
method - It's completely unsupported in Flutter 3.29binaryMessenger
instead of flutterEngine.dartExecutor
onAttachedToEngine()
- This ensures proper lifecycle handlingonDetachedFromEngine()
- Remove your method handlers to prevent memory leaksI recently had to migrate several plugins for a project, and while the process is straightforward, it's easy to miss small details. The most common issue I encountered was forgetting to properly clean up resources in onDetachedFromEngine()
, which led to some weird behavior when hot restarting the Flutter app during development. You might notice a lot of third party plugins you are using have this issue and cause your build to fail - you should be able to fix this just by updating to the latest version in your pubspec.yaml.
Flutter's ecosystem continues to mature, and this change helps standardize how plugins are structured. While it might be annoying to update your code now, the new API is more robust and will help prevent issues down the line. If you're maintaining plugins, now's definitely the time to migrate!