[Flutter] Hướng dẫn tạo plugin và gọi thư viện native (Phần cuối)
Xem thêm Phần 1-2 Xem thêm Phần 3
Phần 4: Hướng dẫn thêm thư viện native
Trong phần này, mình sẽ demo việc gửi 1 DateTime từ flutter xuống native code để kiểm tra xem có phải ngày hiện tại hay không? Mình sẽ sử dụng thư viện Tempo của tác giả cesarferreira cho Android và SwiftDate của tác giả Daniele Margutti cho iOS.
Vì flutter và native không giao tiếp với nhau bằng biến loại DateTime được, nên mình sẽ cần chuyển DateTime sang dạng string UTC để xử lý nhé.
Thêm code flutter để hiển thị kết quả
Trong file lib/src/sample_call_native.dart các bạn thêm 1 hàm như sau:
static Future isToday(DateTime dateTime) async {
final date = dateTime.toUtc().toIso8601String();
final bool? isSuccess = await _channel.invokeMethod(
'isToday',
{
'dateTime': date,
},
);
return isSuccess;
}
Trong file example/lib/main.dart bạn đổi lại code như sau:
import 'package:flutter/material.dart';
import 'package:sample_plugin_flutter/sample_plugin_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// Phần 2. Hướng dẫn tạo Widget với plugin
SampleButton(
text: "Sample Button",
onPressed: () {
print("Sample Button Click");
},
),
/// Phần 3. Hướng dẫn gọi native code từ plugin
FutureBuilder(
future: SampleCallNativeFlutter.platformVersion,
builder: (_, snapshoot) {
return Text(snapshoot.data ?? '');
},
),
/// Phần 4. Hướng dẫn gọi native code từ plugin
FutureBuilder(
future: SampleCallNativeFlutter.isToday(DateTime.now()),
builder: (_, snapshoot) {
return Text('isToDay: ${DateTime.now()} is ${snapshoot.data}');
},
),
FutureBuilder(
future: SampleCallNativeFlutter.isToday(DateTime(2021,01,01)),
builder: (_, snapshoot) {
return Text('isToDay: ${DateTime(2021,01,01)} is ${snapshoot.data}');
},
),
],
),
),
),
);
}
}
Thêm thư viện cho iOS
Thường khi thêm 1 thư viện vào code iOS, bạn cần sử dụng Cocoapods thêm nó vào Podfile. Nhưng với plugin thì bạn sẽ thêm dependency nó vào ios/sample_plugin_flutter.podspec.
File này cũng giúp bạn khai báo s.static_framework = true(1 số thư viện native cần phải khai báo biến này) hay s.ios.deployment_target = ‘9.0’ (để giới hạn version build iOS).
(Nếu bạn chưa biết Cocoapods là gì, bạn có thể tham khảo tại đây)
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint sample_plugin_flutter.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'sample_plugin_flutter'
s.version = '0.0.1'
s.summary = 'A new flutter plugin project.'
s.description = < '../LICENSE' }
s.author = { 'Your Company' => '[email protected]' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
s.dependency 'SwiftDate' # Khai báo thư viện iOS tại đây
s.platform = :ios, '8.0'
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.swift_version = '5.0'
end
Sau đó bạn cần chạy pod install cho thư mục example/ios và vào Xcode chọn menu Product/Clean Build Folder. Trong file SwiftSamplePluginFlutterPlugin bạn đổi lại code như sau:
import Flutter
import UIKit
import SwiftDate
public class SwiftSamplePluginFlutterPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "sample_plugin_flutter", binaryMessenger: registrar.messenger())
let instance = SwiftSamplePluginFlutterPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
case "isToday":
isToday(call, result)
default:
result(nil)
}
}
private func isToday(_ call: FlutterMethodCall,_ result: @escaping FlutterResult) {
let arguments = call.arguments as! Dictionary
let dateTime = arguments["dateTime"] as! String;
// Convert to local
let localDate = dateTime.toDate(nil, region: Region.current)
// Check isToday
let checkToday = localDate?.isToday
result(checkToday)
}
}
Thế là xong bên iOS, giờ qua phần của Android.
Thêm thư viện cho Android
Trong Gradle Scripts/build.gradle(Module: android.sample_plugin_flutter) bạn thêm dòng bên dưới ở cuối file và nhấn Sync now
dependencies {
implementation 'com.github.cesarferreira:tempo:+'
}
Trong file android/src/main/kotlin/com/example/sample_plugin_flutter/SamplePluginFlutterPlugin.kt bạn đổi lại code như sau:
package com.example.sample_plugin_flutter
import androidx.annotation.NonNull
import com.cesarferreira.tempo.Tempo
import com.cesarferreira.tempo.isToday
import io.flutter.embedding.engine.plugins.FlutterPlugin
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
import java.text.SimpleDateFormat
import java.util.*
/** SamplePluginFlutterPlugin */
class SamplePluginFlutterPlugin: FlutterPlugin, MethodCallHandler {
/// 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
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "sample_plugin_flutter")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
"getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}")
"isToday" -> isToday(call, result)
else -> {
result.notImplemented()
}
}
}
private fun isToday(@NonNull call: MethodCall, @NonNull result: Result) {
var arguments = call.arguments as Map
var dateTime = arguments["dateTime"] as String
var localDate = dateTime.toDate()
var checkToday = localDate.isToday // library Tempo check isToday
result.success(checkToday)
}
private fun String.toDate(dateFormat: String = "yyyy-MM-dd'T'HH:mm:ss", timeZone: TimeZone = TimeZone.getTimeZone("UTC")): Date {
val parser = SimpleDateFormat(dateFormat, Locale.getDefault())
parser.timeZone = timeZone
return parser.parse(this)
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
Xong rồi, giờ chạy flutter run để xem thành quả cuối cùng thôi nào.
Kết thúc
Hi vọng qua bài viết của mình giúp ích cho các bạn phần nào việc làm qua viết plugin cho Flutter. Mình để link Github ở đây để các bạn tham khảo nha.
Nguồn tham khảo:
- Developing packages & plugins
- Bảng mapping các loại biến giữa các nền tảng
- Thư viện Tempo
- Thư viện SwiftDate
- Cocoapods
Bài viết đầy đủ tại Viblo Cảm ơn các bạn đã xem bài viết.
Tác giả
Phạm Tiến Dũng [email protected]