From 62143581acf476dce54b8aabaeaafdffde85cb39 Mon Sep 17 00:00:00 2001 From: PartyDonut <42371342+PartyDonut@users.noreply.github.com> Date: Fri, 25 Oct 2024 19:58:51 +0200 Subject: [PATCH] feature: Improved about page (#75) Co-authored-by: PartyDonut --- lib/l10n/app_en.arb | 21 +- lib/routes/auto_router.dart | 1 + lib/routes/auto_router.gr.dart | 224 ++++++++++-------- lib/screens/settings/about_settings_page.dart | 116 +++++++++ lib/screens/settings/settings_screen.dart | 9 +- lib/util/application_info.dart | 4 +- pubspec.lock | 8 + pubspec.yaml | 1 + 8 files changed, 273 insertions(+), 111 deletions(-) create mode 100644 lib/screens/settings/about_settings_page.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 907c3bc..a3c2283 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1029,5 +1029,24 @@ "type": "String" } } - } + }, + "aboutVersion": "Version: {version}", + "@aboutVersion":{ + "placeholders": { + "version":{ + "type": "String" + } + } + }, + "aboutBuild": "Build: {buildNumber}", + "@aboutBuild":{ + "placeholders": { + "buildNumber":{ + "type": "String" + } + } + }, + "aboutCreatedBy": "Created by DonutWare", + "aboutSocials": "Socials", + "aboutLicenses": "Licenses" } diff --git a/lib/routes/auto_router.dart b/lib/routes/auto_router.dart index 7851ed5..e241e39 100644 --- a/lib/routes/auto_router.dart +++ b/lib/routes/auto_router.dart @@ -96,6 +96,7 @@ final List _settingsChildren = [ page: ClientSettingsRoute.page, initial: true, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'client'), CustomRoute(page: SecuritySettingsRoute.page, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'security'), CustomRoute(page: PlayerSettingsRoute.page, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'player'), + CustomRoute(page: AboutSettingsRoute.page, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'about'), ]; class LockScreenGuard extends AutoRouteGuard { diff --git a/lib/routes/auto_router.gr.dart b/lib/routes/auto_router.gr.dart index edb1ef4..5459e04 100644 --- a/lib/routes/auto_router.gr.dart +++ b/lib/routes/auto_router.gr.dart @@ -8,32 +8,52 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i14; -import 'package:fladder/models/item_base_model.dart' as _i15; -import 'package:fladder/models/items/photos_model.dart' as _i18; +import 'package:auto_route/auto_route.dart' as _i15; +import 'package:fladder/models/item_base_model.dart' as _i16; +import 'package:fladder/models/items/photos_model.dart' as _i19; import 'package:fladder/models/library_search/library_search_options.dart' - as _i17; -import 'package:fladder/routes/nested_details_screen.dart' as _i3; -import 'package:fladder/screens/dashboard/dashboard_screen.dart' as _i2; -import 'package:fladder/screens/favourites/favourites_screen.dart' as _i4; -import 'package:fladder/screens/home_screen.dart' as _i5; + as _i18; +import 'package:fladder/routes/nested_details_screen.dart' as _i4; +import 'package:fladder/screens/dashboard/dashboard_screen.dart' as _i3; +import 'package:fladder/screens/favourites/favourites_screen.dart' as _i5; +import 'package:fladder/screens/home_screen.dart' as _i6; import 'package:fladder/screens/library_search/library_search_screen.dart' - as _i6; -import 'package:fladder/screens/login/lock_screen.dart' as _i7; -import 'package:fladder/screens/login/login_screen.dart' as _i8; -import 'package:fladder/screens/settings/client_settings_page.dart' as _i1; -import 'package:fladder/screens/settings/player_settings_page.dart' as _i9; -import 'package:fladder/screens/settings/security_settings_page.dart' as _i10; -import 'package:fladder/screens/settings/settings_screen.dart' as _i11; -import 'package:fladder/screens/splash_screen.dart' as _i12; -import 'package:fladder/screens/syncing/synced_screen.dart' as _i13; -import 'package:flutter/foundation.dart' as _i16; -import 'package:flutter/material.dart' as _i19; + as _i7; +import 'package:fladder/screens/login/lock_screen.dart' as _i8; +import 'package:fladder/screens/login/login_screen.dart' as _i9; +import 'package:fladder/screens/settings/about_settings_page.dart' as _i1; +import 'package:fladder/screens/settings/client_settings_page.dart' as _i2; +import 'package:fladder/screens/settings/player_settings_page.dart' as _i10; +import 'package:fladder/screens/settings/security_settings_page.dart' as _i11; +import 'package:fladder/screens/settings/settings_screen.dart' as _i12; +import 'package:fladder/screens/splash_screen.dart' as _i13; +import 'package:fladder/screens/syncing/synced_screen.dart' as _i14; +import 'package:flutter/foundation.dart' as _i17; +import 'package:flutter/material.dart' as _i20; /// generated route for -/// [_i1.ClientSettingsPage] -class ClientSettingsRoute extends _i14.PageRouteInfo { - const ClientSettingsRoute({List<_i14.PageRouteInfo>? children}) +/// [_i1.AboutSettingsPage] +class AboutSettingsRoute extends _i15.PageRouteInfo { + const AboutSettingsRoute({List<_i15.PageRouteInfo>? children}) + : super( + AboutSettingsRoute.name, + initialChildren: children, + ); + + static const String name = 'AboutSettingsRoute'; + + static _i15.PageInfo page = _i15.PageInfo( + name, + builder: (data) { + return const _i1.AboutSettingsPage(); + }, + ); +} + +/// generated route for +/// [_i2.ClientSettingsPage] +class ClientSettingsRoute extends _i15.PageRouteInfo { + const ClientSettingsRoute({List<_i15.PageRouteInfo>? children}) : super( ClientSettingsRoute.name, initialChildren: children, @@ -41,18 +61,18 @@ class ClientSettingsRoute extends _i14.PageRouteInfo { static const String name = 'ClientSettingsRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i1.ClientSettingsPage(); + return const _i2.ClientSettingsPage(); }, ); } /// generated route for -/// [_i2.DashboardScreen] -class DashboardRoute extends _i14.PageRouteInfo { - const DashboardRoute({List<_i14.PageRouteInfo>? children}) +/// [_i3.DashboardScreen] +class DashboardRoute extends _i15.PageRouteInfo { + const DashboardRoute({List<_i15.PageRouteInfo>? children}) : super( DashboardRoute.name, initialChildren: children, @@ -60,22 +80,22 @@ class DashboardRoute extends _i14.PageRouteInfo { static const String name = 'DashboardRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i2.DashboardScreen(); + return const _i3.DashboardScreen(); }, ); } /// generated route for -/// [_i3.DetailsScreen] -class DetailsRoute extends _i14.PageRouteInfo { +/// [_i4.DetailsScreen] +class DetailsRoute extends _i15.PageRouteInfo { DetailsRoute({ String id = '', - _i15.ItemBaseModel? item, - _i16.Key? key, - List<_i14.PageRouteInfo>? children, + _i16.ItemBaseModel? item, + _i17.Key? key, + List<_i15.PageRouteInfo>? children, }) : super( DetailsRoute.name, args: DetailsRouteArgs( @@ -89,7 +109,7 @@ class DetailsRoute extends _i14.PageRouteInfo { static const String name = 'DetailsRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { final queryParams = data.queryParams; @@ -99,7 +119,7 @@ class DetailsRoute extends _i14.PageRouteInfo { 'id', '', ))); - return _i3.DetailsScreen( + return _i4.DetailsScreen( id: args.id, item: args.item, key: args.key, @@ -117,9 +137,9 @@ class DetailsRouteArgs { final String id; - final _i15.ItemBaseModel? item; + final _i16.ItemBaseModel? item; - final _i16.Key? key; + final _i17.Key? key; @override String toString() { @@ -128,9 +148,9 @@ class DetailsRouteArgs { } /// generated route for -/// [_i4.FavouritesScreen] -class FavouritesRoute extends _i14.PageRouteInfo { - const FavouritesRoute({List<_i14.PageRouteInfo>? children}) +/// [_i5.FavouritesScreen] +class FavouritesRoute extends _i15.PageRouteInfo { + const FavouritesRoute({List<_i15.PageRouteInfo>? children}) : super( FavouritesRoute.name, initialChildren: children, @@ -138,18 +158,18 @@ class FavouritesRoute extends _i14.PageRouteInfo { static const String name = 'FavouritesRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i4.FavouritesScreen(); + return const _i5.FavouritesScreen(); }, ); } /// generated route for -/// [_i5.HomeScreen] -class HomeRoute extends _i14.PageRouteInfo { - const HomeRoute({List<_i14.PageRouteInfo>? children}) +/// [_i6.HomeScreen] +class HomeRoute extends _i15.PageRouteInfo { + const HomeRoute({List<_i15.PageRouteInfo>? children}) : super( HomeRoute.name, initialChildren: children, @@ -157,26 +177,26 @@ class HomeRoute extends _i14.PageRouteInfo { static const String name = 'HomeRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i5.HomeScreen(); + return const _i6.HomeScreen(); }, ); } /// generated route for -/// [_i6.LibrarySearchScreen] -class LibrarySearchRoute extends _i14.PageRouteInfo { +/// [_i7.LibrarySearchScreen] +class LibrarySearchRoute extends _i15.PageRouteInfo { LibrarySearchRoute({ String? viewModelId, List? folderId, bool? favourites, - _i17.SortingOrder? sortOrder, - _i17.SortingOptions? sortingOptions, - _i18.PhotoModel? photoToView, - _i16.Key? key, - List<_i14.PageRouteInfo>? children, + _i18.SortingOrder? sortOrder, + _i18.SortingOptions? sortingOptions, + _i19.PhotoModel? photoToView, + _i17.Key? key, + List<_i15.PageRouteInfo>? children, }) : super( LibrarySearchRoute.name, args: LibrarySearchRouteArgs( @@ -200,7 +220,7 @@ class LibrarySearchRoute extends _i14.PageRouteInfo { static const String name = 'LibrarySearchRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { final queryParams = data.queryParams; @@ -212,7 +232,7 @@ class LibrarySearchRoute extends _i14.PageRouteInfo { sortOrder: queryParams.get('sortOrder'), sortingOptions: queryParams.get('sortOptions'), )); - return _i6.LibrarySearchScreen( + return _i7.LibrarySearchScreen( viewModelId: args.viewModelId, folderId: args.folderId, favourites: args.favourites, @@ -242,13 +262,13 @@ class LibrarySearchRouteArgs { final bool? favourites; - final _i17.SortingOrder? sortOrder; + final _i18.SortingOrder? sortOrder; - final _i17.SortingOptions? sortingOptions; + final _i18.SortingOptions? sortingOptions; - final _i18.PhotoModel? photoToView; + final _i19.PhotoModel? photoToView; - final _i16.Key? key; + final _i17.Key? key; @override String toString() { @@ -257,9 +277,9 @@ class LibrarySearchRouteArgs { } /// generated route for -/// [_i7.LockScreen] -class LockRoute extends _i14.PageRouteInfo { - const LockRoute({List<_i14.PageRouteInfo>? children}) +/// [_i8.LockScreen] +class LockRoute extends _i15.PageRouteInfo { + const LockRoute({List<_i15.PageRouteInfo>? children}) : super( LockRoute.name, initialChildren: children, @@ -267,18 +287,18 @@ class LockRoute extends _i14.PageRouteInfo { static const String name = 'LockRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i7.LockScreen(); + return const _i8.LockScreen(); }, ); } /// generated route for -/// [_i8.LoginScreen] -class LoginRoute extends _i14.PageRouteInfo { - const LoginRoute({List<_i14.PageRouteInfo>? children}) +/// [_i9.LoginScreen] +class LoginRoute extends _i15.PageRouteInfo { + const LoginRoute({List<_i15.PageRouteInfo>? children}) : super( LoginRoute.name, initialChildren: children, @@ -286,18 +306,18 @@ class LoginRoute extends _i14.PageRouteInfo { static const String name = 'LoginRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i8.LoginScreen(); + return const _i9.LoginScreen(); }, ); } /// generated route for -/// [_i9.PlayerSettingsPage] -class PlayerSettingsRoute extends _i14.PageRouteInfo { - const PlayerSettingsRoute({List<_i14.PageRouteInfo>? children}) +/// [_i10.PlayerSettingsPage] +class PlayerSettingsRoute extends _i15.PageRouteInfo { + const PlayerSettingsRoute({List<_i15.PageRouteInfo>? children}) : super( PlayerSettingsRoute.name, initialChildren: children, @@ -305,18 +325,18 @@ class PlayerSettingsRoute extends _i14.PageRouteInfo { static const String name = 'PlayerSettingsRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i9.PlayerSettingsPage(); + return const _i10.PlayerSettingsPage(); }, ); } /// generated route for -/// [_i10.SecuritySettingsPage] -class SecuritySettingsRoute extends _i14.PageRouteInfo { - const SecuritySettingsRoute({List<_i14.PageRouteInfo>? children}) +/// [_i11.SecuritySettingsPage] +class SecuritySettingsRoute extends _i15.PageRouteInfo { + const SecuritySettingsRoute({List<_i15.PageRouteInfo>? children}) : super( SecuritySettingsRoute.name, initialChildren: children, @@ -324,18 +344,18 @@ class SecuritySettingsRoute extends _i14.PageRouteInfo { static const String name = 'SecuritySettingsRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i10.SecuritySettingsPage(); + return const _i11.SecuritySettingsPage(); }, ); } /// generated route for -/// [_i11.SettingsScreen] -class SettingsRoute extends _i14.PageRouteInfo { - const SettingsRoute({List<_i14.PageRouteInfo>? children}) +/// [_i12.SettingsScreen] +class SettingsRoute extends _i15.PageRouteInfo { + const SettingsRoute({List<_i15.PageRouteInfo>? children}) : super( SettingsRoute.name, initialChildren: children, @@ -343,21 +363,21 @@ class SettingsRoute extends _i14.PageRouteInfo { static const String name = 'SettingsRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { - return const _i11.SettingsScreen(); + return const _i12.SettingsScreen(); }, ); } /// generated route for -/// [_i12.SplashScreen] -class SplashRoute extends _i14.PageRouteInfo { +/// [_i13.SplashScreen] +class SplashRoute extends _i15.PageRouteInfo { SplashRoute({ dynamic Function(bool)? loggedIn, - _i19.Key? key, - List<_i14.PageRouteInfo>? children, + _i20.Key? key, + List<_i15.PageRouteInfo>? children, }) : super( SplashRoute.name, args: SplashRouteArgs( @@ -369,12 +389,12 @@ class SplashRoute extends _i14.PageRouteInfo { static const String name = 'SplashRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { final args = data.argsAs(orElse: () => const SplashRouteArgs()); - return _i12.SplashScreen( + return _i13.SplashScreen( loggedIn: args.loggedIn, key: args.key, ); @@ -390,7 +410,7 @@ class SplashRouteArgs { final dynamic Function(bool)? loggedIn; - final _i19.Key? key; + final _i20.Key? key; @override String toString() { @@ -399,12 +419,12 @@ class SplashRouteArgs { } /// generated route for -/// [_i13.SyncedScreen] -class SyncedRoute extends _i14.PageRouteInfo { +/// [_i14.SyncedScreen] +class SyncedRoute extends _i15.PageRouteInfo { SyncedRoute({ - _i19.ScrollController? navigationScrollController, - _i19.Key? key, - List<_i14.PageRouteInfo>? children, + _i20.ScrollController? navigationScrollController, + _i20.Key? key, + List<_i15.PageRouteInfo>? children, }) : super( SyncedRoute.name, args: SyncedRouteArgs( @@ -416,12 +436,12 @@ class SyncedRoute extends _i14.PageRouteInfo { static const String name = 'SyncedRoute'; - static _i14.PageInfo page = _i14.PageInfo( + static _i15.PageInfo page = _i15.PageInfo( name, builder: (data) { final args = data.argsAs(orElse: () => const SyncedRouteArgs()); - return _i13.SyncedScreen( + return _i14.SyncedScreen( navigationScrollController: args.navigationScrollController, key: args.key, ); @@ -435,9 +455,9 @@ class SyncedRouteArgs { this.key, }); - final _i19.ScrollController? navigationScrollController; + final _i20.ScrollController? navigationScrollController; - final _i19.Key? key; + final _i20.Key? key; @override String toString() { diff --git a/lib/screens/settings/about_settings_page.dart b/lib/screens/settings/about_settings_page.dart new file mode 100644 index 0000000..5e9f31d --- /dev/null +++ b/lib/screens/settings/about_settings_page.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; + +import 'package:auto_route/auto_route.dart'; +import 'package:ficonsax/ficonsax.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import 'package:fladder/screens/settings/settings_scaffold.dart'; +import 'package:fladder/screens/shared/fladder_icon.dart'; +import 'package:fladder/screens/shared/media/external_urls.dart'; +import 'package:fladder/util/application_info.dart'; +import 'package:fladder/util/list_padding.dart'; +import 'package:fladder/util/localization_helper.dart'; + +class _Socials { + final String label; + final String url; + final IconData icon; + + const _Socials(this.label, this.url, this.icon); +} + +const socials = [ + _Socials( + 'Github', + 'https://github.com/DonutWare/Fladder', + FontAwesomeIcons.githubAlt, + ), + _Socials( + 'Weblate', + 'https://hosted.weblate.org/projects/fladder/', + IconsaxOutline.global, + ), +]; + +@RoutePage() +class AboutSettingsPage extends ConsumerWidget { + const AboutSettingsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final applicationInfo = ref.watch(applicationInfoProvider); + return Card( + child: SettingsScaffold( + label: "", + items: [ + Wrap( + runAlignment: WrapAlignment.center, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + runSpacing: 24, + spacing: 24, + children: [ + const FladderIcon(size: 85), + Text( + "Fladder", + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.displayLarge?.copyWith(fontWeight: FontWeight.bold), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(context.localized.aboutVersion(applicationInfo.versionAndPlatform)), + Text(context.localized.aboutBuild(applicationInfo.buildNumber)), + const SizedBox(height: 16), + Text(context.localized.aboutCreatedBy), + ], + ), + const Divider(), + Column( + children: [ + Text( + context.localized.aboutSocials, + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 6), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: socials + .map( + (e) => IconButton.filledTonal( + onPressed: () => launchUrl(context, e.url), + icon: Column( + children: [ + Icon(e.icon), + Text(e.label), + ], + ), + ), + ) + .toList() + .addInBetween(const SizedBox(width: 16)), + ) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FilledButton.tonal( + onPressed: () => showLicensePage( + context: context, + applicationIcon: const FladderIcon(size: 85), + applicationVersion: applicationInfo.versionPlatformBuild, + applicationLegalese: "DonutWare", + ), + child: Text(context.localized.aboutLicenses), + ) + ], + ), + ].addInBetween(const SizedBox(height: 16)), + ), + ); + } +} diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index b66e2b1..0a6b52a 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -12,7 +12,6 @@ import 'package:fladder/screens/settings/settings_list_tile.dart'; import 'package:fladder/screens/settings/settings_scaffold.dart'; import 'package:fladder/screens/shared/fladder_icon.dart'; import 'package:fladder/util/adaptive_layout.dart'; -import 'package:fladder/util/application_info.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/router_extension.dart'; import 'package:fladder/util/theme_extensions.dart'; @@ -136,6 +135,7 @@ class _SettingsScreenState extends ConsumerState { SettingsListTile( label: Text(context.localized.about), subLabel: const Text("Fladder"), + selected: containsRoute(const AboutSettingsRoute()), suffix: Opacity( opacity: 1, child: FladderIconOutlined( @@ -143,12 +143,7 @@ class _SettingsScreenState extends ConsumerState { color: context.colors.onSurfaceVariant, ), ), - onTap: () => showAboutDialog( - context: context, - applicationIcon: const FladderIcon(size: 85), - applicationVersion: ref.watch(applicationInfoProvider).versionAndPlatform, - applicationLegalese: "Donut Factory", - ), + onTap: () => navigateTo(const AboutSettingsRoute()), ), ], floatingActionButton: Padding( diff --git a/lib/util/application_info.dart b/lib/util/application_info.dart index 89b2a18..b3ccfa7 100644 --- a/lib/util/application_info.dart +++ b/lib/util/application_info.dart @@ -24,7 +24,9 @@ class ApplicationInfo with _$ApplicationInfo { required String os, }) = _ApplicationInfo; - String get versionAndPlatform => "$version ($os)\n#$buildNumber"; + String get versionPlatformBuild => "$version ($os)\n#$buildNumber"; + + String get versionAndPlatform => "$version ($os)"; @override String toString() => 'ApplicationInfo(name: $name, version: $version, os: $os)'; diff --git a/pubspec.lock b/pubspec.lock index 3309c4f..ed95485 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -714,6 +714,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.14.12" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f" + url: "https://pub.dev" + source: hosted + version: "10.7.0" freezed: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 7094544..6696bf1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -86,6 +86,7 @@ dependencies: flutter_blurhash: ^0.8.2 extended_image: ^8.1.1 flutter_widget_from_html: ^0.14.11 + font_awesome_flutter: ^10.7.0 # Navigation auto_route: ^9.2.2