概要
どうも、@daiki1003です!この記事は、Flutter #1 Advent Calendarの10日目の記事です。
こんなやつ実装します。
コード量的には200行ほどで出来てしまいます!
(自分でもびっくり)
実際の動作 |
---|
今回の全コードはgist.github.comに載せています!
・main.dartにコピペ
・pubspec.yamlに依存関係を追加
だけで動くと思います。
ざっくりとした手順
// 機能
1. 表示するバッジ数を取得 (本記事では説明は割愛)
2. バッジ数を保持するためのStateProviderを定義
3. それぞれのバッジ数をStateProviderに保持
4. アプリの状態を監視する (本記事では説明は割愛)
5. バックグラウンドに行く際にアプリのバッジを設定
// UI
6. お知らせバッジWidget生成
7. タブアイコンWidget生成
8. BottomNavigationBarの生成
それではやっていきましょう!
機能
1. 表示するバッジ数を取得 (本記事では説明は割愛)
こちらは詳細は割愛しますが、APIか内部処理で取得することになるでしょう。
2. バッジ数を保持するためのStateProviderを定義
final _homeBadgeProvider = StateProvider<int>( (ref) => 0, ); final _notificationBadgeProvider = StateProvider<int>( (ref) => 0, ); final _myPageBadgeProvider = StateProvider<int>( (ref) => 0, ); final _appBadgeProvider = Provider<int>( (ref) { final homeBadge = ref.watch(_homeBadgeProvider).state; final notificationBadge = ref.watch(_notificationBadgeProvider).state; final myPageBadge = ref.watch(_myPageBadgeProvider).state; return homeBadge + notificationBadge + myPageBadge; }, );
タブの分だけStateProviderを用意します。
後に通知数を更新する必要があるので、StateProviderを使用しています。
そして、最後アプリのアイコンに付けるバッジ数を
保持する_appBadgeProviderです。
アプリのタブに表示するバッジ数をref.watchで監視し、
それらが更新された時に勝手に再計算されます。
なので、僕らが意図的に値を更新することはありません。
そのため、Providerを使っています。
※ここでは、同じファイルで参照するのでprivateになっていますが、
別ファイルにpublicで定義してもOKです!
3. それぞれのバッジ数をStateProviderに保持
final badgesResponse = await http.get(url); final json = json.decode(badgesResponse.body); context.read(_homeBadgeProvider).state = json['home_count']; context.read(_notificationBadgeProvider).state = json['notification_count']; context.read(_myPageBadgeProvider).state = json['my_page_count'];
取得する部分は簡略化していますが、何らかの方法で取得したバッジ数を、
定義したStateProviderに保持していきます。
4. アプリの状態を監視する (本記事では説明は割愛)
WidgetsBindingObserver と言うmixinに準拠することによって
アプリの状態を監視する事が出来ます。
詳しくは別記事で解説しようと思っています。
5. バックグラウンドに行く際にアプリのバッジを設定
flutter_app_badgerと言うパッケージを使います。
パッケージのインストール方法に関してはこちらからどうぞ。
VSCodeを使っている方は圧倒的にこちらの方がおすすめです!
@override void didChangeAppLifecycleState(AppLifecycleState state) async { super.didChangeAppLifecycleState(state); if (state != AppLifecycleState.inactive) { return; } if (!await FlutterAppBadger.isAppBadgeSupported()) { return; } FlutterAppBadger.updateBadgeCount( context.read(_appBadgeProvider), );
上記メソッドはアプリの状態が変化した時に呼ばれます。
AppLifecyccleStateは
enum AppLifecycleState { // アプリ画面が表示されており、ユーザのインプットに反応できる状態 resumed, // ユーザのインプットに反応出来ないが、Flutterのホストビューは動いている状態 // 認証画面が出ていたり、電話がかかってきたりアップスイッチャーが立ち上がっていたりといった状態 inactive, // アプリ画面は表示されておらず、ユーのインプットに反応もせず、バックグラウンドで動いている状態 paused, // Flutterエンジン上で動いているが、、ホストビューからは切り離されている状態 detached, }
の様に定義されています。
今回はアプリがバックグラウンドに行く際に、アイコンにバッジを付けたいので
inactiveのみ監視します。(pausedではない)
次に、「アプリがバッジをサポートしているか」
つまりユーザがプッシュ通知に対して許可をしているかを確認します。
最後に、アプリのバッジ数を更新をします。
ここまでで、機能面の実装は終わりました。
タブにバッジを付けるUI部分を実装していきましょう。
UI
6. お知らせバッジWidget生成
class NotificationBadge extends StatelessWidget { const NotificationBadge({ @required this.badgeCount, }); final int badgeCount; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), decoration: BoxDecoration( color: Color.fromRGBO(250, 47, 49, 1.0), shape: BoxShape.circle, ), child: Text( badgeCount.toString(), style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), ); } }
特筆すべきことはありません、バッジ数を取得して赤背景で表示するだけです。
7. タブアイコンWidget生成
enum TabType { home, notification, myPage, } extension TabTypeEx on TabType { ... ... /// choose provider for observing StateProvider get provider { switch (this) { case TabType.home: return _homeBadgeProvider; case TabType.notification: return _notificationBadgeProvider; case TabType.myPage: return _myPageBadgeProvider; } throw UnimplementedError(); } } class TabItem extends HookWidget { const TabItem( this.type, { @required this.selected, }); final TabType type; final bool selected; @override Widget build(BuildContext context) { final badgeCount = useProvider(type.provider).state; final showBadge = 0 < badgeCount; return Stack( overflow: Overflow.visible, children: [ Icon(type.iconData), if (showBadge) Positioned( top: -7, right: -12, child: NotificationBadge(badgeCount: badgeCount), ), ], ); } }
TabTypeはタブの種類を表すenumです。
extensionを使ってそれぞれのタブに応じたデータを返す様にしています。
肝は
final badgeCount = useProvider(type.provider).state;
の部分。
3の手順で、stateを更新した際にこの TabItemクラスがリビルドされ
バッジ数が更新される様になっています。
そして、バッジ数が0以上の時にバッジが表示されると言う流れです。
ビューの部分に関して少しだけ補足。
NotificationBadgeを右上にはみ出る様に設置しています。
このはみ出た部分も含めて正しく描画するために、Stackにoverflowプロパティを設定しています。
8. BottomNavigationBarの生成
class Sample extends HookWidget { @override Widget build(BuildContext context) { final tabType = useState(TabType.home); return Scaffold( bottomNavigationBar: BottomNavigationBar( currentIndex: tabType.value.index, items: TabType.values .map( (tabType) => BottomNavigationBarItem( icon: TabItem(tabType, selected: false), activeIcon: TabItem(tabType, selected: true), label: tabType.title, ), ) .toList(), onTap: (value) => tabType.value = TabType.values[value], ), ); } }
今回の内容ではないので詳細は省きますが、
useStateを使うことによって値が更新された時にリビルドされる様にしています。
BottomNavigationBarItemを使って
非アクティブ時、アクティブ時のアイコンをそれぞれTabItemを指定します。
最後に
いかがでしたでしょうか?
記事執筆現在(2020/12/10)ではバッジをriverpodで実装した例は
見当たらないので(僕のググラビリティが低いだけの可能性ありw)、
皆様の参考になれば幸いです。
Twitterフォローお願いします
「次回以降も記事を読んでみたい!」「この辺分からなかったから質問したい!」
そんな時は、是非Twitter (@daiki1003)やInstagram (@ashdik_flutter)のフォローお願いします♪
Twitterコミュニティ参加お願いします
Twitterコミュニティ「Flutter lovers」を開設しました!参加お待ちしております😁
☕️ Buy me a coffee
また、記事がとても役に立ったと思う人はコーヒーを奢っていただけると非常に嬉しいです!
コメント