Skip to content

Instantly share code, notes, and snippets.

@daiki1003
Last active February 26, 2023 13:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daiki1003/53c30c3d582c0e38a93ecbbe36ab1ba2 to your computer and use it in GitHub Desktop.
Save daiki1003/53c30c3d582c0e38a93ecbbe36ab1ba2 to your computer and use it in GitHub Desktop.
An app which has BottomNavigationBar displaying badge count also has app icon.
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