【Flutter】PackageInfoをPackageInfo.of(context)で呼べるようにする
MediaQuery.of(context)
と同じようにpackage_info_plusをPackageInfo.of(context)
で呼び出せるようにする方法です。
PackageInfoWidgetの作成
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart' as p;
class PackageInfoWidget extends StatefulWidget implements ProxyWidget {
final Widget child;
const PackageInfoWidget({super.key, required this.child});
PackageInfoWidgetState createState() => PackageInfoWidgetState();
}
class PackageInfoWidgetState extends State<PackageInfoWidget> {
p.PackageInfo? _info;
void initState() {
p.PackageInfo.fromPlatform().then((value) {
_info = value;
});
super.initState();
}
Widget build(BuildContext context) =>
PackageInfo(() => _info, child: widget.child);
}
class PackageInfo extends InheritedWidget {
final p.PackageInfo? Function() _getInfo;
const PackageInfo(this._getInfo, {super.key, required super.child});
static p.PackageInfo? maybeOf(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<PackageInfo>()?._getInfo();
static p.PackageInfo of(BuildContext context) {
final result = maybeOf(context);
assert(result != null, 'No PackageInfo found in context');
return result!;
}
bool updateShouldNotify(_) => false;
}
of(context)
で呼び出せるようにするにはInheritedWidget
が必要なのですが、InheritedWidget
だけだとこのWidgetが再ビルドされた時に_info
が初期化されてしまう可能性があるため、StatefulWidget
を親に配置しています。
本来は保持している値(今回は_info
)に変化があった場合、updateShouldNotify
でtrue
を返す必要がありますが、p.PackageInfo
は一度生成した後は変化することがないので常にfalse
を返しています。
また、_info
をInheritedWidget
の引数として渡し、初期化が終わった時にsetState
で一度ビルドし直す方法がFlutterの設計としては正しいのですが、無駄に重くなるので_getInfo()
で呼び出す方式にしています。
実際に利用する
特に理由がなければrunApp
の真下に配置すると楽です。
runApp(PackageInfoWidget(child: MyApp()));
これでbuild(BuildContext context)
内で呼び出せるようになります。
print(PackageInfo.of(context).buildNumber);
基本的に呼び出す頃には初期化が終わっていると思いますが、アプリを起動してすぐに利用することがあるなど、PackageInfo
の初期化が間に合わない可能性のある場合はmaybeOf
を使用してください。
余談
Flutterは、このようにInheritedWidget
に流したいModelクラスを持たせる方法が一応公式の書き方です(参考)。
これでは必要なModelクラスの数だけInheritedWidget
を作らなければなりません。超面倒
恐らく公式としては「誰かもっと便利にInheritedWidget
を使えるパッケージ作ってな!」という考えのもと、この書き方だけしか作らなかったのだと思いますが、
それに呼応するように Remi Rousselet 氏が「InheritedWidget
をもっと使いやすく、再利用性を高めるためのラッパー[1]」としてproviderを作り、単なるラッパーでは限界を感じたのか更なる利便性を求めてriverpodを作りました。
Remi氏凄すぎ。
備考
本記事は以下の環境で検証しました
Flutter 3.19.2 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 7482962148 (9 days ago) • 2024-02-27 16:51:22 -0500
Engine • revision 04817c99c9
Tools • Dart 3.3.0 • DevTools 2.31.1
Discussion