본문 바로가기

프로그래밍/Flutter

[flutter] 플러그인 프로젝트 자동 생성 요소

1. 플러그인 프로젝트 생성

flutter create --org com.example --template=plugin --platforms=android,ios -i swift hello_world

 

/pubspec.yaml

flutter:
   plugin:
      ios:
         pluginClass: HelloWorldPlugin

 

2. API 구현

1) PlatformInterface

플랫폼 api 정의

lib/hello_world_platform_interface.dart

자동생성 코드는 정의부와 구현부를 분리하기 위해 별도 클래스를 생성
기본적인 인스턴스 할당과 구현이 필요한 인터페이스 메쏘드들을 정의

import 'package:plugin_platform_interface/plugin_platform_interface.dart';

abstract class HelloWorldPlatform extends PlatformInterface {
   HelloWorldPlatform() : super(token: _token);
   
   static final Object _token = Object();
   
   static HelloWorldPlatform _instance = MethodChannelHelloWorld();
   
   static HelloWorldPlatform get instance => _instance;
   static set instance(HelloWorldPlatform instance) {
      PlatformInterface.verityToken(instance, _token);
      _instance = instance;
   }
   
   Future<String>? getPlatformVersion() {
      throw UnimplementedError('platformVersion() has not been implemented.');
   }
}

 

 

2) MethodChannel

lib/hello_world_method_channel.dart

플랫폼 인터페이스의 구현부로 실제 플랫폼의 메쏘드 이름을 매핑해 호출하고 결과를 반환

채널명이 "hello_world" 인 메쏘드 채널 생성
해당 메쏘드 채널의 특정 메쏘드 호출

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

import 'hello_world_platform_interface.dart';

// api 구현부
class MethodChannelHelloWorld extends HelloWorldPlatform {
   @visibleForTesting
   final methodChannel = const MethodChannel('hello_world');
   
   @override
   Future<String?> getPlatformVersion() async {
      final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
      return version;
   }
}

 

 

3) API 메인 클래스

/lib/hello_world.dart

import 'hello_world_platform_interface.dart'

class HelloWorld {
   Future<String?> getPlatformVersion() {
      return HelloWorldPlatform.instance.getPlatformVersion();
   }
}

 

3. ios 플러그인 구현

FlutterPlugin 상속받은 클래스로 메쏘드가 호출되었을때 처리부 handle(_ call: result:) 를 구현하기 위한 클래스
이 클래스가 정상 호출되도록 하기위해서는 FlutterPluginRegistrar를 통해 등록해 주어야 하는데, 샘플에서는 static 메쏘드로 채널명이 "hello_world" 인 메쏘드 채널을 등록한다.
이 static 메쏘드를 앱 시작할때에 등록해주면 해당 메쏘드 채널과 핸들러가 등록된다.

swift 로 프로젝트 생성 시

/ios/Classes/HelloWorldPlugin.swift

import Flutter

public class HelloWorldPlugin: NSObject, FlutterPlugin {
   public static func register(with registrar: FlutterPluginRegistrar) {
      let channel = FlutterMethodChannel(name: "hello_world", binaryMessenger: registrar.messenger())
      let instance = HelloWorldPlugin()
      registrar.addMethodCallDelegate(instance, channel: channel)
   }
   
   public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
      return IODevice.current.systemVersion
   }
}

 

objc로 프로젝트 생성 시

/ios/Classes/HelloWorldPlugin.h, .m

@interface HelloWorldPlugin : NSObject<FlutterPlugin>
@end


@implementation HelloPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
   FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"hello_world" binaryMessenger:[registrar messenger]];
   HelloWorldPlugin* instance = [[HelloWorldPlugin alloc] init];
   [registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
   if (@"getPlatformVersion" isEqualToString:call.method]) {
      result([@"" stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
   } else {
      result(FlutterMethodNotImplemented);
   }
}
@end

 

4. 샘플 앱에서 플러그인 사용

example/pubspec.yaml

1) dependency 설정

dependencies:
   flutter:
      sdk: flutter
   
   hello_world:
      path: ../

 

2) 자동생성 등록 루틴

example/ios/Runner/GeneratedPluginRegistrant.h, .m

프로젝트 생성 후에는 이 메쏘드가 존재하지 않으며, 앱 build 시에 자동 생성(flutter build ios --no-codesign)
플러그인 구현부의 static 메쏘드를 호출해 메쏘드채널과 핸들러를 등록한다.

@interface GeneratedPluginRegistrant: NSObject
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
@end


@impleementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
   [HelloWorldPlugin registerWithRegistrar: [registry registrarForPlugin:@"HelloWorldPlugin"]];
}

 

3) AppDelegate 플러그인 등록

실제 AppDelegate는 Flutter 내부에 있으며, FlutterAppDelegate 를 상속받아 생성된 등록 루틴을 호출한다.

import Flutter

@UIApplicationMain
@objc class Adddelegate: FlutterAppDelegate {
   override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
      GeneratedPluginRegistrant.register(with: self)
      return super.application(application, didFinishLaunchingWithOptions: launchOptions)
   }
}

 

4) 플러그인 API 사용

example/lib/main.dart

// entry point
void main() {
   runApp(const MyApp());
}

class MyApp extends StatefulWidget {
   const MyApp({super.key});
   
   @override
   State<MyApp> createState() => _MyAppState();
}


// api 객체 생성 및 사용
class _MyAppState extends State<MyApp> {
   String _platformVersion = 'Unknown';
   final _helloWorldPlugin = HelloWorld();
   
   
   Future<void> initPlaotformState() async {
      String platformVersion;
      
      try {
         platformVersion = await _helloWorldPlugin.getPlatformVersion() ?? 'unknown';
      } on PlatformException {
         platformVersion = 'faliled';
      }
      
      if (!mounted) return;
      
      setState(() {
         _platformVersion = platformVersion;
      });
   }
   
   
   @Override
   Widget build(BuildContext context) {
      return MaterialApp(home: 
         Scaffold(
            appBar: AppBar(title: const Text('Plugin example app'),
            body: Center(child: Text('version: $_platformVersion')
         )
      );
   }
}