[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:+'
}

Sample 5

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.

Sample 6

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:

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]

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.