May 15, 2023 by Marvin
How to Create a Flutter Plugin from Scratch and integrate it into your App
What is a Flutter Plugin anyway?
Flutter Plugins provide the ability to use native device capabilities that are not available in standard Flutter apps. Common examples are camera access, payment SDK, location APIs and many more. There are already a lot of ready to use plugins available for developers: Flutter Plugins.
If there is no plugin available which helps you acquire the native functionality you need for your app or if you need to develop a Flutter Plugin as part of your product like we do, we have a solution. This article will serve you as a Flutter Plugin development tutorial on how to create a plugin by developing a plugin that will show an alert dialog.
Check out our Docutain SDK
Integrate high quality document scanning, text recognition and data extraction into your apps. It is also available as Flutter Plugin. To learn more about the Docutain SDK and its packages contact us at SDK@Docutain.com.
Prerequesites
Create a Flutter Plugin Project
Open Android Studio and go to File > New > New Flutter Project... to create a new Flutter project.
Ensure the Flutter SDK path leads to the installation location of the Flutter SDK, then click Next.
In the next screen you have several options to configure your Flutterplugin Project. Most importantly you have to select Plugin as the Project type.
Chose the Platforms and Android/iOS languages as you like. In this sample we checked Android and iOS as platforms and Kotlin and Swift as languages. Once you have selected the options you like, click Finish.
Once configuration is complete, the project tree should look similar to this flutter project structure:
The structure contains four important parts:
- A folder lib that contains the interface of your plugin.
- A folder for each platform (Android and iOS) that will contain the native implementation of your interface.
- An example project that will show a sample integration of your plugin.
Implement the interface
We want to have a method showAlert which will show our alert dialog. In order to do this, go to lib > flutter_plugin_alert_platform_interface.dart and add the following code right after getPlatformVersion:
1
2
3
Future showAlert() {
throw UnimplementedError('showAlert() has not been implemented.');
}
Future showAlert() {
throw UnimplementedError('showAlert() has not been implemented.');
}
Now go to lib > flutter_plugin_alert_method_channel.dart and override this method with the following code:
1
2
3
4
@override
Future showAlert() async {
return await methodChannel.invokeMethod('showAlert');
}
@override
Future showAlert() async {
return await methodChannel.invokeMethod('showAlert');
}
Now go to lib > flutter_plugin_alert.dart and add the following code:
1
2
3
Future showAlert() {
return FlutterPluginAlertPlatform.instance.showAlert();
}
Future showAlert() {
return FlutterPluginAlertPlatform.instance.showAlert();
}
Implement the native code
Now that we have defined the interface of our showAlert method, it is time to implement the native Android and iOS code that will actually show
the alert.
Assuming you know Android and iOS, and how programming works for them.
Android native code (Kotlin)
Go to android > src.main > kotlin > FlutterPluginAlertPlugin.
When you have opened the native Android class you will notice a lot of unresolved references (red lines). The project loads as a Flutter plugin, which causes this to happen.
If you like to write native Android code, select Open for Editing in Android Studio in the top right corner. The class will be updated and the Android classes will be fixed. This will allow you to start writing your own Android code.
In order to show a native alert dialog, we make use of the class android.app.AlertDialog. To construct an AlertDialog we need to pass
the current activity.
To find the current activity in a Flutter App, we can use the Flutter ActivityAware plugin. It overrides some methods to track the current activity.
Inside the onMethodCall we need to check for our showAlert method call that we have defined in our interface.
Replace the whole content of FlutterPluginAlertPlugin with the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.example.flutter_plugin_alert
import android.app.AlertDialog
import android.content.DialogInterface
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/* FlutterPluginAlertPlugin */
class FlutterPluginAlertPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
private var currentActivity: android.app.Activity? = null
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_plugin_alert")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
"getPlatformVersion" -> {
result.success("Android ${ android.os.Build.VERSION.RELEASE}
)
}
"showAlert" -> {
return currentActivity?.let {
val builder = AlertDialog.Builder(it)
builder.setTitle("Hello")
builder.setMessage("I am a native alert dialog.")
<.setPositiveButton(android.R.string.ok,
DialogInterface.OnClickListener { dialog, id ->
})
builder.create().show()
result.success(true)
} ?: result.error("0", "Current activity null", "Can not show dialog because current activity is null.")
}
else -> {
result.notImplemented()
}
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
currentActivity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
currentActivity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
currentActivity = binding.activity
}
override fun onDetachedFromActivity() {
currentActivity = null
}
}
package com.example.flutter_plugin_alert
import android.app.AlertDialog
import android.content.DialogInterface
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/* FlutterPluginAlertPlugin */
class FlutterPluginAlertPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
private var currentActivity: android.app.Activity? = null
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_plugin_alert")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
"getPlatformVersion" -> {
result.success("Android ${ android.os.Build.VERSION.RELEASE}
)
}
"showAlert" -> {
return currentActivity?.let {
val builder = AlertDialog.Builder(it)
builder.setTitle("Hello")
builder.setMessage("I am a native alert dialog.")
<.setPositiveButton(android.R.string.ok,
DialogInterface.OnClickListener { dialog, id ->
})
builder.create().show()
result.success(true)
} ?: result.error("0", "Current activity null", "Can not show dialog because current activity is null.")
}
else -> {
result.notImplemented()
}
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
currentActivity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
currentActivity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
currentActivity = binding.activity
}
override fun onDetachedFromActivity() {
currentActivity = null
}
}
iOS native code (Swift)
In iOS we use the class UIAlertController to show the alert dialog.
Go to iOS > Classes > SwiftFlutterPluginAlertPlugin.swift and replace the handle method with the following:
1
2
3
4
5
6
7
8
9
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if(call.method == "getPlatformVersion"){
result("iOS" + UIDevice.current.systemVersion)
} else if(call.method == "showAlert"){
let alert = UIAlertController(title: "Hello", message: "I am a native alert dialog.", preferredStyle: .alert);
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil);
}
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if(call.method == "getPlatformVersion"){
result("iOS" + UIDevice.current.systemVersion)
} else if(call.method == "showAlert"){
let alert = UIAlertController(title: "Hello", message: "I am a native alert dialog.", preferredStyle: .alert);
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil);
}
}
The plugin code is now ready to use. Let's see how to use it in our example project.
Test the Flutter plugin
Go to example > lib > main.dart. This is the code of the Flutter example app where we will show our alert dialog.
We keep it simple by adding a button to the screen that will show the alert dialog when clicked.
To do this, replace the content of the build Widget at the end of the class with the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: MaterialButton(onPressed: (){
FlutterPluginAlert().showAlert();
},color: Colors.green, child: const Text("Show Alert")
),
),
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: MaterialButton(onPressed: (){
FlutterPluginAlert().showAlert();
},color: Colors.green, child: const Text("Show Alert")
),
),
),
);
}
You can now run the app. Once you click the button, the output should be the following:
Check out our Docutain Flutter SDK
Integrate a high quality document scanner, text recognition and data extraction into your apps. It is also available as Flutter Plugin. To learn more about the Flutter Document Scanner SDK, check out our Developer Documentation for Flutter or contact us at SDK@Docutain.com.
Congratulations! You now know the basics of how to create Flutter plugin. If you enjoyed this Flutter tutorial we would appreciate you sharing it among your colleagues, partners and friends.
You might also be interested in the following articles