FlutterでAndroid/iOSの機能を使う
Flutterの課題
前回の記事でFlutterの特徴は、クロスプラットフォームが最大の特徴
と伝えましたが、クロスプラットフォームゆえ様々な課題を抱えています。
- プラットフォーム(Android/iOS)の機能を最大限活かすことができない
- 独自のUIでレンダリング(描画)するため、プラットフォーム毎にUIを変更することができない
- Dartで開発するため、学習コストが必要
今回はMethodChannelを活用し、ネイティブの機能を実装する方法をご紹介します。
MethodChannel
MethodChannelとは、Dartからネイティブ(Java,Kotlin,Objective-C,Swift)の機能を呼び出すもしくは、プラットフォームからDartの機能を呼び出すことができる方法です。
iPhoneのバッテリー状況を取得してみる
SwiftのUIDevice classをDartで呼び出し、Flutterで作成したアプリにバッテリー状況を表示するデモアプリをご紹介します。
Flutter(Dart)のプログラム
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/cupertino.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { static const platform = const MethodChannel('samples.flutter.dev/battery'); String _batteryLevel = 'Battery Level'; Future<void> _getBatteryLevel() async { String batteryLevel; try { final int result = await platform.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { _batteryLevel = batteryLevel; }); } @override Widget build(BuildContext context) { return Material( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( child: Text('Get Battery Level'), onPressed: _getBatteryLevel, ), Text(_batteryLevel), ], ), ), ); } }
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 let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery", binaryMessenger: controller.binaryMessenger) batteryChannel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in // Note: this method is invoked on the UI thread. guard call.method == "getBatteryLevel" else { result(FlutterMethodNotImplemented) return } self?.receiveBatteryLevel(result: result) }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func receiveBatteryLevel(result: FlutterResult) { let device = UIDevice.current device.isBatteryMonitoringEnabled = true if device.batteryState == UIDevice.BatteryState.unknown { result(FlutterError(code: "UNAVAILABLE", message: "Battery info unavailable", details: nil)) } else { result(Int(device.batteryLevel * 100)) } } }
実行結果
まとめ
MethodChannelを活用することにより、プラットフォームの機能を活かすことができ、効率よく開発することができます。