Instantly share code, notes, and snippets.
Last active
February 26, 2023 13:36
-
Save daiki1003/53c30c3d582c0e38a93ecbbe36ab1ba2 to your computer and use it in GitHub Desktop.
An app which has BottomNavigationBar displaying badge count also has app icon.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
/// Add dependencies these four packages :) | |
import 'package:flutter_hooks/flutter_hooks.dart'; | |
import 'package:hooks_riverpod/hooks_riverpod.dart'; | |
import 'package:flutter_riverpod/flutter_riverpod.dart'; | |
import 'package:flutter_app_badger/flutter_app_badger.dart'; | |
/// Define providers for holding badge number. | |
final _homeBadgeProvider = StateProvider<int>( | |
(ref) => 0, | |
); | |
final _notificationBadgeProvider = StateProvider<int>( | |
(ref) => 12, | |
); | |
final _myPageBadgeProvider = StateProvider<int>( | |
(ref) => 20, | |
); | |
/// Watch all providers for app icon. | |
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; | |
}, | |
); | |
void main() => runApp(ProviderScope(child: App())); | |
class App extends StatefulWidget { | |
@override | |
_AppState createState() => _AppState(); | |
} | |
class _AppState extends State<App> with WidgetsBindingObserver { | |
@override | |
Widget build(BuildContext context) => MaterialApp(home: Sample()); | |
/// for watching app lifecycle state, addObserver. | |
@override | |
void initState() { | |
super.initState(); | |
WidgetsBinding.instance.addObserver(this); | |
} | |
@override | |
void dispose() { | |
WidgetsBinding.instance.addObserver(this); | |
super.dispose(); | |
} | |
@override | |
void didChangeAppLifecycleState(AppLifecycleState state) async { | |
super.didChangeAppLifecycleState(state); | |
if (state != AppLifecycleState.inactive) { | |
return; | |
} | |
if (!await FlutterAppBadger.isAppBadgeSupported()) { | |
return; | |
} | |
FlutterAppBadger.updateBadgeCount( | |
context.read(_appBadgeProvider), | |
); | |
} | |
} | |
/// for describing tab types. | |
enum TabType { | |
home, | |
notification, | |
myPage, | |
; | |
String get title { | |
switch (this) { | |
case home: | |
return 'ホーム'; | |
case notification: | |
return 'お知らせ'; | |
case myPage: | |
return 'マイページ'; | |
} | |
throw UnimplementedError(); | |
} | |
IconData get iconData { | |
switch (this) { | |
case home: | |
return Icons.home; | |
case notification: | |
return Icons.notifications; | |
case myPage: | |
return Icons.people; | |
} | |
throw UnimplementedError(); | |
} | |
/// choose provider for observing | |
StateProvider get provider { | |
switch (this) { | |
case home: | |
return _homeBadgeProvider; | |
case notification: | |
return _notificationBadgeProvider; | |
case myPage: | |
return _myPageBadgeProvider; | |
} | |
throw UnimplementedError(); | |
} | |
} | |
/// icon Widget for displaying in BottomNavigationBarItem | |
class TabItem extends HookWidget { | |
const TabItem( | |
this.type, { | |
super.key, | |
@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), | |
), | |
], | |
); | |
} | |
} | |
/// An widget which displays a badge number with red background. | |
class NotificationBadge extends StatelessWidget { | |
const NotificationBadge({ | |
super.key, | |
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(219, 73, 58, 1.0), | |
shape: BoxShape.circle, | |
), | |
child: Text( | |
badgeCount.toString(), | |
style: const TextStyle( | |
color: Colors.white, | |
fontSize: 10, | |
fontWeight: FontWeight.bold, | |
letterSpacing: 0.75, | |
), | |
), | |
); | |
} | |
} | |
/// Main widget with BottomNavigationBar. | |
class Sample extends HookWidget { | |
const Sample({ | |
super.key, | |
}); | |
@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], | |
), | |
body: Center( | |
child: ButtonBar( | |
alignment: MainAxisAlignment.center, | |
children: [ | |
FlatButton.icon( | |
onPressed: () { | |
context.read(tabType.value.provider).state--; | |
}, | |
icon: Icon(Icons.remove), | |
label: Text(''), | |
), | |
FlatButton.icon( | |
onPressed: () { | |
context.read(tabType.value.provider).state++; | |
}, | |
icon: Icon(Icons.add), | |
label: Text(''), | |
), | |
], | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment