This story starts when one of your clients or a designer asks you to implement a feature that can be done using native APIs but it has not included on Flutter yet.
The first thing that you try to do a little search on pub.dev, someone has been in my situation and has developed a package to resolve this situation and has been kind enough to share it with the community. But unfortunately you don’t find anything that solves your problem, you have only one solution left: TO CREATE A PLUGIN.
But wait, first we should tell you what is a plugin. There are two types of packages in the Flutter ecosystem:
- Dart packages: general packages with only dart code (e.g. the path package)
- Plugin packages: special packages that combine native code with a dart interface and allow you to use platform-specific code (e.g. the url_launcher package)
So…let’s do a plugin
If you’re using Android Studio you can go under File -> New -> New Flutter Project… and then select Flutter Plugin
flutter create --template=plugin
You can also specify the platform for which you want to develop the plugin and the language you will use
flutter create --org com.example --template=plugin --platforms=android,ios -I swift -a kotlin flutter_is_awesome
This will create a plugin called
flutter_is_awesome with the iOS part in Swift and the Android part in Kotlin.
This is what will be created:
androidfolder: we will put all the Android specific code here.
iOSfolder: the same as above, but for iOS.
libfolder: this folder will contain all the Dart part of the plugin, that will be invoked by the application.
testfolder: Here you will develop the tests for your plugin.
examplefolder: An application that imports our package as dependency, you can use this to try out the plugin without importing it in another app.
The plugin uses a a channel called
MethodChannel to communicate between the Dart part and the native part, it’s like a tunnel where the dart part sends messages and the native part listen to those messages and react to them. It can be used also to send messages from the native part to the dart part, but this will not be covered in this article.
Our plugin will have just one function that will show the native contacts picker, let the user choose one contact, and return the name of the contact selected to the user.
In this example we will return a simple string to the Dart part, but you can return more complex data structures using for example a map, or a list, you can find the list of the data type supported by the plugins here.
Now that we’ve created the plugin and defined what it will do we need to do those those things:
- Write the Android part of the plugin
- Write the iOS part of the plugin
- Write the dart part of the plugin
When the plugin has been created the default class has been generated, it contains two methods:
onMethodCall. The first one is called when the plugin is initialized, it creates the channel of communication with the dart part and begins to listen to the channel for messages. The second is invoked whenever there’s a new message in the channel; it has 2 parameters:
call which contains the details of the invocation (the method and eventual parameters) and
result that will be used to send the result back to the dart part.
Let’s define some variable for our plugin that will be use later
Now we need to implement the
PluginRegistry.ActivityResultListener protocols, we need those to retrieve the activity that will be used to show our contact picker.
The only part that’s missing is the code to show the contacts picker and retrieve the result. In our
onMethodCall start the activity to choose a contact and in
onActivityResult retrieve the result and send it to the dart part using the result object that we’ve previously saved.
The Android part is done! ✅
The iOS part will be very similar to the Android one, here we have the
register function, which is the equivalent of the
onAttachedToEngine that we’ve seen for Android, and the
handle function, that will be called when new messages will arrive.
Now we need to create a new class
ContactPickerDelegate that will receive the callback of the contacts picker view controller (when a contact is selected or when the picker is dismissed) and will inform the plugin class. It has 2 blocks variable
onCancelthat will be invoked when the picker will inform this class with the
What we need to do now is to display the contacts picker when the
getAContact message is received, so let’s add this to the plugin class:
The iOS part is done! ✅
The last piece of the puzzle 🧩 is to create the Dart part of the plugin that will glue all the other pieces toghether.
As you can see it’s very simple, it invokes the method
getAContact on the channel and waits for the result and returns it. Note that all the functions that interact with the native part are asynchronous and will return a
Now we only need to test out our plugin, we’ve done a simple app in the example folder, with just one button and a label to test out if everything is working fine.
And here’s the final result:
You can find the final code of the plugin at this GitHub link.
What we’ve developed here is a basic example that returns just a string to the dart part but, as said before you could return a more complex data structure and you can also pass parameters to the native part when calling the
The things that you can do with the plugins are almost infinite, we’ve just scratched the surface, the limit is your imagination.