iOSのアプリをFlutterでコーディング中にダブルタップからのフリックでアプリをキルするタイミングに処理を施したいなんて時に、色々調べて試しました。
Flutter(Dart)〜プラットフォーム(Swift)間でのやり取りから学習し、最終的にはFlutterのみで解決できることが発覚💦
Swift(プラットフォーム)〜Dart(Flutter)間での呼び出しのタイミングとメソッドの操作
ios/Runner/AppDelegate.swift
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Flutter側からメソッドを検知する
let _controller :FlutterViewController = window?.rootViewController as! FlutterViewController
let _channel = FlutterMethodChannel(name: "test_hoge", binaryMessenger: _controller.binaryMessenger)
_channel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
switch call.method {
// Flutter側のコードを取得できたらネイティブメソッドを呼び出す
case "getHoge":
self.receiveWillGetHoge(result: result,_controller: _controller)
// 引数がある場合はcall.argumentsから取得
return
default:
result(FlutterMethodNotImplemented)
return
}
// Flutterへの戻り値はresult()で返す
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// ネイティブ(Swift)側で呼び出すメソッド
func receiveWillGetHoge(result: FlutterResult,_controller: FlutterViewController) {
print("FlutterのGET HOGE!!ボタンが押されました")
}
// フリックしてアプリを終了させた時に5秒間呼ばれ、その後アプリが破棄される
override func applicationWillTerminate(_ application: UIApplication) {
self.window.isHidden = true;
print("applicationWillTerminate start")
// Flutter側のメソッドを呼び出したい時はinvokeMethodを利用iOS側では第2引数が省略できない為nilで対応
let _controller :FlutterViewController = window?.rootViewController as! FlutterViewController
let _channel = FlutterMethodChannel(name: "test_hoge", binaryMessenger: _controller.binaryMessenger)
_channel.invokeMethod("getHoge", arguments: nil)
sleep(5)
print("applicationWillTerminate end")
}
// ダブルタップ後にアプリを保留にした時に呼ばれる
override func applicationWillResignActive(_ application: UIApplication) {
print("applicationWillResignActive")
self.window.isHidden = false;
}
// アプリを開いた時に呼ばれる
override func applicationDidBecomeActive(_ application: UIApplication) {
print("applicationDidBecomeActive")
self.window.isHidden = false;
}
// 保留中のアプリを選択してアプリに入った時に呼ばれる
override func applicationWillEnterForeground(_ application: UIApplication) {
print("applicationWillEnterForeground")
self.window.isHidden = false;
}
// アプリは保留中だがタップしてホームに戻った時に呼ばれる
override func applicationDidEnterBackground(_ application: UIApplication) {
print("applicationDidEnterBackground")
self.window.isHidden = false;
}
}
// AndroidであればKotlinでの実装になりますので別の記事を参照下さい。
main.dartでは、Swift側に送るメソッドとFlutter側で受け取るメソッドを記述
lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class TestView extends HookConsumerWidget {
const TestView({Key? key}) : super(key: key);
// FlutterとSwiftを結ぶ為のMethodChannelAPI
static const platform = MethodChannel('test_hoge');
@override
Widget build(BuildContext context, WidgetRef ref) {
// Swiftに送るメソッド
Future<void> _getHoge() async {
String willGetHoge;
try {
//Swiftに'getHoge'メッセージを送る
await platform.invokeMethod('getHoge');
} on PlatformException catch (e) {
willGetHoge = "Failed to get hoge";
}
}
// Flutter側で受け取る
platform.setMethodCallHandler((MethodCall call) {
switch (call.method) {
case 'getHoge':
debugPrint('ネイティブ側のメソッドを取得');
// 引数がある場合はcall.argumentsから取得
break;
default:
debugPrint('');
break;
} throw Exception('');
});
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// ボタンを押して'getHoge'を送信 プラットフォーム側のメソッドが呼ばれる
ElevatedButton(
child: const Text('GET HOGE!!'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueAccent,
foregroundColor: Colors.white,
),
onPressed: () => _getHoge(),
)
])));
}
}
とまあ、上記の要領でやりとりをする手段もあるのですが正確に言えばアプリがkillしたタイミングがわかればいいのでもっと他にあるのではと調べていましたら、AppLifecycleState
を発見!
WidgetsBindingObserver
を利用してdidChangeAppLifecycleState
メソッドでアプリのライフサイクルを検知するすることができるらしく挑戦してみるものの、pausedやresumedは取得できたがHooks
を使っているコードだからか全ての状態を上手く取得することができなかった。調べているとuseAppLifecycleState
で通知している記事を発見!
ドキュメントを辿っていくと、useOnAppLifecycleStateChange
に辿り着いた!
useOnAppLifecycleStateChangeでアプリKILLのタイミングを検知
lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class TestView extends HookConsumerWidget {
const TestView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
// if文での処理
useOnAppLifecycleStateChange((beforeState, afterState) async {
if (afterState == AppLifecycleState.detached) {
// 今回目標としていたアプリがKILLされたタイミング!ここに処理を書く。
debugPrint('保留状態からフリックしてアプリをKILL(破棄)したタイミング');
}
});
// switch文での処理
useOnAppLifecycleStateChange((_, state) {
switch (state) {
case AppLifecycleState.resumed:
debugPrint('ホームからダブルタップして保留中のアプリを再開したタイミング');
break;
case AppLifecycleState.inactive:
debugPrint('ダブルタップでアプリ保留(バックグラウンド状態)したタイミング');
break;
case AppLifecycleState.paused:
debugPrint('ワンタップしてホームに戻った状態(一時停止状態)の時に呼ばれる');
break;
case AppLifecycleState.detached:
// 上記と同じ
break;
}
});
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text("test")
])));
}
}