Flutter provides you with a powerful, beautiful, simple and efficient cross-platform UI,
But no matter how gorgeous the appearance of beauty, there is no inside is just an empty shell, you will like her? Plugins allow Flutter to easily communicate with the current platform, calling methods on the platform. This article introduces a bunch of MethodChannel concepts, but let’s see how to use them. In this article you will learn:
[1]. How to create a Flutter plugin [2]. How to interact with Android and iOS in Flutter (this article uses Kotlin and Swift) [3]Copy the code
1. Create and structure analysis of Flutter plug-in
Create a Flutter plugin
File-->new-->new Flutter Project...
Fill in the information
Package name and language selection
1.2: The Structure of the Flutter plugin project
There are three pieces of code:
Write android native code under Android, using Java or Kotlin. If using JNI, it may also involve C++ to write ios native code under ios folder. Use Object-C or Swift lib folder to write Flutter code, using Dart languageCopy the code
That means a plugin might be in 6 languages, lol, shudder humans…
1.3: Run the plug-in example
Although complex, but simple there must be simple cost, complex there must be complex value.
One catch: You need cocoapods installed on your MAC
---->[localinfo]---- toly:~ MAC $ruby -v Ruby 2.3.7p456 (2018-03-28 Revision 63024) [universal.x86_64-darwin18] Toly :~ MAC $ Gem -v 2.5.2.3 toly:~ MAC $gem sources -l *** CURRENT sources *** https://rubygems.org/ ---->[replace Ruby source]---- gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/ ---->[replace ruby source complete]---- toly:~ MAC $gem sources -l *** CURRENT SOURCES *** https://gems.ruby-china.com ---->[install cocoapods]---- toly:~ MAC $sudo gem install -n /usr/local/bin cocoapods toly:~ mac$ pod setupCopy the code
2. Code analysis of the first plug-in
Create an iA_Version project here specifically to see how the sample plug-in is done.
2.1: Flutter code:
Dart defines a class iA_version.dart with a MethodChannel static constant, _channel, that accepts a string, The static method platformVersion is returned using an asynchronous call to the getPlatformVersion method of _channel.
---->[lib/ia_version.dart]---- class IaVersion { static const MethodChannel _channel = const MethodChannel('ia_version'); static Future<String> get platformVersion async { final String version = await _channel.invokeMethod('getPlatformVersion'); return version; }}Copy the code
2.2:Android Kotlin code:
[1]. Define the IaVersionPlugin class to inherit from MethodCallHandler. [2]. Create the registerWith static method via the Registrar type variable. [3]. Registrar Messenger and identifier creation of MethodChannel objects [4]. Set the IaVersionPlugin object to the MethodChannel for callback processing. [5]. Overwrites the onMethodCall method, calls back the MethodCall and Result object, and uses the Result object to execute the method passing in the Android version information in the method body based on the method name 'getPlatformVersion'. ---->[com.toly1994.ia_version.IaVersionPlugin]---- class IaVersionPlugin: MethodCallHandler { companion object { @JvmStatic fun registerWith(registrar: Registrar) { val channel = MethodChannel(registrar.messenger(), "ia_version") channel.setMethodCallHandler(IaVersionPlugin()) } } override fun onMethodCall(call: MethodCall, result: Result) { if (call.method == "getPlatformVersion") { result.success("Android ${android.os.Build.VERSION.RELEASE}") } else { result.notImplemented() } } }Copy the code
2.3: Swift code of iOS
[1]. Define SwiftIaVersionPlugin class inherited from NSObject, FlutterPlugin [2]. Create the static method Register and pass in the FlutterPluginRegistrar type variable. [3]. Create FlutterMethodChannel object via Registrar Messenger and identifier [4]. Set the SwiftIaVersionPlugin object to MethodChannel for callback handling. [5]. Handle method, which calls back FlutterMethodCall and FlutterResult object, and uses the Result object to execute the method passing in iOS version information. ---->[ios/Classes/SwiftIaVersionPlugin.swift]---- public class SwiftIaVersionPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "ia_version", binaryMessenger: registrar.messenger()) let instance = SwiftIaVersionPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { result("iOS " + UIDevice.current.systemVersion) } }Copy the code
There are also two files written using OC, about which I don’t know much. Here is an introduction from a friend of the Flutter group
---->[ios/Classes/IaVersionPlugin.h]----
@interface IaVersionPlugin : NSObject<FlutterPlugin>
@end
---->[ios/Classes/IaVersionPlugin.m]----
@implementation IaVersionPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftIaVersionPlugin registerWithRegistrar:registrar];
}
@end
Copy the code
2.4: Use plug-ins
It is very convenient to use, and it is OK to call it.
import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter/services.dart'; import 'package:ia_version/ia_version.dart'; void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { String _platformVersion = 'Unknown'; @override void initState() { super.initState(); initPlatformState(); } Future<void> initPlatformState() async {// Async initialization platform state String platformVersion; Try {/ / capture PlatformException platformVersion = await IaVersion. PlatformVersion; PlatformException {platformVersion = 'Failed to get platform version.'; } // If the widget is removed from the tree during the asynchronous platform message run, // we want to discard the response rather than call setState to update the non-existent appearance. if (! mounted) return; SetState (() {// Update state _platformVersion = platformVersion; }); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Plugin example app'), ), body: Center( child: Text('Running on: $_platformVersion\n'), ), ), ); }}Copy the code
3. Obtain the cache folder
I’m sure you’ve all used the path_provider and found it handy to get file paths in Flutter
Let’s look at how to make a plug-in get the cache folder
3.1: Dart plug-in file
import 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; Class IaPath {static const methodChannel_channel =// Plugin's channel name const MethodChannel('com.toly1994.ia_path'); Future<Directory> getTemporaryDirectory() async { final String path = await _channel.invokeMethod<String>('getTemporaryDirectory'); if (path == null) { return null; } return Directory(path); }}Copy the code
3.2: the Android file
class IaPathPlugin(registrar: Registrar) : MethodCallHandler {
private var mRegistrar: Registrar? = registrar
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "com.toly1994.ia_path")
val instance = IaPathPlugin(registrar)
channel.setMethodCallHandler(instance)
}
}
override fun onMethodCall(call: MethodCall, result: Result) {
when(call.method){
"getTemporaryDirectory"->{
result.success(geTemporaryDirectory())
}
}
}
private fun geTemporaryDirectory(): String {
return mRegistrar!!.context().cacheDir.path;
}
}
Copy the code
3.3: iOS files
public class SwiftIaPathPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "com.toly1994.ia_path", binaryMessenger: registrar.messenger())
let instance = SwiftIaPathPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getTemporaryDirectory" :
result(NSTemporaryDirectory())
default :
result("UnKnown")
}
}
}
Copy the code
3.4:
import 'package:flutter/material.dart'; import 'package:ia_path/ia_path.dart'; void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { String _str = 'CacheDir:\n'; @override void initState() { super.initState(); IaPath().getTemporaryDirectory().then((dir){ setState(() { _str+=dir.path; }); }); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Plugin example app'), ), body: Container( child: Text('Running on: $_str\n'), ), ), ); }}Copy the code
4. Other project reference plug-ins
4.1: Publish to the Public network
There’s a wall. It’s up to you.
-- -- -- - > [post] - flutter packages pub publish -- -- -- - > [use] - dependencies: ia_path: ^ 0.0.1Copy the code
4.2: Local use
After testing, the use is correct
dependencies:
ia_path:
path: /Volumes/coder/Project/Flutter/plugins/ia_path
Copy the code
Of course, you can also call the Android and iOS methods directly in this project, just as you did in the plugin.
This article looks at plugin customization and code handling on both platforms. The next article will cover MethodChannel in more detail so that you don’t have to worry about Flutter.
conclusion
This is the end of this article. If you want to taste Flutter quickly, Flutter For Seven days is a must-have. If you want to explore it, follow in my footsteps and complete a Flutter tour. In addition, I have a Flutter wechat communication group. You are welcome to join and discuss Flutter issues together. My wechat account is ZDL1994328.