diff --git a/packages/neon/neon/lib/src/blocs/apps.dart b/packages/neon/neon/lib/src/blocs/apps.dart index 5d3b43a7..b5b00059 100644 --- a/packages/neon/neon/lib/src/blocs/apps.dart +++ b/packages/neon/neon/lib/src/blocs/apps.dart @@ -50,27 +50,22 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates }); appImplementations.listen((final result) { - if (result.hasData) { - final options = _accountsBloc.getOptionsFor(_account); - unawaited( - options.initialApp.stream.first.then((var initialApp) async { - if (initialApp == null) { - if (result.requireData.tryFind('files') != null) { - initialApp = 'files'; - } else if (result.requireData.isNotEmpty) { - // This should never happen, because the files app is always installed and can not be removed, but just in - // case this changes at a later point. - initialApp = result.requireData.first.id; - } - } - if (!activeApp.hasValue && initialApp != null) { - await setActiveApp(initialApp); - } - }), - ); - - unawaited(_checkCompatibility()); + if (!result.hasData) { + return; } + + final options = _accountsBloc.getOptionsFor(_account); + unawaited( + options.initialApp.stream.first.then((var initialApp) async { + initialApp ??= _getInitialAppFallback(); + + if (!activeApp.hasValue && initialApp != null) { + await setActiveApp(initialApp); + } + }), + ); + + unawaited(_checkCompatibility()); }); _capabilitiesBloc.capabilities.listen((final result) { @@ -86,6 +81,25 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates unawaited(refresh()); } + /// Determines the appid of initial app. + /// + /// It requires [appImplementations] to have both a value and data. + /// + /// The files app is always installed and can not be removed so it will be used, but in the + /// case this changes at a later point the first supported app will be returned. + /// + /// Returns null when no app is supported by the server. + String? _getInitialAppFallback() { + final supportedApps = appImplementations.value.requireData; + if (supportedApps.tryFind('files') != null) { + return 'files'; + } else if (supportedApps.isNotEmpty) { + return supportedApps.first.id; + } + + return null; + } + Future _checkCompatibility() async { final apps = appImplementations.valueOrNull; final capabilities = _capabilitiesBloc.capabilities.valueOrNull; diff --git a/packages/neon/neon/lib/src/models/app_implementation.dart b/packages/neon/neon/lib/src/models/app_implementation.dart index 750c7b7f..427e7e19 100644 --- a/packages/neon/neon/lib/src/models/app_implementation.dart +++ b/packages/neon/neon/lib/src/models/app_implementation.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/bloc/bloc.dart'; import 'package:neon/src/blocs/accounts.dart'; @@ -68,6 +69,34 @@ abstract class AppImplementation ); } + /// Main branch displayed in the home page. + /// + /// There's usually no need to override this. + StatefulShellBranch get mainBranch => StatefulShellBranch( + routes: [ + route, + ], + ); + + /// Route for the app. + /// + /// All pages of the app must be specified as subroutes. + /// If this is not [GoRoute] an inital route name must be specified by overriding [initialRouteName]. + RouteBase get route; + + /// Name of the initial route for this app. + /// + /// Subclasses that don't provide a [GoRoute] for [route] must override this. + String get initialRouteName { + final route = this.route; + + if (route is GoRoute && route.name != null) { + return route.name!; + } + + throw FlutterError('No name for the initial route provided.'); + } + Widget buildIcon({ final Size size = const Size.square(32), final Color? color, diff --git a/packages/neon/neon/lib/src/router.dart b/packages/neon/neon/lib/src/router.dart index cad5b2af..fb6068bc 100644 --- a/packages/neon/neon/lib/src/router.dart +++ b/packages/neon/neon/lib/src/router.dart @@ -1,6 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; +import 'package:meta/meta.dart'; import 'package:neon/src/blocs/accounts.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/models/app_implementation.dart'; @@ -17,13 +18,14 @@ import 'package:provider/provider.dart'; part 'router.g.dart'; +@internal class AppRouter extends GoRouter { AppRouter({ required final GlobalKey navigatorKey, required final AccountsBloc accountsBloc, }) : super( debugLogDiagnostics: kDebugMode, - refreshListenable: StreamListenable.behaviorSubject(accountsBloc.activeAccount), + refreshListenable: StreamListenable(accountsBloc.activeAccount), navigatorKey: navigatorKey, initialLocation: const HomeRoute().location, redirect: (final context, final state) { diff --git a/packages/neon/neon/lib/src/utils/app_route.dart b/packages/neon/neon/lib/src/utils/app_route.dart new file mode 100644 index 00000000..459b5edd --- /dev/null +++ b/packages/neon/neon/lib/src/utils/app_route.dart @@ -0,0 +1,16 @@ +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; + +/// [RouteData] for the initial page of an app. +/// +/// Subclasses must override one of [build] or [redirect]. +abstract class NeonAppRoute extends GoRouteData { + const NeonAppRoute(); + + @override + Page buildPage(final BuildContext context, final GoRouterState state) => NoTransitionPage( + child: build(context, state), + ); +} + +const appsRoutePrefix = '/apps/'; diff --git a/packages/neon/neon/lib/src/utils/stream_listenable.dart b/packages/neon/neon/lib/src/utils/stream_listenable.dart index d1e5b65e..3f47903b 100644 --- a/packages/neon/neon/lib/src/utils/stream_listenable.dart +++ b/packages/neon/neon/lib/src/utils/stream_listenable.dart @@ -9,32 +9,36 @@ import 'package:rxdart/rxdart.dart'; /// Objects need to be manually disposed. class StreamListenable extends ChangeNotifier { /// Listenable Stream - /// - /// Implementation for all types of [Stream]s. - /// For an implementation tailored towards [BehaviorSubject] have a look at [StreamListenable.behaviorSubject]. StreamListenable(final Stream stream) { - notifyListeners(); - - _subscription = stream.asBroadcastStream().listen((final value) { + if (stream is! BehaviorSubject) { notifyListeners(); - }); + } + + addSubscription(stream); } - /// Listenable BehaviorSubject + /// Listenable for multiple Streams. /// - /// Implementation for a [BehaviorSubject]. It ensures to not unececcary notify listeners. - /// For an implementation tailored towards otnher kinds of [Stream] have a look at [StreamListenable]. - StreamListenable.behaviorSubject(final BehaviorSubject subject) { - _subscription = subject.listen((final value) { - notifyListeners(); - }); + /// Notifies it's listeners on every event emitted by any of the streams. + StreamListenable.multiListenable(final Iterable> streams) { + streams.forEach(addSubscription); + } + + void addSubscription(final Stream stream) { + _subscriptions.add( + stream.asBroadcastStream().listen((final _) { + notifyListeners(); + }), + ); } - late final StreamSubscription _subscription; + final List> _subscriptions = []; @override void dispose() { - unawaited(_subscription.cancel()); + for (final subscription in _subscriptions) { + unawaited(subscription.cancel()); + } super.dispose(); } diff --git a/packages/neon/neon/lib/utils.dart b/packages/neon/neon/lib/utils.dart index a51944c8..6f1522a1 100644 --- a/packages/neon/neon/lib/utils.dart +++ b/packages/neon/neon/lib/utils.dart @@ -1,3 +1,4 @@ +export 'package:neon/src/utils/app_route.dart'; export 'package:neon/src/utils/confirmation_dialog.dart'; export 'package:neon/src/utils/exceptions.dart'; export 'package:neon/src/utils/hex_color.dart'; diff --git a/packages/neon/neon_files/build.yaml b/packages/neon/neon_files/build.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/neon/neon_files/lib/neon_files.dart b/packages/neon/neon_files/lib/neon_files.dart index 246b507a..9a3dbb54 100644 --- a/packages/neon/neon_files/lib/neon_files.dart +++ b/packages/neon/neon_files/lib/neon_files.dart @@ -9,6 +9,7 @@ import 'package:file_icons/file_icons.dart'; import 'package:file_picker/file_picker.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:intersperse/intersperse.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; @@ -21,6 +22,7 @@ import 'package:neon/theme.dart'; import 'package:neon/utils.dart'; import 'package:neon/widgets.dart'; import 'package:neon_files/l10n/localizations.dart'; +import 'package:neon_files/routes.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:open_file/open_file.dart'; import 'package:path/path.dart' as p; @@ -47,7 +49,9 @@ class FilesApp extends AppImplementation { FilesApp(super.sharedPreferences, super.requestManager, super.platform); @override - String id = 'files'; + String id = appId; + + static const String appId = 'files'; @override LocalizationsDelegate localizationsDelegate = AppLocalizations.delegate; @@ -68,4 +72,7 @@ class FilesApp extends AppImplementation { @override Widget get page => const FilesMainPage(); + + @override + RouteBase get route => $filesAppRoute; } diff --git a/packages/neon/neon_files/lib/routes.dart b/packages/neon/neon_files/lib/routes.dart new file mode 100644 index 00000000..395b10a4 --- /dev/null +++ b/packages/neon/neon_files/lib/routes.dart @@ -0,0 +1,18 @@ +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; +import 'package:neon/utils.dart'; +import 'package:neon_files/neon_files.dart'; + +part 'routes.g.dart'; + +@TypedGoRoute( + path: '$appsRoutePrefix${FilesApp.appId}', + name: FilesApp.appId, +) +@immutable +class FilesAppRoute extends NeonAppRoute { + const FilesAppRoute(); + + @override + Widget build(final BuildContext context, final GoRouterState state) => const FilesMainPage(); +} diff --git a/packages/neon/neon_files/lib/routes.g.dart b/packages/neon/neon_files/lib/routes.g.dart new file mode 100644 index 00000000..d8943daa --- /dev/null +++ b/packages/neon/neon_files/lib/routes.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'routes.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $filesAppRoute, + ]; + +RouteBase get $filesAppRoute => GoRouteData.$route( + path: '/apps/files', + name: 'files', + factory: $FilesAppRouteExtension._fromState, + ); + +extension $FilesAppRouteExtension on FilesAppRoute { + static FilesAppRoute _fromState(GoRouterState state) => const FilesAppRoute(); + + String get location => GoRouteData.$location( + '/apps/files', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} diff --git a/packages/neon/neon_files/pubspec.yaml b/packages/neon/neon_files/pubspec.yaml index a2c729cb..f33d30f4 100644 --- a/packages/neon/neon_files/pubspec.yaml +++ b/packages/neon/neon_files/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: filesize: ^2.0.1 flutter: sdk: flutter + go_router: ^8.0.3 image_picker: ^0.8.7+5 intersperse: ^2.0.0 material_design_icons_flutter: ^7.0.7296 @@ -35,6 +36,8 @@ dependencies: share_plus: ^7.0.0 dev_dependencies: + build_runner: ^2.4.4 + go_router_builder: ^2.0.2 nit_picking: git: url: https://github.com/stack11/dart_nit_picking diff --git a/packages/neon/neon_news/build.yaml b/packages/neon/neon_news/build.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/neon/neon_news/lib/neon_news.dart b/packages/neon/neon_news/lib/neon_news.dart index 6a7c4f62..3a534290 100644 --- a/packages/neon/neon_news/lib/neon_news.dart +++ b/packages/neon/neon_news/lib/neon_news.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_html/flutter_html.dart'; +import 'package:go_router/go_router.dart'; import 'package:html/dom.dart' as html_dom; import 'package:html/parser.dart' as html_parser; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; @@ -16,6 +17,7 @@ import 'package:neon/sort_box.dart'; import 'package:neon/utils.dart'; import 'package:neon/widgets.dart'; import 'package:neon_news/l10n/localizations.dart'; +import 'package:neon_news/routes.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; @@ -53,7 +55,9 @@ class NewsApp extends AppImplementation { NewsApp(super.sharedPreferences, super.requestManager, super.platform); @override - String id = 'news'; + String id = appId; + + static const String appId = 'news'; @override LocalizationsDelegate localizationsDelegate = AppLocalizations.delegate; @@ -74,6 +78,9 @@ class NewsApp extends AppImplementation { @override Widget get page => const NewsMainPage(); + @override + RouteBase get route => $newsAppRoute; + @override BehaviorSubject getUnreadCounter(final NewsBloc bloc) => bloc.unreadCounter; } diff --git a/packages/neon/neon_news/lib/routes.dart b/packages/neon/neon_news/lib/routes.dart new file mode 100644 index 00000000..cba521f9 --- /dev/null +++ b/packages/neon/neon_news/lib/routes.dart @@ -0,0 +1,18 @@ +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; +import 'package:neon/utils.dart'; +import 'package:neon_news/neon_news.dart'; + +part 'routes.g.dart'; + +@TypedGoRoute( + path: '$appsRoutePrefix${NewsApp.appId}', + name: NewsApp.appId, +) +@immutable +class NewsAppRoute extends NeonAppRoute { + const NewsAppRoute(); + + @override + Widget build(final BuildContext context, final GoRouterState state) => const NewsMainPage(); +} diff --git a/packages/neon/neon_news/lib/routes.g.dart b/packages/neon/neon_news/lib/routes.g.dart new file mode 100644 index 00000000..db34106d --- /dev/null +++ b/packages/neon/neon_news/lib/routes.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'routes.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $newsAppRoute, + ]; + +RouteBase get $newsAppRoute => GoRouteData.$route( + path: '/apps/news', + name: 'news', + factory: $NewsAppRouteExtension._fromState, + ); + +extension $NewsAppRouteExtension on NewsAppRoute { + static NewsAppRoute _fromState(GoRouterState state) => const NewsAppRoute(); + + String get location => GoRouteData.$location( + '/apps/news', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} diff --git a/packages/neon/neon_news/pubspec.yaml b/packages/neon/neon_news/pubspec.yaml index 7e0b3289..c4c3a2c0 100644 --- a/packages/neon/neon_news/pubspec.yaml +++ b/packages/neon/neon_news/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: flutter: sdk: flutter flutter_html: ^3.0.0-alpha.6 + go_router: ^8.0.3 html: ^0.15.3 material_design_icons_flutter: ^7.0.7296 neon: @@ -28,6 +29,8 @@ dependencies: webview_flutter: ^4.2.0 dev_dependencies: + build_runner: ^2.4.4 + go_router_builder: ^2.0.2 nit_picking: git: url: https://github.com/stack11/dart_nit_picking diff --git a/packages/neon/neon_notes/build.yaml b/packages/neon/neon_notes/build.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/neon/neon_notes/lib/neon_notes.dart b/packages/neon/neon_notes/lib/neon_notes.dart index 41e73862..a5c29136 100644 --- a/packages/neon/neon_notes/lib/neon_notes.dart +++ b/packages/neon/neon_notes/lib/neon_notes.dart @@ -7,6 +7,7 @@ import 'package:built_collection/built_collection.dart'; import 'package:crypto/crypto.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:go_router/go_router.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:neon/blocs.dart'; import 'package:neon/models.dart'; @@ -16,6 +17,7 @@ import 'package:neon/sort_box.dart'; import 'package:neon/utils.dart'; import 'package:neon/widgets.dart'; import 'package:neon_notes/l10n/localizations.dart'; +import 'package:neon_notes/routes.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:provider/provider.dart'; import 'package:queue/queue.dart'; @@ -44,7 +46,9 @@ class NotesApp extends AppImplementation { NotesApp(super.sharedPreferences, super.requestManager, super.platform); @override - String id = 'notes'; + String id = appId; + + static const String appId = 'notes'; @override List supportedLocales = AppLocalizations.supportedLocales; @@ -64,4 +68,7 @@ class NotesApp extends AppImplementation { @override Widget get page => const NotesMainPage(); + + @override + RouteBase get route => $notesAppRoute; } diff --git a/packages/neon/neon_notes/lib/routes.dart b/packages/neon/neon_notes/lib/routes.dart new file mode 100644 index 00000000..8cbb7f3f --- /dev/null +++ b/packages/neon/neon_notes/lib/routes.dart @@ -0,0 +1,18 @@ +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; +import 'package:neon/utils.dart'; +import 'package:neon_notes/neon_notes.dart'; + +part 'routes.g.dart'; + +@TypedGoRoute( + path: '$appsRoutePrefix${NotesApp.appId}', + name: NotesApp.appId, +) +@immutable +class NotesAppRoute extends NeonAppRoute { + const NotesAppRoute(); + + @override + Widget build(final BuildContext context, final GoRouterState state) => const NotesMainPage(); +} diff --git a/packages/neon/neon_notes/lib/routes.g.dart b/packages/neon/neon_notes/lib/routes.g.dart new file mode 100644 index 00000000..cb68c111 --- /dev/null +++ b/packages/neon/neon_notes/lib/routes.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'routes.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $notesAppRoute, + ]; + +RouteBase get $notesAppRoute => GoRouteData.$route( + path: '/apps/notes', + name: 'notes', + factory: $NotesAppRouteExtension._fromState, + ); + +extension $NotesAppRouteExtension on NotesAppRoute { + static NotesAppRoute _fromState(GoRouterState state) => const NotesAppRoute(); + + String get location => GoRouteData.$location( + '/apps/notes', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} diff --git a/packages/neon/neon_notes/pubspec.yaml b/packages/neon/neon_notes/pubspec.yaml index 5952d3f1..6319ea2b 100644 --- a/packages/neon/neon_notes/pubspec.yaml +++ b/packages/neon/neon_notes/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: flutter: sdk: flutter flutter_markdown: ^0.6.14 + go_router: ^8.0.3 material_design_icons_flutter: ^7.0.7296 neon: git: @@ -28,6 +29,8 @@ dependencies: wakelock: ^0.6.2 dev_dependencies: + build_runner: ^2.4.4 + go_router_builder: ^2.0.2 nit_picking: git: url: https://github.com/stack11/dart_nit_picking diff --git a/packages/neon/neon_notifications/build.yaml b/packages/neon/neon_notifications/build.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/neon/neon_notifications/lib/neon_notifications.dart b/packages/neon/neon_notifications/lib/neon_notifications.dart index e0d4d696..eee8591f 100644 --- a/packages/neon/neon_notifications/lib/neon_notifications.dart +++ b/packages/neon/neon_notifications/lib/neon_notifications.dart @@ -3,6 +3,7 @@ library notifications; import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:neon/blocs.dart'; import 'package:neon/models.dart'; @@ -10,6 +11,7 @@ import 'package:neon/settings.dart'; import 'package:neon/utils.dart'; import 'package:neon/widgets.dart'; import 'package:neon_notifications/l10n/localizations.dart'; +import 'package:neon_notifications/routes.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; @@ -23,7 +25,9 @@ class NotificationsApp extends AppImplementation const NotificationsMainPage(); + @override + RouteBase get route => $notificationsAppRoute; + @override BehaviorSubject getUnreadCounter(final NotificationsBloc bloc) => bloc.unreadCounter; } diff --git a/packages/neon/neon_notifications/lib/routes.dart b/packages/neon/neon_notifications/lib/routes.dart new file mode 100644 index 00000000..39f79ba7 --- /dev/null +++ b/packages/neon/neon_notifications/lib/routes.dart @@ -0,0 +1,18 @@ +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; +import 'package:neon/utils.dart'; +import 'package:neon_notifications/neon_notifications.dart'; + +part 'routes.g.dart'; + +@TypedGoRoute( + path: '$appsRoutePrefix${NotificationsApp.appId}', + name: NotificationsApp.appId, +) +@immutable +class NotificationsAppRoute extends NeonAppRoute { + const NotificationsAppRoute(); + + @override + Widget build(final BuildContext context, final GoRouterState state) => const NotificationsMainPage(); +} diff --git a/packages/neon/neon_notifications/lib/routes.g.dart b/packages/neon/neon_notifications/lib/routes.g.dart new file mode 100644 index 00000000..46db16cb --- /dev/null +++ b/packages/neon/neon_notifications/lib/routes.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'routes.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $notificationsAppRoute, + ]; + +RouteBase get $notificationsAppRoute => GoRouteData.$route( + path: '/apps/notifications', + name: 'notifications', + factory: $NotificationsAppRouteExtension._fromState, + ); + +extension $NotificationsAppRouteExtension on NotificationsAppRoute { + static NotificationsAppRoute _fromState(GoRouterState state) => const NotificationsAppRoute(); + + String get location => GoRouteData.$location( + '/apps/notifications', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} diff --git a/packages/neon/neon_notifications/pubspec.yaml b/packages/neon/neon_notifications/pubspec.yaml index 4c764866..944ebdef 100644 --- a/packages/neon/neon_notifications/pubspec.yaml +++ b/packages/neon/neon_notifications/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: flutter: sdk: flutter + go_router: ^8.0.3 material_design_icons_flutter: ^7.0.7296 neon: git: @@ -22,6 +23,8 @@ dependencies: rxdart: ^0.27.7 dev_dependencies: + build_runner: ^2.4.4 + go_router_builder: ^2.0.2 nit_picking: git: url: https://github.com/stack11/dart_nit_picking