From 74fa458d71e68e67a8e3da8e9470dc77321e2449 Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Tue, 22 Sep 2026 18:11:17 -0300 Subject: [PATCH 1/6] chore: upgrade to Flutter 3.47.5 / Dart 3.13 and latest dependencies - .fvmrc 3.41.3 -> 3.47.5; packages require sdk ^3.13.0, flutter >=3.47.0 - Major bumps: get_it 9, go_router 18, permission_handler 13, package_info_plus 10, app_links 7, firebase_core 4, flutter_dotenv 6, equatable 3, flutter_lints 6 - flutter_dotenv 6: testLoad -> loadFromString in the EnvConfig tests - dio 5.9+: map DioExceptionType.transformTimeout (analyzer error on main) - lints 6 (strict_top_level_inference): add the missing getter types - flutter_web_plugins: import url_strategy.dart, the cross-platform entry point; the web-only library no longer compiles for mobile - Flutter 3.47's analysis_options migration: exclude build/platform dirs, and generated l10n code - dart format with the 3.13 language version (tall style) Co-Authored-By: Claude Opus 5.5 (1M context) --- .fvmrc | 2 +- app/analysis_options.yaml | 9 + app/lib/main.dart | 5 +- app/lib/main/app.dart | 15 +- app/lib/main/env/env_config.dart | 17 +- app/lib/main/env/main_dev.dart | 5 +- app/lib/main/env/main_qa.dart | 5 +- app/lib/main/init.dart | 2 +- app/lib/presentation/navigation/routers.dart | 174 +++++---- .../resources/custom_network_image.dart | 63 ++-- app/lib/presentation/resources/images.dart | 47 ++- app/lib/presentation/themes/app_themes.dart | 22 +- app/lib/presentation/themes/local_theme.dart | 334 +++++++++--------- .../themes/resources/app_theme_data.dart | 56 +-- .../themes/resources/dark_theme_colors.dart | 172 ++++----- .../themes/resources/light_theme_colors.dart | 168 ++++----- .../presentation/ui/base/tracked_page.dart | 6 +- .../ui/components/primary_button.dart | 21 +- .../ui/custom/app_theme_switch.dart | 6 +- app/lib/presentation/ui/custom/cookies.dart | 13 +- .../presentation/ui/custom/debug_banner.dart | 10 +- .../ui/custom/environment_selector.dart | 26 +- .../ui/custom/failure_widget.dart | 7 +- .../ui/custom/loading_screen.dart | 9 +- .../ui/custom/terms_services_check.dart | 14 +- .../ui/pages/auth/login/login_form.dart | 24 +- .../ui/pages/auth/sign_up/sign_up_form.dart | 24 +- .../ui/pages/onboarding/onboarding_page.dart | 29 +- .../ui/pages/splash/splash_page.dart | 6 +- app/pubspec.yaml | 46 +-- modules/common/analysis_options.yaml | 3 + .../lib/core/failure/failure_mapper.dart | 1 + modules/common/lib/core/resource.dart | 14 +- .../abstract/permission_manager.dart | 2 +- .../concrete/web/_permissions_web.dart | 11 +- .../platform/abstract/app_platform.dart | 14 +- modules/common/lib/ui/responsive.dart | 6 +- .../common/lib/validators/form_validator.dart | 6 +- modules/common/pubspec.yaml | 26 +- .../common/test/core/result_type_test.dart | 3 +- modules/data/analysis_options.yaml | 3 + modules/data/lib/init.dart | 10 +- .../lib/network/config/network_config.dart | 26 +- .../lib/network/config/network_constants.dart | 3 +- .../interceptors/auth_token_interceptor.dart | 12 +- .../repositories/auth_repository_impl.dart | 10 +- modules/data/pubspec.yaml | 20 +- modules/domain/analysis_options.yaml | 3 + modules/domain/lib/bloc/app/app_cubit.dart | 11 +- modules/domain/lib/bloc/app/app_state.dart | 10 +- modules/domain/lib/bloc/auth/auth_cubit.dart | 15 +- modules/domain/lib/models/user.dart | 5 +- modules/domain/lib/services/auth_service.dart | 6 +- modules/domain/pubspec.yaml | 18 +- modules/domain/test/env/env_config_test.dart | 14 +- 55 files changed, 752 insertions(+), 837 deletions(-) diff --git a/.fvmrc b/.fvmrc index c6c5344..ab8148c 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.41.3" + "flutter": "3.47.5" } \ No newline at end of file diff --git a/app/analysis_options.yaml b/app/analysis_options.yaml index cd6fba3..a58a4df 100644 --- a/app/analysis_options.yaml +++ b/app/analysis_options.yaml @@ -3,6 +3,15 @@ # # Lint rules come from `package:flutter_lints/flutter.yaml`. To customize, add # entries under `linter.rules` below. +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - linux/** + # intl_utils output (regenerate with `melos run gen:l10n`, never edit by hand). + - lib/presentation/resources/locale/generated/** include: package:flutter_lints/flutter.yaml linter: diff --git a/app/lib/main.dart b/app/lib/main.dart index 337e98d..73aad52 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -4,10 +4,7 @@ import 'package:app/main/init.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - FlavorConfig( - flavor: Flavor.prod, - values: FlavorValues(), - ); + FlavorConfig(flavor: Flavor.prod, values: FlavorValues()); //Add your firebase configuration here /*await Firebase.initializeApp( options: const FirebaseOptions( diff --git a/app/lib/main/app.dart b/app/lib/main/app.dart index 8d9e56a..d9878a0 100644 --- a/app/lib/main/app.dart +++ b/app/lib/main/app.dart @@ -50,10 +50,7 @@ class _AppState extends State { if (mounted) { setState(() { - _router = Routes.init( - context, - initialLocation: initialLocation, - ); + _router = Routes.init(context, initialLocation: initialLocation); _isRouterReady = true; }); } @@ -67,11 +64,7 @@ class _AppState extends State { BlocProvider(create: (_) => getIt()), ], child: !_isRouterReady - ? const Material( - child: Center( - child: CircularProgressIndicator(), - ), - ) + ? const Material(child: Center(child: CircularProgressIndicator())) : BlocBuilder( builder: (context, state) { return MaterialApp.router( @@ -88,9 +81,7 @@ class _AppState extends State { builder: (context, child) => child ?? const Material( - child: Center( - child: CircularProgressIndicator(), - ), + child: Center(child: CircularProgressIndicator()), ), routerConfig: _router, ); diff --git a/app/lib/main/env/env_config.dart b/app/lib/main/env/env_config.dart index a14d0f7..9351cba 100644 --- a/app/lib/main/env/env_config.dart +++ b/app/lib/main/env/env_config.dart @@ -19,10 +19,8 @@ class Environment { static String? get portalUrl => dotenv.env['API_URL']; - static String get envName => const String.fromEnvironment( - 'ENV', - defaultValue: 'dev', - ); + static String get envName => + const String.fromEnvironment('ENV', defaultValue: 'dev'); static String get envConfigFile => 'env/.$envName'; } @@ -38,15 +36,8 @@ class FlavorConfig { static FlavorConfig? _instance; - factory FlavorConfig({ - required Flavor flavor, - required FlavorValues values, - }) { - _instance ??= FlavorConfig._internal( - flavor, - flavor.toString(), - values, - ); + factory FlavorConfig({required Flavor flavor, required FlavorValues values}) { + _instance ??= FlavorConfig._internal(flavor, flavor.toString(), values); switch (flavor) { case Flavor.dev: EnvConfig.env = EnvConfig.kDevEnv; diff --git a/app/lib/main/env/main_dev.dart b/app/lib/main/env/main_dev.dart index 09e7e38..af304b3 100644 --- a/app/lib/main/env/main_dev.dart +++ b/app/lib/main/env/main_dev.dart @@ -4,10 +4,7 @@ import 'package:app/main/init.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - FlavorConfig( - flavor: Flavor.dev, - values: FlavorValues(), - ); + FlavorConfig(flavor: Flavor.dev, values: FlavorValues()); //Add your firebase configuration here /*await Firebase.initializeApp( options: const FirebaseOptions( diff --git a/app/lib/main/env/main_qa.dart b/app/lib/main/env/main_qa.dart index ad041d7..a3c77c2 100644 --- a/app/lib/main/env/main_qa.dart +++ b/app/lib/main/env/main_qa.dart @@ -4,10 +4,7 @@ import 'package:app/main/init.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - FlavorConfig( - flavor: Flavor.qa, - values: FlavorValues(), - ); + FlavorConfig(flavor: Flavor.qa, values: FlavorValues()); //Add your firebase configuration here /*await Firebase.initializeApp( options: const FirebaseOptions( diff --git a/app/lib/main/init.dart b/app/lib/main/init.dart index f8b29f1..0074ef4 100644 --- a/app/lib/main/init.dart +++ b/app/lib/main/init.dart @@ -6,7 +6,7 @@ import 'package:data/init.dart'; import 'package:domain/init.dart'; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; -import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:flutter_web_plugins/url_strategy.dart'; void init() async { WidgetsFlutterBinding.ensureInitialized(); diff --git a/app/lib/presentation/navigation/routers.dart b/app/lib/presentation/navigation/routers.dart index 44bd1a0..c302c20 100644 --- a/app/lib/presentation/navigation/routers.dart +++ b/app/lib/presentation/navigation/routers.dart @@ -26,10 +26,7 @@ enum Routes { String get subPath => name; void go(BuildContext context, {Object? extra}) { - context.router.goNamed( - name, - extra: extra, - ); + context.router.goNamed(name, extra: extra); } static GoRouter init(BuildContext context, {String? initialLocation}) => @@ -46,106 +43,103 @@ class Routers { static GoRouter appRouter( BuildContext context, { String? initialLocation, - }) => - GoRouter( - navigatorKey: rootNavigatorKey, - initialLocation: initialLocation ?? - (getIt().isLoggedIn() - ? Routes.app.path - : Routes.onboarding.path), - routes: [ - GoRoute( - path: '/', - builder: (context, state) { - return BlocListener( - listenWhen: (previous, current) => current is RSuccess, - listener: (_, appState) { - if (appState is RSuccess) { - switch (appState.data) { - case AuthStateAuthenticated _: - debugPrint('User is authenticated: ${state.fullPath}'); - if (state.fullPath?.startsWith(Routes.app.path) ?? - false) { - // Already navigating to app, do nothing - return; - } - debugPrint('Navigating to app route'); - Routes.app.go(context); - break; - case AuthStateUnauthenticated _: - debugPrint( - 'User is unauthenticated: ${state.fullPath}'); - if (state.fullPath?.startsWith(Routes.auth.path) ?? - false) { - // Already navigating to auth, do nothing - return; - } - debugPrint('Navigating to auth route'); - Routes.onboarding.go(context); - break; - case _: + }) => GoRouter( + navigatorKey: rootNavigatorKey, + initialLocation: + initialLocation ?? + (getIt().isLoggedIn() + ? Routes.app.path + : Routes.onboarding.path), + routes: [ + GoRoute( + path: '/', + builder: (context, state) { + return BlocListener( + listenWhen: (previous, current) => current is RSuccess, + listener: (_, appState) { + if (appState is RSuccess) { + switch (appState.data) { + case AuthStateAuthenticated _: + debugPrint('User is authenticated: ${state.fullPath}'); + if (state.fullPath?.startsWith(Routes.app.path) ?? false) { + // Already navigating to app, do nothing + return; } - } - }, - child: const SplashPage(), - ); + debugPrint('Navigating to app route'); + Routes.app.go(context); + break; + case AuthStateUnauthenticated _: + debugPrint('User is unauthenticated: ${state.fullPath}'); + if (state.fullPath?.startsWith(Routes.auth.path) ?? false) { + // Already navigating to auth, do nothing + return; + } + debugPrint('Navigating to auth route'); + Routes.onboarding.go(context); + break; + case _: + } + } }, + child: const SplashPage(), + ); + }, + routes: [ + ShellRoute( + builder: (context, state, child) => + kDebugMode ? DebugBanner(child: child) : child, routes: [ - ShellRoute( - builder: (context, state, child) => - kDebugMode ? DebugBanner(child: child) : child, + GoRoute( + name: Routes.onboarding.name, + path: Routes.onboarding.path, + builder: (context, state) => const OnboardingPage(), + ), + GoRoute( + name: Routes.auth.name, + path: Routes.auth.path, + redirect: (context, state) { + if (getIt().isLoggedIn()) { + return Routes.app.path; + } + return null; + }, + builder: (context, state) => const LoginPage(), routes: [ GoRoute( - name: Routes.onboarding.name, - path: Routes.onboarding.path, - builder: (context, state) => const OnboardingPage(), - ), - GoRoute( - name: Routes.auth.name, - path: Routes.auth.path, - redirect: (context, state) { - if (getIt().isLoggedIn()) { - return Routes.app.path; - } - return null; - }, - builder: (context, state) => const LoginPage(), - routes: [ - GoRoute( - name: Routes.signup.name, - path: Routes.signup.subPath, - builder: (context, state) => const SignUpPage(), - ), - ], + name: Routes.signup.name, + path: Routes.signup.subPath, + builder: (context, state) => const SignUpPage(), ), ], ), - ShellRoute( - builder: (context, state, child) => - kDebugMode ? DebugBanner(child: child) : child, + ], + ), + ShellRoute( + builder: (context, state, child) => + kDebugMode ? DebugBanner(child: child) : child, + routes: [ + GoRoute( + name: Routes.app.name, + path: Routes.app.path, + redirect: (context, state) { + if (!getIt().isLoggedIn()) { + return Routes.auth.path; + } + return null; + }, + builder: (context, state) => const HomePage(), routes: [ GoRoute( - name: Routes.app.name, - path: Routes.app.path, - redirect: (context, state) { - if (!getIt().isLoggedIn()) { - return Routes.auth.path; - } - return null; - }, - builder: (context, state) => const HomePage(), - routes: [ - GoRoute( - name: Routes.placeholder.name, - path: Routes.placeholder.subPath, - builder: (context, state) => const Placeholder(), - ), - ], + name: Routes.placeholder.name, + path: Routes.placeholder.subPath, + builder: (context, state) => const Placeholder(), ), ], ), ], ), ], - ); + ), + ], + ); } diff --git a/app/lib/presentation/resources/custom_network_image.dart b/app/lib/presentation/resources/custom_network_image.dart index 9b9afca..a58b9e0 100644 --- a/app/lib/presentation/resources/custom_network_image.dart +++ b/app/lib/presentation/resources/custom_network_image.dart @@ -43,15 +43,17 @@ class CustomNetworkImage extends StatelessWidget { Widget build(BuildContext context) { return isSvg(imageUrl) ? Container( - decoration: BoxDecoration(boxShadow: [ - if ((shadow ?? false)) - BoxShadow( - color: Colors.black.shadow(_shadowOpacity), - spreadRadius: 0, - blurRadius: _blurRadius, - offset: const Offset(5, 5), - ), - ]), + decoration: BoxDecoration( + boxShadow: [ + if ((shadow ?? false)) + BoxShadow( + color: Colors.black.shadow(_shadowOpacity), + spreadRadius: 0, + blurRadius: _blurRadius, + offset: const Offset(5, 5), + ), + ], + ), child: SvgPicture.network( imageUrl ?? "", width: width, @@ -62,32 +64,35 @@ class CustomNetworkImage extends StatelessWidget { ), ) : Container( - decoration: BoxDecoration(boxShadow: [ - if ((shadow ?? false)) - BoxShadow( - color: Colors.black.shadow(_shadowOpacity), - spreadRadius: 0, - blurRadius: _blurRadius, - offset: const Offset(5, 5), - ), - ]), + decoration: BoxDecoration( + boxShadow: [ + if ((shadow ?? false)) + BoxShadow( + color: Colors.black.shadow(_shadowOpacity), + spreadRadius: 0, + blurRadius: _blurRadius, + offset: const Offset(5, 5), + ), + ], + ), child: FadeInImage( width: width, height: height, fit: imageBoxFit, image: NetworkImage(imageUrl ?? ""), placeholder: NetworkImage(loaderImage ?? ""), - imageErrorBuilder: ( - BuildContext context, - Object object, - StackTrace? stackTrace, - ) { - return CustomNetworkImage( - imageUrl: imageUrlError, - width: width, - height: height, - ); - }, + imageErrorBuilder: + ( + BuildContext context, + Object object, + StackTrace? stackTrace, + ) { + return CustomNetworkImage( + imageUrl: imageUrlError, + width: width, + height: height, + ); + }, ), ); } diff --git a/app/lib/presentation/resources/images.dart b/app/lib/presentation/resources/images.dart index 1ca7915..4181776 100644 --- a/app/lib/presentation/resources/images.dart +++ b/app/lib/presentation/resources/images.dart @@ -3,9 +3,7 @@ part of 'resources.dart'; /// Add images references here /// call: Images.appLogo.get() /// **/ -enum Images { - appLogo, -} +enum Images { appLogo } extension LoadImage on Images { static const assetsFolder = "assets"; @@ -26,16 +24,15 @@ extension LoadImage on Images { Color? color, Alignment alignment = Alignment.center, String? semanticLabel, - }) => - _SvgImage.asset( - value, - width: width, - height: height, - fit: fit, - color: color, - alignment: alignment, - semanticLabel: semanticLabel, - ); + }) => _SvgImage.asset( + value, + width: width, + height: height, + fit: fit, + color: color, + alignment: alignment, + semanticLabel: semanticLabel, + ); Widget get({ Key? key, @@ -75,16 +72,15 @@ extension LoadImage on Images { double? height, BoxFit fit = BoxFit.contain, Alignment alignment = Alignment.center, - }) => - Image.asset( - value, - bundle: assetImage().bundle, - height: width, - width: width, - scale: scale, - fit: fit, - alignment: alignment, - ); + }) => Image.asset( + value, + bundle: assetImage().bundle, + height: width, + width: width, + scale: scale, + fit: fit, + alignment: alignment, + ); AssetImage assetImage() => AssetImage(value); } @@ -106,8 +102,9 @@ class _SvgImage { width: width, height: height, fit: fit, - colorFilter: - color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null, + colorFilter: color != null + ? ColorFilter.mode(color, BlendMode.srcIn) + : null, alignment: alignment, semanticsLabel: semanticLabel, package: package, diff --git a/app/lib/presentation/themes/app_themes.dart b/app/lib/presentation/themes/app_themes.dart index 835e4b9..d38773a 100644 --- a/app/lib/presentation/themes/app_themes.dart +++ b/app/lib/presentation/themes/app_themes.dart @@ -22,24 +22,12 @@ class AppThemes { class LightTheme extends LocalTheme { LightTheme() - : super( - themeData: AppThemeData( - Brightness.light, - LightThemeColors(), - 8.0, - ), - ); + : super(themeData: AppThemeData(Brightness.light, LightThemeColors(), 8.0)); } class DarkTheme extends LocalTheme { DarkTheme() - : super( - themeData: AppThemeData( - Brightness.dark, - DarkThemeColors(), - 8.0, - ), - ); + : super(themeData: AppThemeData(Brightness.dark, DarkThemeColors(), 8.0)); } /// For projects where the design is custom and use more colors than the ones definded for Material Design, @@ -60,10 +48,8 @@ class DarkTheme extends LocalTheme { /// https://m3.material.io/styles/color/the-color-system/color-roles extension AppLocalTheme on BuildContext { LocalTheme get localTheme => AppThemes.getAppTheme( - select( - (value) => value.state.themeType, - ), - ); + select((value) => value.state.themeType), + ); ThemeData get theme => Theme.of(this); diff --git a/app/lib/presentation/themes/local_theme.dart b/app/lib/presentation/themes/local_theme.dart index 750b95a..d8737b7 100644 --- a/app/lib/presentation/themes/local_theme.dart +++ b/app/lib/presentation/themes/local_theme.dart @@ -12,28 +12,29 @@ abstract class LocalTheme { } CupertinoThemeData get cupertinoThemeData => CupertinoThemeData.raw( - colors.brightness, - colors.primary, - colors.inversePrimary, - CupertinoTextThemeData( - textStyle: bodyS, - actionTextStyle: buttonText, - tabLabelTextStyle: overLine, - navTitleTextStyle: titleL, - navLargeTitleTextStyle: titleXL, - navActionTextStyle: titleM, - pickerTextStyle: bodyS, - dateTimePickerTextStyle: bodyS, - ), - colors.surface, - colors.secondaryContainer, - CupertinoColors.systemBlue, - true, - ); + colors.brightness, + colors.primary, + colors.inversePrimary, + CupertinoTextThemeData( + textStyle: bodyS, + actionTextStyle: buttonText, + tabLabelTextStyle: overLine, + navTitleTextStyle: titleL, + navLargeTitleTextStyle: titleXL, + navActionTextStyle: titleM, + pickerTextStyle: bodyS, + dateTimePickerTextStyle: bodyS, + ), + colors.surface, + colors.secondaryContainer, + CupertinoColors.systemBlue, + true, + ); // By using this configuration you should have this output for each material widget: // https://flutter.github.io/samples/web/material_3_demo - ThemeData get data => ThemeData.from( + ThemeData get data => + ThemeData.from( useMaterial3: true, colorScheme: colors, textTheme: Typography.blackCupertino.copyWith( @@ -70,8 +71,9 @@ abstract class LocalTheme { tapTargetSize: MaterialTapTargetSize.shrinkWrap, alignment: Alignment.center, shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(themeData.borderRadius)), + borderRadius: BorderRadius.all( + Radius.circular(themeData.borderRadius), + ), ), ), ), @@ -122,154 +124,154 @@ abstract class LocalTheme { final double labelSFontSize = 8; final double captionFontSize = 12; - get titleXLHeight => 32.4 / titleXLFontSize; - - get titleLHeight => 26.4 / titleLFontSize; - - get titleMHeight => 24 / titleMFontSize; - - get titleSHeight => 19.2 / titleSFontSize; - - get titleXSHeight => 16.8 / titleXSFontSize; - - get buttonHeight => 19.6 / buttonFontSize; - - get bodyHeight => 16.41 / bodyFontSize; - - get bodySHeight => 14 / bodyFontSize; - - get subtitleMHeight => 16.41 / subtitleMFontSize; - - get subtitleSHeight => 12.89 / subtitleSFontSize; - - get inputTextHeight => 13 / inputTextFontSize; + double get titleXLHeight => 32.4 / titleXLFontSize; - get labelHeight => 9 / labelFontSize; - - get inputFieldLabelHeight => 7 / labelFontSize; - - get captionHeight => 14 / captionFontSize; - - get titleXL => TextStyle( - fontFamily: primaryFontBlack, - fontSize: titleXLFontSize, - height: titleXLHeight, - letterSpacing: -0.07, - color: colors.primary, - ); - - get titleL => TextStyle( - fontFamily: primaryFontBlack, - fontSize: titleLFontSize, - height: titleLHeight, - letterSpacing: -0.22, - ); - - get titleM => TextStyle( - fontFamily: primaryFontBlack, - fontSize: titleMFontSize, - height: titleMHeight, - letterSpacing: -0.2, - ); - - get titleS => TextStyle( - fontFamily: primaryFontBlack, - fontSize: titleSFontSize, - height: titleSHeight, - letterSpacing: -0.16, - ); - - get titleXS => TextStyle( - fontFamily: primaryFontHeavy, - fontSize: titleXSFontSize, - height: titleXSHeight, - letterSpacing: -0.14, - ); - - get titleXSS => TextStyle( - fontFamily: primaryFontHeavy, - fontSize: titleXSSFontSize, - height: titleXSHeight, - letterSpacing: -0.14, - ); - - get subtitleL => TextStyle( - fontFamily: primaryFontMedium, - fontSize: subtitleLFontSize, - height: subtitleMHeight, - letterSpacing: 1.2, - ); - - get subtitleM => TextStyle( - fontFamily: primaryFontMedium, - fontSize: subtitleMFontSize, - height: subtitleMHeight, - letterSpacing: 1.2, - ); - - get subtitleS => TextStyle( - fontFamily: primaryFontMedium, - fontSize: subtitleSFontSize, - height: subtitleSHeight, - letterSpacing: 1.65, - ); - - get buttonText => TextStyle( - fontFamily: primaryFontBlack, - fontSize: buttonFontSize, - height: buttonHeight, - letterSpacing: 1.4, - ); - - get body => TextStyle( - fontFamily: primaryFontLight, - fontSize: bodyFontSize, - height: bodyHeight, - fontWeight: FontWeight.normal, - letterSpacing: -0.14, - ); + double get titleLHeight => 26.4 / titleLFontSize; + + double get titleMHeight => 24 / titleMFontSize; + + double get titleSHeight => 19.2 / titleSFontSize; + + double get titleXSHeight => 16.8 / titleXSFontSize; + + double get buttonHeight => 19.6 / buttonFontSize; + + double get bodyHeight => 16.41 / bodyFontSize; + + double get bodySHeight => 14 / bodyFontSize; + + double get subtitleMHeight => 16.41 / subtitleMFontSize; + + double get subtitleSHeight => 12.89 / subtitleSFontSize; + + double get inputTextHeight => 13 / inputTextFontSize; + + double get labelHeight => 9 / labelFontSize; + + double get inputFieldLabelHeight => 7 / labelFontSize; + + double get captionHeight => 14 / captionFontSize; + + TextStyle get titleXL => TextStyle( + fontFamily: primaryFontBlack, + fontSize: titleXLFontSize, + height: titleXLHeight, + letterSpacing: -0.07, + color: colors.primary, + ); + + TextStyle get titleL => TextStyle( + fontFamily: primaryFontBlack, + fontSize: titleLFontSize, + height: titleLHeight, + letterSpacing: -0.22, + ); + + TextStyle get titleM => TextStyle( + fontFamily: primaryFontBlack, + fontSize: titleMFontSize, + height: titleMHeight, + letterSpacing: -0.2, + ); + + TextStyle get titleS => TextStyle( + fontFamily: primaryFontBlack, + fontSize: titleSFontSize, + height: titleSHeight, + letterSpacing: -0.16, + ); + + TextStyle get titleXS => TextStyle( + fontFamily: primaryFontHeavy, + fontSize: titleXSFontSize, + height: titleXSHeight, + letterSpacing: -0.14, + ); + + TextStyle get titleXSS => TextStyle( + fontFamily: primaryFontHeavy, + fontSize: titleXSSFontSize, + height: titleXSHeight, + letterSpacing: -0.14, + ); + + TextStyle get subtitleL => TextStyle( + fontFamily: primaryFontMedium, + fontSize: subtitleLFontSize, + height: subtitleMHeight, + letterSpacing: 1.2, + ); + + TextStyle get subtitleM => TextStyle( + fontFamily: primaryFontMedium, + fontSize: subtitleMFontSize, + height: subtitleMHeight, + letterSpacing: 1.2, + ); + + TextStyle get subtitleS => TextStyle( + fontFamily: primaryFontMedium, + fontSize: subtitleSFontSize, + height: subtitleSHeight, + letterSpacing: 1.65, + ); + + TextStyle get buttonText => TextStyle( + fontFamily: primaryFontBlack, + fontSize: buttonFontSize, + height: buttonHeight, + letterSpacing: 1.4, + ); + + TextStyle get body => TextStyle( + fontFamily: primaryFontLight, + fontSize: bodyFontSize, + height: bodyHeight, + fontWeight: FontWeight.normal, + letterSpacing: -0.14, + ); TextStyle get bodyS => TextStyle( - fontFamily: primaryFontMedium, - fontSize: bodySFontSize, - height: bodySHeight, - fontWeight: FontWeight.normal, - letterSpacing: -0.14, - ); - - get inputText => TextStyle( - fontFamily: primaryFontMedium, - fontSize: titleXSFontSize, - height: inputTextHeight, - letterSpacing: 0.1, - ); - - get overLine => TextStyle( - fontFamily: primaryFontLight, - fontSize: titleXSFontSize, - height: inputTextHeight, - letterSpacing: 0.1, - ); - - get caption => TextStyle( - fontFamily: primaryFontHeavy, - fontSize: captionFontSize, - height: captionHeight, - ); - - get label => TextStyle( - fontFamily: primaryFontBlack, - fontSize: labelFontSize, - height: labelHeight, - letterSpacing: 1, - ); - - get labelS => TextStyle( - fontFamily: primaryFontBlack, - fontSize: labelSFontSize, - height: labelHeight, - letterSpacing: 1, - ); + fontFamily: primaryFontMedium, + fontSize: bodySFontSize, + height: bodySHeight, + fontWeight: FontWeight.normal, + letterSpacing: -0.14, + ); + + TextStyle get inputText => TextStyle( + fontFamily: primaryFontMedium, + fontSize: titleXSFontSize, + height: inputTextHeight, + letterSpacing: 0.1, + ); + + TextStyle get overLine => TextStyle( + fontFamily: primaryFontLight, + fontSize: titleXSFontSize, + height: inputTextHeight, + letterSpacing: 0.1, + ); + + TextStyle get caption => TextStyle( + fontFamily: primaryFontHeavy, + fontSize: captionFontSize, + height: captionHeight, + ); + + TextStyle get label => TextStyle( + fontFamily: primaryFontBlack, + fontSize: labelFontSize, + height: labelHeight, + letterSpacing: 1, + ); + + TextStyle get labelS => TextStyle( + fontFamily: primaryFontBlack, + fontSize: labelSFontSize, + height: labelHeight, + letterSpacing: 1, + ); } extension ColorShadow on Color { diff --git a/app/lib/presentation/themes/resources/app_theme_data.dart b/app/lib/presentation/themes/resources/app_theme_data.dart index 1cda14f..9550224 100644 --- a/app/lib/presentation/themes/resources/app_theme_data.dart +++ b/app/lib/presentation/themes/resources/app_theme_data.dart @@ -11,34 +11,34 @@ class AppThemeData { AppThemeData(this.brightness, this.palette, this.borderRadius); ColorScheme get colorScheme => ColorScheme( - brightness: brightness, - primary: palette.primary.v40, - secondary: palette.secondary.v40, - error: palette.error.v40, - tertiary: palette.tertiary.v40, - onPrimary: palette.primary.v100, - primaryContainer: palette.primary.v90, - onPrimaryContainer: palette.primary.v10, - onSecondary: palette.secondary.v100, - secondaryContainer: palette.secondary.v90, - onSecondaryContainer: palette.secondary.v10, - onTertiary: palette.tertiary.v100, - tertiaryContainer: palette.tertiary.v90, - onTertiaryContainer: palette.tertiary.v10, - onError: palette.error.v100, - errorContainer: palette.error.v90, - onErrorContainer: palette.error.v10, - surface: palette.neutral.v99, - onSurface: palette.neutral.v10, - onSurfaceVariant: palette.neutralVariant.v30, - surfaceContainerHighest: palette.neutralVariant.v90, - outline: palette.neutralVariant.v50, - outlineVariant: palette.neutralVariant.v80, - shadow: palette.neutral.v0, - scrim: palette.neutral.v0, - inverseSurface: palette.neutral.v20, - inversePrimary: palette.primary.v80, - ); + brightness: brightness, + primary: palette.primary.v40, + secondary: palette.secondary.v40, + error: palette.error.v40, + tertiary: palette.tertiary.v40, + onPrimary: palette.primary.v100, + primaryContainer: palette.primary.v90, + onPrimaryContainer: palette.primary.v10, + onSecondary: palette.secondary.v100, + secondaryContainer: palette.secondary.v90, + onSecondaryContainer: palette.secondary.v10, + onTertiary: palette.tertiary.v100, + tertiaryContainer: palette.tertiary.v90, + onTertiaryContainer: palette.tertiary.v10, + onError: palette.error.v100, + errorContainer: palette.error.v90, + onErrorContainer: palette.error.v10, + surface: palette.neutral.v99, + onSurface: palette.neutral.v10, + onSurfaceVariant: palette.neutralVariant.v30, + surfaceContainerHighest: palette.neutralVariant.v90, + outline: palette.neutralVariant.v50, + outlineVariant: palette.neutralVariant.v80, + shadow: palette.neutral.v0, + scrim: palette.neutral.v0, + inverseSurface: palette.neutral.v20, + inversePrimary: palette.primary.v80, + ); } abstract class ThemeColors { diff --git a/app/lib/presentation/themes/resources/dark_theme_colors.dart b/app/lib/presentation/themes/resources/dark_theme_colors.dart index 94d2f0f..bf3fc8e 100644 --- a/app/lib/presentation/themes/resources/dark_theme_colors.dart +++ b/app/lib/presentation/themes/resources/dark_theme_colors.dart @@ -10,20 +10,20 @@ class DarkThemeColors implements ThemeColors { /// 100: onPrimary @override MaterialColor get primary => const MaterialColor(0xFFD0BCFF, { - 0: Color(0xFF000000), - 10: Color(0xFF21005D), // onPrimaryContainer - 20: Color(0xFF381E72), - 30: Color(0xFF4F378B), - 40: Color(0xFF6750A4), // inversePrimary (light mode primary) - 50: Color(0xFF7F67BE), - 60: Color(0xFF9A82DB), - 70: Color(0xFFB69DF8), - 80: Color(0xFFD0BCFF), // primary - Main brand color in dark - 90: Color(0xFFEADDFF), // primaryContainer - 95: Color(0xFFF6EDFF), - 99: Color(0xFFFFFBFE), - 100: Color(0xFFFFFFFF), // onPrimary - }); + 0: Color(0xFF000000), + 10: Color(0xFF21005D), // onPrimaryContainer + 20: Color(0xFF381E72), + 30: Color(0xFF4F378B), + 40: Color(0xFF6750A4), // inversePrimary (light mode primary) + 50: Color(0xFF7F67BE), + 60: Color(0xFF9A82DB), + 70: Color(0xFFB69DF8), + 80: Color(0xFFD0BCFF), // primary - Main brand color in dark + 90: Color(0xFFEADDFF), // primaryContainer + 95: Color(0xFFF6EDFF), + 99: Color(0xFFFFFBFE), + 100: Color(0xFFFFFFFF), // onPrimary + }); /// 10: onSecondaryContainer /// 40: secondary @@ -31,20 +31,20 @@ class DarkThemeColors implements ThemeColors { /// 100: onSecondary @override MaterialColor get secondary => const MaterialColor(0xFFCCC2DC, { - 0: Color(0xFF000000), - 10: Color(0xFF1D192B), // onSecondaryContainer - 20: Color(0xFF332D41), - 30: Color(0xFF4A4458), - 40: Color(0xFF625B71), - 50: Color(0xFF7A7289), - 60: Color(0xFF958DA5), - 70: Color(0xFFB0A7C0), - 80: Color(0xFFCCC2DC), // secondary - 90: Color(0xFFE8DEF8), // secondaryContainer - 95: Color(0xFFF6EDFF), - 99: Color(0xFFFFFBFE), - 100: Color(0xFFFFFFFF), // onSecondary - }); + 0: Color(0xFF000000), + 10: Color(0xFF1D192B), // onSecondaryContainer + 20: Color(0xFF332D41), + 30: Color(0xFF4A4458), + 40: Color(0xFF625B71), + 50: Color(0xFF7A7289), + 60: Color(0xFF958DA5), + 70: Color(0xFFB0A7C0), + 80: Color(0xFFCCC2DC), // secondary + 90: Color(0xFFE8DEF8), // secondaryContainer + 95: Color(0xFFF6EDFF), + 99: Color(0xFFFFFBFE), + 100: Color(0xFFFFFFFF), // onSecondary + }); /// 10: onTertiaryContainer /// 40: tertiary @@ -52,20 +52,20 @@ class DarkThemeColors implements ThemeColors { /// 100: onTertiary @override MaterialColor get tertiary => const MaterialColor(0xFFEFB8C8, { - 0: Color(0xFF000000), - 10: Color(0xFF31111D), // onTertiaryContainer - 20: Color(0xFF492532), - 30: Color(0xFF633B48), - 40: Color(0xFF7D5260), - 50: Color(0xFF986977), - 60: Color(0xFFB58392), - 70: Color(0xFFD29DAC), - 80: Color(0xFFEFB8C8), // tertiary - 90: Color(0xFFFFD8E4), // tertiaryContainer - 95: Color(0xFFFFECF1), - 99: Color(0xFFFFFBFA), - 100: Color(0xFFFFFFFF), // onTertiary - }); + 0: Color(0xFF000000), + 10: Color(0xFF31111D), // onTertiaryContainer + 20: Color(0xFF492532), + 30: Color(0xFF633B48), + 40: Color(0xFF7D5260), + 50: Color(0xFF986977), + 60: Color(0xFFB58392), + 70: Color(0xFFD29DAC), + 80: Color(0xFFEFB8C8), // tertiary + 90: Color(0xFFFFD8E4), // tertiaryContainer + 95: Color(0xFFFFECF1), + 99: Color(0xFFFFFBFA), + 100: Color(0xFFFFFFFF), // onTertiary + }); /// 10: onErrorContainer /// 40: error @@ -73,20 +73,20 @@ class DarkThemeColors implements ThemeColors { /// 100: onError @override MaterialColor get error => const MaterialColor(0xFFF2B8B5, { - 0: Color(0xFF000000), - 10: Color(0xFF410E0B), // onErrorContainer - 20: Color(0xFF601410), - 30: Color(0xFF8C1D18), - 40: Color(0xFFB3261E), - 50: Color(0xFFDC362E), - 60: Color(0xFFE46962), - 70: Color(0xFFEC928E), - 80: Color(0xFFF2B8B5), // error - 90: Color(0xFFF9DEDC), // errorContainer - 95: Color(0xFFFCEEEE), - 99: Color(0xFFFFFBF9), - 100: Color(0xFFFFFFFF), // onError - }); + 0: Color(0xFF000000), + 10: Color(0xFF410E0B), // onErrorContainer + 20: Color(0xFF601410), + 30: Color(0xFF8C1D18), + 40: Color(0xFFB3261E), + 50: Color(0xFFDC362E), + 60: Color(0xFFE46962), + 70: Color(0xFFEC928E), + 80: Color(0xFFF2B8B5), // error + 90: Color(0xFFF9DEDC), // errorContainer + 95: Color(0xFFFCEEEE), + 99: Color(0xFFFFFBF9), + 100: Color(0xFFFFFFFF), // onError + }); /// 0: shadow / scrim /// 10: onSurface @@ -94,21 +94,20 @@ class DarkThemeColors implements ThemeColors { /// 99: surface @override MaterialColor get neutral => const MaterialColor(0xFF1C1B1F, { - 0: Color(0xFF000000), // shadow / scrim - 10: Color( - 0xFFE6E1E5), // onSurface - Main text color in dark (light color) - 20: Color(0xFFF4EFF4), // inverseSurface (light color) - 30: Color(0xFFAEAAAE), - 40: Color(0xFF939094), - 50: Color(0xFF787579), - 60: Color(0xFF605D62), - 70: Color(0xFF484649), - 80: Color(0xFF313033), - 90: Color(0xFF1C1B1F), - 95: Color(0xFF141316), - 99: Color(0xFF141316), // surface - Main background in dark (dark color) - 100: Color(0xFF000000), - }); + 0: Color(0xFF000000), // shadow / scrim + 10: Color(0xFFE6E1E5), // onSurface - Main text color in dark (light color) + 20: Color(0xFFF4EFF4), // inverseSurface (light color) + 30: Color(0xFFAEAAAE), + 40: Color(0xFF939094), + 50: Color(0xFF787579), + 60: Color(0xFF605D62), + 70: Color(0xFF484649), + 80: Color(0xFF313033), + 90: Color(0xFF1C1B1F), + 95: Color(0xFF141316), + 99: Color(0xFF141316), // surface - Main background in dark (dark color) + 100: Color(0xFF000000), + }); /// 30: onSurfaceVariant /// 50: outline @@ -116,19 +115,20 @@ class DarkThemeColors implements ThemeColors { /// 90: surfaceContainerHighest @override MaterialColor get neutralVariant => const MaterialColor(0xFF49454F, { - 0: Color(0xFF000000), - 10: Color(0xFFF5EEFA), - 20: Color(0xFFE7E0EC), - 30: Color( - 0xFFC9C5D0), // onSurfaceVariant - Secondary text (lighter in dark) - 40: Color(0xFFAEA9B4), - 50: Color(0xFF938F99), // outline - Borders, dividers - 60: Color(0xFF79747E), - 70: Color(0xFF605D66), - 80: Color(0xFF49454F), // outlineVariant - Subtle borders - 90: Color(0xFF322F37), // surfaceContainerHighest - 95: Color(0xFF1D1A22), - 99: Color(0xFF000000), - 100: Color(0xFF000000), - }); + 0: Color(0xFF000000), + 10: Color(0xFFF5EEFA), + 20: Color(0xFFE7E0EC), + 30: Color( + 0xFFC9C5D0, + ), // onSurfaceVariant - Secondary text (lighter in dark) + 40: Color(0xFFAEA9B4), + 50: Color(0xFF938F99), // outline - Borders, dividers + 60: Color(0xFF79747E), + 70: Color(0xFF605D66), + 80: Color(0xFF49454F), // outlineVariant - Subtle borders + 90: Color(0xFF322F37), // surfaceContainerHighest + 95: Color(0xFF1D1A22), + 99: Color(0xFF000000), + 100: Color(0xFF000000), + }); } diff --git a/app/lib/presentation/themes/resources/light_theme_colors.dart b/app/lib/presentation/themes/resources/light_theme_colors.dart index 8713f3f..e11721b 100644 --- a/app/lib/presentation/themes/resources/light_theme_colors.dart +++ b/app/lib/presentation/themes/resources/light_theme_colors.dart @@ -9,20 +9,20 @@ class LightThemeColors implements ThemeColors { /// 100: onPrimary @override MaterialColor get primary => const MaterialColor(0xFF6750A4, { - 0: Color(0xFF000000), - 10: Color(0xFF21005D), // onPrimaryContainer - 20: Color(0xFF381E72), - 30: Color(0xFF4F378B), - 40: Color(0xFF6750A4), // primary - Main brand color - 50: Color(0xFF7F67BE), - 60: Color(0xFF9A82DB), - 70: Color(0xFFB69DF8), - 80: Color(0xFFD0BCFF), // inversePrimary - 90: Color(0xFFEADDFF), // primaryContainer - 95: Color(0xFFF6EDFF), - 99: Color(0xFFFFFBFE), - 100: Color(0xFFFFFFFF), // onPrimary - }); + 0: Color(0xFF000000), + 10: Color(0xFF21005D), // onPrimaryContainer + 20: Color(0xFF381E72), + 30: Color(0xFF4F378B), + 40: Color(0xFF6750A4), // primary - Main brand color + 50: Color(0xFF7F67BE), + 60: Color(0xFF9A82DB), + 70: Color(0xFFB69DF8), + 80: Color(0xFFD0BCFF), // inversePrimary + 90: Color(0xFFEADDFF), // primaryContainer + 95: Color(0xFFF6EDFF), + 99: Color(0xFFFFFBFE), + 100: Color(0xFFFFFFFF), // onPrimary + }); /// 10: onSecondaryContainer /// 40: secondary @@ -30,20 +30,20 @@ class LightThemeColors implements ThemeColors { /// 100: onSecondary @override MaterialColor get secondary => const MaterialColor(0xFF625B71, { - 0: Color(0xFF000000), - 10: Color(0xFF1D192B), // onSecondaryContainer - 20: Color(0xFF332D41), - 30: Color(0xFF4A4458), - 40: Color(0xFF625B71), // secondary - 50: Color(0xFF7A7289), - 60: Color(0xFF958DA5), - 70: Color(0xFFB0A7C0), - 80: Color(0xFFCCC2DC), - 90: Color(0xFFE8DEF8), // secondaryContainer - 95: Color(0xFFF6EDFF), - 99: Color(0xFFFFFBFE), - 100: Color(0xFFFFFFFF), // onSecondary - }); + 0: Color(0xFF000000), + 10: Color(0xFF1D192B), // onSecondaryContainer + 20: Color(0xFF332D41), + 30: Color(0xFF4A4458), + 40: Color(0xFF625B71), // secondary + 50: Color(0xFF7A7289), + 60: Color(0xFF958DA5), + 70: Color(0xFFB0A7C0), + 80: Color(0xFFCCC2DC), + 90: Color(0xFFE8DEF8), // secondaryContainer + 95: Color(0xFFF6EDFF), + 99: Color(0xFFFFFBFE), + 100: Color(0xFFFFFFFF), // onSecondary + }); /// 10: onTertiaryContainer /// 40: tertiary @@ -51,20 +51,20 @@ class LightThemeColors implements ThemeColors { /// 100: onTertiary @override MaterialColor get tertiary => const MaterialColor(0xFF7D5260, { - 0: Color(0xFF000000), - 10: Color(0xFF31111D), // onTertiaryContainer - 20: Color(0xFF492532), - 30: Color(0xFF633B48), - 40: Color(0xFF7D5260), // tertiary - 50: Color(0xFF986977), - 60: Color(0xFFB58392), - 70: Color(0xFFD29DAC), - 80: Color(0xFFEFB8C8), - 90: Color(0xFFFFD8E4), // tertiaryContainer - 95: Color(0xFFFFECF1), - 99: Color(0xFFFFFBFA), - 100: Color(0xFFFFFFFF), // onTertiary - }); + 0: Color(0xFF000000), + 10: Color(0xFF31111D), // onTertiaryContainer + 20: Color(0xFF492532), + 30: Color(0xFF633B48), + 40: Color(0xFF7D5260), // tertiary + 50: Color(0xFF986977), + 60: Color(0xFFB58392), + 70: Color(0xFFD29DAC), + 80: Color(0xFFEFB8C8), + 90: Color(0xFFFFD8E4), // tertiaryContainer + 95: Color(0xFFFFECF1), + 99: Color(0xFFFFFBFA), + 100: Color(0xFFFFFFFF), // onTertiary + }); /// 10: onErrorContainer /// 40: error @@ -72,20 +72,20 @@ class LightThemeColors implements ThemeColors { /// 100: onError @override MaterialColor get error => const MaterialColor(0xFFB3261E, { - 0: Color(0xFF000000), - 10: Color(0xFF410E0B), // onErrorContainer - 20: Color(0xFF601410), - 30: Color(0xFF8C1D18), - 40: Color(0xFFB3261E), // error - 50: Color(0xFFDC362E), - 60: Color(0xFFE46962), - 70: Color(0xFFEC928E), - 80: Color(0xFFF2B8B5), - 90: Color(0xFFF9DEDC), // errorContainer - 95: Color(0xFFFCEEEE), - 99: Color(0xFFFFFBF9), - 100: Color(0xFFFFFFFF), // onError - }); + 0: Color(0xFF000000), + 10: Color(0xFF410E0B), // onErrorContainer + 20: Color(0xFF601410), + 30: Color(0xFF8C1D18), + 40: Color(0xFFB3261E), // error + 50: Color(0xFFDC362E), + 60: Color(0xFFE46962), + 70: Color(0xFFEC928E), + 80: Color(0xFFF2B8B5), + 90: Color(0xFFF9DEDC), // errorContainer + 95: Color(0xFFFCEEEE), + 99: Color(0xFFFFFBF9), + 100: Color(0xFFFFFFFF), // onError + }); /// 0: shadow / scrim /// 10: onSurface @@ -93,20 +93,20 @@ class LightThemeColors implements ThemeColors { /// 99: surface @override MaterialColor get neutral => const MaterialColor(0xFF1C1B1F, { - 0: Color(0xFF000000), // shadow / scrim - 10: Color(0xFF1C1B1F), // onSurface - Main text color - 20: Color(0xFF313033), // inverseSurface - 30: Color(0xFF484649), - 40: Color(0xFF605D62), - 50: Color(0xFF787579), - 60: Color(0xFF939094), - 70: Color(0xFFAEAAAE), - 80: Color(0xFFC9C5CA), - 90: Color(0xFFE6E1E5), - 95: Color(0xFFF4EFF4), - 99: Color(0xFFFFFBFE), // surface - Main background - 100: Color(0xFFFFFFFF), - }); + 0: Color(0xFF000000), // shadow / scrim + 10: Color(0xFF1C1B1F), // onSurface - Main text color + 20: Color(0xFF313033), // inverseSurface + 30: Color(0xFF484649), + 40: Color(0xFF605D62), + 50: Color(0xFF787579), + 60: Color(0xFF939094), + 70: Color(0xFFAEAAAE), + 80: Color(0xFFC9C5CA), + 90: Color(0xFFE6E1E5), + 95: Color(0xFFF4EFF4), + 99: Color(0xFFFFFBFE), // surface - Main background + 100: Color(0xFFFFFFFF), + }); /// 30: onSurfaceVariant /// 50: outline @@ -114,18 +114,18 @@ class LightThemeColors implements ThemeColors { /// 90: surfaceContainerHighest @override MaterialColor get neutralVariant => const MaterialColor(0xFF49454F, { - 0: Color(0xFF000000), - 10: Color(0xFF1D1A22), - 20: Color(0xFF322F37), - 30: Color(0xFF49454F), // onSurfaceVariant - Secondary text, icons - 40: Color(0xFF605D66), - 50: Color(0xFF79747E), // outline - Borders, dividers - 60: Color(0xFF938F99), - 70: Color(0xFFAEA9B4), - 80: Color(0xFFC9C5D0), // outlineVariant - Subtle borders - 90: Color(0xFFE7E0EC), // surfaceContainerHighest - 95: Color(0xFFF5EEFA), - 99: Color(0xFFFFFBFE), - 100: Color(0xFFFFFFFF), - }); + 0: Color(0xFF000000), + 10: Color(0xFF1D1A22), + 20: Color(0xFF322F37), + 30: Color(0xFF49454F), // onSurfaceVariant - Secondary text, icons + 40: Color(0xFF605D66), + 50: Color(0xFF79747E), // outline - Borders, dividers + 60: Color(0xFF938F99), + 70: Color(0xFFAEA9B4), + 80: Color(0xFFC9C5D0), // outlineVariant - Subtle borders + 90: Color(0xFFE7E0EC), // surfaceContainerHighest + 95: Color(0xFFF5EEFA), + 99: Color(0xFFFFFBFE), + 100: Color(0xFFFFFFFF), + }); } diff --git a/app/lib/presentation/ui/base/tracked_page.dart b/app/lib/presentation/ui/base/tracked_page.dart index e677dc4..18c0506 100644 --- a/app/lib/presentation/ui/base/tracked_page.dart +++ b/app/lib/presentation/ui/base/tracked_page.dart @@ -55,8 +55,10 @@ class _TrackedPageState extends State with RouteAware { AnalyticsClient get analytics => getIt(); void _track(String phase) { - analytics.trackEvent('${widget.trackingName}_$phase', - properties: widget.trackingProperties); + analytics.trackEvent( + '${widget.trackingName}_$phase', + properties: widget.trackingProperties, + ); } @override diff --git a/app/lib/presentation/ui/components/primary_button.dart b/app/lib/presentation/ui/components/primary_button.dart index b8a2eb8..401d54f 100644 --- a/app/lib/presentation/ui/components/primary_button.dart +++ b/app/lib/presentation/ui/components/primary_button.dart @@ -18,28 +18,25 @@ class PrimaryButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, }) : assert( - leadingIcon == null || trailingIcon == null, - 'Only one of leadingIcon or trailingIcon can be provided', - ); + leadingIcon == null || trailingIcon == null, + 'Only one of leadingIcon or trailingIcon can be provided', + ); @override Widget build(BuildContext context) { return ElevatedButton.icon( style: Theme.of(context).elevatedButtonTheme.style?.copyWith( - minimumSize: WidgetStateProperty.all( - const Size( - double.infinity, - Dimen.buttonHeightM, - ), - ), - ), + minimumSize: WidgetStateProperty.all( + const Size(double.infinity, Dimen.buttonHeightM), + ), + ), onPressed: isLoading || !isEnabled ? null : onPressed, icon: leadingIcon ?? trailingIcon ?? const SizedBox.shrink(), iconAlignment: leadingIcon != null ? IconAlignment.start : trailingIcon != null - ? IconAlignment.end - : null, + ? IconAlignment.end + : null, label: isLoading ? SizedBox( width: Dimen.loadingSpinnerSizeS, diff --git a/app/lib/presentation/ui/custom/app_theme_switch.dart b/app/lib/presentation/ui/custom/app_theme_switch.dart index 8aecf72..7246530 100644 --- a/app/lib/presentation/ui/custom/app_theme_switch.dart +++ b/app/lib/presentation/ui/custom/app_theme_switch.dart @@ -16,9 +16,9 @@ class AppThemeSwitch extends StatelessWidget { value: theme == ThemeType.dark, thumbIcon: const WidgetStatePropertyAll(Icon(Icons.brightness_6)), onChanged: (value) { - context - .read() - .updateTheme(value ? ThemeType.dark : ThemeType.light); + context.read().updateTheme( + value ? ThemeType.dark : ThemeType.light, + ); }, ); } diff --git a/app/lib/presentation/ui/custom/cookies.dart b/app/lib/presentation/ui/custom/cookies.dart index ffc0ecb..072353c 100644 --- a/app/lib/presentation/ui/custom/cookies.dart +++ b/app/lib/presentation/ui/custom/cookies.dart @@ -51,7 +51,8 @@ class _CookiesState extends State { widget.child, Align( alignment: widget.alignment, - child: widget.cookiesChild ?? + child: + widget.cookiesChild ?? Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width, @@ -66,14 +67,16 @@ class _CookiesState extends State { children: [ Text( S.of(context).cookiesTitle, - style: - Theme.of(context).textTheme.headlineMedium, + style: Theme.of(context) + .textTheme + .headlineMedium, ), const Gap(Dimen.spacingS), SelectableText( S.of(context).cookiesBody, - style: - Theme.of(context).textTheme.headlineMedium, + style: Theme.of(context) + .textTheme + .headlineMedium, ), const Gap(Dimen.spacingS), Align( diff --git a/app/lib/presentation/ui/custom/debug_banner.dart b/app/lib/presentation/ui/custom/debug_banner.dart index 96d26b5..baef312 100644 --- a/app/lib/presentation/ui/custom/debug_banner.dart +++ b/app/lib/presentation/ui/custom/debug_banner.dart @@ -11,10 +11,7 @@ import 'package:gap/gap.dart'; class DebugBanner extends StatelessWidget { final Widget child; - const DebugBanner({ - super.key, - required this.child, - }); + const DebugBanner({super.key, required this.child}); @override Widget build(BuildContext context) { @@ -32,8 +29,9 @@ class DebugBanner extends StatelessWidget { const Gap(Dimen.spacingS), Text( S.of(context).debugModeLabel, - style: - textTheme.bodyLarge?.copyWith(color: colorScheme.onError), + style: textTheme.bodyLarge?.copyWith( + color: colorScheme.onError, + ), ), const Spacer(), BlocBuilder( diff --git a/app/lib/presentation/ui/custom/environment_selector.dart b/app/lib/presentation/ui/custom/environment_selector.dart index 728a24b..eebf4c6 100644 --- a/app/lib/presentation/ui/custom/environment_selector.dart +++ b/app/lib/presentation/ui/custom/environment_selector.dart @@ -7,27 +7,25 @@ import 'package:flutter/material.dart'; import '../../../main/init.dart'; class EnvironmentSelector extends StatelessWidget { - EnvironmentSelector({ - super.key, - }); + EnvironmentSelector({super.key}); final EnvironmentService environmentService = getIt(); DropdownMenuItem _item( - String value, String label, TextStyle textStyle) => - DropdownMenuItem( - value: value, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: Dimen.spacingXs), - child: Text(label, style: textStyle), - ), - ); + String value, + String label, + TextStyle textStyle, + ) => DropdownMenuItem( + value: value, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: Dimen.spacingXs), + child: Text(label, style: textStyle), + ), + ); @override Widget build(BuildContext context) { - final textStyle = Theme.of(context) - .textTheme - .bodyLarge + final textStyle = Theme.of(context).textTheme.bodyLarge ?.copyWith(color: Theme.of(context).colorScheme.primary.v0); final items = >[ diff --git a/app/lib/presentation/ui/custom/failure_widget.dart b/app/lib/presentation/ui/custom/failure_widget.dart index c34efb7..c53d27c 100644 --- a/app/lib/presentation/ui/custom/failure_widget.dart +++ b/app/lib/presentation/ui/custom/failure_widget.dart @@ -8,8 +8,11 @@ class FailureWidget extends StatelessWidget { final Failure? failure; final VoidCallback onRetry; - const FailureWidget( - {super.key, required this.failure, required this.onRetry}); + const FailureWidget({ + super.key, + required this.failure, + required this.onRetry, + }); @override Widget build(BuildContext context) { diff --git a/app/lib/presentation/ui/custom/loading_screen.dart b/app/lib/presentation/ui/custom/loading_screen.dart index 1883dad..6e34759 100644 --- a/app/lib/presentation/ui/custom/loading_screen.dart +++ b/app/lib/presentation/ui/custom/loading_screen.dart @@ -3,10 +3,7 @@ import 'package:flutter/material.dart'; class LoadingScreen extends StatelessWidget { final Color? color; - const LoadingScreen({ - super.key, - this.color, - }); + const LoadingScreen({super.key, this.color}); @override Widget build(BuildContext context) { @@ -16,9 +13,7 @@ class LoadingScreen extends StatelessWidget { child: SizedBox( width: 24, height: 24, - child: CircularProgressIndicator( - color: color, - ), + child: CircularProgressIndicator(color: color), ), ), ); diff --git a/app/lib/presentation/ui/custom/terms_services_check.dart b/app/lib/presentation/ui/custom/terms_services_check.dart index ca75294..23d6b25 100644 --- a/app/lib/presentation/ui/custom/terms_services_check.dart +++ b/app/lib/presentation/ui/custom/terms_services_check.dart @@ -19,11 +19,11 @@ class TermsServicesCheck extends StatelessWidget { width: double.infinity, child: TextButton( style: Theme.of(context).textButtonTheme.style?.copyWith( - alignment: Alignment.centerLeft, // <-- important - padding: WidgetStateProperty.all( - EdgeInsets.zero, - ), // optional, but helps - ), + alignment: Alignment.centerLeft, // <-- important + padding: WidgetStateProperty.all( + EdgeInsets.zero, + ), // optional, but helps + ), onPressed: () => onChanged?.call(!agreeToTerms), child: Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -45,9 +45,7 @@ class TermsServicesCheck extends StatelessWidget { IconButton( onPressed: () { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(S.of(context).hintTermsAndConditions), - ), + SnackBar(content: Text(S.of(context).hintTermsAndConditions)), ); }, icon: const Icon(Icons.info), diff --git a/app/lib/presentation/ui/pages/auth/login/login_form.dart b/app/lib/presentation/ui/pages/auth/login/login_form.dart index 25be1bd..497b547 100644 --- a/app/lib/presentation/ui/pages/auth/login/login_form.dart +++ b/app/lib/presentation/ui/pages/auth/login/login_form.dart @@ -50,9 +50,7 @@ class _LoginFormState extends State { ), const Gap(Dimen.spacingL), TextFormField( - decoration: InputDecoration( - labelText: S.of(context).labelEmail, - ), + decoration: InputDecoration(labelText: S.of(context).labelEmail), keyboardType: TextInputType.emailAddress, autofillHints: const [AutofillHints.username, AutofillHints.email], controller: emailController, @@ -66,9 +64,7 @@ class _LoginFormState extends State { ), const Gap(Dimen.spacingM), TextFormField( - decoration: InputDecoration( - labelText: S.of(context).labelPassword, - ), + decoration: InputDecoration(labelText: S.of(context).labelPassword), obscureText: true, controller: passwordController, validator: (value) { @@ -96,10 +92,10 @@ class _LoginFormState extends State { if (state is RError) ...[ Text( S.of(context).loginErrorInvalidCredentials, - style: - Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Theme.of(context).colorScheme.error, - ), + style: Theme.of(context).textTheme.headlineMedium + ?.copyWith( + color: Theme.of(context).colorScheme.error, + ), ), const Gap(Dimen.spacingM), ], @@ -109,15 +105,15 @@ class _LoginFormState extends State { if ((_formKey.currentState?.validate() ?? false) && agreeToTerms) { context.read().login( - email: emailController.text, - password: passwordController.text, - ); + email: emailController.text, + password: passwordController.text, + ); } }, isEnabled: agreeToTerms, isLoading: state is RLoading, trailingIcon: const Icon(Icons.login), - ) + ), ], ); }, diff --git a/app/lib/presentation/ui/pages/auth/sign_up/sign_up_form.dart b/app/lib/presentation/ui/pages/auth/sign_up/sign_up_form.dart index 162ac91..8a26bf3 100644 --- a/app/lib/presentation/ui/pages/auth/sign_up/sign_up_form.dart +++ b/app/lib/presentation/ui/pages/auth/sign_up/sign_up_form.dart @@ -51,9 +51,7 @@ class _SignUpFormState extends State { ), const Gap(Dimen.spacingL), TextFormField( - decoration: InputDecoration( - labelText: S.of(context).labelEmail, - ), + decoration: InputDecoration(labelText: S.of(context).labelEmail), keyboardType: TextInputType.emailAddress, autofillHints: const [AutofillHints.username, AutofillHints.email], controller: emailController, @@ -67,9 +65,7 @@ class _SignUpFormState extends State { ), const Gap(Dimen.spacingM), TextFormField( - decoration: InputDecoration( - labelText: S.of(context).labelPassword, - ), + decoration: InputDecoration(labelText: S.of(context).labelPassword), obscureText: true, controller: passwordController, validator: (value) { @@ -116,10 +112,10 @@ class _SignUpFormState extends State { if (state is RError) ...[ Text( S.of(context).loginErrorInvalidCredentials, - style: - Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Theme.of(context).colorScheme.error, - ), + style: Theme.of(context).textTheme.headlineMedium + ?.copyWith( + color: Theme.of(context).colorScheme.error, + ), ), const Gap(Dimen.spacingM), ], @@ -129,15 +125,15 @@ class _SignUpFormState extends State { if ((_formKey.currentState?.validate() ?? false) && agreeToTerms) { context.read().signUp( - email: emailController.text, - password: passwordController.text, - ); + email: emailController.text, + password: passwordController.text, + ); } }, isEnabled: agreeToTerms, isLoading: state is RLoading, trailingIcon: const Icon(Icons.login), - ) + ), ], ); }, diff --git a/app/lib/presentation/ui/pages/onboarding/onboarding_page.dart b/app/lib/presentation/ui/pages/onboarding/onboarding_page.dart index 7bac335..b963c1f 100644 --- a/app/lib/presentation/ui/pages/onboarding/onboarding_page.dart +++ b/app/lib/presentation/ui/pages/onboarding/onboarding_page.dart @@ -80,17 +80,17 @@ class _OnboardingPageState extends State { Padding( padding: const EdgeInsets.all(Dimen.spacingM), child: Semantics( - label: S.of(context).onboardingPageIndicator( - _currentPage + 1, - _totalPages, - ), + label: S + .of(context) + .onboardingPageIndicator(_currentPage + 1, _totalPages), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( _totalPages, (index) => Container( margin: const EdgeInsets.symmetric( - horizontal: Dimen.spacingXs), + horizontal: Dimen.spacingXs, + ), width: _currentPage == index ? Dimen.spacingL : Dimen.spacingS, @@ -183,28 +183,21 @@ class _OnboardingPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - icon, - size: Dimen.onboardingIconSize, - color: color, - ), + Icon(icon, size: Dimen.onboardingIconSize, color: color), const SizedBox(height: Dimen.spacingXl), Text( title, - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of(context).textTheme.headlineMedium + ?.copyWith(fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), const SizedBox(height: Dimen.spacingM), Text( description, style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withValues(alpha: 0.6), - ), + color: Theme.of(context).colorScheme.onSurface + .withValues(alpha: 0.6), + ), textAlign: TextAlign.center, ), ], diff --git a/app/lib/presentation/ui/pages/splash/splash_page.dart b/app/lib/presentation/ui/pages/splash/splash_page.dart index 95ba5fd..1f75512 100644 --- a/app/lib/presentation/ui/pages/splash/splash_page.dart +++ b/app/lib/presentation/ui/pages/splash/splash_page.dart @@ -28,10 +28,6 @@ class _SplashPageState extends State { @override Widget build(BuildContext context) { - return const Material( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const Material(child: Center(child: CircularProgressIndicator())); } } diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 9d2b9bd..1c8bb29 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -20,27 +20,27 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=3.6.0 <4.0.0" - flutter: ">=3.41.0" + sdk: ^3.13.0 + flutter: ">=3.47.0" dependencies: flutter: sdk: flutter - flutter_svg: ^2.2.0 - shared_preferences: ^2.5.3 - intl: ^0.20.2 - intl_utils: ^2.8.14 - cupertino_icons: ^1.0.5 - dio: ^5.8.0 - async: ^2.11.0 - flutter_bloc: ^9.0.0 - get_it: ^8.0.0 - package_info_plus: ^8.3.0 - permission_handler: ^12.0.0 - universal_html: ^2.2.4 - go_router: ^17.2.3 - equatable: ^2.0.7 - firebase_core: ^3.15.0 + flutter_svg: ^2.3.0 + shared_preferences: ^2.5.5 + intl: ^0.20.3 + intl_utils: ^2.8.16 + cupertino_icons: ^1.0.9 + dio: ^5.11.1 + async: ^2.13.1 + flutter_bloc: ^9.1.1 + get_it: ^9.3.0 + package_info_plus: ^10.2.1 + permission_handler: ^13.0.2 + universal_html: ^2.3.0 + go_router: ^18.0.1 + equatable: ^3.0.0 + firebase_core: ^4.15.0 flutter_localizations: sdk: flutter domain: @@ -50,20 +50,20 @@ dependencies: data: path: ../modules/data - flutter_dotenv: ^5.2.1 + flutter_dotenv: ^6.0.1 flutter_web_plugins: sdk: flutter - app_links: ^6.4.1 + app_links: ^7.2.1 gap: ^3.0.1 dev_dependencies: bloc_test: ^10.0.0 - build_runner: ^2.15.0 - flutter_gen: + build_runner: ^2.16.1 + flutter_gen: ^5.15.0 flutter_test: sdk: flutter - mocktail: ^1.0.4 - flutter_lints: ^5.0.0 + mocktail: ^1.0.5 + flutter_lints: ^6.0.0 flutter_intl: enabled: true # Required. Must be set to true to activate the package. Default: false diff --git a/modules/common/analysis_options.yaml b/modules/common/analysis_options.yaml index a5744c1..e46654b 100644 --- a/modules/common/analysis_options.yaml +++ b/modules/common/analysis_options.yaml @@ -1,3 +1,6 @@ +analyzer: + exclude: + - build/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/modules/common/lib/core/failure/failure_mapper.dart b/modules/common/lib/core/failure/failure_mapper.dart index 71001f2..2dd28ae 100644 --- a/modules/common/lib/core/failure/failure_mapper.dart +++ b/modules/common/lib/core/failure/failure_mapper.dart @@ -8,6 +8,7 @@ extension FailureMapper on DioException { case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: + case DioExceptionType.transformTimeout: return SocketTimeOutFailure(message); case DioExceptionType.badResponse: return HttpFailure(response?.statusCode, name: message); diff --git a/modules/common/lib/core/resource.dart b/modules/common/lib/core/resource.dart index b97ef96..8cea132 100644 --- a/modules/common/lib/core/resource.dart +++ b/modules/common/lib/core/resource.dart @@ -10,10 +10,8 @@ class Resource { RLoading isLoading() => RLoading(data: data); - RError isError({Exception? exception}) => RError( - exception: exception, - data: data, - ); + RError isError({Exception? exception}) => + RError(exception: exception, data: data); } class RLoading extends Resource { @@ -27,11 +25,7 @@ class RSuccess extends Resource { class RError extends Resource { RError({super.state = RState.error, super.data, required super.exception}); - get message => exception?.toString(); + String? get message => exception?.toString(); } -enum RState { - loading, - success, - error; -} +enum RState { loading, success, error } diff --git a/modules/common/lib/devices/permissions/abstract/permission_manager.dart b/modules/common/lib/devices/permissions/abstract/permission_manager.dart index 24e1b5d..6c784c2 100644 --- a/modules/common/lib/devices/permissions/abstract/permission_manager.dart +++ b/modules/common/lib/devices/permissions/abstract/permission_manager.dart @@ -17,5 +17,5 @@ enum AppPermissionStatus { deniedPermanently, /// i.e when location permission is granted only when the app is in use - limited + limited, } diff --git a/modules/common/lib/devices/permissions/concrete/web/_permissions_web.dart b/modules/common/lib/devices/permissions/concrete/web/_permissions_web.dart index 7d74c8e..b71fb2a 100644 --- a/modules/common/lib/devices/permissions/concrete/web/_permissions_web.dart +++ b/modules/common/lib/devices/permissions/concrete/web/_permissions_web.dart @@ -4,12 +4,15 @@ import 'package:universal_html/html.dart' as html; class AppPermissions extends PermissionManager { @override Future requestCameraPermission() async { - final permission = - await html.window.navigator.permissions?.query({"name": "camera"}); + final permission = await html.window.navigator.permissions?.query({ + "name": "camera", + }); if (_permissionStatus(permission) != AppPermissionStatus.granted) { await html.window.navigator.getUserMedia(video: true); - final check = await html.window.navigator.permissions - ?.query({"request": "", "name": "camera"}); + final check = await html.window.navigator.permissions?.query({ + "request": "", + "name": "camera", + }); return _permissionStatus(check); } return AppPermissionStatus.granted; diff --git a/modules/common/lib/devices/platform/abstract/app_platform.dart b/modules/common/lib/devices/platform/abstract/app_platform.dart index 92fdacf..39abf81 100644 --- a/modules/common/lib/devices/platform/abstract/app_platform.dart +++ b/modules/common/lib/devices/platform/abstract/app_platform.dart @@ -1,19 +1,19 @@ import 'package:common/devices/platform/abstract/platform_info.dart'; abstract class AppPlatform { - get isWeb => false; + bool get isWeb => false; - get isMacOS => false; + bool get isMacOS => false; - get isFuchsia => false; + bool get isFuchsia => false; - get isLinux => false; + bool get isLinux => false; - get isWindows => false; + bool get isWindows => false; - get isIOS => false; + bool get isIOS => false; - get isAndroid => false; + bool get isAndroid => false; PlatformType get currentPlatform { if (isWeb) return PlatformType.web; diff --git a/modules/common/lib/ui/responsive.dart b/modules/common/lib/ui/responsive.dart index a614312..2cae3f1 100644 --- a/modules/common/lib/ui/responsive.dart +++ b/modules/common/lib/ui/responsive.dart @@ -33,11 +33,7 @@ class ResponsiveBuilder extends StatelessWidget { builder = medium ?? big; } - return builder.call( - context, - orientation, - platform, - ); + return builder.call(context, orientation, platform); } } diff --git a/modules/common/lib/validators/form_validator.dart b/modules/common/lib/validators/form_validator.dart index 31dedc4..b8a7e5f 100644 --- a/modules/common/lib/validators/form_validator.dart +++ b/modules/common/lib/validators/form_validator.dart @@ -4,7 +4,8 @@ class FormValidator { return false; } final emailRegex = RegExp( - r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9]+\.[a-zA-Z]+"); + r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9]+\.[a-zA-Z]+", + ); return emailRegex.hasMatch(value); } @@ -15,7 +16,8 @@ class FormValidator { } // At least 8 characters, one uppercase, one lowercase, one number and one special character final passwordRegex = RegExp( - r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'); + r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$', + ); return passwordRegex.hasMatch(value); } } diff --git a/modules/common/pubspec.yaml b/modules/common/pubspec.yaml index 1564241..6269466 100644 --- a/modules/common/pubspec.yaml +++ b/modules/common/pubspec.yaml @@ -5,29 +5,29 @@ version: 0.0.1 resolution: workspace environment: - sdk: ">=3.6.0 <4.0.0" - flutter: ">=3.41.0" + sdk: ^3.13.0 + flutter: ">=3.47.0" dependencies: flutter: sdk: flutter - intl: ^0.20.2 - intl_utils: ^2.8.14 - cupertino_icons: ^1.0.5 - async: ^2.11.0 - flutter_bloc: ^9.0.0 - get_it: ^8.0.0 - package_info_plus: ^8.3.0 - permission_handler: ^12.0.0 - universal_html: ^2.2.4 - dio: ^5.8.0 + intl: ^0.20.3 + intl_utils: ^2.8.16 + cupertino_icons: ^1.0.9 + async: ^2.13.1 + flutter_bloc: ^9.1.1 + get_it: ^9.3.0 + package_info_plus: ^10.2.1 + permission_handler: ^13.0.2 + universal_html: ^2.3.0 + dio: ^5.11.1 flutter_localizations: sdk: flutter dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^5.0.0 + flutter_lints: ^6.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/modules/common/test/core/result_type_test.dart b/modules/common/test/core/result_type_test.dart index daa644f..522dfbf 100644 --- a/modules/common/test/core/result_type_test.dart +++ b/modules/common/test/core/result_type_test.dart @@ -13,8 +13,7 @@ void main() { expect(received, same(failure)); }); - test('keeps the original error when the callback returns no Exception', - () { + test('keeps the original error when the callback returns no Exception', () { final failure = UnexpectedFailure('boom'); final result = TError(failure).mapError((_) {}); diff --git a/modules/data/analysis_options.yaml b/modules/data/analysis_options.yaml index a5744c1..e46654b 100644 --- a/modules/data/analysis_options.yaml +++ b/modules/data/analysis_options.yaml @@ -1,3 +1,6 @@ +analyzer: + exclude: + - build/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/modules/data/lib/init.dart b/modules/data/lib/init.dart index af0612e..f3eb005 100644 --- a/modules/data/lib/init.dart +++ b/modules/data/lib/init.dart @@ -22,18 +22,18 @@ class DataInit { // Network getIt.registerLazySingleton( - () => AuthTokenInterceptor(getIt())); + () => AuthTokenInterceptor(getIt()), + ); getIt.registerLazySingleton(() => NetworkConfig.provideDio(getIt())); getIt.registerLazySingleton( - () => EnvironmentServiceImpl(getIt())); + () => EnvironmentServiceImpl(getIt()), + ); // Data Sources // Repositories getIt.registerLazySingleton( - () => AuthRepositoryImpl( - getIt(), - ), + () => AuthRepositoryImpl(getIt()), ); getIt.registerLazySingleton( () => CommonRepositoryImpl(getIt()), diff --git a/modules/data/lib/network/config/network_config.dart b/modules/data/lib/network/config/network_config.dart index c130d50..4cc6bce 100644 --- a/modules/data/lib/network/config/network_config.dart +++ b/modules/data/lib/network/config/network_config.dart @@ -9,26 +9,24 @@ class NetworkConfig { static Dio provideDio(AuthTokenInterceptor? authTokenInterceptor) { final options = BaseOptions( baseUrl: EnvConfig.apiUrl, - connectTimeout: const Duration( - seconds: NetworkConstants.connectTimeout, - ), - receiveTimeout: const Duration( - seconds: NetworkConstants.receiveTimeout, - ), + connectTimeout: const Duration(seconds: NetworkConstants.connectTimeout), + receiveTimeout: const Duration(seconds: NetworkConstants.receiveTimeout), ); final dio = Dio(options); // Add debug logging only in debug mode if (kDebugMode) { - dio.interceptors.add(LogInterceptor( - requestBody: true, - responseBody: true, - requestHeader: true, - responseHeader: true, - error: true, - logPrint: (object) => debugPrint(object.toString()), - )); + dio.interceptors.add( + LogInterceptor( + requestBody: true, + responseBody: true, + requestHeader: true, + responseHeader: true, + error: true, + logPrint: (object) => debugPrint(object.toString()), + ), + ); } if (authTokenInterceptor != null) { diff --git a/modules/data/lib/network/config/network_constants.dart b/modules/data/lib/network/config/network_constants.dart index 16ec30d..a0ba2c5 100644 --- a/modules/data/lib/network/config/network_constants.dart +++ b/modules/data/lib/network/config/network_constants.dart @@ -3,8 +3,7 @@ class NetworkConstants { static const receiveTimeout = 2; //static const baseUrl = String.fromEnvironment('API_URL', defaultValue: "NA"); static const productsPath = "/products"; - static const baseUrl = - "http://www.example.com"; // To get started: comment this and uncomment line 4 + static const baseUrl = "http://www.example.com"; // To get started: comment this and uncomment line 4 static const tokenHeader = "token"; static const unauthorizedStatusCode = 401; static const forbiddenStatusCode = 403; diff --git a/modules/data/lib/network/interceptors/auth_token_interceptor.dart b/modules/data/lib/network/interceptors/auth_token_interceptor.dart index e073576..37ffe18 100644 --- a/modules/data/lib/network/interceptors/auth_token_interceptor.dart +++ b/modules/data/lib/network/interceptors/auth_token_interceptor.dart @@ -16,13 +16,11 @@ class AuthTokenInterceptor extends Interceptor { try { final token = _preferences.getToken(); if (token != null && token.isNotEmpty) { - options.headers.addAll( - { - NetworkConstants.tokenHeader: token, - NetworkConstants.contentTypeHeader: - NetworkConstants.applicationJsonContentType, - }, - ); + options.headers.addAll({ + NetworkConstants.tokenHeader: token, + NetworkConstants.contentTypeHeader: + NetworkConstants.applicationJsonContentType, + }); } else { await _clearCredentials(); } diff --git a/modules/data/lib/repositories/auth_repository_impl.dart b/modules/data/lib/repositories/auth_repository_impl.dart index 9c3d51a..2f351f3 100644 --- a/modules/data/lib/repositories/auth_repository_impl.dart +++ b/modules/data/lib/repositories/auth_repository_impl.dart @@ -16,10 +16,7 @@ class AuthRepositoryImpl implements AuthRepository { } @override - Future> login( - String username, - String password, - ) async { + Future> login(String username, String password) async { await Future.delayed(const Duration(seconds: 1)); _preferences.setToken('new-token'); return TSuccess(null); @@ -31,10 +28,7 @@ class AuthRepositoryImpl implements AuthRepository { } @override - Future> signUp( - String username, - String password, - ) async { + Future> signUp(String username, String password) async { await Future.delayed(const Duration(seconds: 1)); _preferences.setToken('new-token'); return TSuccess(null); diff --git a/modules/data/pubspec.yaml b/modules/data/pubspec.yaml index 1ab3654..a336114 100644 --- a/modules/data/pubspec.yaml +++ b/modules/data/pubspec.yaml @@ -6,18 +6,18 @@ publish_to: none resolution: workspace environment: - sdk: ">=3.6.0 <4.0.0" - flutter: ">=3.41.0" + sdk: ^3.13.0 + flutter: ">=3.47.0" dependencies: flutter: sdk: flutter - shared_preferences: ^2.5.3 - dio: ^5.8.0 - async: ^2.11.0 - flutter_bloc: ^9.0.0 - get_it: ^8.0.0 - equatable: ^2.0.7 + shared_preferences: ^2.5.5 + dio: ^5.11.1 + async: ^2.13.1 + flutter_bloc: ^9.1.1 + get_it: ^9.3.0 + equatable: ^3.0.0 flutter_localizations: sdk: flutter domain: @@ -28,8 +28,8 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^5.0.0 - mocktail: ^1.0.4 + flutter_lints: ^6.0.0 + mocktail: ^1.0.5 #connectivity_plus: ^5.0.2. Steps to add connectivity plus: #Add platform :ios, '12.0' in ios/Podfile and check no new crashings are in ios 16 diff --git a/modules/domain/analysis_options.yaml b/modules/domain/analysis_options.yaml index a5744c1..e46654b 100644 --- a/modules/domain/analysis_options.yaml +++ b/modules/domain/analysis_options.yaml @@ -1,3 +1,6 @@ +analyzer: + exclude: + - build/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/modules/domain/lib/bloc/app/app_cubit.dart b/modules/domain/lib/bloc/app/app_cubit.dart index 0f07fcd..2ad5aae 100644 --- a/modules/domain/lib/bloc/app/app_cubit.dart +++ b/modules/domain/lib/bloc/app/app_cubit.dart @@ -8,10 +8,12 @@ class AppCubit extends Cubit { final CommonRepository _commonRepository; AppCubit(this._commonRepository) - : super(AppState( + : super( + AppState( themeType: _commonRepository.getTheme(), appLang: _commonRepository.getAppLang(), - )); + ), + ); void updateTheme(ThemeType theme) { _commonRepository.setTheme(theme); @@ -25,9 +27,6 @@ class AppCubit extends Cubit { Future resetApp() async { await _commonRepository.deepClean(); - emit(AppState( - themeType: ThemeType.light, - appLang: AppLang.en, - )); + emit(AppState(themeType: ThemeType.light, appLang: AppLang.en)); } } diff --git a/modules/domain/lib/bloc/app/app_state.dart b/modules/domain/lib/bloc/app/app_state.dart index 3488442..da410c4 100644 --- a/modules/domain/lib/bloc/app/app_state.dart +++ b/modules/domain/lib/bloc/app/app_state.dart @@ -5,15 +5,9 @@ class AppState { final ThemeType themeType; final AppLang appLang; - AppState({ - required this.themeType, - required this.appLang, - }); + AppState({required this.themeType, required this.appLang}); - AppState copyWith({ - ThemeType? themeType, - AppLang? appLang, - }) { + AppState copyWith({ThemeType? themeType, AppLang? appLang}) { return AppState( themeType: themeType ?? this.themeType, appLang: appLang ?? this.appLang, diff --git a/modules/domain/lib/bloc/auth/auth_cubit.dart b/modules/domain/lib/bloc/auth/auth_cubit.dart index b4cdda7..c5452eb 100644 --- a/modules/domain/lib/bloc/auth/auth_cubit.dart +++ b/modules/domain/lib/bloc/auth/auth_cubit.dart @@ -8,25 +8,16 @@ class AuthCubit extends BaseCubit { final AuthService _authService; AuthCubit(this._authService) : super(RSuccess(data: AuthStateUnknown())); - Future login({ - required String email, - required String password, - }) async { + Future login({required String email, required String password}) async { isLoading(); - final authResult = await _authService.logInWithCredentials( - email, - password, - ); + final authResult = await _authService.logInWithCredentials(email, password); authResult ..mapSuccess((_) => isLogin()) ..mapError((failure) => isError(failure)); } - Future signUp({ - required String email, - required String password, - }) async { + Future signUp({required String email, required String password}) async { isLoading(); final signUpResult = await _authService.signUpWithCredentials( email, diff --git a/modules/domain/lib/models/user.dart b/modules/domain/lib/models/user.dart index 1a6e761..63e6e7c 100644 --- a/modules/domain/lib/models/user.dart +++ b/modules/domain/lib/models/user.dart @@ -2,8 +2,5 @@ class User { final String name; final String lastName; - const User({ - required this.name, - required this.lastName, - }); + const User({required this.name, required this.lastName}); } diff --git a/modules/domain/lib/services/auth_service.dart b/modules/domain/lib/services/auth_service.dart index 2b8dddd..963e06a 100644 --- a/modules/domain/lib/services/auth_service.dart +++ b/modules/domain/lib/services/auth_service.dart @@ -9,14 +9,12 @@ class AuthService { Future> logInWithCredentials( String username, String password, - ) => - _authRepository.login(username, password); + ) => _authRepository.login(username, password); Future> signUpWithCredentials( String username, String password, - ) => - _authRepository.signUp(username, password); + ) => _authRepository.signUp(username, password); bool isLoggedIn() => _authRepository.isLoggedIn(); diff --git a/modules/domain/pubspec.yaml b/modules/domain/pubspec.yaml index bb89432..9f1c2dd 100644 --- a/modules/domain/pubspec.yaml +++ b/modules/domain/pubspec.yaml @@ -6,17 +6,17 @@ publish_to: none resolution: workspace environment: - sdk: ">=3.6.0 <4.0.0" - flutter: ">=3.41.0" + sdk: ^3.13.0 + flutter: ">=3.47.0" dependencies: flutter: sdk: flutter - async: ^2.11.0 - flutter_bloc: ^9.0.0 - get_it: ^8.0.0 - equatable: ^2.0.7 - flutter_dotenv: ^5.2.1 + async: ^2.13.1 + flutter_bloc: ^9.1.1 + get_it: ^9.3.0 + equatable: ^3.0.0 + flutter_dotenv: ^6.0.1 flutter_localizations: sdk: flutter common: @@ -25,8 +25,8 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^5.0.0 - mocktail: ^1.0.4 + flutter_lints: ^6.0.0 + mocktail: ^1.0.5 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/modules/domain/test/env/env_config_test.dart b/modules/domain/test/env/env_config_test.dart index 4506480..27ff07a 100644 --- a/modules/domain/test/env/env_config_test.dart +++ b/modules/domain/test/env/env_config_test.dart @@ -7,15 +7,16 @@ void main() { group('EnvConfig.apiUrl', () { test('reads the unsuffixed API_URL of a per-flavor file', () { - dotenv.testLoad(fileInput: 'API_URL=https://dev.example.com'); + dotenv.loadFromString(envString: 'API_URL=https://dev.example.com'); EnvConfig.env = EnvConfig.kDevEnv; expect(EnvConfig.apiUrl, 'https://dev.example.com'); }); test('reads the key suffixed with the active flavor', () { - dotenv.testLoad( - fileInput: 'API_URL_DEV=https://dev.example.com\n' + dotenv.loadFromString( + envString: + 'API_URL_DEV=https://dev.example.com\n' 'API_URL_QA=https://qa.example.com', ); EnvConfig.env = EnvConfig.kQaEnv; @@ -24,8 +25,9 @@ void main() { }); test('prefers the suffixed key over the unsuffixed one', () { - dotenv.testLoad( - fileInput: 'API_URL=https://fallback.example.com\n' + dotenv.loadFromString( + envString: + 'API_URL=https://fallback.example.com\n' 'API_URL_PROD=https://prod.example.com', ); EnvConfig.env = EnvConfig.kProdEnv; @@ -34,7 +36,7 @@ void main() { }); test('is empty when no API_URL key is defined', () { - dotenv.testLoad(fileInput: 'ENV=dev'); + dotenv.loadFromString(envString: 'ENV=dev'); expect(EnvConfig.apiUrl, isEmpty); }); From f1ee670a244de45025b97beadb69806b76f42c6a Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Tue, 22 Sep 2026 18:11:17 -0300 Subject: [PATCH 2/6] chore: upgrade the Android and iOS native projects to Flutter 3.47 standards Android - Gradle 9.3.1, AGP 9.1.0, Kotlin 2.4.0, Java 17, AGP 9 compatibility flags (as generated by the Flutter 3.47 template); layout.buildDirectory - Fixes the build on main (Kotlin 1.8.22 was too old for the plugins) - compileSdk 37 (required by permission_handler 13), target 36, min 24 - Identity (applicationId, namespace, launcher label) now lives only in build.properties; the manifest label uses an appLabel placeholder - Release builds fall back to debug signing when key.properties is absent - Removed the deprecated manifest package attributes iOS - Deployment target 15.0 (Xcode 27 rejects 12/13, which broke every build) - CocoaPods removed (pod deintegrate): every plugin supports Swift Package Manager, Flutter's default; Flutter added its SPM integration - UIScene lifecycle migration performed by the Flutter tool - Identity in Flutter/AppIdentity.xcconfig (APP_BUNDLE_ID, APP_DISPLAY_NAME); every configuration derives bundle ID and display name from it - Fixed the flavor xcconfigs: wrong Generated.xcconfig include path, release Pods include in debug configs, Debug bundle ID applied .debug twice - PRODUCT_NAME is Runner again; version comes from pubspec only - Removed the stray root ios/Podfile Co-Authored-By: Claude Opus 5.5 (1M context) --- app/.gitignore | 4 + app/android/app/build.gradle | 97 ++++----- app/android/app/src/debug/AndroidManifest.xml | 3 +- app/android/app/src/main/AndroidManifest.xml | 5 +- .../app/src/profile/AndroidManifest.xml | 3 +- app/android/build.gradle | 18 +- app/android/build.properties | 12 +- app/android/gradle.properties | 6 +- .../gradle/wrapper/gradle-wrapper.properties | 3 +- app/android/settings.gradle | 7 +- app/ios/.gitignore | 4 + app/ios/Flutter/AppFrameworkInfo.plist | 2 - app/ios/Flutter/AppIdentity.xcconfig | 5 + app/ios/Flutter/Debug.xcconfig | 9 +- app/ios/Flutter/Release.xcconfig | 9 +- app/ios/Podfile | 97 --------- app/ios/Runner.xcodeproj/project.pbxproj | 206 ++++++------------ .../xcshareddata/xcschemes/Dev.xcscheme | 26 ++- .../xcshareddata/xcschemes/QA.xcscheme | 24 +- .../xcshareddata/xcschemes/Runner.xcscheme | 28 ++- .../contents.xcworkspacedata | 3 - app/ios/Runner/AppDelegate.swift | 9 +- app/ios/Runner/Info.plist | 41 +++- app/ios/dev.xcconfig | 12 +- app/ios/qa.xcconfig | 12 +- ios/Podfile | 43 ---- 26 files changed, 284 insertions(+), 404 deletions(-) create mode 100644 app/ios/Flutter/AppIdentity.xcconfig delete mode 100644 app/ios/Podfile delete mode 100644 ios/Podfile diff --git a/app/.gitignore b/app/.gitignore index f419504..d0fb9d2 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -141,3 +141,7 @@ firebase-debug.log # Local development files local.properties *.properties + +# Tracked Android build configuration (the rules above would ignore it). +!android/build.properties +!android/gradle.properties diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index 7e83317..f4b22e9 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -1,17 +1,13 @@ plugins { id "com.android.application" - id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id "dev.flutter.flutter-gradle-plugin" } -def flutterProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - flutterProperties.load(reader) - } -} - +/** + * Project identity and SDK levels live in android/build.properties. + * The identity keys are written by the project initializer (`melos run init`). + */ def buildProperties = new Properties() def buildPropertiesFile = rootProject.file('build.properties') if (buildPropertiesFile.exists()) { @@ -20,6 +16,14 @@ if (buildPropertiesFile.exists()) { } } +def requiredBuildProperty = { String key -> + def value = buildProperties.getProperty(key) + if (value == null || value.trim().isEmpty()) { + throw new GradleException("Missing '$key' in android/build.properties") + } + return value.trim() +} + /** * Declare the env vars here * **/ @@ -36,70 +40,63 @@ if (project.hasProperty('dart-defines')) { } } -def flutterVersionCode = flutterProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = flutterProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +def releaseSigningConfigured = keystorePropertiesFile.exists() +if (releaseSigningConfigured) { + keystorePropertiesFile.withInputStream { keystoreProperties.load(it) } } android { - namespace 'com.rootstrap.base.flutter_base_rootstrap' - compileSdkVersion buildProperties.getProperty('flutter.compileSdkVersion').toInteger() - ndkVersion flutter.ndkVersion + namespace = requiredBuildProperty('flutter.namespace') + compileSdk = requiredBuildProperty('flutter.compileSdkVersion').toInteger() + ndkVersion = flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } defaultConfig { - applicationId "com.rs." + buildProperties.getProperty('flutter.appId') - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion buildProperties.getProperty('flutter.minSdkVersion') - targetSdkVersion buildProperties.getProperty('flutter.targetSdkVersion') - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + applicationId = requiredBuildProperty('flutter.applicationId') + minSdk = requiredBuildProperty('flutter.minSdkVersion').toInteger() + targetSdk = requiredBuildProperty('flutter.targetSdkVersion').toInteger() + // Uses the version from pubspec.yaml. + versionCode = flutter.versionCode + versionName = flutter.versionName + // Added to (not replacing) the placeholders the Flutter plugin sets. + manifestPlaceholders.put('appLabel', requiredBuildProperty('flutter.appName')) } signingConfigs { - release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null - storePassword keystoreProperties['storePassword'] + if (releaseSigningConfigured) { + release { + keyAlias = keystoreProperties['keyAlias'] + keyPassword = keystoreProperties['keyPassword'] + storeFile = keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword = keystoreProperties['storePassword'] + } } } buildTypes { release { - signingConfig signingConfigs.release + // Signed with android/key.properties when present. Without it, the debug keys are + // used so `flutter build apk --release` works in the template and in CI. + signingConfig = releaseSigningConfigured ? signingConfigs.release : signingConfigs.debug } debug { - applicationIdSuffix ".debug" + applicationIdSuffix = ".debug" } } } -flutter { - source '../..' +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } } +flutter { + source = '../..' +} diff --git a/app/android/app/src/debug/AndroidManifest.xml b/app/android/app/src/debug/AndroidManifest.xml index 85cce84..399f698 100644 --- a/app/android/app/src/debug/AndroidManifest.xml +++ b/app/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,4 @@ - + - + - flutter_base_rootstrap + RS Base