diff --git a/packages/neon/neon/lib/src/utils/app_implementation.dart b/packages/neon/neon/lib/src/utils/app_implementation.dart index aa6ee3ee..9456afb3 100644 --- a/packages/neon/neon/lib/src/utils/app_implementation.dart +++ b/packages/neon/neon/lib/src/utils/app_implementation.dart @@ -41,6 +41,18 @@ abstract class AppImplementation(context, listen: false); + final account = accountsBloc.activeAccount.value!; + final bloc = getBloc(account); + + return NeonNavigationDestination( + label: name(context), + icon: buildIcon, + notificationCount: getUnreadCounter(bloc), + ); + } + Widget buildIcon({ final Size size = const Size.square(32), final Color? color, diff --git a/packages/neon/neon/lib/src/widgets/drawer_destination.dart b/packages/neon/neon/lib/src/widgets/drawer_destination.dart new file mode 100644 index 00000000..8d0824e2 --- /dev/null +++ b/packages/neon/neon/lib/src/widgets/drawer_destination.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; +import 'package:neon/neon.dart'; +import 'package:rxdart/subjects.dart'; + +typedef DestinationIconBuilder = Widget Function({Size size, Color color}); + +class NeonNavigationDestination { + const NeonNavigationDestination({ + required this.label, + required this.icon, + this.selectedIcon, + this.notificationCount, + }); + + final String label; + final DestinationIconBuilder icon; + final Widget? selectedIcon; + final BehaviorSubject? notificationCount; +} + +extension NavigationDestinationExtension on NavigationDestination { + static NavigationDestination fromNeonDestination(final NeonNavigationDestination neonDestination) => + NavigationDestination( + label: neonDestination.label, + icon: neonDestination.icon(), + selectedIcon: neonDestination.selectedIcon, + ); +} + +extension NavigationRailDestinationExtension on NavigationRailDestination { + static NavigationRailDestination fromNeonDestination(final NeonNavigationDestination neonDestination) { + final iconWIdget = StreamBuilder( + stream: neonDestination.notificationCount, + initialData: 0, + builder: (final context, final snapshot) { + final colorScheme = Theme.of(context).colorScheme; + + final color = snapshot.data! > 0 ? colorScheme.primary : colorScheme.onBackground; + const size = Size.square(kAvatarSize * 2 / 3); + + final icon = Container( + margin: const EdgeInsets.all(5), + child: neonDestination.icon(size: size, color: color), + ); + + if (snapshot.data! <= 0) { + return icon; + } + + final notificationIdicator = Builder( + builder: (final context) { + final style = TextStyle( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ); + + return Text( + snapshot.data!.toString(), + style: style, + ); + }, + ); + + return Stack( + alignment: Alignment.bottomRight, + children: [ + icon, + notificationIdicator, + ], + ); + }, + ); + + return NavigationRailDestination( + label: Text(neonDestination.label), + icon: iconWIdget, + selectedIcon: neonDestination.selectedIcon, + ); + } +} + +extension NavigationDrawerDestinationExtension on NavigationDrawerDestination { + static NavigationDrawerDestination fromNeonDestination(final NeonNavigationDestination neonDestination) { + final labelWidget = StreamBuilder( + stream: neonDestination.notificationCount, + initialData: 0, + builder: (final context, final snapshot) { + final label = Text(neonDestination.label); + + if (snapshot.data! <= 0) { + return label; + } + + final notificationIdicator = Padding( + padding: const EdgeInsets.only(left: 12, right: 24), + child: Builder( + builder: (final context) { + final style = TextStyle( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + fontSize: 14, + ); + + return Text( + snapshot.data!.toString(), + style: style, + ); + }, + ), + ); + + return Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + label, + notificationIdicator, + ], + ), + ); + }, + ); + + return NavigationDrawerDestination( + label: labelWidget, + icon: neonDestination.icon(), + selectedIcon: neonDestination.selectedIcon, + ); + } +} + +extension TabExtension on Tab { + static Tab fromNeonDestination(final NeonNavigationDestination neonDestination) => Tab( + text: neonDestination.label, + icon: neonDestination.icon(), + ); +}