【Flutter】初めてのPigeon
この記事はとりあえずPigeonを使ってiOS側と通信するのを試すものです。Android側は省いています🙇♂️
Pigeonって何
Flutterでネイティブ側との通信に使われるMethod Channelという機能があります。
Pigeonは、そのMethod Channelを型安全に行うのためのパッケージです。
まだMethod Channelを触ったことがない方は公式にチュートリアルがあるのでそれをやってから、Pigeonを触ってみることをお勧めします。
Pigeonの読み方
Pigeonは”ピジョン”と読むっぽい。
環境
$ flutter --version
╔════════════════════════════════════════════════════════════════════════════╗
║ A new version of Flutter is available! ║
║ ║
║ To update to the latest version, run "flutter upgrade". ║
╚════════════════════════════════════════════════════════════════════════════╝
Flutter 2.5.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 18116933e7 (4 months ago) • 2021-10-15 10:46:35 -0700
Engine • revision d3ea636dc5
Tools • Dart 2.14.4
始める
とりあえずPigeonの公式のexampleを参考に、より簡易にアレンジしたものを使います。
Pigeonを導入する
dev_dependencies:
pigeon: ^1.0.17
ネイティブ側とやりとりするクラスを作成する
ルートにpigeonsディレクトリを作成し、その中にmessage.dartファイルを作成する。
import 'package:pigeon/pigeon.dart';
class Book {
String? title;
String? author;
}
()
abstract class BookApi {
List<Book?> recommend();
}
pigeonにコードを生成させる
$ flutter pub run pigeon \
--input pigeons/message.dart \
--dart_out lib/pigeon.dart \
--objc_header_out ios/Runner/pigeon.h \
--objc_source_out ios/Runner/pigeon.m
コマンド | 意味 |
---|---|
--input | objective-c, javaに変換する対象のファイル |
--dart_out | flutter側から使うコードの出力先 |
--objc_header_out | objective-cのheaderに変換されたコードの出力先 |
--objc_source_out | objective-cのsourceに変換されたコードの出力先 |
上記のコマンドを実行すると、以下の3つのファイルが作成されます。
- lib/pigeon.dart
- ios/Runner/pigeon.h
- ios/Runner/pigeon.m
Flutter側を書く
ボタンをタップするとiOS側で作ったBookの情報を取得してprintするだけの画面
import 'package:flutter/cupertino.dart';
import 'package:pigeon_tutorial/pigeon.dart';
class BookPage extends StatefulWidget {
const BookPage({Key? key}) : super(key: key);
State<StatefulWidget> createState() {
return _BookPageState();
}
}
class _BookPageState extends State<BookPage> {
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CupertinoButton(
child: const Text('tap!'),
onPressed: callNativeApi,
),
],
),
);
}
void callNativeApi () async {
final api = BookApi();
final result = await api.recommend();
final book = result[0]!;
print("${book.title}: ${book.author}");
}
}
ほぼflutterプロジェクトを立ち上げた状態。
表示する画面を上で作成したBookPageにしている。
import 'package:flutter/material.dart';
import 'package:pigeon_tutorial/book_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const BookPage(), // ここを変更した
);
}
}
...
iOS側を書く
ファイルを作成し、Flutterk側で作成したBookApiを継承したMyApiを作成する。
import Foundation
class MyApi: NSObject, BookApi {
func recommendWithError(_ error: AutoreleasingUnsafeMutablePointer<FlutterError?>) -> [Book]? {
return [Book.make(withTitle: "タイトル", author: "著者")]
}
}
ここで注目していただきたいのは、Flutter側で定義したBookApiのrecommendとちょっと関数の名前や引数が違う。
実は、Pigeonが色々調整してくれるようです。
ありがたいですが、どう書いたらいいのかわからないのは困りものです。
ここの書き方は、ios/Runner/pigeon.h
をみるとわかる。
...
@interface Book : NSObject
+ (instancetype)makeWithTitle:(nullable NSString *)title
author:(nullable NSString *)author;
...
...
@protocol BookApi
- (nullable NSArray<Book *> *)recommendWithError:(FlutterError *_Nullable *_Nonnull)error;
@end
...
ここをみるとどのように対応したら良いかがわかる。
AppDelegate.swiftを修正する
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
BookApiSetup(controller.binaryMessenger, MyApi())
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
追加したコードは以下の二行。
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
BookApiSetup(controller.binaryMessenger, MyApi())
実行する
tap! と書かれている部分をタップすると以下のように出力される。
もし動かなかったら、、、
生成したpigeon.h, pigen.mがうまく読み込めていない可能性があります。
これでやってみてください。
Discussion