##実装方法
- flutterのshared_preferencesで表示したい値を保存
- flutterのMethodCannelでswiftのコードを呼び出す
- swiftで保存した値をUserDefaults(flutterの形式)で取り出す
- AppGroupを用いてWidgetKitと連携
- UserDefaultsをiOSの形式で保存し直す
- WidgetKit側でUserDefaultsを参照し表示
Widgetの値を変更したい場合は、再度MethodCannelを呼び出して、UserDefaultsの値を変更したのち、Widgetを更新すれば良い
##1. flutterのshared_preferencesで表示したい値を保存
main.dart内で次のように実装
int _counter = 0;
final SharedPreferences _prefs = await SharedPreferences.getInstance();
_prefs.setInt('count', _counter);
##2. flutterのMethodCannelでswiftのコードを呼び出す
static const methodChannel = MethodChannel('[your app name]/sample');
try {
final int result = await methodChannel.invokeMethod('setDataForWidgetKit');
print('SET setUserDefaultsForAppGroup: $result');
} on PlatformException catch (e) {
print('ERROR setUserDefaultsData: ${e.message}');
}
##3. swiftで保存した値をUserDefault(flutterの形式)で取り出す
AppDelegate内のApplication関数内に次のコードを追加
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: "[your app name]/sample",
binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "setDataForWidgetKit" {
self.setUserDefaultsForAppGroup(result: result)
}
result(FlutterMethodNotImplemented)
return
})
[your app name]/sampleという名前のMethodCannelの、setDataForWidgetKit関数がflutterからinvokeされたら、swiftのsetUserDefaultsForAppGroup関数を呼び出すという意味である。
##4. AppGroupを用いてWidgetKitと連携
Xcode上のTARGETのRunner->Signing & Capabilities -> + CapabilityからApp Groupを追加
グループの名前は名前は慣例的にgroup.[bundle id]とする
次に、File -> New -> TargetからWidget Extensionを追加する
##5. UserDefaultsをiOSの形式で保存し直す
AppDelegate.swift内のAppDalegateクラス内に以下の関数を追加する
private func setUserDefaultsForAppGroup(result: FlutterResult) {
guard let userDefaults = UserDefaults(suiteName: "group.[your bundle id]") else {
return result(FlutterError(code: "UNAVAILABLE",
message: "setUserDefaultsForAppGroup Failed",
details: nil))
}
let defaults = UserDefaults.standard
let count = defaults.value(forKey: "flutter.count") as? Int
userDefaults.setValue(count, forKey: "count")
if #available(iOS 14.0, *) {
WidgetCenter.shared.reloadAllTimelines()
}
result(true)
}
flutter.countというUserDefaults(flutterとSwiftでデータを共有するためのもの)を取得し、countというUserDefaults(App Group内でデータを共有するためのもの)に定義し直している。
また、iOS14.0以降の端末でWidgetを更新するようにしている
##6. WidgetKit側でUserDefaultを参照し表示
最後にWidgetKitのテンプレートを修正して、値を表示させれば良い
struct SimpleEntry内にプロパティを追加
let count: Int
getSnapshot関数内に以下のコードを追加する
let userDefaults = UserDefaults(suiteName: "group.[your bundle id]")
let countData = userDefaults?.value(forKey: "count") as? Int
最後にmy_widgetEntryViewのbodyの値を次のように変更する
Text(String(entry.count))
あとはnull Saftyに注意しながら、entryの引数を修正していけば実装できる。
##つまずいたところ
・Swiftの機能であるApp GroupやUserDefaultsの仕組みをある程度理解しなければならない
・swiftのコードの実行時にエラーやログがまともに出ないため、手探りでミスを見つけなければならない
・userDefaultsやMethodCannelで使うKeyのタイプミスで時間が取られる可能性がある
・WidgetKitのUIを作るとき、結局ある程度はswiftUIを理解しなければならない
・flutterに必ずresultを返さないと例外が発生する
##参考資料
以下の記事がわかりやすく参考になった。