跳到主要内容

เนื้อหาภาษาไทย / 泰语内容

系统指南 内容已审核

การสร้าง Plugin สำหรับ Flutter

บทความนี้อธิบายถึงวิธีการสร้าง Flutter Plugin เพื่อใช้ประโยชน์จาก Native code โดยใช้ Platform Channels เป็นกลไกการสื่อสารหลักระหว่าง Flutter และ Native code (An...

การสร้าง Plugin สำหรับ Flutter

ในบทความที่แล้ว เราได้เรียนรู้เกี่ยวกับ Platform Channels ซึ่งเป็นกลไกสำคัญที่ช่วยให้ Flutter สามารถสื่อสารกับ Native code ของแต่ละแพลตฟอร์มได้ ในบทความนี้ เราจะนำความรู้เรื่อง Platform Channels มาประยุกต์ใช้ในการสร้าง Plugin สำหรับ Flutter ซึ่งจะช่วยให้เราสามารถนำโค้ด Native ที่เขียนไว้แล้วมาใช้ซ้ำในหลายๆ แอปพลิเคชันได้อย่างง่ายดาย

Plugin คืออะไร?

Plugin ใน Flutter คือ Package ที่ประกอบไปด้วยโค้ด Dart และ Native code (เช่น Java/Kotlin สำหรับ Android, Objective-C/Swift สำหรับ iOS) ซึ่งช่วยให้ Flutter สามารถเข้าถึงฟังก์ชันการทำงานของอุปกรณ์หรือระบบปฏิบัติการที่ Flutter เองไม่สามารถทำได้โดยตรง ตัวอย่างเช่น การเข้าถึง Camera, GPS, Bluetooth หรือการใช้ Native UI Components

Plugin ช่วยให้เรา:

  • นำโค้ด Native ที่มีอยู่แล้วมาใช้ซ้ำ
  • เข้าถึงฟังก์ชันการทำงานของอุปกรณ์ที่ Flutter ยังไม่รองรับ
  • สร้าง Component ที่มีประสิทธิภาพสูงซึ่งใช้ประโยชน์จาก Native APIs

โครงสร้าง Plugin

โครงสร้างพื้นฐานของ Plugin Flutter จะประกอบด้วย:

  • `lib/`: โฟลเดอร์ที่เก็บโค้ด Dart ซึ่งเป็น Interface สำหรับ Plugin
  • `android/`: โฟลเดอร์ที่เก็บโค้ด Native สำหรับ Android (Java/Kotlin)
  • `ios/`: โฟลเดอร์ที่เก็บโค้ด Native สำหรับ iOS (Objective-C/Swift)
  • `example/`: โฟลเดอร์ที่เก็บแอปพลิเคชันตัวอย่างที่ใช้ Plugin
  • `pubspec.yaml`: ไฟล์ที่กำหนดข้อมูลของ Plugin เช่น ชื่อ, เวอร์ชั่น, dependencies

เราจะมาสร้าง Plugin อย่างง่ายเพื่อแสดงข้อความจาก Native code กัน

ขั้นตอนการสร้าง Plugin

  1. สร้าง Plugin Project: ใช้ Flutter CLI สร้าง Plugin Project โดยใช้คำสั่ง:
  2. flutter create --template=plugin hello_plugin
  3. แก้ไข `pubspec.yaml`: แก้ไข `pubspec.yaml` เพื่อกำหนดข้อมูลของ Plugin เช่น ชื่อ, คำอธิบาย, เวอร์ชั่น, และ dependencies
    name: hello_plugin
    description: A new Flutter plugin project.
    version: 0.0.1
    homepage: https://example.com
    
    environment:
      sdk: ">=2.12.0 <3.0.0"
      flutter: ">=1.20.0"
    
    dependencies:
      flutter:
        sdk: flutter
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      flutter_lints: ^1.0.0
    
    flutter:
      plugin:
        platforms:
          android:
            package: com.example.hello_plugin
            pluginClass: HelloPlugin
          ios:
            pluginClass: HelloPlugin
    
  4. เขียน Dart Interface ใน `lib/hello_plugin.dart`: สร้าง Class ที่เป็น Interface สำหรับ Plugin ของเรา
    import 'dart:async';
    
    import 'package:flutter/services.dart';
    
    class HelloPlugin {
      static const MethodChannel _channel = const MethodChannel('hello_plugin');
    
      static Future<String?> getPlatformVersion() async {
        final String? version = await _channel.invokeMethod('getPlatformVersion');
        return version;
      }
    
      static Future<String?> getNativeGreeting(String name) async {
        final String? greeting = await _channel.invokeMethod('getNativeGreeting', {'name': name});
        return greeting;
      }
    }
    

    ในโค้ดนี้ เราได้สร้าง `MethodChannel` ชื่อ `hello_plugin` และสร้าง Method 2 ตัวคือ `getPlatformVersion` และ `getNativeGreeting` ซึ่งจะถูกเรียกจาก Native code ในภายหลัง

  5. เขียน Native Code สำหรับ Android ใน `android/src/main/kotlin/com/example/hello_plugin/HelloPlugin.kt`: สร้าง Plugin Class ที่จะ Handle Method Calls จาก Flutter
    package com.example.hello_plugin
    
    import androidx.annotation.NonNull
    
    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
    
    /** HelloPlugin */
    class HelloPlugin: FlutterPlugin, MethodCallHandler {
      private lateinit var channel : MethodChannel
    
      override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "hello_plugin")
        channel.setMethodCallHandler(this)
      }
    
      override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
        when (call.method) {
            "getPlatformVersion" -> {
                result.success("Android ${android.os.Build.VERSION.RELEASE}")
            }
            "getNativeGreeting" -> {
                val name = call.argument<String>("name") ?: "World"
                result.success("Hello, $name from Android!")
            }
            else -> {
                result.notImplemented()
            }
        }
      }
    
      override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
        channel.setMethodCallHandler(null)
      }
    }
    

    ในโค้ดนี้ เราได้สร้าง `HelloPlugin` Class ที่ Implement `FlutterPlugin` และ `MethodCallHandler` interface และ Override Method `onMethodCall` เพื่อ Handle Method Calls จาก Flutter โดย Method `getPlatformVersion` จะ Return Android Version และ Method `getNativeGreeting` จะ Return Greeting Message จาก Android

  6. เขียน Native Code สำหรับ iOS ใน `ios/Classes/HelloPlugin.m`: สร้าง Plugin Class ที่จะ Handle Method Calls จาก Flutter
    // HelloPlugin.m
    #import "HelloPlugin.h"
    
    @implementation HelloPlugin
    + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
      FlutterMethodChannel* channel = [FlutterMethodChannel
          methodChannelWithName:@"hello_plugin"
                binaryMessenger:[registrar messenger]];
      HelloPlugin* instance = [[HelloPlugin alloc] init];
      [registrar addMethodCallDelegate:instance channel:channel];
    }
    
    - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
      if ([@"getPlatformVersion" isEqualToString:call.method]) {
        result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
      } else if ([@"getNativeGreeting" isEqualToString:call.method]) {
        NSString *name = call.arguments[@"name"] ?: @"World";
        NSString *greeting = [NSString stringWithFormat:@"Hello, %@ from iOS!", name];
        result(greeting);
      } else {
        result(FlutterMethodNotImplemented);
      }
    }
    
    @end
    

    ในโค้ดนี้ เราได้สร้าง `HelloPlugin` Class ที่ Implement `FlutterPlugin` protocol และ Override Method `handleMethodCall` เพื่อ Handle Method Calls จาก Flutter โดย Method `getPlatformVersion` จะ Return iOS Version และ Method `getNativeGreeting` จะ Return Greeting Message จาก iOS

    สำหรับ Swift ให้สร้างไฟล์ `ios/Classes/HelloPlugin.swift` และ `ios/hello_plugin.podspec` ที่ถูกต้อง

  7. ทดสอบ Plugin ใน Example App: แก้ไข `example/lib/main.dart` เพื่อทดสอบ Plugin ของเรา
    import 'package:flutter/material.dart';
    import 'dart:async';
    
    import 'package:flutter/services.dart';
    import 'package:hello_plugin/hello_plugin.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      String _platformVersion = 'Unknown';
      String _greetingMessage = 'No Greeting Yet';
    
      @override
      void initState() {
        super.initState();
        initPlatformState();
      }
    
      Future<void> initPlatformState() async {
        String platformVersion;
        String greetingMessage;
        try {
          platformVersion = await HelloPlugin.getPlatformVersion() ?? 'Unknown platform version';
          greetingMessage = await HelloPlugin.getNativeGreeting("Flutter User") ?? 'No Greeting';
        } on PlatformException {
          platformVersion = 'Failed to get platform version.';
          greetingMessage = 'Failed to get greeting.';
        }
    
        setState(() {
          _platformVersion = platformVersion;
          _greetingMessage = greetingMessage;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: const Text('Hello Plugin example app'),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('Running on: $_platformVersion\n'),
                  Text('Greeting: $_greetingMessage\n'),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    ในโค้ดนี้ เราได้เรียก Method `getPlatformVersion` และ `getNativeGreeting` จาก Plugin ของเรา และแสดงผลลัพธ์บนหน้าจอ

  8. Run Example App: รัน Example App เพื่อทดสอบ Plugin ของเรา
    cd example
    flutter run

    คุณควรจะเห็น Platform Version และ Greeting Message ที่มาจาก Native Code บนหน้าจอ

กรณีใช้งานจริงและข้อผิดพลาดที่พบบ่อย

  • การจัดการ Error Handling: ควรมีการจัดการ Error Handling ที่ดีทั้งใน Dart และ Native Code เพื่อให้แอปพลิเคชันทำงานได้อย่างราบรื่น แม้ว่าจะเกิดข้อผิดพลาดขึ้น
  • การจัดการ Asynchronous Operations: Method Calls ระหว่าง Flutter และ Native Code เป็น Asynchronous Operations ดังนั้นควรใช้ `async` และ `await` อย่างเหมาะสม
  • การจัดการ Threading: ควรระมัดระวังเรื่อง Threading โดยเฉพาะใน Native Code เพื่อหลีกเลี่ยงปัญหา UI Blocking
  • Dependency Management: ตรวจสอบให้แน่ใจว่า Dependencies ใน `pubspec.yaml` และ Native Build Files ถูกต้องและเป็นปัจจุบัน
  • Platform-Specific Code: ทดสอบ Plugin บนทั้ง Android และ iOS เพื่อให้แน่ใจว่าทำงานได้อย่างถูกต้องบนทั้งสองแพลตฟอร์ม
  • Versioning: จัดการ Version ของ Plugin อย่างเหมาะสม เพื่อให้ผู้ใช้งานสามารถอัปเดต Plugin ได้อย่างง่ายดาย

สรุป

การสร้าง Plugin สำหรับ Flutter เป็นวิธีที่มีประสิทธิภาพในการนำโค้ด Native ที่มีอยู่แล้วมาใช้ซ้ำ และเข้าถึงฟังก์ชันการทำงานของอุปกรณ์ที่ Flutter ยังไม่รองรับ โดยการใช้ Platform Channels เป็นกลไกหลักในการสื่อสารระหว่าง Flutter และ Native Code ในบทความนี้ เราได้เรียนรู้วิธีการสร้าง Plugin อย่างง่าย พร้อมทั้งกรณีใช้งานจริงและข้อผิดพลาดที่พบบ่อย ในบทความถัดไป เราจะพูดถึง GraphQL และการใช้งานแทน REST API ใน Flutter

Office.in.th Custom Service

想基于此结构开发或定制系统?

Office.in.th 承接企业级 Web Application、API Integration 以及政府/企业管理系统的设计与开发,并提供长期支持。

查看软件开发服务