Skip to content
DeveloperMemos

Flutter 3.29: Migrating from Deprecated PluginRegistry.Registrar

Flutter, Android, Plugins, Migration, Mobile Development1 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.

The Old Way

If your plugin code still looks like this, you're in trouble:

1class DeviceScannerPlugin(private val registrar: Registrar) : MethodCallHandler {
2
3 companion object {
4 @JvmStatic
5 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.

The Modern Way (Flutter 3.29+)

Here's how your plugin should look now, using the FlutterPlugin interface:

1class DeviceScannerPlugin : FlutterPlugin, MethodCallHandler {
2 private lateinit var channel: MethodChannel
3
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.

Real World Migration Example

Let's look at a complete transformation of a real plugin.

Before (Using the old registerWith)

1package com.example.hardware_monitor
2
3import io.flutter.plugin.common.MethodCall
4import io.flutter.plugin.common.MethodChannel
5import io.flutter.plugin.common.PluginRegistry.Registrar
6
7class HardwareMonitorPlugin : MethodChannel.MethodCallHandler {
8 companion object {
9 @JvmStatic
10 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}

After (Using FlutterPlugin)

1package com.example.hardware_monitor
2
3import io.flutter.embedding.engine.plugins.FlutterPlugin
4import io.flutter.plugin.common.MethodCall
5import io.flutter.plugin.common.MethodChannel
6
7class HardwareMonitorPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
8 private lateinit var channel: MethodChannel
9
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}

Migration Checklist

Here's what you need to watch for:

  1. Kill the registerWith method - It's completely unsupported in Flutter 3.29
  2. Use binaryMessenger instead of flutterEngine.dartExecutor
  3. Always create your channels in onAttachedToEngine() - This ensures proper lifecycle handling
  4. Clean up in onDetachedFromEngine() - Remove your method handlers to prevent memory leaks
  5. Test thoroughly - The embedding API has subtle differences that might affect your implementations

My Experience

I 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.

Wrap Up

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!