How to Develop a Flutter Plugin (English)
@neonankiti
https://gyazo.com/05bfb8c905a8517e0eff0acff5f9f3a6
About Me
https://gyazo.com/27bdad70541d1f9c6bd4867bd179fdc0
Yuki Nanri
Nickname
Bison
What to do
iOS Developer
Android Developer
About Company
https://gyazo.com/cb4d096fe5f36d7e4ebdc16cd942147b
Today's Topic
Concrete Development Step for Flutter Plugin
* will not talk about how the interfaces and natives(iOS/Android) are bridging.
Why I'm here Today
Flutter Study Group in my company. (5 people)
Determined to publish Flutter Book(Self-publishing)
What is Flutter
mobile cross platform (Android/iOS)SDK made by Google
Dart
Flutter Plugin
Messaging System over Channels
https://gyazo.com/3789021c84e88e00447e267086156318
Implementations for Native Bridging
Dart(Interfaces)
iOS/Android(Concrete Implementation)
about 30 packages
We decided to make a Healthcare App!!
https://gyazo.com/94aca8c68e519d73cd48d00415bc200f
No HealthKit/Google Fit Combined Plugins
There are 2 ways for Native(iOS/Android) to get health related data.
2. Device App or Specific API ×
Flutter only provides Sensors Plugin => We need to implement a Plugin!!!
Steps to Implement Plugin
1. Decide Interfaces for Natives(iOS/Android)
2. Plugin Development
Interface
iOS/Android
3. Interface Clients
Plugin Development Environment Setups
Code
code:setup
$ flutter create --org com.bison --template=plugin -i swift -a kotlin health_fit
Defaults are Objective-C, Java
Auto generated in iOS/Android
SwiftHealthFitPlugin.swift
HealthFitPlugin.kt
Build
code:build
cd health_fit/example; flutter build apk
https://gyazo.com/0eb548e51c6426b47fd91745f890d355
Interface Design
lib(health_fit.dart)
requires iOS/Android Framework knowledge to implemente Interface
Is it possible only for full-stack Engineer?
Not at all.
Divide tasks into each platform
The development itself is just like general web development(server-client relations)
Define the common Interface
Think about UseCase not about existing iOS/Android things.
In Healthcare data
Permission for Healthcare data
Create, Get, Delete, Update Healthcare data
Disable Permission for Healthcare data
Permission for Healthcare data
required parameters
DataType
Step
Weight
PermissionType
Read
Write
finally check them in iOS/Android
Source Code for Permission
Interface
code:health_fit.dart
static Future<bool> requestPermission(DataType type) async {
final bool requestPermission = await _channel.invokeMethod(
'requestPermission', {"dataType": type.toString(), "permission": 0});
return requestPermission;
}
Call Android(iOS) method
pass the variants that Android will receive
code:health_fit.dart
// function name in first arguments
_channel.invokeMethod('requestPermission', ...)
Decide returned value for Flutter App
Set as bool because we want to know if the request is success or not.
code:health_fit.dart
static Future<bool> requestPermission(DataType type) async {...}
Receive variants in Android(iOS) from Interface
When Flutter App call the function, onMethodCall() in Android would be called.
call.method has a variants defined in Interface.
code: HealthFitPlugin.kt
override fun onMethodCall(call: MethodCall, result: Result): Unit {
when (call.method) {
// functiona name defined in Interface(Dart)
"requestPermission" -> requestPermission(call, result)
}
}
How to handle Context in Android
The way of management of memory are different between Android and iOS.
In Android, Context is often used
Context is required when we request permission for Healthcare data.
code: HealthFitPlugin.kt
GoogleSignIn.requestPermissions(
activity, // <- This is Context
GOOGLE_FIT_PERMISSIONS_REQUEST_CODE,
it,
optionsBuilder.build())
Passing it as a constructor argument
code:HealthFitPlugin.kt
// put context(=activity)
class HealthFitPlugin(private val activity: Activity) {
companion object {
@JvmStatic
fun registerWith(registrar: Registrar): Unit {
val plugin = HealthFitPlugin(registrar.activity()) // This is Context!!!!!!
val channel = MethodChannel(registrar.messenger(), "health_fit") // interface file name
channel.setMethodCallHandler(plugin); // channelにコールバックをセット
}
}
}
How to handle AndroidonActivityResult()
In requesting permission, next steps will go.
1. app screen -> 2. requestPermission() -> 3.stack new screen -> 4. grant(deny) -> 5.app screen
1,2.https://gyazo.com/f27b46da5954ce7ae7ebac502b1b2d7f 3,4https://gyazo.com/091cf864b74942536f64c8880936dcac 5https://gyazo.com/f27b46da5954ce7ae7ebac502b1b2d7f
In Android, onActivityResult() is called when backing to app page(screen#5)
Implementation
Implement ActivityResultListener in Android
register callback in registart (addActivityResultListener)
code: HealthFitPlugin.kt
class HealthFitPlugin(private val activity: Activity) : ActivityResultListener {
companion object {
@JvmStatic
fun registerWith(registrar: Registrar): Unit {
val plugin = HealthFitPlugin(registrar.activity())
// register callback
registrar.addActivityResultListener(plugin)
}
}
}
// would be called after permission request page.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Boolean = when (resultCode) {
// write you code!!
}
Give the result back to Flutter App
result as 2nd argument onMethodCall(call: MethodCall, result: Result) has some functions, and you can pass some values to Flutter app through it code:result
// Success
result.success("Any Object Here")
// Fail
result.error("errorCode", "errorMessage", "errorDetails"))
Create, Get, Delete, Update Healthcare data
Only GET
required parameters
DataType
Step
Weight
DateTime
startAt: start time to collect data
DateTime
endAt: end time to collect data
TimeUnit
time unit to devide time
finally check them in iOS/Android
Source Code for Get Healthcare data
Interface
code:health_fit.dart
static Future<String> getData(DataType type, DateTime startAt, DateTime endAt, TimeUnit timeUnit) async {
final String data = await _channel.invokeMethod('getData', {
"dataType": type.toString(),
"startAt": startAt.millisecondsSinceEpoch,
"endAt": endAt.millisecondsSinceEpoch,
"timeUnit": timeUnit.toString(),
});
return data;
}
Pass variants to Android(iOS)
determine key, value and pass them as 2nd argument of invokeMethod()
code:health_fit.dart
_channel.invokeMethod('getData', {"dataType": type.toString()});
Receive variants in Android(iOS)
onMethodCall(call: MethodCall, result: Result)
call.argument<Type>("the variants name defined in interface")
code:HealthFitPlugin.kt
private fun getDataType(call: MethodCall): DataType = when (call.argument<String>("dataType")) {
"DataType.STEP" -> DataType.TYPE_STEP_COUNT_DELTA
}
Sum up
Flutter Plugin Development itself is not that hard.
might require some iOS/Android knowledge
We're Recruiting
FiNC wants iOS/Android engineers!! English only speakers are acceptable. https://gyazo.com/bea1ca9b71bbdce93867ee5057ba7664
How to Develop a Flutter Plugin_2