diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..f7258f4601 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..f7258f4601 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/automatic.svg b/assets/images/automatic.svg new file mode 100644 index 0000000000..ee487024f6 --- /dev/null +++ b/assets/images/automatic.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/images/dark_mode.svg b/assets/images/dark_mode.svg new file mode 100644 index 0000000000..425ef43d1f --- /dev/null +++ b/assets/images/dark_mode.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/light_mode.svg b/assets/images/light_mode.svg new file mode 100644 index 0000000000..695071f8fb --- /dev/null +++ b/assets/images/light_mode.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/locales/en.po b/assets/locales/en.po index cb8c8b02e7..2c38dd9cfc 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -1466,10 +1466,22 @@ msgstr "Your Pro plan was purchased through Google Play. Please manage your subs msgid "manage_subscription_apple_app_store" msgstr "Your Pro plan was purchased through the Apple App Store. Please manage your subscription from your Apple ID account settings." - msgid "create_account_error" msgstr "Create Account Error" +msgid "appearance" +msgstr "Appearance" + +msgid "light" +msgstr "Light" + +msgid "dark" +msgstr "Dark" + +msgid "system" +msgstr "System" + + diff --git a/lib/core/common/app_asset.dart b/lib/core/common/app_asset.dart index 526228a213..a37f5e787c 100644 --- a/lib/core/common/app_asset.dart +++ b/lib/core/common/app_asset.dart @@ -17,6 +17,9 @@ class AppImage extends StatelessWidget { final AssetType type; final OnPressed? onPressed; final BoxFit? fit; + // Set to false for decorative/multicolor assets (illustrations, logos) + // that should not be recolored by the theme. + final bool useThemeColor; const AppImage({ required this.path, @@ -26,6 +29,7 @@ class AppImage extends StatelessWidget { this.type = AssetType.svg, this.onPressed, this.fit, + this.useThemeColor = true, super.key, }); @@ -33,6 +37,9 @@ class AppImage extends StatelessWidget { Widget build(BuildContext context) { switch (type) { case AssetType.svg: + // Use explicit color > theme icon color > no filter (when opted out) + final effectiveColor = + color ?? (useThemeColor ? Theme.of(context).iconTheme.color : null); return GestureDetector( onTap: onPressed, child: SvgPicture.asset( @@ -40,8 +47,8 @@ class AppImage extends StatelessWidget { height: height, width: width, fit: fit ?? BoxFit.contain, - colorFilter: color != null - ? ColorFilter.mode(color!, BlendMode.srcIn) + colorFilter: effectiveColor != null + ? ColorFilter.mode(effectiveColor, BlendMode.srcIn) : null, ), ); diff --git a/lib/core/common/app_buttons.dart b/lib/core/common/app_buttons.dart index 492dc199a5..98a4f04332 100644 --- a/lib/core/common/app_buttons.dart +++ b/lib/core/common/app_buttons.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:lantern/core/common/app_asset.dart'; import 'package:lantern/core/common/app_colors.dart'; import 'package:lantern/core/common/app_dimens.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import 'package:lantern/core/common/app_text_styles.dart'; import 'package:lantern/core/common/cap_scaling.dart'; @@ -21,6 +22,7 @@ class PrimaryButton extends StatelessWidget { final Color? bgColor; final Color? textColor; final bool? isTaller; + final bool? useThemeColor; // Default constructor for button without an icon const PrimaryButton({ @@ -33,6 +35,7 @@ class PrimaryButton extends StatelessWidget { this.expanded = true, this.isTaller = false, this.showBorder = false, + this.useThemeColor, this.icon, super.key, }); @@ -56,6 +59,7 @@ class PrimaryButton extends StatelessWidget { path: icon!, height: iconHeight, color: iconColor, + useThemeColor: useThemeColor ?? true, ), label: Text(label), style: _buildButtonStyle(context, button!, iconSz), @@ -69,6 +73,7 @@ class PrimaryButton extends StatelessWidget { ) { final verticalPad = hCap(context, 12); final fontSz = spCap(context, 16); + final isDark = Theme.of(context).brightness == Brightness.dark; // Heights final minHeight = isTaller == true ? hCap(context, 56) : hCap(context, 48); @@ -78,36 +83,42 @@ class PrimaryButton extends StatelessWidget { backgroundColor: WidgetStateProperty.resolveWith( (Set states) { if (states.contains(WidgetState.disabled)) { - return AppColors.gray2; // Disabled background color + // primary-disabled-bg: Gray.200 light / Gray.700 dark + return isDark ? AppColors.gray7 : AppColors.gray2; } if (states.contains(WidgetState.hovered) && bgColor == AppColors.blue1) { - return AppColors.blue2; // Pressed background color + return AppColors.blue2; } if (states.contains(WidgetState.hovered)) { - return AppColors.blue8; // Hovered background color + // primary-bg-hover: Blue.800 light / Blue.500 dark + return isDark ? AppColors.blue5 : AppColors.blue8; } - return bgColor ?? AppColors.blue10; // Default background color + // primary-bg: Blue.1000 light / Blue.600 dark + return bgColor ?? (isDark ? AppColors.blue6 : AppColors.blue10); }, ), side: WidgetStateProperty.resolveWith( (Set states) { if (states.contains(WidgetState.disabled)) { - return BorderSide(color: AppColors.gray4, width: 1); + // primary-disabled-border: Gray.400 light / Gray.500 dark + return BorderSide( + color: isDark ? AppColors.gray5 : AppColors.gray4, + width: 1, + ); } if (showBorder) { - return BorderSide(color: AppColors.gray2, width: 1); + return BorderSide( + color: isDark ? AppColors.gray7 : AppColors.gray2, + width: 1, + ); } return BorderSide.none; }, ), - // backgroundColor: WidgetStatePropertyAll(bgColor ?? AppColors.blue7), iconSize: WidgetStatePropertyAll(iconSz), padding: WidgetStatePropertyAll( - EdgeInsets.symmetric( - vertical: verticalPad, - horizontal: 40.0, - ), + EdgeInsets.symmetric(vertical: verticalPad, horizontal: 40.0), ), textStyle: WidgetStatePropertyAll( AppTextStyles.primaryButtonTextStyle.copyWith( @@ -115,6 +126,7 @@ class PrimaryButton extends StatelessWidget { fontWeight: FontWeight.w500, ), ), + // primary-text: Gray.100 both / primary-disabled-text: Gray.500 both foregroundColor: WidgetStatePropertyAll( enabled == false ? AppColors.gray5 : textColor ?? AppColors.gray1, ), @@ -138,6 +150,8 @@ class SecondaryButton extends StatelessWidget { final Color? bgColor; final bool? isTaller; + final bool? useThemeColor; + final Color? iconColor; const SecondaryButton({ super.key, @@ -148,6 +162,8 @@ class SecondaryButton extends StatelessWidget { required this.onPressed, this.icon, this.bgColor, + this.useThemeColor, + this.iconColor, }); @override @@ -168,6 +184,8 @@ class SecondaryButton extends StatelessWidget { icon: AppImage( path: icon!, height: iconHeight, + color: iconColor, + useThemeColor: useThemeColor ?? false, ), label: Text(label), style: _buildButtonStyle(context, button!, iconSz), @@ -181,40 +199,58 @@ class SecondaryButton extends StatelessWidget { ) { final verticalPad = hCap(context, 12); final fontSz = spCap(context, 16); + final isDark = Theme.of(context).brightness == Brightness.dark; // Heights final height = isTaller == true ? hCap(context, 56) : hCap(context, 50); + // secondary-text: Gray.900 light / Gray.100 dark + final textColor = isDark ? AppColors.gray1 : AppColors.gray9; + return style.copyWith( backgroundColor: WidgetStateProperty.resolveWith( (Set states) { if (states.contains(WidgetState.disabled)) { - return AppColors.gray2; // Disabled background color + // secondary-disabled-bg: Gray.200 light / Gray.900 dark + return isDark ? AppColors.gray9 : AppColors.gray2; } - return bgColor ?? AppColors.gray1; // Default background color + if (states.contains(WidgetState.hovered)) { + // secondary-bg-hover: Gray.200 light / Gray.800 dark + return isDark ? AppColors.gray8 : AppColors.gray2; + } + // secondary-bg: Gray.100 light / Gray.900 dark + return bgColor ?? (isDark ? AppColors.gray9 : AppColors.gray1); }, ), side: WidgetStateProperty.resolveWith( (Set states) { if (states.contains(WidgetState.disabled)) { - return BorderSide(color: AppColors.gray4, width: 1); + // secondary-disabled-border: Gray.400 light / Gray.700 dark + return BorderSide( + color: isDark ? AppColors.gray7 : AppColors.gray4, + width: 1, + ); } - return BorderSide(color: AppColors.gray4, width: 1); + // secondary-border: Gray.500 light / Gray.600 dark + return BorderSide( + color: isDark ? AppColors.gray6 : AppColors.gray5, + width: 1, + ); }, ), - overlayColor: WidgetStatePropertyAll(AppColors.gray2), - foregroundColor: WidgetStatePropertyAll(AppColors.gray9), + // secondary-bg-hover used as overlay + overlayColor: WidgetStatePropertyAll( + isDark ? AppColors.gray8 : AppColors.gray2, + ), + foregroundColor: WidgetStatePropertyAll(textColor), iconSize: WidgetStatePropertyAll(iconSz), padding: WidgetStatePropertyAll( - EdgeInsets.symmetric( - vertical: verticalPad, - horizontal: 40.0, - ), + EdgeInsets.symmetric(vertical: verticalPad, horizontal: 40.0), ), textStyle: WidgetStatePropertyAll( AppTextStyles.primaryButtonTextStyle.copyWith( fontSize: expanded ? fontSz : 16.0, - color: AppColors.gray9, + color: textColor, fontWeight: FontWeight.w600, ), ), @@ -259,7 +295,8 @@ class AppTextButton extends StatelessWidget { underLine ? TextDecoration.underline : TextDecoration.none, fontSize: cappedFontSize, ), - foregroundColor: textColor ?? AppColors.blue7, + // text.link from semantic token + foregroundColor: textColor ?? context.textLink, ), child: Text(label), ); @@ -306,7 +343,6 @@ class AppRadioButton extends StatelessWidget { @override Widget build(BuildContext context) { final sz = hCap(context, 24); - return SizedBox( width: sz, height: sz, @@ -314,6 +350,7 @@ class AppRadioButton extends StatelessWidget { value: value, groupValue: groupValue, onChanged: onChanged, + activeColor: context.textLink, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: VisualDensity.compact, ), diff --git a/lib/core/common/app_colors.dart b/lib/core/common/app_colors.dart index 3737411824..b6d6df04f1 100644 --- a/lib/core/common/app_colors.dart +++ b/lib/core/common/app_colors.dart @@ -1,133 +1,136 @@ import 'package:flutter/material.dart'; class AppColors { - static Color get black => const Color(0xFF000000); + static const Color black = Color(0xFF000000); - static Color get black1 => const Color(0xFF1A1B1C); + static const Color black1 = Color(0xFF1A1B1C); - static Color get white => Colors.white; + static const Color white = Color(0xFFFFFFFF); - static Color get whiteBlur => const Color(0xCCF8FAFB); + static const Color whiteBlur = Color(0xCCF8FAFB); - static Color get blue1 => const Color(0xFFF0FDFF); + static const Color blue1 = Color(0xFFF0FDFF); - static Color get blue2 => const Color(0xFFD6F6FA); + static const Color blue2 = Color(0xFFD6F6FA); - static Color get blue3 => const Color(0xFF6AD8E7); + static const Color blue3 = Color(0xFF6AD8E7); - static Color get blue4 => const Color(0xFF00BDD6); + static const Color blue4 = Color(0xFF00BDD6); - static Color get blue5 => const Color(0xFF009EB2); + static const Color blue5 = Color(0xFF009EB2); - static Color get blue6 => const Color(0xFF008394); + static const Color blue6 = Color(0xFF008394); - static Color get blue7 => const Color(0xFF005F61); + static const Color blue7 = Color(0xFF005F61); - static Color get blue8 => const Color(0xFF004D57); + static const Color blue8 = Color(0xFF004D57); - static Color get blue9 => const Color(0xFF00353D); + static const Color blue9 = Color(0xFF00353D); - static Color get blue10 => const Color(0xFF012D2D); + static const Color blue10 = Color(0xFF012D2D); - static Color get gray0 => Colors.white; + static const Color gray0 = Color(0xFFFFFFFF); - static Color get gray1 => const Color(0xFFF8FAFB); + static const Color gray1 = Color(0xFFF8FAFB); - static Color get gray2 => const Color(0xFFEDEFEF); + static const Color gray2 = Color(0xFFEDEFEF); - static Color get gray3 => const Color(0xFFDEDFDF); + static const Color gray3 = Color(0xFFDEDFDF); - static Color get gray4 => const Color(0xFFBFBFBF); + static const Color gray4 = Color(0xFFBFBFBF); - static Color get gray5 => const Color(0xFFA2A2A2); + static const Color gray5 = Color(0xFFA2A2A2); - static Color get gray6 => const Color(0xFF848484); + static const Color gray6 = Color(0xFF848484); - static Color get gray7 => const Color(0xFF616569); + static const Color gray7 = Color(0xFF616569); - static Color get gray8 => const Color(0xFF3E464E); + static const Color gray8 = Color(0xFF3E464E); - static Color get gray9 => const Color(0xFF1B1C1D); + // Gray 850 – dark mode elevated/input surface (#2d3136) + static const Color gray850 = Color(0xFF2D3136); - static Color get gray10 => Colors.black; + static const Color gray9 = Color(0xFF1B1C1D); - static Color get gray11 => const Color(0xFF040404); + static const Color gray10 = Color(0xFF000000); - static Color get gray12 => const Color(0xFFBDBDBD); - static Color get lightGray => const Color(0xFF838383); + static const Color gray11 = Color(0xFF040404); - static Color get yellow0 => const Color(0xFFFEFDF6); + static const Color gray12 = Color(0xFFBDBDBD); + static const Color lightGray = Color(0xFF838383); - static Color get yellow1 => const Color(0xFFFCF8E8); + static const Color yellow0 = Color(0xFFFEFDF6); - static Color get yellow2 => const Color(0xFFFAF1D6); + static const Color yellow1 = Color(0xFFFCF8E8); - static Color get yellow3 => const Color(0xFFFFC105); + static const Color yellow2 = Color(0xFFFAF1D6); - static Color get yellow4 => const Color(0xFFD6A100); + static const Color yellow3 = Color(0xFFFFC105); - static Color get yellow5 => const Color(0xFFB88A00); + static const Color yellow4 = Color(0xFFD6A100); - static Color get yellow6 => const Color(0xFF946F00); + static const Color yellow5 = Color(0xFFB88A00); - static Color get yellow7 => const Color(0xFF7A5C00); + static const Color yellow6 = Color(0xFF946F00); - static Color get yellow8 => const Color(0xFF503C02); + static const Color yellow7 = Color(0xFF7A5C00); - static Color get yellow9 => const Color(0xFF3D2D00); + static const Color yellow8 = Color(0xFF503C02); - static Color get yellow10 => const Color(0xFF241800); + static const Color yellow9 = Color(0xFF3D2D00); - static Color get green0 => const Color(0xFFFBFFFA); + static const Color yellow10 = Color(0xFF241800); - static Color get green1 => const Color(0xFFF0FFF4); + static const Color green0 = Color(0xFFFBFFFA); - static Color get green2 => const Color(0xFFD4FAD6); + static const Color green1 = Color(0xFFF0FFF4); - static Color get green3 => const Color(0xFFA2DDAF); + static const Color green2 = Color(0xFFD4FAD6); - static Color get green4 => const Color(0xFF6FC087); + static const Color green3 = Color(0xFFA2DDAF); - static Color get green5 => const Color(0xFF3DA360); + static const Color green4 = Color(0xFF6FC087); - static Color get green6 => const Color(0xFF0A8638); + static const Color green5 = Color(0xFF3DA360); - static Color get green7 => const Color(0xFF006E2B); + static const Color green6 = Color(0xFF0A8638); - static Color get green8 => const Color(0xFF00531F); + static const Color green7 = Color(0xFF006E2B); - static Color get green9 => const Color(0xFF003913); + static const Color green8 = Color(0xFF00531F); - static Color get green10 => const Color(0xFF002108); + static const Color green9 = Color(0xFF003913); - static Color get green11 => const Color(0xFF005F61); + static const Color green10 = Color(0xFF002108); - static Color get green12 => const Color(0xFF1FBF63); + static const Color green11 = Color(0xFF005F61); - static Color get red0 => const Color(0xFFFFF8F7); + static const Color green12 = Color(0xFF1FBF63); - static Color get red1 => const Color(0xFFFFEDEA); + static const Color red0 = Color(0xFFFFF8F7); - static Color get red2 => const Color(0xFFFFDAD5); + static const Color red1 = Color(0xFFFFEDEA); - static Color get red3 => const Color(0xFFFFB4AA); + static const Color red2 = Color(0xFFFFDAD5); - static Color get red4 => const Color(0xFFFF8A7C); + static const Color red3 = Color(0xFFFFB4AA); - static Color get red5 => const Color(0xFFFF5447); + static const Color red4 = Color(0xFFFF8A7C); - static Color get red6 => const Color(0xFFDB1C1C); + static const Color red5 = Color(0xFFFF5447); - static Color get red7 => const Color(0xFFC0000E); + static const Color red6 = Color(0xFFDB1C1C); - static Color get red8 => const Color(0xFF930008); + static const Color red7 = Color(0xFFC0000E); - static Color get red9 => const Color(0xFF690004); + static const Color red8 = Color(0xFF930008); - static Color get red10 => const Color(0xFF410001); + static const Color red9 = Color(0xFF690004); - static Color get shadowColor => const Color(0x19006162); + static const Color red10 = Color(0xFF410001); - static Color get logTextColor => const Color(0xFF3D454D); - static Color get linkColor => const Color(0xFF005F60); + static const Color shadowColor = Color(0x19006162); + + static const Color logTextColor = Color(0xFF3D454D); + static const Color linkColor = Color(0xFF005F60); } diff --git a/lib/core/common/app_dialog.dart b/lib/core/common/app_dialog.dart index c779559d52..a32ced004d 100644 --- a/lib/core/common/app_dialog.dart +++ b/lib/core/common/app_dialog.dart @@ -15,13 +15,9 @@ class AppDialog { barrierDismissible: false, builder: (context) { return AlertDialog( - backgroundColor: AppColors.gray1, + // backgroundColor and shape come from dialogTheme in app_theme.dart contentPadding: EdgeInsets.symmetric(horizontal: defaultSize), actionsPadding: EdgeInsets.all(24), - // contentPadding: EdgeInsets.zero, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), content: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -83,7 +79,7 @@ class AppDialog { return AlertDialog( actionsAlignment: MainAxisAlignment.end, actionsOverflowAlignment: OverflowBarAlignment.end, - backgroundColor: AppColors.gray1, + // backgroundColor and shape come from dialogTheme in app_theme.dart contentPadding: EdgeInsets.symmetric(horizontal: size24), actionsPadding: actionPadding ?? EdgeInsets.only( @@ -91,10 +87,6 @@ class AppDialog { bottom: defaultSize, left: defaultSize, right: defaultSize), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - side: BorderSide(color: AppColors.gray3, width: 1), - ), content: content, actions: action, ); @@ -113,17 +105,13 @@ class AppDialog { barrierDismissible: false, builder: (context) { return AlertDialog( - backgroundColor: AppColors.gray3, + // backgroundColor and shape come from dialogTheme in app_theme.dart contentPadding: EdgeInsets.symmetric(horizontal: defaultSize), actionsPadding: EdgeInsets.only( top: defaultSize, bottom: defaultSize, left: defaultSize, right: defaultSize), - // contentPadding: EdgeInsets.zero, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), content: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -161,17 +149,13 @@ class AppDialog { barrierDismissible: false, builder: (context) { return AlertDialog( - backgroundColor: AppColors.gray3, + // backgroundColor and shape come from dialogTheme in app_theme.dart contentPadding: EdgeInsets.symmetric(horizontal: defaultSize), actionsPadding: EdgeInsets.only( top: defaultSize, bottom: defaultSize, left: defaultSize, right: defaultSize), - // contentPadding: EdgeInsets.zero, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), content: Column( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/core/common/app_dropdown.dart b/lib/core/common/app_dropdown.dart index f3e965fa09..84db83bee9 100644 --- a/lib/core/common/app_dropdown.dart +++ b/lib/core/common/app_dropdown.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:lantern/core/common/app_asset.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import 'app_colors.dart' show AppColors; @@ -28,7 +29,7 @@ class AppDropdown extends StatelessWidget { height: 56, decoration: BoxDecoration( border: Border.all( - color: AppColors.gray3, + color: context.borderInput, width: 1, ), borderRadius: BorderRadius.circular(16), @@ -41,7 +42,7 @@ class AppDropdown extends StatelessWidget { isExpanded: true, padding: EdgeInsets.only(left: prefixIconPath != null ? 8 : 0), style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray9, + color: context.textPrimary, ), value: value, borderRadius: BorderRadius.circular(16), @@ -63,7 +64,7 @@ class AppDropdown extends StatelessWidget { child: Text( label!, style: textTheme.labelLarge?.copyWith( - color: AppColors.gray8, + color: context.textSecondary, fontSize: 14.sp, ), ), diff --git a/lib/core/common/app_image_paths.dart b/lib/core/common/app_image_paths.dart index a7a3d7cab6..84250edfd6 100644 --- a/lib/core/common/app_image_paths.dart +++ b/lib/core/common/app_image_paths.dart @@ -108,6 +108,9 @@ class AppImagePaths { static const nonProfit = 'assets/images/non_profit.svg'; static const privateServerIntro = 'assets/images/private_server_intro.svg'; static const smartRouteMode = 'assets/images/smart_route_mode.svg'; + static const automatic = 'assets/images/automatic.svg'; + static const lightMode = 'assets/images/light_mode.svg'; + static const darkMode = 'assets/images/dark_mode.svg'; /// Validates and returns a safe flag path for the given country code. /// Returns null if the country code is invalid or the flag asset doesn't exist. diff --git a/lib/core/common/app_semantic_colors.dart b/lib/core/common/app_semantic_colors.dart new file mode 100644 index 0000000000..74b15e2d5a --- /dev/null +++ b/lib/core/common/app_semantic_colors.dart @@ -0,0 +1,272 @@ +import 'package:flutter/material.dart'; +import 'app_colors.dart'; + +/// BuildContext extension that maps Figma Color/Semantic tokens to AppColors. +/// Every property has a light and dark value derived directly from the JSON export. +/// +/// Usage: context.textPrimary, context.bgElevated, context.borderInput … +extension AppSemanticColors on BuildContext { + bool get _isDark => Theme.of(this).brightness == Brightness.dark; + + // ── Text ──────────────────────────────────────────────────────────────────── + + /// text.primary Gray.900 light / Gray.200 dark + Color get textPrimary => _isDark ? AppColors.gray2 : AppColors.gray9; + + /// text.secondary Gray.800 light / Gray.300 dark + Color get textSecondary => _isDark ? AppColors.gray3 : AppColors.gray8; + + /// text.tertiary Gray.700 light / Gray.400 dark + Color get textTertiary => _isDark ? AppColors.gray4 : AppColors.gray7; + + /// text.link Blue.700 light / Blue.200 dark + Color get textLink => _isDark ? AppColors.blue2 : AppColors.blue7; + + /// text.disabled Gray.600 light / Gray.500 dark + Color get textDisabled => _isDark ? AppColors.gray5 : AppColors.gray6; + + /// text.inverse Gray.100 light / Gray.900 dark + Color get textInverse => _isDark ? AppColors.gray9 : AppColors.gray1; + + /// text.inverse-color Blue.400 light / Blue.600 dark + Color get textInverseColor => _isDark ? AppColors.blue6 : AppColors.blue4; + + /// text.promo-icon Yellow.300 both modes + Color get textPromoIcon => AppColors.yellow3; + + // ── Background ────────────────────────────────────────────────────────────── + + /// bg.surface Gray.100 light / Gray.900 dark + Color get bgSurface => _isDark ? AppColors.gray9 : AppColors.gray1; + + /// bg.elevated White light / Gray.850 dark + Color get bgElevated => _isDark ? AppColors.gray850 : AppColors.white; + + /// bg.input White light / Gray.850 dark + Color get bgInput => _isDark ? AppColors.gray850 : AppColors.white; + + /// bg.hover Blue.100 light / Blue.900 dark + Color get bgHover => _isDark ? AppColors.blue9 : AppColors.blue1; + + /// bg.overlay Gray.100 light / Gray.900 dark + Color get bgOverlay => _isDark ? AppColors.gray9 : AppColors.gray1; + + /// bg.callout Gray.200 light / Gray.800 dark + Color get bgCallout => _isDark ? AppColors.gray8 : AppColors.gray2; + + /// bg.snackbar Blue.900 light / Blue.200 dark + Color get bgSnackbar => _isDark ? AppColors.blue2 : AppColors.blue9; + + /// bg.snackbar-error Red.700 light / Red.500 dark + Color get bgSnackbarError => _isDark ? AppColors.red5 : AppColors.red7; + + /// bg.promo Yellow.100 light / Gray.900 dark + Color get bgPromo => _isDark ? AppColors.gray9 : AppColors.yellow1; + + // ── Border ────────────────────────────────────────────────────────────────── + + /// border.default Gray.200 light / Gray.800 dark + Color get borderDefault => _isDark ? AppColors.gray8 : AppColors.gray2; + + /// border.input Gray.300 light / Gray.700 dark + Color get borderInput => _isDark ? AppColors.gray7 : AppColors.gray3; + + /// border.input-focus Blue.800 light / Blue.200 dark + Color get borderInputFocus => _isDark ? AppColors.blue2 : AppColors.blue8; + + /// border.input-filled Gray.900 light / Gray.400 dark + Color get borderInputFilled => _isDark ? AppColors.gray4 : AppColors.gray9; + + /// border.error Red.600 light / Red.500 dark + Color get borderError => _isDark ? AppColors.red5 : AppColors.red6; + + /// border.promo Yellow.500 both modes + Color get borderPromo => AppColors.yellow5; + + // ── Status ────────────────────────────────────────────────────────────────── + + /// status.error-text Red.800 light / Red.200 dark + Color get statusErrorText => _isDark ? AppColors.red2 : AppColors.red8; + + /// status.error-bg Red.200 light / Red.800 dark + Color get statusErrorBg => _isDark ? AppColors.red8 : AppColors.red2; + + /// status.error-border Red.400 light / Red.600 dark + Color get statusErrorBorder => _isDark ? AppColors.red6 : AppColors.red4; + + /// status.success-text Green.800 light / Green.300 dark + Color get statusSuccessText => _isDark ? AppColors.green3 : AppColors.green8; + + /// status.success-bg Green.200 light / Green.700 dark + Color get statusSuccessBg => _isDark ? AppColors.green7 : AppColors.green2; + + /// status.success-border Green.400 light / Green.600 dark + Color get statusSuccessBorder => + _isDark ? AppColors.green6 : AppColors.green4; + + /// status.warning-text Yellow.500 light / Yellow.200 dark + Color get statusWarningText => _isDark ? AppColors.yellow2 : AppColors.yellow5; + + /// status.warning-bg-dot Yellow.300 light / Yellow.500 dark + Color get statusWarningBgDot => + _isDark ? AppColors.yellow5 : AppColors.yellow3; + + /// status.neutral-text Gray.600 light / Gray.200 dark + Color get statusNeutralText => _isDark ? AppColors.gray2 : AppColors.gray6; + + /// status.informational-text Blue.800 light / Blue.200 dark + Color get statusInfoText => _isDark ? AppColors.blue2 : AppColors.blue8; + + /// status.Informational-bg Blue.200 light / Blue.700 dark + Color get statusInfoBg => _isDark ? AppColors.blue7 : AppColors.blue2; + + /// status.Informational-border Blue.400 light / Blue.600 dark + Color get statusInfoBorder => _isDark ? AppColors.blue6 : AppColors.blue4; + + /// status.error-bg-dot Red.600 light / Red.800 dark + Color get statusErrorBgDot => _isDark ? AppColors.red8 : AppColors.red6; + + /// status.error-border-dot Red.300 light / Red.500 dark + Color get statusErrorBorderDot => _isDark ? AppColors.red5 : AppColors.red3; + + /// status.warning-border-dot Yellow.200 light / Yellow.400 dark + Color get statusWarningBorderDot => + _isDark ? AppColors.yellow4 : AppColors.yellow2; + + /// status.success-bg-dot Green.600 light / Green.700 dark + Color get statusSuccessBgDot => + _isDark ? AppColors.green7 : AppColors.green6; + + /// status.success-border-dot Green.300 light / Green.500 dark + Color get statusSuccessBorderDot => + _isDark ? AppColors.green5 : AppColors.green3; + + /// status.neutral-bg-dot Gray.500 light / Gray.700 dark + Color get statusNeutralBgDot => _isDark ? AppColors.gray7 : AppColors.gray5; + + /// status.neutral-border-dot Gray.300 light / Gray.500 dark + Color get statusNeutralBorderDot => + _isDark ? AppColors.gray5 : AppColors.gray3; + + // ── Action / Primary ──────────────────────────────────────────────────────── + + /// action.primary.primary-bg Blue.1000 light / Blue.600 dark + Color get actionPrimaryBg => _isDark ? AppColors.blue6 : AppColors.blue10; + + /// action.primary.primary-bg-hover Blue.800 light / Blue.500 dark + Color get actionPrimaryBgHover => _isDark ? AppColors.blue5 : AppColors.blue8; + + /// action.primary.primary-text Gray.100 both modes + Color get actionPrimaryText => AppColors.gray1; + + /// action.primary.primary-disabled-bg Gray.200 light / Gray.700 dark + Color get actionPrimaryDisabledBg => + _isDark ? AppColors.gray7 : AppColors.gray2; + + /// action.primary.primary-disabled-text Gray.500 both modes + Color get actionPrimaryDisabledText => AppColors.gray5; + + /// action.primary.primary-disabled-border Gray.400 light / Gray.500 dark + Color get actionPrimaryDisabledBorder => + _isDark ? AppColors.gray5 : AppColors.gray4; + + // ── Action / Secondary ────────────────────────────────────────────────────── + + /// action.secondary.secondary-bg Gray.100 light / Gray.900 dark + Color get actionSecondaryBg => _isDark ? AppColors.gray9 : AppColors.gray1; + + /// action.secondary.secondary-bg-hover Gray.200 light / Gray.800 dark + Color get actionSecondaryBgHover => + _isDark ? AppColors.gray8 : AppColors.gray2; + + /// action.secondary.secondary-text Gray.900 light / Gray.100 dark + Color get actionSecondaryText => _isDark ? AppColors.gray1 : AppColors.gray9; + + /// action.secondary.secondary-border Gray.500 light / Gray.600 dark + Color get actionSecondaryBorder => + _isDark ? AppColors.gray6 : AppColors.gray5; + + /// action.secondary.secondary-disabled-bg Gray.200 light / Gray.900 dark + Color get actionSecondaryDisabledBg => + _isDark ? AppColors.gray9 : AppColors.gray2; + + /// action.secondary.secondary-disabled-text Gray.500 both modes + Color get actionSecondaryDisabledText => AppColors.gray5; + + /// action.secondary.secondary-disabled-border Gray.300 light / Gray.700 dark + Color get actionSecondaryDisabledBorder => + _isDark ? AppColors.gray7 : AppColors.gray3; + + // ── Action / Tertiary ─────────────────────────────────────────────────────── + + /// action.tertiary.tertiary-text Gray.900 light / Gray.100 dark + Color get actionTertiaryText => _isDark ? AppColors.gray1 : AppColors.gray9; + + /// action.tertiary.tertiary-hover-bg Gray.200 light / Gray.800 dark + Color get actionTertiaryHoverBg => + _isDark ? AppColors.gray8 : AppColors.gray2; + + /// action.tertiary.tertiary-disabled-text Gray.500 both modes + Color get actionTertiaryDisabledText => AppColors.gray5; + + // ── Action / Tonal ────────────────────────────────────────────────────────── + + /// action.tonal.tonal-bg Blue.100 light / Blue.700 dark + Color get actionTonalBg => _isDark ? AppColors.blue7 : AppColors.blue1; + + /// action.tonal.tonal-border Gray.200 light / Gray.800 dark + Color get actionTonalBorder => _isDark ? AppColors.gray8 : AppColors.gray2; + + /// action.tonal.tonal-bg-hover Blue.200 light / Blue.600 dark + Color get actionTonalBgHover => _isDark ? AppColors.blue6 : AppColors.blue2; + + /// action.tonal.tonal-text Gray.900 light / Gray.100 dark + Color get actionTonalText => _isDark ? AppColors.gray1 : AppColors.gray9; + + /// action.tonal.tonal-disabled-bg Gray.100 light / Gray.800 dark + Color get actionTonalDisabledBg => + _isDark ? AppColors.gray8 : AppColors.gray1; + + /// action.tonal.tonal-disabled-border Gray.200 light / Gray.800 dark + Color get actionTonalDisabledBorder => + _isDark ? AppColors.gray8 : AppColors.gray2; + + /// action.tonal.tonal-disabled-text Gray.400 light / Gray.500 dark + Color get actionTonalDisabledText => + _isDark ? AppColors.gray5 : AppColors.gray4; + + // ── Action / Toggle ───────────────────────────────────────────────────────── + + /// action.toggle.toggle-active-bg Green.500 light / Green.700 dark + Color get actionToggleActiveBg => + _isDark ? AppColors.green7 : AppColors.green5; + + /// action.toggle.toggle-brand-active-bg Blue.400 light / Blue.600 dark + Color get actionToggleBrandActiveBg => + _isDark ? AppColors.blue6 : AppColors.blue4; + + /// action.toggle.toggle-disabled-bg Gray.700 both modes + Color get actionToggleDisabledBg => AppColors.gray7; + + /// action.toggle.toggle-knob-bg Gray.000 light / Gray.100 dark + Color get actionToggleKnobBg => _isDark ? AppColors.gray1 : AppColors.gray0; + + /// action.toggle.toggle-border Gray.200 light / Gray.700 dark + Color get actionToggleBorder => _isDark ? AppColors.gray7 : AppColors.gray2; + + // ── Action / Tabbar ───────────────────────────────────────────────────────── + + /// action.tabbar.tabbar-bg Blue.200 light / Blue.800 dark + Color get actionTabbarBg => _isDark ? AppColors.blue8 : AppColors.blue2; + + /// action.tabbar.tabbar-border Blue.300 light / Blue.700 dark + Color get actionTabbarBorder => _isDark ? AppColors.blue7 : AppColors.blue3; + + /// action.tabbar.tabbar-selected-text Blue.1000 light / Blue.100 dark + Color get actionTabbarSelectedText => + _isDark ? AppColors.blue1 : AppColors.blue10; + + /// action.tabbar.tabbar-disabled-text Gray.600 light / Gray.200 dark + Color get actionTabbarDisabledText => + _isDark ? AppColors.gray2 : AppColors.gray6; +} diff --git a/lib/core/common/app_text_field.dart b/lib/core/common/app_text_field.dart index a94eee284a..7fa8a7683d 100644 --- a/lib/core/common/app_text_field.dart +++ b/lib/core/common/app_text_field.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:lantern/core/common/app_asset.dart'; -import 'package:lantern/core/common/app_colors.dart'; import 'package:lantern/core/common/app_dimens.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import 'package:lantern/core/common/cap_scaling.dart'; class AppTextField extends StatelessWidget { @@ -82,14 +82,14 @@ class AppTextField extends StatelessWidget { onEditingComplete: onEditingComplete, readOnly: onTap != null, onTap: onTap, - cursorColor: AppColors.blue10, + // cursorColor from textSelectionTheme autovalidateMode: autovalidateMode, validator: validator, cursorRadius: Radius.circular(16), cursorHeight: defaultSize, cursorOpacityAnimates: true, style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray9, + // text color from colorScheme.onSurface via theme fontSize: spCap(context, 14), ), textInputAction: textInputAction, @@ -100,50 +100,18 @@ class AppTextField extends StatelessWidget { required maxLength}) => counter, decoration: InputDecoration( - contentPadding: EdgeInsets.symmetric(vertical: 20, horizontal: 16), + // borders, hintStyle, contentPadding come from inputDecorationTheme filled: true, - fillColor: enable ? AppColors.white : AppColors.gray3, + fillColor: enable + ? context.bgElevated // bg.input = bg.elevated + : context.bgCallout, // bg.callout for disabled hintText: hintText, - hintStyle: textTheme.bodyMedium!.copyWith( - color: AppColors.gray4, - ), - prefixIcon: prefixIcon != null ? _buildFix(prefixIcon!) : null, - suffixIcon: suffixIcon != null ? _buildFix(suffixIcon!) : null, - border: OutlineInputBorder( - borderRadius: defaultBorderRadius, - borderSide: BorderSide( - color: AppColors.gray3, - width: 1, - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: defaultBorderRadius, - borderSide: BorderSide( - color: AppColors.gray3, - width: 1, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: defaultBorderRadius, - borderSide: BorderSide( - color: AppColors.blue8, - width: 2, - ), - ), - errorBorder: OutlineInputBorder( - borderRadius: defaultBorderRadius, - borderSide: BorderSide( - color: Colors.grey, - width: 1, - ), - ), - disabledBorder: OutlineInputBorder( - borderRadius: defaultBorderRadius, - borderSide: BorderSide( - color: AppColors.gray3, - width: 1, - ), - ), + prefixIcon: prefixIcon != null + ? _buildFix(prefixIcon!, iconColor: context.textPrimary) + : null, + suffixIcon: suffixIcon != null + ? _buildFix(suffixIcon!, iconColor: context.textPrimary) + : null, )); // If a label is provided, wrap the input field in a Column with a Text widget above. @@ -157,7 +125,7 @@ class AppTextField extends StatelessWidget { child: Text( label!, style: textTheme.labelLarge?.copyWith( - color: AppColors.gray8, + color: context.textSecondary, // text.secondary fontSize: spCap(context, 14), ), ), @@ -171,14 +139,14 @@ class AppTextField extends StatelessWidget { return inputField; } - Widget _buildFix(Object iconPath) { + Widget _buildFix(Object iconPath, {Color? iconColor}) { Widget? appAsset; if (iconPath is IconData) { - appAsset = Icon(iconPath, color: AppColors.yellow9); + appAsset = Icon(iconPath, color: iconColor); } else if (iconPath is String) { appAsset = AppImage( path: iconPath, - color: AppColors.yellow9, + color: iconColor, ); } else if (iconPath is Widget) { appAsset = iconPath; diff --git a/lib/core/common/app_text_styles.dart b/lib/core/common/app_text_styles.dart index 6c94e33224..ed02f41c36 100644 --- a/lib/core/common/app_text_styles.dart +++ b/lib/core/common/app_text_styles.dart @@ -9,37 +9,37 @@ class AppTextStyles { static TextStyle get displayLarge => GoogleFonts.urbanist( fontSize: 56, fontWeight: FontWeight.w700, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get displayMedium => GoogleFonts.urbanist( fontSize: 44, fontWeight: FontWeight.w700, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get displaySmall => GoogleFonts.urbanist( fontSize: 36, fontWeight: FontWeight.w700, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get headingLarge => GoogleFonts.urbanist( fontSize: 32, fontWeight: FontWeight.w600, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get headingMedium => GoogleFonts.urbanist( fontSize: 28, fontWeight: FontWeight.w600, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get headingSmall => GoogleFonts.urbanist( fontSize: 24, fontWeight: FontWeight.w600, - color: AppColors.black, + // color: AppColors.black, letterSpacing: 0, ); @@ -47,75 +47,75 @@ class AppTextStyles { fontSize: 14, fontWeight: FontWeight.w400, letterSpacing: 0, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get labelLargeBold => GoogleFonts.urbanist( fontSize: 14, fontWeight: FontWeight.bold, letterSpacing: 0, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get labelMedium => GoogleFonts.urbanist( fontSize: 12, fontWeight: FontWeight.w500, - color: AppColors.black, + // color: AppColors.black, letterSpacing: 0.0, ); static TextStyle get labelSmall => GoogleFonts.urbanist( fontSize: 10, fontWeight: FontWeight.w500, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get titleLarge => GoogleFonts.urbanist( fontSize: 22, fontWeight: FontWeight.w600, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get titleMedium => GoogleFonts.urbanist( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get titleSmall => GoogleFonts.urbanist( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get bodyLarge => GoogleFonts.urbanist( fontSize: 16, fontWeight: FontWeight.w400, - color: AppColors.black, + // color: AppColors.black, letterSpacing: 0); static TextStyle get bodyLargeBold => GoogleFonts.urbanist( fontSize: 16, fontWeight: FontWeight.bold, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get bodyMedium => GoogleFonts.urbanist( fontSize: 14, fontWeight: FontWeight.w400, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get bodyMediumBold => GoogleFonts.urbanist( fontSize: 14, fontWeight: FontWeight.bold, - color: AppColors.black, + // color: AppColors.black, ); static TextStyle get bodySmall => GoogleFonts.urbanist( fontSize: 12, fontWeight: FontWeight.w400, - color: AppColors.black, + // color: AppColors.black, ); //Text style for button diff --git a/lib/core/common/app_theme.dart b/lib/core/common/app_theme.dart index 34e32d7ecd..68c3bf94b5 100644 --- a/lib/core/common/app_theme.dart +++ b/lib/core/common/app_theme.dart @@ -5,16 +5,76 @@ import 'package:lantern/core/common/app_text_styles.dart'; import 'app_colors.dart'; +// ───────────────────────────────────────────────────────────────────────────── +// Figma token → ColorScheme slot reference +// +// token light dark +// ───────────────────────────────────────────────────────────────────────────── +// bg.elevated white gray850 → surface +// bg.surface gray1 gray9 → surfaceContainer +// bg.callout gray2 gray8 → surfaceContainerHighest +// bg.hover blue1 blue9 → primaryContainer +// text.primary gray9 gray2 → onSurface +// text.secondary gray8 gray3 → onSurfaceVariant +// text.link blue8(≈blue7) blue2 → primary (textButtonTheme) +// border.default gray2 gray8 → outline +// border.input gray3 gray7 → outlineVariant +// border.input-focus blue8 blue2 → primary (focused border) +// border.error red6 red5 → error +// status.error-bg red2 red8 → errorContainer +// status.error-text red8 red2 → onErrorContainer +// action.primary.bg blue10 blue6 → primary (elevatedButton) +// ───────────────────────────────────────────────────────────────────────────── + class AppTheme { + // ── Light ────────────────────────────────────────────────────────────────── + static ThemeData appTheme() { + const cs = ColorScheme.light( + // Primary action / brand + primary: AppColors.blue10, // Blue.1000 – action.primary.bg + onPrimary: AppColors.gray1, // Gray.100 – action.primary.text + primaryContainer: AppColors.blue1, // Blue.100 – bg.hover + onPrimaryContainer: AppColors.gray9, + // Secondary + secondary: AppColors.blue7, // Blue.700 + onSecondary: AppColors.gray1, + secondaryContainer: AppColors.blue2, // Blue.200 + onSecondaryContainer: AppColors.gray9, + // Success (tertiary) + tertiary: AppColors.green5, // Green.500 – toggle-active-bg + onTertiary: AppColors.gray1, + tertiaryContainer: AppColors.green2, // Green.200 – status.success-bg + onTertiaryContainer: AppColors.green8, // Green.800 – status.success-text + // Error + error: AppColors.red6, // Red.600 – border.error + onError: AppColors.gray1, + errorContainer: AppColors.red2, // Red.200 – status.error-bg + onErrorContainer: AppColors.red8, // Red.800 – status.error-text + // Surfaces + surface: AppColors.white, // White – bg.elevated (Card, Dialog, Sheet) + onSurface: AppColors.gray9, // Gray.900 – text.primary + onSurfaceVariant: AppColors.gray8, // Gray.800 – text.secondary + surfaceContainer: AppColors.gray1, // Gray.100 – bg.surface + surfaceContainerHighest: AppColors.gray2, // Gray.200 – bg.callout + // Borders + outline: AppColors.gray2, // Gray.200 – border.default + outlineVariant: AppColors.gray3, // Gray.300 – border.input + ); + return ThemeData( useMaterial3: true, + colorScheme: cs, hoverColor: AppColors.blue1, + scaffoldBackgroundColor: AppColors.gray1, // bg.surface + primaryColor: AppColors.blue10, pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: FadeForwardsPageTransitionsBuilder(), }, ), + + // ── Text ──────────────────────────────────────────────────────────────── textSelectionTheme: TextSelectionThemeData( cursorColor: AppColors.blue10, selectionColor: AppColors.blue6, @@ -37,6 +97,8 @@ class AppTheme { titleMedium: AppTextStyles.titleMedium, titleSmall: AppTextStyles.titleSmall, ), + + // ── AppBar ────────────────────────────────────────────────────────────── appBarTheme: AppBarTheme( centerTitle: true, surfaceTintColor: AppColors.white, @@ -45,7 +107,7 @@ class AppTheme { ), titleSpacing: 0, elevation: 0, - backgroundColor: AppColors.gray1, + backgroundColor: AppColors.gray1, // bg.surface systemOverlayStyle: SystemUiOverlayStyle( statusBarColor: AppColors.white, statusBarBrightness: Brightness.light, @@ -53,38 +115,113 @@ class AppTheme { systemNavigationBarColor: AppColors.gray1, systemNavigationBarIconBrightness: Brightness.dark, ), - iconTheme: IconThemeData( - color: AppColors.blue10, - ), + iconTheme: IconThemeData(color: AppColors.blue10), ), - primaryColor: AppColors.blue10, - scaffoldBackgroundColor: AppColors.gray1, + + // ── Card ──────────────────────────────────────────────────────────────── cardTheme: CardThemeData( elevation: 0, margin: EdgeInsets.zero, - color: AppColors.white, + color: cs.surface, // bg.elevated clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.0), - side: BorderSide( - color: AppColors.gray2, - width: 1, - ), + side: BorderSide(color: cs.outline, width: 1), // border.default + ), + ), + + // ── Divider ───────────────────────────────────────────────────────────── + dividerTheme: DividerThemeData( + color: cs.outline, // border.default + thickness: 1, + ), + + // ── Input / TextField ──────────────────────────────────────────────────── + // Widgets using TextFormField / TextField inherit these automatically. + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: cs.surface, // bg.input = bg.elevated + contentPadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16), + hintStyle: TextStyle(color: AppColors.gray4), + labelStyle: TextStyle(color: cs.onSurfaceVariant), // text.secondary + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.outlineVariant), // border.input + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.outlineVariant), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: AppColors.blue8, width: 2), // border.input-focus + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.error), // border.error + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.error, width: 2), + ), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.outlineVariant), + ), + ), + + // ── Dialog ────────────────────────────────────────────────────────────── + dialogTheme: DialogThemeData( + backgroundColor: cs.surface, // bg.elevated + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide(color: cs.outlineVariant, width: 1), + ), + titleTextStyle: AppTextStyles.headingSmall, + contentTextStyle: AppTextStyles.bodyMedium, + ), + + // ── Bottom Sheet ───────────────────────────────────────────────────────── + bottomSheetTheme: BottomSheetThemeData( + backgroundColor: cs.surface, // bg.elevated + modalBackgroundColor: cs.surface, + dragHandleColor: AppColors.gray4, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), ), + + // ── ListTile (used by AppTile) ─────────────────────────────────────────── + listTileTheme: ListTileThemeData( + textColor: cs.onSurface, // text.primary + iconColor: cs.onSurface, + selectedColor: cs.primary, + selectedTileColor: cs.primaryContainer, // bg.hover + ), + + // ── TextButton (used by AppTextButton) ─────────────────────────────────── + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: AppColors.blue7, // text.link light + ), + ), + + // ── Radio ─────────────────────────────────────────────────────────────── radioTheme: RadioThemeData( - fillColor: WidgetStatePropertyAll(AppColors.gray9), + fillColor: WidgetStatePropertyAll(cs.onSurface), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, splashRadius: 10.0, ), + + // ── ElevatedButton (PrimaryButton) ─────────────────────────────────────── elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( elevation: 0, - backgroundColor: AppColors.blue10, + backgroundColor: cs.primary, // action.primary.primary-bg enableFeedback: true, - foregroundColor: AppColors.gray1, + foregroundColor: cs.onPrimary, // action.primary.primary-text textStyle: AppTextStyles.primaryButtonTextStyle - .copyWith(fontSize: 18.0, color: AppColors.gray1), + .copyWith(fontSize: 18.0, color: cs.onPrimary), overlayColor: AppColors.blue6, minimumSize: const Size(double.infinity, 52), tapTargetSize: MaterialTapTargetSize.padded, @@ -97,7 +234,212 @@ class AppTheme { ); } + // ── Dark ─────────────────────────────────────────────────────────────────── + static ThemeData darkTheme() { - return ThemeData(); + const cs = ColorScheme.dark( + // Primary action / brand + primary: AppColors.blue6, // Blue.600 – action.primary.bg + onPrimary: AppColors.gray1, // Gray.100 – action.primary.text + primaryContainer: AppColors.blue9, // Blue.900 – bg.hover + onPrimaryContainer: AppColors.gray2, + // Secondary + secondary: AppColors.blue5, // Blue.500 + onSecondary: AppColors.gray1, + secondaryContainer: AppColors.blue7, // Blue.700 + onSecondaryContainer: AppColors.gray2, + // Success (tertiary) + tertiary: AppColors.green7, // Green.700 – toggle-active-bg dark + onTertiary: AppColors.gray1, + tertiaryContainer: AppColors.green7, + onTertiaryContainer: AppColors.green3, // Green.300 – status.success-text dark + // Error + error: AppColors.red5, // Red.500 – border.error dark + onError: AppColors.gray1, + errorContainer: AppColors.red8, // Red.800 – status.error-bg dark + onErrorContainer: AppColors.red2, // Red.200 – status.error-text dark + // Surfaces + surface: AppColors.gray850, // Gray.850 – bg.elevated (Card, Dialog, Sheet) + onSurface: AppColors.gray2, // Gray.200 – text.primary dark + onSurfaceVariant: AppColors.gray3, // Gray.300 – text.secondary dark + surfaceContainer: AppColors.gray9, // Gray.900 – bg.surface dark + surfaceContainerHighest: AppColors.gray8, // Gray.800 – bg.callout dark + // Borders + outline: AppColors.gray8, // Gray.800 – border.default dark + outlineVariant: AppColors.gray7, // Gray.700 – border.input dark + ); + + return ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + colorScheme: cs, + hoverColor: AppColors.blue9, + scaffoldBackgroundColor: AppColors.gray9, // bg.surface dark + primaryColor: AppColors.blue6, + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: FadeForwardsPageTransitionsBuilder(), + }, + ), + + // ── Text ──────────────────────────────────────────────────────────────── + textSelectionTheme: TextSelectionThemeData( + cursorColor: AppColors.blue6, + selectionColor: AppColors.blue7, + selectionHandleColor: AppColors.blue5, + ), + textTheme: GoogleFonts.urbanistTextTheme( + ThemeData(brightness: Brightness.dark).textTheme, + ).copyWith( + bodyLarge: AppTextStyles.bodyLarge, + bodyMedium: AppTextStyles.bodyMedium, + bodySmall: AppTextStyles.bodySmall, + displayLarge: AppTextStyles.displayLarge, + displayMedium: AppTextStyles.displayMedium, + displaySmall: AppTextStyles.displaySmall, + headlineLarge: AppTextStyles.headingLarge, + headlineMedium: AppTextStyles.headingMedium, + headlineSmall: AppTextStyles.headingSmall, + labelLarge: AppTextStyles.labelLarge, + labelMedium: AppTextStyles.labelMedium, + labelSmall: AppTextStyles.labelSmall, + titleLarge: AppTextStyles.titleLarge, + titleMedium: AppTextStyles.titleMedium, + titleSmall: AppTextStyles.titleSmall, + ), + + // ── AppBar ────────────────────────────────────────────────────────────── + appBarTheme: AppBarTheme( + centerTitle: true, + surfaceTintColor: AppColors.gray850, + titleTextStyle: AppTextStyles.headingSmall.copyWith( + color: cs.onSurface, // text.primary dark + ), + titleSpacing: 0, + elevation: 0, + backgroundColor: AppColors.gray9, // bg.surface dark + systemOverlayStyle: SystemUiOverlayStyle( + statusBarColor: AppColors.gray9, + statusBarBrightness: Brightness.dark, + statusBarIconBrightness: Brightness.light, + systemNavigationBarColor: AppColors.gray9, + systemNavigationBarIconBrightness: Brightness.light, + ), + iconTheme: IconThemeData(color: cs.onSurface), + ), + + // ── Card ──────────────────────────────────────────────────────────────── + cardTheme: CardThemeData( + elevation: 0, + margin: EdgeInsets.zero, + color: cs.surface, // bg.elevated dark + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + side: BorderSide(color: cs.outline, width: 1), // border.default dark + ), + ), + + // ── Divider ───────────────────────────────────────────────────────────── + dividerTheme: DividerThemeData( + color: cs.outline, // border.default dark + thickness: 1, + ), + + // ── Input / TextField ──────────────────────────────────────────────────── + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: cs.surface, // bg.input dark (gray850) + contentPadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16), + hintStyle: TextStyle(color: AppColors.gray5), // text.disabled dark + labelStyle: TextStyle(color: cs.onSurfaceVariant), // text.secondary dark + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.outlineVariant), // border.input dark + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.outlineVariant), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: AppColors.blue2, width: 2), // border.input-focus dark + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.error), // border.error dark + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.error, width: 2), + ), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide(color: cs.outlineVariant), + ), + ), + + // ── Dialog ────────────────────────────────────────────────────────────── + dialogTheme: DialogThemeData( + backgroundColor: cs.surface, // bg.elevated dark + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide(color: cs.outlineVariant, width: 1), + ), + titleTextStyle: AppTextStyles.headingSmall, + contentTextStyle: AppTextStyles.bodyMedium, + ), + + // ── Bottom Sheet ───────────────────────────────────────────────────────── + bottomSheetTheme: BottomSheetThemeData( + backgroundColor: cs.surface, // bg.elevated dark + modalBackgroundColor: cs.surface, + dragHandleColor: AppColors.gray6, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16)), + ), + ), + + // ── ListTile (used by AppTile) ─────────────────────────────────────────── + listTileTheme: ListTileThemeData( + textColor: cs.onSurface, // text.primary dark + iconColor: cs.onSurface, + selectedColor: cs.primary, + selectedTileColor: cs.primaryContainer, // bg.hover dark + ), + + // ── TextButton (used by AppTextButton) ─────────────────────────────────── + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: AppColors.blue2, // text.link dark + ), + ), + + // ── Radio ─────────────────────────────────────────────────────────────── + radioTheme: RadioThemeData( + fillColor: WidgetStatePropertyAll(cs.onSurface), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + splashRadius: 10.0, + ), + + // ── ElevatedButton (PrimaryButton) ─────────────────────────────────────── + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + elevation: 0, + backgroundColor: cs.primary, // action.primary.primary-bg dark + enableFeedback: true, + foregroundColor: cs.onPrimary, // action.primary.primary-text + textStyle: AppTextStyles.primaryButtonTextStyle + .copyWith(fontSize: 18.0, color: cs.onPrimary), + overlayColor: AppColors.blue5, // action.primary.primary-bg-hover dark + minimumSize: const Size(double.infinity, 52), + tapTargetSize: MaterialTapTargetSize.padded, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(32.0), + side: BorderSide.none, + ), + ), + ), + ); } } diff --git a/lib/core/common/common.dart b/lib/core/common/common.dart index 5046e4a002..b99a68f31f 100644 --- a/lib/core/common/common.dart +++ b/lib/core/common/common.dart @@ -4,9 +4,9 @@ import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - import 'package:lantern/core/common/app_build_info.dart'; import 'package:lantern/core/common/app_eum.dart'; import 'package:lantern/core/common/app_urls.dart'; @@ -32,11 +32,12 @@ export 'package:lantern/core/common/app_dialog.dart'; export 'package:lantern/core/common/app_dimens.dart'; export 'package:lantern/core/common/app_eum.dart'; export 'package:lantern/core/common/app_image_paths.dart'; +export 'package:lantern/core/common/app_semantic_colors.dart'; export 'package:lantern/core/common/app_text_field.dart'; export 'package:lantern/core/common/app_theme.dart'; -export 'package:lantern/core/common/date_formatters.dart'; // Utils export 'package:lantern/core/common/app_urls.dart'; +export 'package:lantern/core/common/date_formatters.dart'; //Desktop export export 'package:lantern/core/desktop/app_intent.dart'; export 'package:lantern/core/desktop/app_shortcuts.dart'; @@ -209,6 +210,17 @@ ServerLocationEntity initialServerLocation() { ); } +ThemeMode resolveThemeMode(String raw) { + switch (raw) { + case 'light': + return ThemeMode.light; + case 'dark': + return ThemeMode.dark; + default: + return ThemeMode.system; + } +} + Future isStageEnvironment() async { final dir = await AppStorageUtils.getAppDirectory(); final envFile = File('${dir.path}/.radiance_env'); diff --git a/lib/core/extensions/context.dart b/lib/core/extensions/context.dart index 3a0512bc57..946b27a86e 100644 --- a/lib/core/extensions/context.dart +++ b/lib/core/extensions/context.dart @@ -1,6 +1,6 @@ // Extension for showing error SnackBars. import 'package:flutter/material.dart'; -import 'package:lantern/core/common/app_colors.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import 'package:loader_overlay/loader_overlay.dart'; import '../common/app_dimens.dart' show defaultPadding; @@ -15,13 +15,13 @@ extension SnackBarExtensions on BuildContext { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), - backgroundColor: AppColors.red7, + backgroundColor: bgSnackbarError, showCloseIcon: closeButton, - closeIconColor: AppColors.white, + closeIconColor: textInverse, content: Text( message, style: textTheme!.copyWith( - color: AppColors.white, + color: textInverse, ), ), duration: Duration(seconds: 5), @@ -38,13 +38,13 @@ extension SnackBarExtensions on BuildContext { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), - backgroundColor: AppColors.blue9, + backgroundColor: bgSnackbar, showCloseIcon: closeButton, - closeIconColor: AppColors.white, + closeIconColor: textInverse, content: Text( message, style: textTheme!.copyWith( - color: AppColors.white, + color: textInverse, ), ), duration: Duration(seconds: 5), diff --git a/lib/core/models/entity/app_setting_entity.dart b/lib/core/models/entity/app_setting_entity.dart index 705d35b3ee..1283efbaab 100644 --- a/lib/core/models/entity/app_setting_entity.dart +++ b/lib/core/models/entity/app_setting_entity.dart @@ -20,6 +20,7 @@ class AppSetting { String routingModeRaw; String dataCapThreshold; bool onboardingCompleted; + String themeMode; String environment; AppSetting({ @@ -38,6 +39,7 @@ class AppSetting { this.routingModeRaw = 'full_tunnel', this.dataCapThreshold = '', this.onboardingCompleted = false, + this.themeMode = 'system', this.environment = 'prod', }); @@ -56,6 +58,7 @@ class AppSetting { String? routingModeRaw, String? dataCapThreshold, bool? onboardingCompleted, + String? themeMode, String? environment, }) { return AppSetting( @@ -74,6 +77,7 @@ class AppSetting { routingModeRaw: routingModeRaw ?? this.routingModeRaw, dataCapThreshold: dataCapThreshold ?? this.dataCapThreshold, onboardingCompleted: onboardingCompleted ?? this.onboardingCompleted, + themeMode: themeMode ?? this.themeMode, environment: environment ?? this.environment, ); } diff --git a/lib/core/router/router.dart b/lib/core/router/router.dart index 17cdd8b5ad..6bb8c84719 100644 --- a/lib/core/router/router.dart +++ b/lib/core/router/router.dart @@ -176,6 +176,10 @@ class AppRouter extends RootStackRouter { path: '/developer-mode', page: DeveloperMode.page, ), + AutoRoute( + path: '/appearance', + page: Appearance.page, + ), AutoRoute( path: '/smart-routing', page: SmartRouting.page, diff --git a/lib/core/router/router.gr.dart b/lib/core/router/router.gr.dart index 65e33ae03e..f2e685b236 100644 --- a/lib/core/router/router.gr.dart +++ b/lib/core/router/router.gr.dart @@ -9,75 +9,76 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i43; -import 'package:collection/collection.dart' as _i47; -import 'package:flutter/material.dart' as _i44; -import 'package:lantern/core/common/common.dart' as _i45; +import 'package:auto_route/auto_route.dart' as _i44; +import 'package:collection/collection.dart' as _i48; +import 'package:flutter/material.dart' as _i45; +import 'package:lantern/core/common/common.dart' as _i46; import 'package:lantern/core/widgets/app_webview.dart' as _i3; import 'package:lantern/features/account/account.dart' as _i1; -import 'package:lantern/features/account/delete_account.dart' as _i8; +import 'package:lantern/features/account/delete_account.dart' as _i9; import 'package:lantern/features/auth/add_email.dart' as _i2; -import 'package:lantern/features/auth/choose_payment_method.dart' as _i5; -import 'package:lantern/features/auth/confirm_email.dart' as _i6; -import 'package:lantern/features/auth/create_password.dart' as _i7; -import 'package:lantern/features/auth/device_limit_reached.dart' as _i10; -import 'package:lantern/features/auth/lantern_pro_license.dart' as _i17; -import 'package:lantern/features/auth/reset_password.dart' as _i31; -import 'package:lantern/features/auth/reset_password_email.dart' as _i32; -import 'package:lantern/features/auth/sign_in_email.dart' as _i35; -import 'package:lantern/features/auth/sign_in_password.dart' as _i36; -import 'package:lantern/features/developer/developer_mode.dart' as _i9; -import 'package:lantern/features/home/home.dart' as _i13; -import 'package:lantern/features/language/language.dart' as _i16; -import 'package:lantern/features/logs/logs.dart' as _i18; +import 'package:lantern/features/auth/choose_payment_method.dart' as _i6; +import 'package:lantern/features/auth/confirm_email.dart' as _i7; +import 'package:lantern/features/auth/create_password.dart' as _i8; +import 'package:lantern/features/auth/device_limit_reached.dart' as _i11; +import 'package:lantern/features/auth/lantern_pro_license.dart' as _i18; +import 'package:lantern/features/auth/reset_password.dart' as _i32; +import 'package:lantern/features/auth/reset_password_email.dart' as _i33; +import 'package:lantern/features/auth/sign_in_email.dart' as _i36; +import 'package:lantern/features/auth/sign_in_password.dart' as _i37; +import 'package:lantern/features/developer/developer_mode.dart' as _i10; +import 'package:lantern/features/home/home.dart' as _i14; +import 'package:lantern/features/language/language.dart' as _i17; +import 'package:lantern/features/logs/logs.dart' as _i19; import 'package:lantern/features/macos_extension/macos_extension_dialog.dart' - as _i19; -import 'package:lantern/features/onboarding/onboarding.dart' as _i22; -import 'package:lantern/features/plans/plans.dart' as _i23; + as _i20; +import 'package:lantern/features/onboarding/onboarding.dart' as _i23; +import 'package:lantern/features/plans/plans.dart' as _i24; import 'package:lantern/features/private_server/join_private_server.dart' - as _i15; + as _i16; import 'package:lantern/features/private_server/manage_private_server.dart' - as _i20; -import 'package:lantern/features/private_server/manually_server_setup.dart' as _i21; +import 'package:lantern/features/private_server/manually_server_setup.dart' + as _i22; import 'package:lantern/features/private_server/private_server_add_billing.dart' - as _i24; -import 'package:lantern/features/private_server/private_server_deploy.dart' as _i25; -import 'package:lantern/features/private_server/private_server_locations.dart' +import 'package:lantern/features/private_server/private_server_deploy.dart' as _i26; -import 'package:lantern/features/private_server/private_server_setup.dart' +import 'package:lantern/features/private_server/private_server_locations.dart' as _i27; -import 'package:lantern/features/private_server/private_sever_details.dart' +import 'package:lantern/features/private_server/private_server_setup.dart' as _i28; -import 'package:lantern/features/qr_scanner/qr_code_scanner.dart' as _i29; -import 'package:lantern/features/report_Issue/report_issue.dart' as _i30; -import 'package:lantern/features/setting/download_links.dart' as _i11; -import 'package:lantern/features/setting/follow_us.dart' as _i12; -import 'package:lantern/features/setting/invite_friends.dart' as _i14; -import 'package:lantern/features/setting/setting.dart' as _i34; -import 'package:lantern/features/setting/smart_routing.dart' as _i37; -import 'package:lantern/features/setting/vpn_setting.dart' as _i41; +import 'package:lantern/features/private_server/private_sever_details.dart' + as _i29; +import 'package:lantern/features/qr_scanner/qr_code_scanner.dart' as _i30; +import 'package:lantern/features/report_Issue/report_issue.dart' as _i31; +import 'package:lantern/features/setting/appearance.dart' as _i4; +import 'package:lantern/features/setting/download_links.dart' as _i12; +import 'package:lantern/features/setting/follow_us.dart' as _i13; +import 'package:lantern/features/setting/invite_friends.dart' as _i15; +import 'package:lantern/features/setting/setting.dart' as _i35; +import 'package:lantern/features/setting/smart_routing.dart' as _i38; +import 'package:lantern/features/setting/vpn_setting.dart' as _i42; import 'package:lantern/features/split_tunneling/apps_split_tunneling.dart' - as _i4; -import 'package:lantern/features/split_tunneling/split_tunneling.dart' as _i38; + as _i5; +import 'package:lantern/features/split_tunneling/split_tunneling.dart' as _i39; import 'package:lantern/features/split_tunneling/split_tunneling_info.dart' - as _i39; + as _i40; import 'package:lantern/features/split_tunneling/website_split_tunneling.dart' - as _i42; -import 'package:lantern/features/support/support.dart' as _i40; -import 'package:lantern/features/vpn/server_selection.dart' as _i33; -import 'package:lantern/lantern/protos/protos/auth.pb.dart' as _i46; + as _i43; +import 'package:lantern/features/support/support.dart' as _i41; +import 'package:lantern/features/vpn/server_selection.dart' as _i34; +import 'package:lantern/lantern/protos/protos/auth.pb.dart' as _i47; /// generated route for /// [_i1.Account] -class Account extends _i43.PageRouteInfo { - const Account({List<_i43.PageRouteInfo>? children}) +class Account extends _i44.PageRouteInfo { + const Account({List<_i44.PageRouteInfo>? children}) : super(Account.name, initialChildren: children); static const String name = 'Account'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { return const _i1.Account(); @@ -87,12 +88,12 @@ class Account extends _i43.PageRouteInfo { /// generated route for /// [_i2.AddEmail] -class AddEmail extends _i43.PageRouteInfo { +class AddEmail extends _i44.PageRouteInfo { AddEmail({ - _i44.Key? key, - _i45.AuthFlow authFlow = _i45.AuthFlow.signUp, + _i45.Key? key, + _i46.AuthFlow authFlow = _i46.AuthFlow.signUp, String? password, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( AddEmail.name, args: AddEmailArgs(key: key, authFlow: authFlow, password: password), @@ -101,7 +102,7 @@ class AddEmail extends _i43.PageRouteInfo { static const String name = 'AddEmail'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs( @@ -119,13 +120,13 @@ class AddEmail extends _i43.PageRouteInfo { class AddEmailArgs { const AddEmailArgs({ this.key, - this.authFlow = _i45.AuthFlow.signUp, + this.authFlow = _i46.AuthFlow.signUp, this.password, }); - final _i44.Key? key; + final _i45.Key? key; - final _i45.AuthFlow authFlow; + final _i46.AuthFlow authFlow; final String? password; @@ -149,12 +150,12 @@ class AddEmailArgs { /// generated route for /// [_i3.AppWebView] -class AppWebview extends _i43.PageRouteInfo { +class AppWebview extends _i44.PageRouteInfo { AppWebview({ - _i44.Key? key, + _i45.Key? key, required String title, required String url, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( AppWebview.name, args: AppWebviewArgs(key: key, title: title, url: url), @@ -163,7 +164,7 @@ class AppWebview extends _i43.PageRouteInfo { static const String name = 'AppWebview'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); @@ -175,7 +176,7 @@ class AppWebview extends _i43.PageRouteInfo { class AppWebviewArgs { const AppWebviewArgs({this.key, required this.title, required this.url}); - final _i44.Key? key; + final _i45.Key? key; final String title; @@ -198,30 +199,46 @@ class AppWebviewArgs { } /// generated route for -/// [_i4.AppsSplitTunneling] -class AppsSplitTunneling extends _i43.PageRouteInfo { - const AppsSplitTunneling({List<_i43.PageRouteInfo>? children}) +/// [_i4.Appearance] +class Appearance extends _i44.PageRouteInfo { + const Appearance({List<_i44.PageRouteInfo>? children}) + : super(Appearance.name, initialChildren: children); + + static const String name = 'Appearance'; + + static _i44.PageInfo page = _i44.PageInfo( + name, + builder: (data) { + return const _i4.Appearance(); + }, + ); +} + +/// generated route for +/// [_i5.AppsSplitTunneling] +class AppsSplitTunneling extends _i44.PageRouteInfo { + const AppsSplitTunneling({List<_i44.PageRouteInfo>? children}) : super(AppsSplitTunneling.name, initialChildren: children); static const String name = 'AppsSplitTunneling'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i4.AppsSplitTunneling(); + return const _i5.AppsSplitTunneling(); }, ); } /// generated route for -/// [_i5.ChoosePaymentMethod] -class ChoosePaymentMethod extends _i43.PageRouteInfo { +/// [_i6.ChoosePaymentMethod] +class ChoosePaymentMethod extends _i44.PageRouteInfo { ChoosePaymentMethod({ - _i44.Key? key, + _i45.Key? key, required String email, String? code, - required _i45.AuthFlow authFlow, - List<_i43.PageRouteInfo>? children, + required _i46.AuthFlow authFlow, + List<_i44.PageRouteInfo>? children, }) : super( ChoosePaymentMethod.name, args: ChoosePaymentMethodArgs( @@ -235,11 +252,11 @@ class ChoosePaymentMethod extends _i43.PageRouteInfo { static const String name = 'ChoosePaymentMethod'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i5.ChoosePaymentMethod( + return _i6.ChoosePaymentMethod( key: args.key, email: args.email, code: args.code, @@ -257,13 +274,13 @@ class ChoosePaymentMethodArgs { required this.authFlow, }); - final _i44.Key? key; + final _i45.Key? key; final String email; final String? code; - final _i45.AuthFlow authFlow; + final _i46.AuthFlow authFlow; @override String toString() { @@ -286,14 +303,14 @@ class ChoosePaymentMethodArgs { } /// generated route for -/// [_i6.ConfirmEmail] -class ConfirmEmail extends _i43.PageRouteInfo { +/// [_i7.ConfirmEmail] +class ConfirmEmail extends _i44.PageRouteInfo { ConfirmEmail({ - _i44.Key? key, + _i45.Key? key, required String email, String? password, - _i45.AuthFlow authFlow = _i45.AuthFlow.signUp, - List<_i43.PageRouteInfo>? children, + _i46.AuthFlow authFlow = _i46.AuthFlow.signUp, + List<_i44.PageRouteInfo>? children, }) : super( ConfirmEmail.name, args: ConfirmEmailArgs( @@ -307,11 +324,11 @@ class ConfirmEmail extends _i43.PageRouteInfo { static const String name = 'ConfirmEmail'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i6.ConfirmEmail( + return _i7.ConfirmEmail( key: args.key, email: args.email, password: args.password, @@ -326,16 +343,16 @@ class ConfirmEmailArgs { this.key, required this.email, this.password, - this.authFlow = _i45.AuthFlow.signUp, + this.authFlow = _i46.AuthFlow.signUp, }); - final _i44.Key? key; + final _i45.Key? key; final String email; final String? password; - final _i45.AuthFlow authFlow; + final _i46.AuthFlow authFlow; @override String toString() { @@ -358,14 +375,14 @@ class ConfirmEmailArgs { } /// generated route for -/// [_i7.CreatePassword] -class CreatePassword extends _i43.PageRouteInfo { +/// [_i8.CreatePassword] +class CreatePassword extends _i44.PageRouteInfo { CreatePassword({ - _i44.Key? key, + _i45.Key? key, required String email, - required _i45.AuthFlow authFlow, + required _i46.AuthFlow authFlow, required String code, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( CreatePassword.name, args: CreatePasswordArgs( @@ -379,11 +396,11 @@ class CreatePassword extends _i43.PageRouteInfo { static const String name = 'CreatePassword'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i7.CreatePassword( + return _i8.CreatePassword( key: args.key, email: args.email, authFlow: args.authFlow, @@ -401,11 +418,11 @@ class CreatePasswordArgs { required this.code, }); - final _i44.Key? key; + final _i45.Key? key; final String email; - final _i45.AuthFlow authFlow; + final _i46.AuthFlow authFlow; final String code; @@ -430,44 +447,44 @@ class CreatePasswordArgs { } /// generated route for -/// [_i8.DeleteAccount] -class DeleteAccount extends _i43.PageRouteInfo { - const DeleteAccount({List<_i43.PageRouteInfo>? children}) +/// [_i9.DeleteAccount] +class DeleteAccount extends _i44.PageRouteInfo { + const DeleteAccount({List<_i44.PageRouteInfo>? children}) : super(DeleteAccount.name, initialChildren: children); static const String name = 'DeleteAccount'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i8.DeleteAccount(); + return const _i9.DeleteAccount(); }, ); } /// generated route for -/// [_i9.DeveloperMode] -class DeveloperMode extends _i43.PageRouteInfo { - const DeveloperMode({List<_i43.PageRouteInfo>? children}) +/// [_i10.DeveloperMode] +class DeveloperMode extends _i44.PageRouteInfo { + const DeveloperMode({List<_i44.PageRouteInfo>? children}) : super(DeveloperMode.name, initialChildren: children); static const String name = 'DeveloperMode'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i9.DeveloperMode(); + return const _i10.DeveloperMode(); }, ); } /// generated route for -/// [_i10.DeviceLimitReached] -class DeviceLimitReached extends _i43.PageRouteInfo { +/// [_i11.DeviceLimitReached] +class DeviceLimitReached extends _i44.PageRouteInfo { DeviceLimitReached({ - _i44.Key? key, - required List<_i46.UserResponse_Device> devices, - List<_i43.PageRouteInfo>? children, + _i45.Key? key, + required List<_i47.UserResponse_Device> devices, + List<_i44.PageRouteInfo>? children, }) : super( DeviceLimitReached.name, args: DeviceLimitReachedArgs(key: key, devices: devices), @@ -476,11 +493,11 @@ class DeviceLimitReached extends _i43.PageRouteInfo { static const String name = 'DeviceLimitReached'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i10.DeviceLimitReached(key: args.key, devices: args.devices); + return _i11.DeviceLimitReached(key: args.key, devices: args.devices); }, ); } @@ -488,9 +505,9 @@ class DeviceLimitReached extends _i43.PageRouteInfo { class DeviceLimitReachedArgs { const DeviceLimitReachedArgs({this.key, required this.devices}); - final _i44.Key? key; + final _i45.Key? key; - final List<_i46.UserResponse_Device> devices; + final List<_i47.UserResponse_Device> devices; @override String toString() { @@ -502,7 +519,7 @@ class DeviceLimitReachedArgs { if (identical(this, other)) return true; if (other is! DeviceLimitReachedArgs) return false; return key == other.key && - const _i47.ListEquality<_i46.UserResponse_Device>().equals( + const _i48.ListEquality<_i47.UserResponse_Device>().equals( devices, other.devices, ); @@ -511,80 +528,80 @@ class DeviceLimitReachedArgs { @override int get hashCode => key.hashCode ^ - const _i47.ListEquality<_i46.UserResponse_Device>().hash(devices); + const _i48.ListEquality<_i47.UserResponse_Device>().hash(devices); } /// generated route for -/// [_i11.DownloadLinks] -class DownloadLinks extends _i43.PageRouteInfo { - const DownloadLinks({List<_i43.PageRouteInfo>? children}) +/// [_i12.DownloadLinks] +class DownloadLinks extends _i44.PageRouteInfo { + const DownloadLinks({List<_i44.PageRouteInfo>? children}) : super(DownloadLinks.name, initialChildren: children); static const String name = 'DownloadLinks'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i11.DownloadLinks(); + return const _i12.DownloadLinks(); }, ); } /// generated route for -/// [_i12.FollowUs] -class FollowUs extends _i43.PageRouteInfo { - const FollowUs({List<_i43.PageRouteInfo>? children}) +/// [_i13.FollowUs] +class FollowUs extends _i44.PageRouteInfo { + const FollowUs({List<_i44.PageRouteInfo>? children}) : super(FollowUs.name, initialChildren: children); static const String name = 'FollowUs'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i12.FollowUs(); + return const _i13.FollowUs(); }, ); } /// generated route for -/// [_i13.Home] -class Home extends _i43.PageRouteInfo { - const Home({List<_i43.PageRouteInfo>? children}) +/// [_i14.Home] +class Home extends _i44.PageRouteInfo { + const Home({List<_i44.PageRouteInfo>? children}) : super(Home.name, initialChildren: children); static const String name = 'Home'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i13.Home(); + return const _i14.Home(); }, ); } /// generated route for -/// [_i14.InviteFriends] -class InviteFriends extends _i43.PageRouteInfo { - const InviteFriends({List<_i43.PageRouteInfo>? children}) +/// [_i15.InviteFriends] +class InviteFriends extends _i44.PageRouteInfo { + const InviteFriends({List<_i44.PageRouteInfo>? children}) : super(InviteFriends.name, initialChildren: children); static const String name = 'InviteFriends'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i14.InviteFriends(); + return const _i15.InviteFriends(); }, ); } /// generated route for -/// [_i15.JoinPrivateServer] -class JoinPrivateServer extends _i43.PageRouteInfo { +/// [_i16.JoinPrivateServer] +class JoinPrivateServer extends _i44.PageRouteInfo { JoinPrivateServer({ - _i44.Key? key, + _i45.Key? key, Map? deepLinkData, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( JoinPrivateServer.name, args: JoinPrivateServerArgs(key: key, deepLinkData: deepLinkData), @@ -593,13 +610,13 @@ class JoinPrivateServer extends _i43.PageRouteInfo { static const String name = 'JoinPrivateServer'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs( orElse: () => const JoinPrivateServerArgs(), ); - return _i15.JoinPrivateServer( + return _i16.JoinPrivateServer( key: args.key, deepLinkData: args.deepLinkData, ); @@ -610,7 +627,7 @@ class JoinPrivateServer extends _i43.PageRouteInfo { class JoinPrivateServerArgs { const JoinPrivateServerArgs({this.key, this.deepLinkData}); - final _i44.Key? key; + final _i45.Key? key; final Map? deepLinkData; @@ -624,7 +641,7 @@ class JoinPrivateServerArgs { if (identical(this, other)) return true; if (other is! JoinPrivateServerArgs) return false; return key == other.key && - const _i47.MapEquality().equals( + const _i48.MapEquality().equals( deepLinkData, other.deepLinkData, ); @@ -633,33 +650,33 @@ class JoinPrivateServerArgs { @override int get hashCode => key.hashCode ^ - const _i47.MapEquality().hash(deepLinkData); + const _i48.MapEquality().hash(deepLinkData); } /// generated route for -/// [_i16.Language] -class Language extends _i43.PageRouteInfo { - const Language({List<_i43.PageRouteInfo>? children}) +/// [_i17.Language] +class Language extends _i44.PageRouteInfo { + const Language({List<_i44.PageRouteInfo>? children}) : super(Language.name, initialChildren: children); static const String name = 'Language'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i16.Language(); + return const _i17.Language(); }, ); } /// generated route for -/// [_i17.LanternProLicense] -class LanternProLicense extends _i43.PageRouteInfo { +/// [_i18.LanternProLicense] +class LanternProLicense extends _i44.PageRouteInfo { LanternProLicense({ - _i44.Key? key, + _i45.Key? key, required String email, required String code, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( LanternProLicense.name, args: LanternProLicenseArgs(key: key, email: email, code: code), @@ -668,11 +685,11 @@ class LanternProLicense extends _i43.PageRouteInfo { static const String name = 'LanternProLicense'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i17.LanternProLicense( + return _i18.LanternProLicense( key: args.key, email: args.email, code: args.code, @@ -688,7 +705,7 @@ class LanternProLicenseArgs { required this.code, }); - final _i44.Key? key; + final _i45.Key? key; final String email; @@ -711,124 +728,124 @@ class LanternProLicenseArgs { } /// generated route for -/// [_i18.Logs] -class Logs extends _i43.PageRouteInfo { - const Logs({List<_i43.PageRouteInfo>? children}) +/// [_i19.Logs] +class Logs extends _i44.PageRouteInfo { + const Logs({List<_i44.PageRouteInfo>? children}) : super(Logs.name, initialChildren: children); static const String name = 'Logs'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i18.Logs(); + return const _i19.Logs(); }, ); } /// generated route for -/// [_i19.MacOSExtensionDialog] -class MacOSExtensionDialog extends _i43.PageRouteInfo { - const MacOSExtensionDialog({List<_i43.PageRouteInfo>? children}) +/// [_i20.MacOSExtensionDialog] +class MacOSExtensionDialog extends _i44.PageRouteInfo { + const MacOSExtensionDialog({List<_i44.PageRouteInfo>? children}) : super(MacOSExtensionDialog.name, initialChildren: children); static const String name = 'MacOSExtensionDialog'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i19.MacOSExtensionDialog(); + return const _i20.MacOSExtensionDialog(); }, ); } /// generated route for -/// [_i20.ManagePrivateServer] -class ManagePrivateServer extends _i43.PageRouteInfo { - const ManagePrivateServer({List<_i43.PageRouteInfo>? children}) +/// [_i21.ManagePrivateServer] +class ManagePrivateServer extends _i44.PageRouteInfo { + const ManagePrivateServer({List<_i44.PageRouteInfo>? children}) : super(ManagePrivateServer.name, initialChildren: children); static const String name = 'ManagePrivateServer'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i20.ManagePrivateServer(); + return const _i21.ManagePrivateServer(); }, ); } /// generated route for -/// [_i21.ManuallyServerSetup] -class ManuallyServerSetup extends _i43.PageRouteInfo { - const ManuallyServerSetup({List<_i43.PageRouteInfo>? children}) +/// [_i22.ManuallyServerSetup] +class ManuallyServerSetup extends _i44.PageRouteInfo { + const ManuallyServerSetup({List<_i44.PageRouteInfo>? children}) : super(ManuallyServerSetup.name, initialChildren: children); static const String name = 'ManuallyServerSetup'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i21.ManuallyServerSetup(); + return const _i22.ManuallyServerSetup(); }, ); } /// generated route for -/// [_i22.Onboarding] -class Onboarding extends _i43.PageRouteInfo { - const Onboarding({List<_i43.PageRouteInfo>? children}) +/// [_i23.Onboarding] +class Onboarding extends _i44.PageRouteInfo { + const Onboarding({List<_i44.PageRouteInfo>? children}) : super(Onboarding.name, initialChildren: children); static const String name = 'Onboarding'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i22.Onboarding(); + return const _i23.Onboarding(); }, ); } /// generated route for -/// [_i23.Plans] -class Plans extends _i43.PageRouteInfo { - const Plans({List<_i43.PageRouteInfo>? children}) +/// [_i24.Plans] +class Plans extends _i44.PageRouteInfo { + const Plans({List<_i44.PageRouteInfo>? children}) : super(Plans.name, initialChildren: children); static const String name = 'Plans'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i23.Plans(); + return const _i24.Plans(); }, ); } /// generated route for -/// [_i24.PrivateServerAddBilling] -class PrivateServerAddBilling extends _i43.PageRouteInfo { - const PrivateServerAddBilling({List<_i43.PageRouteInfo>? children}) +/// [_i25.PrivateServerAddBilling] +class PrivateServerAddBilling extends _i44.PageRouteInfo { + const PrivateServerAddBilling({List<_i44.PageRouteInfo>? children}) : super(PrivateServerAddBilling.name, initialChildren: children); static const String name = 'PrivateServerAddBilling'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i24.PrivateServerAddBilling(); + return const _i25.PrivateServerAddBilling(); }, ); } /// generated route for -/// [_i25.PrivateServerDeploy] -class PrivateServerDeploy extends _i43.PageRouteInfo { +/// [_i26.PrivateServerDeploy] +class PrivateServerDeploy extends _i44.PageRouteInfo { PrivateServerDeploy({ - _i44.Key? key, + _i45.Key? key, required String serverName, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( PrivateServerDeploy.name, args: PrivateServerDeployArgs(key: key, serverName: serverName), @@ -837,11 +854,11 @@ class PrivateServerDeploy extends _i43.PageRouteInfo { static const String name = 'PrivateServerDeploy'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i25.PrivateServerDeploy( + return _i26.PrivateServerDeploy( key: args.key, serverName: args.serverName, ); @@ -852,7 +869,7 @@ class PrivateServerDeploy extends _i43.PageRouteInfo { class PrivateServerDeployArgs { const PrivateServerDeployArgs({this.key, required this.serverName}); - final _i44.Key? key; + final _i45.Key? key; final String serverName; @@ -873,16 +890,16 @@ class PrivateServerDeployArgs { } /// generated route for -/// [_i26.PrivateServerLocation] +/// [_i27.PrivateServerLocation] class PrivateServerLocation - extends _i43.PageRouteInfo { + extends _i44.PageRouteInfo { PrivateServerLocation({ - _i44.Key? key, + _i45.Key? key, required List location, required String? selectedLocation, required dynamic Function(String) onLocationSelected, - required _i45.CloudProvider provider, - List<_i43.PageRouteInfo>? children, + required _i46.CloudProvider provider, + List<_i44.PageRouteInfo>? children, }) : super( PrivateServerLocation.name, args: PrivateServerLocationArgs( @@ -897,11 +914,11 @@ class PrivateServerLocation static const String name = 'PrivateServerLocation'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i26.PrivateServerLocation( + return _i27.PrivateServerLocation( key: args.key, location: args.location, selectedLocation: args.selectedLocation, @@ -921,7 +938,7 @@ class PrivateServerLocationArgs { required this.provider, }); - final _i44.Key? key; + final _i45.Key? key; final List location; @@ -929,7 +946,7 @@ class PrivateServerLocationArgs { final dynamic Function(String) onLocationSelected; - final _i45.CloudProvider provider; + final _i46.CloudProvider provider; @override String toString() { @@ -941,7 +958,7 @@ class PrivateServerLocationArgs { if (identical(this, other)) return true; if (other is! PrivateServerLocationArgs) return false; return key == other.key && - const _i47.ListEquality().equals(location, other.location) && + const _i48.ListEquality().equals(location, other.location) && selectedLocation == other.selectedLocation && provider == other.provider; } @@ -949,37 +966,37 @@ class PrivateServerLocationArgs { @override int get hashCode => key.hashCode ^ - const _i47.ListEquality().hash(location) ^ + const _i48.ListEquality().hash(location) ^ selectedLocation.hashCode ^ provider.hashCode; } /// generated route for -/// [_i27.PrivateServerSetup] -class PrivateServerSetup extends _i43.PageRouteInfo { - const PrivateServerSetup({List<_i43.PageRouteInfo>? children}) +/// [_i28.PrivateServerSetup] +class PrivateServerSetup extends _i44.PageRouteInfo { + const PrivateServerSetup({List<_i44.PageRouteInfo>? children}) : super(PrivateServerSetup.name, initialChildren: children); static const String name = 'PrivateServerSetup'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i27.PrivateServerSetup(); + return const _i28.PrivateServerSetup(); }, ); } /// generated route for -/// [_i28.PrivateSeverDetails] +/// [_i29.PrivateSeverDetails] class PrivateServerDetails - extends _i43.PageRouteInfo { + extends _i44.PageRouteInfo { PrivateServerDetails({ - _i44.Key? key, + _i45.Key? key, required List accounts, - required _i45.CloudProvider provider, + required _i46.CloudProvider provider, bool isPreFilled = false, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( PrivateServerDetails.name, args: PrivateServerDetailsArgs( @@ -993,11 +1010,11 @@ class PrivateServerDetails static const String name = 'PrivateServerDetails'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i28.PrivateSeverDetails( + return _i29.PrivateSeverDetails( key: args.key, accounts: args.accounts, provider: args.provider, @@ -1015,11 +1032,11 @@ class PrivateServerDetailsArgs { this.isPreFilled = false, }); - final _i44.Key? key; + final _i45.Key? key; final List accounts; - final _i45.CloudProvider provider; + final _i46.CloudProvider provider; final bool isPreFilled; @@ -1033,7 +1050,7 @@ class PrivateServerDetailsArgs { if (identical(this, other)) return true; if (other is! PrivateServerDetailsArgs) return false; return key == other.key && - const _i47.ListEquality().equals(accounts, other.accounts) && + const _i48.ListEquality().equals(accounts, other.accounts) && provider == other.provider && isPreFilled == other.isPreFilled; } @@ -1041,35 +1058,35 @@ class PrivateServerDetailsArgs { @override int get hashCode => key.hashCode ^ - const _i47.ListEquality().hash(accounts) ^ + const _i48.ListEquality().hash(accounts) ^ provider.hashCode ^ isPreFilled.hashCode; } /// generated route for -/// [_i29.QrCodeScanner] -class QrCodeScanner extends _i43.PageRouteInfo { - const QrCodeScanner({List<_i43.PageRouteInfo>? children}) +/// [_i30.QrCodeScanner] +class QrCodeScanner extends _i44.PageRouteInfo { + const QrCodeScanner({List<_i44.PageRouteInfo>? children}) : super(QrCodeScanner.name, initialChildren: children); static const String name = 'QrCodeScanner'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i29.QrCodeScanner(); + return const _i30.QrCodeScanner(); }, ); } /// generated route for -/// [_i30.ReportIssue] -class ReportIssue extends _i43.PageRouteInfo { +/// [_i31.ReportIssue] +class ReportIssue extends _i44.PageRouteInfo { ReportIssue({ - _i44.Key? key, + _i45.Key? key, String? description, String? type, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( ReportIssue.name, args: ReportIssueArgs(key: key, description: description, type: type), @@ -1078,13 +1095,13 @@ class ReportIssue extends _i43.PageRouteInfo { static const String name = 'ReportIssue'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs( orElse: () => const ReportIssueArgs(), ); - return _i30.ReportIssue( + return _i31.ReportIssue( key: args.key, description: args.description, type: args.type, @@ -1096,7 +1113,7 @@ class ReportIssue extends _i43.PageRouteInfo { class ReportIssueArgs { const ReportIssueArgs({this.key, this.description, this.type}); - final _i44.Key? key; + final _i45.Key? key; final String? description; @@ -1121,13 +1138,13 @@ class ReportIssueArgs { } /// generated route for -/// [_i31.ResetPassword] -class ResetPassword extends _i43.PageRouteInfo { +/// [_i32.ResetPassword] +class ResetPassword extends _i44.PageRouteInfo { ResetPassword({ - _i44.Key? key, + _i45.Key? key, required String email, required String code, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( ResetPassword.name, args: ResetPasswordArgs(key: key, email: email, code: code), @@ -1136,11 +1153,11 @@ class ResetPassword extends _i43.PageRouteInfo { static const String name = 'ResetPassword'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i31.ResetPassword( + return _i32.ResetPassword( key: args.key, email: args.email, code: args.code, @@ -1152,7 +1169,7 @@ class ResetPassword extends _i43.PageRouteInfo { class ResetPasswordArgs { const ResetPasswordArgs({this.key, required this.email, required this.code}); - final _i44.Key? key; + final _i45.Key? key; final String email; @@ -1175,12 +1192,12 @@ class ResetPasswordArgs { } /// generated route for -/// [_i32.ResetPasswordEmail] -class ResetPasswordEmail extends _i43.PageRouteInfo { +/// [_i33.ResetPasswordEmail] +class ResetPasswordEmail extends _i44.PageRouteInfo { ResetPasswordEmail({ - _i44.Key? key, + _i45.Key? key, String? email, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( ResetPasswordEmail.name, args: ResetPasswordEmailArgs(key: key, email: email), @@ -1189,13 +1206,13 @@ class ResetPasswordEmail extends _i43.PageRouteInfo { static const String name = 'ResetPasswordEmail'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs( orElse: () => const ResetPasswordEmailArgs(), ); - return _i32.ResetPasswordEmail(key: args.key, email: args.email); + return _i33.ResetPasswordEmail(key: args.key, email: args.email); }, ); } @@ -1203,7 +1220,7 @@ class ResetPasswordEmail extends _i43.PageRouteInfo { class ResetPasswordEmailArgs { const ResetPasswordEmailArgs({this.key, this.email}); - final _i44.Key? key; + final _i45.Key? key; final String? email; @@ -1224,61 +1241,61 @@ class ResetPasswordEmailArgs { } /// generated route for -/// [_i33.ServerSelection] -class ServerSelection extends _i43.PageRouteInfo { - const ServerSelection({List<_i43.PageRouteInfo>? children}) +/// [_i34.ServerSelection] +class ServerSelection extends _i44.PageRouteInfo { + const ServerSelection({List<_i44.PageRouteInfo>? children}) : super(ServerSelection.name, initialChildren: children); static const String name = 'ServerSelection'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i33.ServerSelection(); + return const _i34.ServerSelection(); }, ); } /// generated route for -/// [_i34.Setting] -class Setting extends _i43.PageRouteInfo { - const Setting({List<_i43.PageRouteInfo>? children}) +/// [_i35.Setting] +class Setting extends _i44.PageRouteInfo { + const Setting({List<_i44.PageRouteInfo>? children}) : super(Setting.name, initialChildren: children); static const String name = 'Setting'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i34.Setting(); + return const _i35.Setting(); }, ); } /// generated route for -/// [_i35.SignInEmail] -class SignInEmail extends _i43.PageRouteInfo { - const SignInEmail({List<_i43.PageRouteInfo>? children}) +/// [_i36.SignInEmail] +class SignInEmail extends _i44.PageRouteInfo { + const SignInEmail({List<_i44.PageRouteInfo>? children}) : super(SignInEmail.name, initialChildren: children); static const String name = 'SignInEmail'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i35.SignInEmail(); + return const _i36.SignInEmail(); }, ); } /// generated route for -/// [_i36.SignInPassword] -class SignInPassword extends _i43.PageRouteInfo { +/// [_i37.SignInPassword] +class SignInPassword extends _i44.PageRouteInfo { SignInPassword({ - _i44.Key? key, + _i45.Key? key, required String email, bool fromChangeEmail = false, - List<_i43.PageRouteInfo>? children, + List<_i44.PageRouteInfo>? children, }) : super( SignInPassword.name, args: SignInPasswordArgs( @@ -1291,11 +1308,11 @@ class SignInPassword extends _i43.PageRouteInfo { static const String name = 'SignInPassword'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i36.SignInPassword( + return _i37.SignInPassword( key: args.key, email: args.email, fromChangeEmail: args.fromChangeEmail, @@ -1311,7 +1328,7 @@ class SignInPasswordArgs { this.fromChangeEmail = false, }); - final _i44.Key? key; + final _i45.Key? key; final String email; @@ -1336,97 +1353,97 @@ class SignInPasswordArgs { } /// generated route for -/// [_i37.SmartRouting] -class SmartRouting extends _i43.PageRouteInfo { - const SmartRouting({List<_i43.PageRouteInfo>? children}) +/// [_i38.SmartRouting] +class SmartRouting extends _i44.PageRouteInfo { + const SmartRouting({List<_i44.PageRouteInfo>? children}) : super(SmartRouting.name, initialChildren: children); static const String name = 'SmartRouting'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i37.SmartRouting(); + return const _i38.SmartRouting(); }, ); } /// generated route for -/// [_i38.SplitTunneling] -class SplitTunneling extends _i43.PageRouteInfo { - const SplitTunneling({List<_i43.PageRouteInfo>? children}) +/// [_i39.SplitTunneling] +class SplitTunneling extends _i44.PageRouteInfo { + const SplitTunneling({List<_i44.PageRouteInfo>? children}) : super(SplitTunneling.name, initialChildren: children); static const String name = 'SplitTunneling'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i38.SplitTunneling(); + return const _i39.SplitTunneling(); }, ); } /// generated route for -/// [_i39.SplitTunnelingInfo] -class SplitTunnelingInfo extends _i43.PageRouteInfo { - const SplitTunnelingInfo({List<_i43.PageRouteInfo>? children}) +/// [_i40.SplitTunnelingInfo] +class SplitTunnelingInfo extends _i44.PageRouteInfo { + const SplitTunnelingInfo({List<_i44.PageRouteInfo>? children}) : super(SplitTunnelingInfo.name, initialChildren: children); static const String name = 'SplitTunnelingInfo'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i39.SplitTunnelingInfo(); + return const _i40.SplitTunnelingInfo(); }, ); } /// generated route for -/// [_i40.Support] -class Support extends _i43.PageRouteInfo { - const Support({List<_i43.PageRouteInfo>? children}) +/// [_i41.Support] +class Support extends _i44.PageRouteInfo { + const Support({List<_i44.PageRouteInfo>? children}) : super(Support.name, initialChildren: children); static const String name = 'Support'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i40.Support(); + return const _i41.Support(); }, ); } /// generated route for -/// [_i41.VPNSetting] -class VPNSetting extends _i43.PageRouteInfo { - const VPNSetting({List<_i43.PageRouteInfo>? children}) +/// [_i42.VPNSetting] +class VPNSetting extends _i44.PageRouteInfo { + const VPNSetting({List<_i44.PageRouteInfo>? children}) : super(VPNSetting.name, initialChildren: children); static const String name = 'VPNSetting'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i41.VPNSetting(); + return const _i42.VPNSetting(); }, ); } /// generated route for -/// [_i42.WebsiteSplitTunneling] -class WebsiteSplitTunneling extends _i43.PageRouteInfo { - const WebsiteSplitTunneling({List<_i43.PageRouteInfo>? children}) +/// [_i43.WebsiteSplitTunneling] +class WebsiteSplitTunneling extends _i44.PageRouteInfo { + const WebsiteSplitTunneling({List<_i44.PageRouteInfo>? children}) : super(WebsiteSplitTunneling.name, initialChildren: children); static const String name = 'WebsiteSplitTunneling'; - static _i43.PageInfo page = _i43.PageInfo( + static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - return const _i42.WebsiteSplitTunneling(); + return const _i43.WebsiteSplitTunneling(); }, ); } diff --git a/lib/core/services/db/objectbox-model.json b/lib/core/services/db/objectbox-model.json index 9926100149..064ce660ae 100644 --- a/lib/core/services/db/objectbox-model.json +++ b/lib/core/services/db/objectbox-model.json @@ -59,7 +59,7 @@ }, { "id": "2:687217704776011576", - "lastPropertyId": "22:1798613131332202898", + "lastPropertyId": "23:853140105317312758", "name": "AppSetting", "properties": [ { @@ -142,6 +142,11 @@ "id": "22:1798613131332202898", "name": "environment", "type": 9 + }, + { + "id": "23:853140105317312758", + "name": "themeMode", + "type": 9 } ], "relations": [] diff --git a/lib/core/services/db/objectbox.g.dart b/lib/core/services/db/objectbox.g.dart index e86867a9aa..83066d1841 100644 --- a/lib/core/services/db/objectbox.g.dart +++ b/lib/core/services/db/objectbox.g.dart @@ -93,7 +93,7 @@ final _entities = [ obx_int.ModelEntity( id: const obx_int.IdUid(2, 687217704776011576), name: 'AppSetting', - lastPropertyId: const obx_int.IdUid(22, 1798613131332202898), + lastPropertyId: const obx_int.IdUid(23, 853140105317312758), flags: 0, properties: [ obx_int.ModelProperty( @@ -192,6 +192,12 @@ final _entities = [ type: 9, flags: 0, ), + obx_int.ModelProperty( + id: const obx_int.IdUid(23, 853140105317312758), + name: 'themeMode', + type: 9, + flags: 0, + ), ], relations: [], backlinks: [], @@ -1007,7 +1013,8 @@ obx_int.ModelDefinition getObjectBoxModel() { final routingModeRawOffset = fbb.writeString(object.routingModeRaw); final dataCapThresholdOffset = fbb.writeString(object.dataCapThreshold); final environmentOffset = fbb.writeString(object.environment); - fbb.startTable(23); + final themeModeOffset = fbb.writeString(object.themeMode); + fbb.startTable(24); fbb.addInt64(0, object.id); fbb.addBool(1, object.isPro); fbb.addBool(2, object.isSplitTunnelingOn); @@ -1024,6 +1031,7 @@ obx_int.ModelDefinition getObjectBoxModel() { fbb.addOffset(19, dataCapThresholdOffset); fbb.addBool(20, object.onboardingCompleted); fbb.addOffset(21, environmentOffset); + fbb.addOffset(22, themeModeOffset); fbb.finish(fbb.endTable()); return object.id; }, @@ -1105,6 +1113,9 @@ obx_int.ModelDefinition getObjectBoxModel() { 44, false, ); + final themeModeParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGet(buffer, rootOffset, 48, ''); final environmentParam = const fb.StringReader( asciiOptimization: true, ).vTableGet(buffer, rootOffset, 46, ''); @@ -1124,6 +1135,7 @@ obx_int.ModelDefinition getObjectBoxModel() { routingModeRaw: routingModeRawParam, dataCapThreshold: dataCapThresholdParam, onboardingCompleted: onboardingCompletedParam, + themeMode: themeModeParam, environment: environmentParam, ); @@ -2060,6 +2072,11 @@ class AppSetting_ { static final environment = obx.QueryStringProperty( _entities[1].properties[15], ); + + /// See [AppSetting.themeMode]. + static final themeMode = obx.QueryStringProperty( + _entities[1].properties[16], + ); } /// [DeviceEntity] entity fields to define ObjectBox queries. diff --git a/lib/core/services/stripe_service.dart b/lib/core/services/stripe_service.dart index 8bab05fd6d..735d98dbcd 100644 --- a/lib/core/services/stripe_service.dart +++ b/lib/core/services/stripe_service.dart @@ -30,11 +30,29 @@ class StripeService { // It takes the StripeOptions object and a callback function for success and error handling // this is only used by android Future startStripeSDK({ + required BuildContext context, required StripeOptions options, required OnPressed onSuccess, required Function(dynamic error) onError, }) async { try { + // Extract all context-dependent values before any async gap + final brightness = Theme.of(context).brightness; + final style = + brightness == Brightness.dark ? ThemeMode.dark : ThemeMode.light; + final sheetColors = PaymentSheetAppearanceColors( + background: context.bgSurface, + componentBackground: context.bgElevated, + primary: context.actionPrimaryBg, + primaryText: context.textPrimary, + secondaryText: context.textSecondary, + icon: context.textTertiary, + componentBorder: context.borderInput, + componentDivider: context.borderDefault, + componentText: context.textPrimary, + error: AppColors.red4, + placeholderText: context.textDisabled, + ); if (options.publishableKey != null && options.publishableKey!.isNotEmpty) { Stripe.publishableKey = options.publishableKey!; @@ -63,22 +81,10 @@ class StripeService { testEnv: kDebugMode, ), appearance: PaymentSheetAppearance( - colors: PaymentSheetAppearanceColors( - background: AppColors.gray1, - componentBackground: AppColors.white, - primary: AppColors.blue10, - primaryText: AppColors.gray8, - secondaryText: AppColors.black, - icon: AppColors.gray9, - componentBorder: AppColors.gray3, - componentDivider: AppColors.gray2, - componentText: AppColors.gray8, - error: AppColors.red4, - placeholderText: AppColors.gray9, - ), + colors: sheetColors, shapes: PaymentSheetShape(borderRadius: 16), ), - style: ThemeMode.light, + style: style, ), ); diff --git a/lib/core/utils/decoration.dart b/lib/core/utils/decoration.dart index c3e778f5c6..29d1c0d426 100644 --- a/lib/core/utils/decoration.dart +++ b/lib/core/utils/decoration.dart @@ -1,18 +1,3 @@ import 'package:flutter/material.dart'; import 'package:lantern/core/common/app_colors.dart'; -BoxDecoration get selectedDecoration { - return BoxDecoration( - color: AppColors.blue1, - border: Border.all(color: AppColors.blue7, width: 3), - borderRadius: BorderRadius.circular(16), - ); -} - -BoxDecoration get unselectedDecoration { - return BoxDecoration( - color: AppColors.white, - border: Border.all(color: AppColors.gray3, width: 1.5), - borderRadius: BorderRadius.circular(16), - ); -} diff --git a/lib/core/utils/pro_utils.dart b/lib/core/utils/pro_utils.dart index 6116f25bec..b382f3741b 100644 --- a/lib/core/utils/pro_utils.dart +++ b/lib/core/utils/pro_utils.dart @@ -23,7 +23,7 @@ Future showProAccountFlowDialog({ ? 'set_account_password_message'.i18n : 'update_pro_account_message'.i18n, style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ], @@ -31,7 +31,7 @@ Future showProAccountFlowDialog({ action: [ AppTextButton( label: 'cancel'.i18n, - textColor: AppColors.gray6, + textColor: context.textDisabled, onPressed: () => appRouter.maybePop(), ), AppTextButton( diff --git a/lib/core/widgets/app_pin_field.dart b/lib/core/widgets/app_pin_field.dart index 85b1a272c4..0ee735c43b 100644 --- a/lib/core/widgets/app_pin_field.dart +++ b/lib/core/widgets/app_pin_field.dart @@ -21,8 +21,8 @@ class AppPinField extends StatelessWidget { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 5), decoration: BoxDecoration( - color: AppColors.white, - border: Border.all(color: AppColors.gray3), + color: context.bgElevated, + border: Border.all(color: context.borderInput), borderRadius: BorderRadius.circular(16), ), child: Row( @@ -71,14 +71,14 @@ class AppPinField extends StatelessWidget { Container( width: 2, height: 20, - color: AppColors.gray4, + color: context.textDisabled, ), ], ), preFilledWidget: Container( width: 15, height: 1, - color: AppColors.gray4, + color: context.textDisabled, ), defaultPinTheme: PinTheme( width: 20, diff --git a/lib/core/widgets/app_rich_text.dart b/lib/core/widgets/app_rich_text.dart index 3a565b6f0a..7563d9819e 100644 --- a/lib/core/widgets/app_rich_text.dart +++ b/lib/core/widgets/app_rich_text.dart @@ -1,6 +1,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:lantern/core/common/app_buttons.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import '../common/app_colors.dart'; import '../common/app_text_styles.dart'; @@ -29,14 +30,14 @@ class AppRichText extends StatelessWidget { text: TextSpan( text: texts, style: textTheme.labelLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), children: [ TextSpan( text: boldTexts, style: AppTextStyles.labelLargeBold.copyWith( fontWeight: FontWeight.bold, - color: boldColor ?? AppColors.gray8, + color: boldColor ?? context.textSecondary, decoration: boldUnderline ? TextDecoration.underline : TextDecoration.none, diff --git a/lib/core/widgets/app_text.dart b/lib/core/widgets/app_text.dart index e2ed5276a1..06ae3f62e1 100644 --- a/lib/core/widgets/app_text.dart +++ b/lib/core/widgets/app_text.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lantern/core/common/common.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; class HeaderText extends StatelessWidget { final String text; @@ -26,7 +26,9 @@ class HeaderText extends StatelessWidget { final textTheme = Theme.of(context).textTheme; return Text( text, - style: textTheme.labelLarge?.copyWith(color: AppColors.gray8), + style: textTheme.labelLarge?.copyWith( + color: color ?? context.textSecondary, + ), textAlign: textAlign, maxLines: maxLines, overflow: overflow, diff --git a/lib/core/widgets/app_tile.dart b/lib/core/widgets/app_tile.dart index 49fa146f8c..3824514f4d 100644 --- a/lib/core/widgets/app_tile.dart +++ b/lib/core/widgets/app_tile.dart @@ -33,6 +33,7 @@ class AppTile extends StatelessWidget { final BorderRadius? borderRadius; final VisualDensity? visualDensity; final ListTileTitleAlignment? titleAlignment; + final bool? iconUseThemeColor; const AppTile({ super.key, @@ -59,6 +60,7 @@ class AppTile extends StatelessWidget { this.borderRadius, this.visualDensity, this.titleAlignment, + this.iconUseThemeColor, }); factory AppTile.link({ @@ -92,7 +94,9 @@ class AppTile extends StatelessWidget { final textStyle = tileTextStyle ?? theme.textTheme.labelLarge!.copyWith( - color: enabled ? AppColors.gray9 : AppColors.gray6, + color: enabled + ? context.textPrimary // text.primary + : context.textPrimary.withValues(alpha: 0.38), // text.disabled fontWeight: FontWeight.w400, fontSize: 16, ); @@ -103,11 +107,17 @@ class AppTile extends StatelessWidget { computedLeading = SizedBox( width: 24, height: 24, - child: AppImage(path: icon as String), + child: AppImage( + path: icon as String, + useThemeColor: iconUseThemeColor ?? true, + ), ); } else if (icon is IconData) { - computedLeading = - Icon(icon as IconData, size: 24, color: AppColors.gray9); + computedLeading = Icon( + icon as IconData, + size: 24, + color: context.textPrimary, // text.primary + ); } else if (icon is Image) { computedLeading = icon as Image; } else if (icon is Widget) { @@ -128,8 +138,10 @@ class AppTile extends StatelessWidget { selected: selected, titleAlignment: titleAlignment ?? ListTileTitleAlignment.center, enableFeedback: true, - hoverColor: hoverColor ?? AppColors.blue1, - selectedTileColor: selectedTileColor ?? AppColors.blue1, + // hoverColor: hoverColor ?? AppColors.blue1, + hoverColor: hoverColor ?? theme.hoverColor, + // selectedTileColor: selectedTileColor ?? AppColors.blue1, + selectedTileColor: selectedTileColor ?? theme.hoverColor, tileColor: tileColor, minTileHeight: minHeight ?? effectiveMinHeight, shape: RoundedRectangleBorder( diff --git a/lib/core/widgets/app_webview.dart b/lib/core/widgets/app_webview.dart index 1b41bc2d0f..919e358db2 100644 --- a/lib/core/widgets/app_webview.dart +++ b/lib/core/widgets/app_webview.dart @@ -33,8 +33,8 @@ class AppWebView extends HookConsumerWidget { title: Text(title), centerTitle: true, leading: SizedBox(), - backgroundColor: AppColors.white, - iconTheme: IconThemeData(color: AppColors.black), + backgroundColor: context.bgElevated, + iconTheme: IconThemeData(color: context.textPrimary), actions: [ IconButton( icon: const Icon(Icons.close), diff --git a/lib/core/widgets/base_screen.dart b/lib/core/widgets/base_screen.dart index 753c576db2..23df336502 100644 --- a/lib/core/widgets/base_screen.dart +++ b/lib/core/widgets/base_screen.dart @@ -27,7 +27,8 @@ class BaseScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: backgroundColor ?? AppColors.gray1, + backgroundColor: backgroundColor ?? Theme.of(context).scaffoldBackgroundColor, + // backgroundColor: backgroundColor ?? AppColors.gray1, appBar: appBar ?? CustomAppBar( title: Text(title), diff --git a/lib/core/widgets/bottomsheet.dart b/lib/core/widgets/bottomsheet.dart index 2c4b19d51e..48f83d020f 100644 --- a/lib/core/widgets/bottomsheet.dart +++ b/lib/core/widgets/bottomsheet.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lantern/core/common/app_colors.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import 'package:lantern/core/widgets/divider_space.dart'; typedef BottomSheetBuilder = Function( @@ -10,13 +10,12 @@ void showAppBottomSheet( required BottomSheetBuilder builder, required String title, double scrollControlDisabledMaxHeightRatio = .75}) { - final textTheme = Theme.of(context).textTheme.headlineSmall; + // backgroundColor and shape come from bottomSheetTheme in app_theme.dart showModalBottomSheet( context: context, isDismissible: true, enableDrag: true, showDragHandle: true, - backgroundColor: AppColors.white, scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio, builder: (context) { return DraggableScrollableSheet( @@ -32,14 +31,12 @@ void showAppBottomSheet( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( title, - style: textTheme!.copyWith( - color: AppColors.blue10, + style: Theme.of(context).textTheme.headlineSmall!.copyWith( + color: context.textPrimary, ), ), ), - DividerSpace( - padding: EdgeInsets.only(top: 16), - ), + DividerSpace(padding: EdgeInsets.only(top: 16)), builder(context, scrollController), ], ); diff --git a/lib/core/widgets/card_dropdown.dart b/lib/core/widgets/card_dropdown.dart index 8245fb44d0..9f20cb69d6 100644 --- a/lib/core/widgets/card_dropdown.dart +++ b/lib/core/widgets/card_dropdown.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lantern/core/common/app_asset.dart'; import 'package:lantern/core/common/app_colors.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import 'package:lantern/core/common/app_text_styles.dart'; class CardDropdown extends StatelessWidget { @@ -23,18 +24,18 @@ class CardDropdown extends StatelessWidget { this.enabled = true, }); - Widget? _buildPrefix(Object? iconPath) { + Widget? _buildPrefix(Object? iconPath, BuildContext context) { if (iconPath == null) return null; const pad = EdgeInsets.only(left: 16, right: 16); if (iconPath is IconData) { return Padding( padding: pad, - child: Icon(iconPath, color: AppColors.yellow9), + child: Icon(iconPath, color: context.textPromoIcon), ); } else if (iconPath is String) { return Padding( padding: pad, - child: AppImage(path: iconPath, color: AppColors.yellow9), + child: AppImage(path: iconPath, color: context.textPromoIcon), ); } else if (iconPath is Widget) { return Padding(padding: pad, child: iconPath); @@ -50,20 +51,20 @@ class CardDropdown extends StatelessWidget { onChanged: enabled ? onChanged : null, validator: validator, decoration: InputDecoration( - prefixIcon: _buildPrefix(prefixIcon), + prefixIcon: _buildPrefix(prefixIcon, context), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), filled: true, - fillColor: enabled ? AppColors.white : AppColors.gray3.withOpacity(0.3), + fillColor: enabled ? context.bgElevated : context.borderInput.withOpacity(0.3), hintText: hintText, - hintStyle: AppTextStyles.bodyMedium.copyWith(color: AppColors.gray4), + hintStyle: AppTextStyles.bodyMedium.copyWith(color: context.textDisabled), border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), - borderSide: BorderSide(color: AppColors.gray3, width: 1), + borderSide: BorderSide(color: context.borderInput, width: 1), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), - borderSide: BorderSide(color: AppColors.gray3, width: 1), + borderSide: BorderSide(color: context.borderInput, width: 1), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), @@ -71,16 +72,16 @@ class CardDropdown extends StatelessWidget { ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), - borderSide: BorderSide(color: AppColors.red5, width: 1.2), + borderSide: BorderSide(color: context.statusErrorBorder, width: 1.2), ), disabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: - BorderSide(color: AppColors.gray3.withOpacity(0.5), width: 1), + BorderSide(color: context.borderInput.withOpacity(0.5), width: 1), ), ), style: AppTextStyles.bodyMedium.copyWith( - color: enabled ? AppColors.black1 : AppColors.gray4, + color: enabled ? context.textPrimary : context.textDisabled, ), icon: const Icon(Icons.keyboard_arrow_down_rounded, color: null), // uses default IconTheme diff --git a/lib/core/widgets/divider_space.dart b/lib/core/widgets/divider_space.dart index 1a8572c181..b38ebb3787 100644 --- a/lib/core/widgets/divider_space.dart +++ b/lib/core/widgets/divider_space.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import '../common/app_colors.dart'; - class DividerSpace extends StatelessWidget { final EdgeInsetsGeometry padding; @@ -15,7 +13,9 @@ class DividerSpace extends StatelessWidget { return Padding( padding: padding, child: Divider( - color: AppColors.gray2, + // border.default: Gray.200 light / Gray.800 dark + // color: AppColors.gray2, + color: Theme.of(context).dividerTheme.color, height: 1, ), ); diff --git a/lib/core/widgets/email_tag.dart b/lib/core/widgets/email_tag.dart index 09526deb27..d62117e941 100644 --- a/lib/core/widgets/email_tag.dart +++ b/lib/core/widgets/email_tag.dart @@ -14,7 +14,7 @@ class EmailTag extends StatelessWidget { @override Widget build(BuildContext context) { return Chip( - color: WidgetStatePropertyAll(AppColors.blue1), + color: WidgetStatePropertyAll(context.bgCallout), elevation: 0, avatar: AppImage( path: AppImagePaths.email, @@ -27,7 +27,7 @@ class EmailTag extends StatelessWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100), side: BorderSide( - color: AppColors.gray3, + color: context.borderInput, width: 1, ), ), diff --git a/lib/core/widgets/flag.dart b/lib/core/widgets/flag.dart index be03441105..c096b766d7 100644 --- a/lib/core/widgets/flag.dart +++ b/lib/core/widgets/flag.dart @@ -1,7 +1,6 @@ import 'package:country_flags/country_flags.dart'; import 'package:flutter/material.dart'; - -import '../common/app_colors.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; class Flag extends StatelessWidget { final String countryCode; @@ -14,7 +13,7 @@ class Flag extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - border: Border.all(color: AppColors.gray3, width: .5), + border: Border.all(color: context.borderInput, width: .5), ), child: SizedBox.fromSize( size: size, diff --git a/lib/core/widgets/info_row.dart b/lib/core/widgets/info_row.dart index c77fb407bb..8d3158ce2f 100644 --- a/lib/core/widgets/info_row.dart +++ b/lib/core/widgets/info_row.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lantern/core/common/app_asset.dart'; -import 'package:lantern/core/common/app_colors.dart'; import 'package:lantern/core/common/app_image_paths.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; class InfoRow extends StatelessWidget { final Widget? icon; @@ -38,13 +38,13 @@ class InfoRow extends StatelessWidget { final textTheme = Theme.of(context).textTheme; return ListTile( minTileHeight: minTileHeight, - tileColor: backgroundColor, + tileColor: backgroundColor?? context.bgElevated, onTap: onPressed, contentPadding: padding ?? EdgeInsets.symmetric(horizontal: 16, vertical: 4), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadius), - side: BorderSide(color: borderColor ?? AppColors.gray2), + side: BorderSide(color: borderColor ?? context.borderDefault), ), leading: showLeadingIcon ? AppImage( @@ -56,7 +56,7 @@ class InfoRow extends StatelessWidget { text, style: textStyle ?? (textTheme.bodyMedium)!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), )); } diff --git a/lib/core/widgets/labeled_card_dropdown.dart b/lib/core/widgets/labeled_card_dropdown.dart index 255e22e665..a6e0119621 100644 --- a/lib/core/widgets/labeled_card_dropdown.dart +++ b/lib/core/widgets/labeled_card_dropdown.dart @@ -28,7 +28,7 @@ class LabeledCardDropdownWithFlag extends StatelessWidget { decoration: BoxDecoration( color: finalizeCardColor, borderRadius: BorderRadius.circular(16), - border: Border.all(color: AppColors.gray2, width: 1), + border: Border.all(color: context.borderDefault, width: 1), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -46,7 +46,7 @@ class LabeledCardDropdownWithFlag extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 4), decoration: BoxDecoration( border: Border.all( - color: AppColors.gray2, + color: context.borderDefault, width: 1, ), borderRadius: BorderRadius.circular(12), @@ -68,7 +68,7 @@ class LabeledCardDropdownWithFlag extends StatelessWidget { Text( countryLabelKey.i18n, style: AppTextStyles.bodyLarge.copyWith( - color: AppColors.black1, + color: context.textPrimary, height: 1.62, ), ), diff --git a/lib/core/widgets/labeled_card_input.dart b/lib/core/widgets/labeled_card_input.dart index bd6002cf68..93b580bd1b 100644 --- a/lib/core/widgets/labeled_card_input.dart +++ b/lib/core/widgets/labeled_card_input.dart @@ -39,7 +39,7 @@ class LabeledCardInput extends StatelessWidget { color: cardColor, borderRadius: BorderRadius.circular(16), border: Border.all( - color: AppColors.gray2, + color: context.borderDefault, width: 1, ), ), diff --git a/lib/core/widgets/lantern_logo.dart b/lib/core/widgets/lantern_logo.dart index 94b41f601a..9269520d1c 100644 --- a/lib/core/widgets/lantern_logo.dart +++ b/lib/core/widgets/lantern_logo.dart @@ -34,6 +34,7 @@ class LanternLogo extends StatelessWidget { height: height, width: width, fit: BoxFit.contain, + ); } } diff --git a/lib/core/widgets/oauth_login.dart b/lib/core/widgets/oauth_login.dart index 4a720b5c9b..14bf4f7374 100644 --- a/lib/core/widgets/oauth_login.dart +++ b/lib/core/widgets/oauth_login.dart @@ -29,6 +29,7 @@ class OAuthLogin extends HookConsumerWidget { label: 'continue_with_apple'.i18n, icon: AppImagePaths.apple, isTaller: true, + iconColor: context.textPrimary, onPressed: () => _handleSignIn(SignUpMethodType.apple, ref, context), ); } @@ -36,6 +37,7 @@ class OAuthLogin extends HookConsumerWidget { label: 'continue_with_google'.i18n, icon: AppImagePaths.google, isTaller: true, + useThemeColor: false, onPressed: () => _handleSignIn(SignUpMethodType.google, ref, context), ); } diff --git a/lib/core/widgets/password_criteria.dart b/lib/core/widgets/password_criteria.dart index 514f5fb531..4ffb98b726 100644 --- a/lib/core/widgets/password_criteria.dart +++ b/lib/core/widgets/password_criteria.dart @@ -50,10 +50,10 @@ class _PasswordCriteriaWidgetState extends State { return Container( padding: const EdgeInsets.all(12.0), decoration: BoxDecoration( - color: AppColors.white, + color: context.bgElevated, borderRadius: BorderRadius.circular(16.0), border: Border.all( - color: AppColors.gray3, + color: context.borderInput, width: .5, ), ), @@ -84,7 +84,7 @@ class _PasswordCriteriaWidgetState extends State { children: [ Icon( metCriteria ? Icons.check_circle : Icons.radio_button_unchecked, - color: metCriteria ? AppColors.green6 : Colors.grey, + color: metCriteria ? context.statusSuccessBorder : Colors.grey, size: 18, ), const SizedBox(width: 8), @@ -92,7 +92,7 @@ class _PasswordCriteriaWidgetState extends State { criteria, style: textTheme!.labelMedium!.copyWith( fontSize: 14, - color: AppColors.gray9, + color: context.textPrimary, ), ), ], diff --git a/lib/core/widgets/platform_card.dart b/lib/core/widgets/platform_card.dart index 8dba25820e..695889fd40 100644 --- a/lib/core/widgets/platform_card.dart +++ b/lib/core/widgets/platform_card.dart @@ -17,10 +17,11 @@ class PlatformCard extends StatelessWidget { onPressed: onPressed, style: IconButton.styleFrom( padding: EdgeInsets.all(15.r), - backgroundColor: AppColors.white, + backgroundColor: context.bgElevated, + hoverColor: context.bgHover, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), - side: BorderSide(color: AppColors.gray3, width: .5), + side: BorderSide(color: context.borderInput, width: .5), )), icon: AppImage( fit: BoxFit.contain, diff --git a/lib/core/widgets/pro_banner.dart b/lib/core/widgets/pro_banner.dart index 3ce6a2eea2..24c572f31a 100644 --- a/lib/core/widgets/pro_banner.dart +++ b/lib/core/widgets/pro_banner.dart @@ -23,9 +23,9 @@ class ProBanner extends HookConsumerWidget { margin: EdgeInsets.only(top: topMargin), padding: EdgeInsets.all(defaultSize), decoration: BoxDecoration( - color: AppColors.yellow1, + color: context.bgPromo, borderRadius: BorderRadius.circular(16), - border: Border.all(color: AppColors.yellow4, width: 1)), + border: Border.all(color: context.borderPromo, width: 1)), child: Column( children: [ AutoSizeText( @@ -37,7 +37,7 @@ class ProBanner extends HookConsumerWidget { maxFontSize: 16, overflow: TextOverflow.ellipsis, style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray9, + color: context.textPrimary, fontSize: 16, ), ), diff --git a/lib/core/widgets/pro_button.dart b/lib/core/widgets/pro_button.dart index b07b6047b5..2f45ae8e8f 100644 --- a/lib/core/widgets/pro_button.dart +++ b/lib/core/widgets/pro_button.dart @@ -18,6 +18,7 @@ class ProButton extends StatelessWidget { icon: AppImagePaths.crown, expanded: true, isTaller: true, + useThemeColor: false, onPressed: onPressed, ); } diff --git a/lib/core/widgets/search_bar.dart b/lib/core/widgets/search_bar.dart index 021a1a65e4..fb1b09886f 100644 --- a/lib/core/widgets/search_bar.dart +++ b/lib/core/widgets/search_bar.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:lantern/core/common/app_asset.dart'; -import 'package:lantern/core/common/app_colors.dart'; import 'package:lantern/core/common/app_image_paths.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import 'package:lantern/features/split_tunneling/provider/search_query.dart'; class AppSearchBar extends AppBar { @@ -56,26 +56,29 @@ class _SearchBarContent extends HookConsumerWidget { // Search input or title Expanded( child: isSearching.value - ? TextField( - controller: controller, - autofocus: true, - onChanged: (value) => - ref.read(searchQueryProvider.notifier).setQuery(value), - decoration: InputDecoration( - hintText: hintText, - hintStyle: TextStyle( - color: AppColors.gray7, + ? Theme( + data: ThemeData(), + child: TextField( + controller: controller, + autofocus: true, + onChanged: (value) => + ref.read(searchQueryProvider.notifier).setQuery(value), + decoration: InputDecoration( + hintText: hintText, + hintStyle: TextStyle( + color: context.textSecondary, + fontSize: 16, + fontWeight: FontWeight.w400, + ), + border: InputBorder.none, + isDense: true, + contentPadding: const EdgeInsets.symmetric(vertical: 8), + ), + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w400, + color: context.textPrimary, ), - border: InputBorder.none, - isDense: true, - contentPadding: const EdgeInsets.symmetric(vertical: 8), - ), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: AppColors.gray9, ), ) : Text( @@ -83,7 +86,7 @@ class _SearchBarContent extends HookConsumerWidget { style: TextStyle( fontSize: 24, fontWeight: FontWeight.w600, - color: AppColors.gray9, + color: context.textPrimary, ), ), ), diff --git a/lib/core/widgets/section_label.dart b/lib/core/widgets/section_label.dart index c8f76fa91e..e794553525 100644 --- a/lib/core/widgets/section_label.dart +++ b/lib/core/widgets/section_label.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:lantern/core/common/app_colors.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; class SectionLabel extends StatelessWidget { final String title; @@ -13,7 +13,7 @@ class SectionLabel extends StatelessWidget { child: Text( title, style: Theme.of(context).textTheme.labelLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, fontSize: 14, fontWeight: FontWeight.w500, ), diff --git a/lib/core/widgets/setting_tile.dart b/lib/core/widgets/setting_tile.dart index bf158e6289..31900b4553 100644 --- a/lib/core/widgets/setting_tile.dart +++ b/lib/core/widgets/setting_tile.dart @@ -1,7 +1,9 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; -import '../common/common.dart'; +import '../common/app_asset.dart'; +import '../common/app_semantic_colors.dart'; +import '../extensions/string.dart'; class SettingTile extends StatelessWidget { final String label; @@ -29,7 +31,7 @@ class SettingTile extends StatelessWidget { return InkWell( borderRadius: BorderRadius.circular(16), onTap: onTap, - splashColor: AppColors.gray2, + splashColor: context.bgCallout, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), child: Column( @@ -43,9 +45,11 @@ class SettingTile extends StatelessWidget { child: icon is String ? AppImage(path: icon) : icon as Widget, ), SizedBox(width: 8), - Text(label, - style: - textTheme.labelLarge!.copyWith(color: AppColors.gray7)), + Text( + label, + style: textTheme.labelLarge! + .copyWith(color: context.textSecondary), + ), ], ), Row( @@ -55,12 +59,14 @@ class SettingTile extends StatelessWidget { Expanded(child: child!) else Expanded( - child: AutoSizeText(value, - maxLines: 1, - maxFontSize: 16, - minFontSize: 14, - style: textTheme.titleMedium! - .copyWith(color: AppColors.gray9)), + child: AutoSizeText( + value, + maxLines: 1, + maxFontSize: 16, + minFontSize: 14, + style: textTheme.titleMedium! + .copyWith(color: context.textPrimary), + ), ), ...actions ], @@ -68,9 +74,11 @@ class SettingTile extends StatelessWidget { if (subtitle != null && subtitle!.isNotEmpty) Padding( padding: const EdgeInsets.only(left: 32.0), - child: Text(subtitle!.capitalize, - style: - textTheme.labelLarge!.copyWith(color: AppColors.gray7)), + child: Text( + subtitle!.capitalize, + style: textTheme.labelLarge! + .copyWith(color: context.textSecondary), + ), ), ], ), diff --git a/lib/core/widgets/split_tunneling_tile.dart b/lib/core/widgets/split_tunneling_tile.dart index c4ebca827e..a57f0be084 100644 --- a/lib/core/widgets/split_tunneling_tile.dart +++ b/lib/core/widgets/split_tunneling_tile.dart @@ -27,7 +27,7 @@ class SplitTunnelingTile extends StatelessWidget { ? Text( subtitle!, style: AppTextStyles.labelMedium.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ) : null, diff --git a/lib/core/widgets/subscription_tags.dart b/lib/core/widgets/subscription_tags.dart index ae19beaf98..0f59e1222a 100644 --- a/lib/core/widgets/subscription_tags.dart +++ b/lib/core/widgets/subscription_tags.dart @@ -25,15 +25,15 @@ class SubscriptionTags extends StatelessWidget { vertical: 4, ), decoration: BoxDecoration( - color: isExpired ? AppColors.red2 : AppColors.green2, + color: isExpired ? context.statusErrorBg : context.statusSuccessBg, border: Border.all( - color: isExpired ? AppColors.red4 : AppColors.green4, + color: isExpired ? context.statusErrorBorder : context.statusSuccessBorder, ), borderRadius: BorderRadius.circular(8)), child: Text( isExpired ? 'subscription_expired'.i18n : 'pro'.i18n, style: textTheme.labelMedium!.copyWith( - color: isExpired ? AppColors.red8 : AppColors.green8, + color: isExpired ? context.statusErrorText : context.statusSuccessText, ), ), ); diff --git a/lib/core/widgets/switch_button.dart b/lib/core/widgets/switch_button.dart index d391f111d3..56dc6a0771 100644 --- a/lib/core/widgets/switch_button.dart +++ b/lib/core/widgets/switch_button.dart @@ -30,26 +30,34 @@ class SwitchButton extends StatelessWidget { spacing: 10.h, height: PlatformUtils.isDesktop ? 40.h : 30.h, wrapperBuilder: (context, global, child) { + final isDark = Theme.of(context).brightness == Brightness.dark; return Container( width: trackWidth, padding: const EdgeInsets.symmetric(horizontal: 5), decoration: BoxDecoration( - color: value ? (activeColor ?? AppColors.green5) : AppColors.gray7, + // toggle-active-bg: Green.500 light / Green.700 dark + // toggle-disabled-bg (off state): Gray.700 both + color: value + ? (activeColor ?? + (isDark ? AppColors.green7 : AppColors.green5)) + : AppColors.gray7, borderRadius: BorderRadius.circular(100), ), child: child, ); }, foregroundIndicatorBuilder: (context, global) { + final isDark = Theme.of(context).brightness == Brightness.dark; return GestureDetector( onTap: () { onChanged(value ? false : true); }, child: Container( - decoration: const BoxDecoration( - color: Colors.white, + decoration: BoxDecoration( + // toggle-knob-bg: White light / Gray.100 dark + color: isDark ? AppColors.gray1 : Colors.white, shape: BoxShape.circle, - boxShadow: [ + boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 4, diff --git a/lib/core/widgets/vpn_status_indicator.dart b/lib/core/widgets/vpn_status_indicator.dart index 134a7c42d1..69647027a5 100644 --- a/lib/core/widgets/vpn_status_indicator.dart +++ b/lib/core/widgets/vpn_status_indicator.dart @@ -28,6 +28,7 @@ class VPNStatusIndicator extends StatelessWidget { return AppImage( path: indicator, + useThemeColor: false, color: status == VPNStatus.disconnected ? AppColors.gray3 : null, ); } diff --git a/lib/features/account/account.dart b/lib/features/account/account.dart index d38bbefe0b..03b04e139c 100644 --- a/lib/features/account/account.dart +++ b/lib/features/account/account.dart @@ -32,132 +32,134 @@ class Account extends HookConsumerWidget { final appSettings = ref.watch(appSettingProvider); final theme = Theme.of(buildContext).textTheme; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (isExpired) ...{ - InfoRow( - minTileHeight: 40, - backgroundColor: AppColors.red1, - borderColor: AppColors.red2, - textStyle: theme.labelLarge!.copyWith( - color: AppColors.red9, + return SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (isExpired) ...{ + InfoRow( + minTileHeight: 40, + backgroundColor: AppColors.red1, + borderColor: AppColors.red2, + textStyle: theme.labelLarge!.copyWith( + color: buildContext.statusErrorBg, + ), + text: 'pro_subscription_expired_message'.i18n, ), - text: 'pro_subscription_expired_message'.i18n, - ), + SizedBox(height: defaultSize), + ProButton( + label: 'renew_pro_subscription'.i18n, + onPressed: () { + appRouter.push(const Plans()); + }, + ), + }, SizedBox(height: defaultSize), - ProButton( - label: 'renew_pro_subscription'.i18n, - onPressed: () { - appRouter.push(const Plans()); - }, + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + 'lantern_pro_email'.i18n, + style: theme.labelLarge!.copyWith( + color: buildContext.textSecondary, + ), + ), ), - }, - SizedBox(height: defaultSize), - Padding( - padding: const EdgeInsets.only(left: 16), - child: Text( - 'lantern_pro_email'.i18n, - style: theme.labelLarge!.copyWith( - color: AppColors.gray8, + AppCard( + padding: EdgeInsets.zero, + child: AppTile( + label: appSettings.email.toLowerCase(), + icon: AppImagePaths.email, + contentPadding: EdgeInsets.only(left: 16), + onPressed: kDebugMode + ? () { + copyToClipboard(appSettings.email); + } + : null, + trailing: AppTextButton( + label: 'change_email'.i18n, + onPressed: () { + appRouter.push(SignInPassword( + email: appSettings.email, fromChangeEmail: true)); + }, + ), ), ), - ), - AppCard( - padding: EdgeInsets.zero, - child: AppTile( - label: appSettings.email.toLowerCase(), - icon: AppImagePaths.email, - contentPadding: EdgeInsets.only(left: 16), - onPressed: kDebugMode - ? () { - copyToClipboard(appSettings.email); - } - : null, - trailing: AppTextButton( - label: 'change_email'.i18n, - onPressed: () { - appRouter.push(SignInPassword( - email: appSettings.email, fromChangeEmail: true)); - }, + SizedBox(height: defaultSize), + if (isExpired) + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + 'last_subscription_renewal_date'.i18n, + style: theme.labelLarge!.copyWith( + color: buildContext.textSecondary, + ), + ), + ) + else + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + user!.legacyUserData.subscriptionData.autoRenew + ? 'subscription_renewal_date'.i18n + : 'pro_account_expiration'.i18n, + style: theme.labelLarge!.copyWith( + color: buildContext.textSecondary, + ), + ), ), + AppCard( + padding: EdgeInsets.zero, + child: AppTile( + label: user!.legacyUserData.toDate(), + contentPadding: EdgeInsets.only(left: 16), + icon: AppImagePaths.autoRenew, + trailing: planTrailingWidget(user, buildContext, ref)), ), - ), - SizedBox(height: defaultSize), - if (isExpired) + SizedBox(height: defaultSize), Padding( padding: const EdgeInsets.only(left: 16), child: Text( - 'last_subscription_renewal_date'.i18n, + 'lantern_pro_devices'.i18n, style: theme.labelLarge!.copyWith( - color: AppColors.gray8, + color: buildContext.textSecondary, ), ), - ) - else + ), + UserDevices(), + SizedBox(height: defaultSize), + if (appSettings.userLoggedIn) + AppCard( + padding: EdgeInsets.zero, + child: AppTile( + label: 'logout'.i18n, + icon: AppImagePaths.signIn, + onPressed: () => logoutDialog(buildContext, ref)), + ), + Spacer(), Padding( padding: const EdgeInsets.only(left: 16), child: Text( - user!.legacyUserData.subscriptionData.autoRenew - ? 'subscription_renewal_date'.i18n - : 'pro_account_expiration'.i18n, + 'danger_zone'.i18n, style: theme.labelLarge!.copyWith( - color: AppColors.gray8, + color: buildContext.textSecondary, ), ), ), - AppCard( - padding: EdgeInsets.zero, - child: AppTile( - label: user!.legacyUserData.toDate(), - contentPadding: EdgeInsets.only(left: 16), - icon: AppImagePaths.autoRenew, - trailing: planTrailingWidget(user, buildContext, ref)), - ), - SizedBox(height: defaultSize), - Padding( - padding: const EdgeInsets.only(left: 16), - child: Text( - 'lantern_pro_devices'.i18n, - style: theme.labelLarge!.copyWith( - color: AppColors.gray8, - ), - ), - ), - UserDevices(), - SizedBox(height: defaultSize), - if (appSettings.userLoggedIn) - AppCard( - padding: EdgeInsets.zero, + Card( child: AppTile( - label: 'logout'.i18n, - icon: AppImagePaths.signIn, - onPressed: () => logoutDialog(buildContext, ref)), - ), - Spacer(), - Padding( - padding: const EdgeInsets.only(left: 16), - child: Text( - 'danger_zone'.i18n, - style: theme.labelLarge!.copyWith( - color: AppColors.gray8, - ), - ), - ), - Card( - child: AppTile( - contentPadding: EdgeInsets.only(left: 16), - icon: AppImagePaths.delete, - label: 'delete_account'.i18n, - trailing: AppTextButton( - label: 'delete'.i18n, - textColor: AppColors.red7, - onPressed: _onDeleteTap, + contentPadding: EdgeInsets.only(left: 16), + icon: AppImagePaths.delete, + label: 'delete_account'.i18n, + trailing: AppTextButton( + label: 'delete'.i18n, + textColor: buildContext.statusErrorText, + onPressed: _onDeleteTap, + ), ), ), - ), - SizedBox(height: defaultSize), - ], + SizedBox(height: size24), + ], + ), ); } @@ -337,7 +339,7 @@ class Account extends HookConsumerWidget { action: [ AppTextButton( label: 'not_now'.i18n, - textColor: AppColors.gray8, + textColor: context.textSecondary, onPressed: () { appRouter.pop(); }, @@ -362,7 +364,7 @@ class Account extends HookConsumerWidget { Text( isExpired ? 'logout_message_expired'.i18n : 'logout_message'.i18n, style: theme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ], diff --git a/lib/features/account/delete_account.dart b/lib/features/account/delete_account.dart index cc7978bd4e..7d245662a6 100644 --- a/lib/features/account/delete_account.dart +++ b/lib/features/account/delete_account.dart @@ -48,7 +48,7 @@ class _DeleteAccountState extends ConsumerState { child: Text( 'delete_account_message'.i18n, style: textTheme.bodyLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -58,7 +58,7 @@ class _DeleteAccountState extends ConsumerState { child: Text( 'delete_account_message_two'.i18n, style: textTheme.bodyLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), diff --git a/lib/features/auth/add_email.dart b/lib/features/auth/add_email.dart index 6f157003f3..22288b2adc 100644 --- a/lib/features/auth/add_email.dart +++ b/lib/features/auth/add_email.dart @@ -91,7 +91,7 @@ class _AddEmailState extends ConsumerState { padding: EdgeInsets.symmetric(horizontal: defaultSize), child: Text('lantern_pro_license_applied'.i18n, style: textTheme!.bodyMedium! - .copyWith(color: AppColors.gray6, fontSize: 12)), + .copyWith(color: context.textDisabled, fontSize: 12)), ), SizedBox(height: defaultSize), }, @@ -100,7 +100,7 @@ class _AddEmailState extends ConsumerState { padding: EdgeInsets.symmetric(horizontal: defaultSize), child: Text('change_email_message'.i18n, style: textTheme!.bodyMedium!.copyWith( - color: AppColors.gray6, + color: context.textDisabled, )), ) else @@ -108,7 +108,7 @@ class _AddEmailState extends ConsumerState { padding: EdgeInsets.symmetric(horizontal: defaultSize), child: Text('add_your_email_message'.i18n, style: textTheme!.bodyMedium!.copyWith( - color: AppColors.gray6, + color: context.textDisabled, )), ), SizedBox(height: 32), @@ -451,18 +451,18 @@ class _AddEmailState extends ConsumerState { Text( 'email_deliverability_notice'.i18n, style: textTheme!.headlineSmall!.copyWith( - color: AppColors.gray9, + color: context.textPrimary, ), ), SizedBox(height: defaultSize), Text( 'email_deliverability_notice_message'.i18n, - style: textTheme!.bodyMedium!.copyWith(color: AppColors.gray9), + style: textTheme!.bodyMedium!.copyWith(color: context.textPrimary), ), SizedBox(height: defaultSize), Text( 'email_deliverability_notice_message_two'.i18n, - style: textTheme!.bodyMedium!.copyWith(color: AppColors.gray9), + style: textTheme!.bodyMedium!.copyWith(color: context.textPrimary), ), ], ), diff --git a/lib/features/auth/choose_payment_method.dart b/lib/features/auth/choose_payment_method.dart index 090da39e44..cdc9398f18 100644 --- a/lib/features/auth/choose_payment_method.dart +++ b/lib/features/auth/choose_payment_method.dart @@ -33,10 +33,7 @@ class ChoosePaymentMethod extends HookConsumerWidget { final planData = ref.watch(plansProvider.notifier).getPlanData(); return BaseScreen( title: '', - appBar: CustomAppBar( - title: Text('choose_payment_method'.i18n), - - ), + appBar: CustomAppBar(title: Text('choose_payment_method'.i18n)), body: Column( children: [ SizedBox(height: defaultSize), @@ -109,7 +106,7 @@ class ChoosePaymentMethod extends HookConsumerWidget { action: [ AppTextButton( label: 'cancel'.i18n, - textColor: AppColors.gray8, + textColor: context.textSecondary, underLine: false, onPressed: () { appRouter.pop(); @@ -169,6 +166,7 @@ class ChoosePaymentMethod extends HookConsumerWidget { /// Start stripe SDK sl().startStripeSDK( + context: context, options: StripeOptions.fromJson(stripeData), onSuccess: () { onPurchaseResult(true, context, ref); @@ -334,19 +332,19 @@ class PaymentCheckoutMethods extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 16), child: ExpansionTile( initiallyExpanded: index == 0, - backgroundColor: AppColors.white, - collapsedBackgroundColor: AppColors.white, + backgroundColor: context.bgElevated, + collapsedBackgroundColor: context.bgElevated, collapsedShape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide( - color: AppColors.gray3, + color: context.borderInput, width: 1, ), ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide( - color: AppColors.gray3, + color: context.borderInput, width: 1, ), ), @@ -372,7 +370,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { Text( '${userPlan.formattedMonthlyPrice}/month', style: theme.bodyMedium!.copyWith( - color: AppColors.gray6, + color: context.textDisabled, ), ), ], @@ -390,7 +388,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { Text( 'free'.i18n, style: theme.bodyMedium!.copyWith( - color: AppColors.gray6, + color: context.textDisabled, ), ), ], @@ -402,12 +400,12 @@ class PaymentCheckoutMethods extends HookConsumerWidget { children: [ Text('Order Total:', style: theme.titleSmall!.copyWith( - color: AppColors.gray9, + color: context.textPrimary, )), Text( userPlan.formattedYearlyPrice, style: theme.titleSmall!.copyWith( - color: AppColors.blue10, + color: context.textPrimary, ), ), ], @@ -419,7 +417,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { ? "Billed every ${userPlan.getDurationText()}. Cancel anytime." : 'billed_once'.i18n.capitalize, style: theme.bodySmall!.copyWith( - color: AppColors.gray6, + color: context.textDisabled, ), ), SizedBox(height: defaultSize), diff --git a/lib/features/auth/confirm_email.dart b/lib/features/auth/confirm_email.dart index 6e46abe55e..5f1fc15b39 100644 --- a/lib/features/auth/confirm_email.dart +++ b/lib/features/auth/confirm_email.dart @@ -54,7 +54,7 @@ class ConfirmEmail extends HookConsumerWidget { child: Text( 'confirm_email_code'.i18n, style: textTheme.labelLarge?.copyWith( - color: AppColors.gray8, + color: context.textSecondary, fontSize: 14.sp, ), ), @@ -90,7 +90,7 @@ class ConfirmEmail extends HookConsumerWidget { Center( child: AppTextButton( label: 'resend_email'.i18n, - textColor: AppColors.black, + textColor: context.textPrimary, onPressed: () => onResendEmail(context, ref), ), ) diff --git a/lib/features/auth/create_password.dart b/lib/features/auth/create_password.dart index 289c2a2db6..413af39310 100644 --- a/lib/features/auth/create_password.dart +++ b/lib/features/auth/create_password.dart @@ -45,7 +45,7 @@ class CreatePassword extends HookConsumerWidget { hintText: '', prefixIcon: AppImagePaths.lock, label: "create_password".i18n, - suffixIcon: _buildSuffix(obscureText), + suffixIcon: _buildSuffix(obscureText, context), obscureText: obscureText.value, onSubmitted: (_) { if (passwordTextController.text.isPasswordValid()) { @@ -74,9 +74,9 @@ class CreatePassword extends HookConsumerWidget { ); } - Widget _buildSuffix(ValueNotifier obscureText) { + Widget _buildSuffix(ValueNotifier obscureText, BuildContext context) { return AppImage( - color: AppColors.yellow9, + color: context.textPromoIcon, path: obscureText.value ? AppImagePaths.eyeHide : AppImagePaths.eye, onPressed: () { obscureText.value = !obscureText.value; diff --git a/lib/features/auth/device_limit_reached.dart b/lib/features/auth/device_limit_reached.dart index 92bf3bf34c..0772287b76 100644 --- a/lib/features/auth/device_limit_reached.dart +++ b/lib/features/auth/device_limit_reached.dart @@ -38,7 +38,7 @@ class DeviceLimitReached extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0), child: Text("lantern_pro_devices".i18n, style: textTheme.labelLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, )), ), AppCard( @@ -79,7 +79,7 @@ class DeviceLimitReached extends HookConsumerWidget { Center( child: AppTextButton( label: 'cancel_sign_in'.i18n, - textColor: AppColors.gray9, + textColor: context.textPrimary, onPressed: () { appRouter.popUntilRoot(); }, diff --git a/lib/features/auth/lantern_pro_license.dart b/lib/features/auth/lantern_pro_license.dart index 15c7066d57..9f2b5f7d9d 100644 --- a/lib/features/auth/lantern_pro_license.dart +++ b/lib/features/auth/lantern_pro_license.dart @@ -83,7 +83,7 @@ class LanternProLicense extends HookConsumerWidget { child: Text( '${normalizedLen.value}/25', style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: AppColors.gray6, + color: context.textDisabled, ), ), ), diff --git a/lib/features/auth/reset_password.dart b/lib/features/auth/reset_password.dart index c4f7288bd5..294c37a092 100644 --- a/lib/features/auth/reset_password.dart +++ b/lib/features/auth/reset_password.dart @@ -43,7 +43,7 @@ class ResetPassword extends HookConsumerWidget { controller: passwordController, prefixIcon: AppImagePaths.lock, onChanged: (value) {}, - suffixIcon: _buildSuffix(obscureText), + suffixIcon: _buildSuffix(obscureText, context), ), SizedBox(height: 20), AppTextField( @@ -76,7 +76,7 @@ class ResetPassword extends HookConsumerWidget { } return null; }, - suffixIcon: _buildSuffix(obscureText), + suffixIcon: _buildSuffix(obscureText, context), ), SizedBox(height: 32), PrimaryButton( @@ -96,9 +96,9 @@ class ResetPassword extends HookConsumerWidget { ); } - Widget _buildSuffix(ValueNotifier obscureText) { + Widget _buildSuffix(ValueNotifier obscureText, BuildContext context) { return AppImage( - color: AppColors.yellow9, + color: context.textPromoIcon, path: obscureText.value ? AppImagePaths.eyeHide : AppImagePaths.eye, onPressed: () { obscureText.value = !obscureText.value; @@ -138,7 +138,7 @@ class ResetPassword extends HookConsumerWidget { action: [ AppTextButton( label: 'continue'.i18n, - textColor: AppColors.gray7, + textColor: context.textTertiary, onPressed: () { appRouter.popUntilRoot(); }, diff --git a/lib/features/auth/sign_in_password.dart b/lib/features/auth/sign_in_password.dart index 485bbf0146..4e44501fa1 100644 --- a/lib/features/auth/sign_in_password.dart +++ b/lib/features/auth/sign_in_password.dart @@ -59,23 +59,22 @@ class _SignInPasswordState extends ConsumerState { signInWithPassword(passwordController.text.trim()), onChanged: (value) {}, ), - SizedBox(height: 4), + SizedBox(height: 8), if (!widget.fromChangeEmail) - Padding( - padding: const EdgeInsets.symmetric(horizontal: defaultSize), - child: Text( - 'if_you_have_not_set_password'.i18n, - textAlign: TextAlign.start, - style: textTheme.labelMedium!.copyWith( - color: AppColors.gray6, - ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: defaultSize), + child: Text( + 'if_you_have_not_set_password'.i18n, + textAlign: TextAlign.start, + style: textTheme.labelMedium!.copyWith( + color: context.textDisabled, ), - ), + )), SizedBox(height: 16), if (widget.fromChangeEmail) Text('confirm_password_to_continue'.i18n, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, )), SizedBox(height: 32), PrimaryButton( @@ -90,7 +89,7 @@ class _SignInPasswordState extends ConsumerState { SizedBox(height: 32), AppTextButton( label: 'forgot_password'.i18n, - textColor: AppColors.gray9, + textColor: context.textPrimary, onPressed: () { appRouter.push(ResetPasswordEmail(email: widget.email)); }, @@ -104,7 +103,7 @@ class _SignInPasswordState extends ConsumerState { Widget _buildSuffix(ValueNotifier obscureText) { return AppImage( - color: AppColors.yellow9, + color: context.textPrimary, path: obscureText.value ? AppImagePaths.eyeHide : AppImagePaths.eye, onPressed: () { obscureText.value = !obscureText.value; diff --git a/lib/features/home/data_usage.dart b/lib/features/home/data_usage.dart index 2dd835d2a4..754b237775 100644 --- a/lib/features/home/data_usage.dart +++ b/lib/features/home/data_usage.dart @@ -85,8 +85,8 @@ class DataUsage extends ConsumerWidget { : 'daily_data_usage'.i18n, style: textTheme.labelLarge!.copyWith( color: isDataCapReached - ? AppColors.red8 - : AppColors.gray7, + ? context.statusErrorBg + : context.textTertiary, ), ), Spacer(), @@ -94,7 +94,7 @@ class DataUsage extends ConsumerWidget { Text( '$usageString${'mb'.i18n}', style: textTheme.titleSmall!.copyWith( - color: AppColors.gray9, + color: context.textPrimary, ), ), ], @@ -108,7 +108,7 @@ class DataUsage extends ConsumerWidget { maxFontSize: 12, maxLines: 1, style: textTheme.bodySmall!.copyWith( - color: AppColors.red8, + color: context.statusErrorBg, ), ), ), @@ -118,7 +118,7 @@ class DataUsage extends ConsumerWidget { shape: RoundedRectangleBorder( side: isDataCapReached ? BorderSide.none - : BorderSide(width: 1, color: AppColors.gray3), + : BorderSide(width: 1, color: context.borderInput), borderRadius: BorderRadius.circular(8), ), ), @@ -134,9 +134,9 @@ class DataUsage extends ConsumerWidget { Radius.circular(defaultSize), ), trackGap: 10, - backgroundColor: AppColors.gray1, + backgroundColor: context.bgSurface, valueColor: AlwaysStoppedAnimation( - isDataCapReached ? AppColors.red6 : AppColors.yellow3, + isDataCapReached ? context.borderError : AppColors.yellow3, ), ), ), diff --git a/lib/features/home/home.dart b/lib/features/home/home.dart index 3ad13717dd..811d16978f 100644 --- a/lib/features/home/home.dart +++ b/lib/features/home/home.dart @@ -90,8 +90,7 @@ class _HomeState extends ConsumerState { ref.read(appEventProvider); return Scaffold( appBar: AppBar( - backgroundColor: AppColors.white, - title: LanternLogo(isPro: isUserPro), + title: LanternLogo(isPro: isUserPro,color: context.textPrimary,), bottom: PreferredSize( preferredSize: Size.fromHeight(0), child: DividerSpace(padding: EdgeInsets.zero), @@ -264,31 +263,31 @@ class _HomeState extends ConsumerState { SizedBox(height: 24), Text( 'help_improve_lantern'.i18n, - style: textTheme!.headlineSmall!.copyWith(color: AppColors.gray9), + style: textTheme!.headlineSmall!.copyWith(color: context.textPrimary), ), SizedBox(height: defaultSize), Text( 'share_anonymous_usage_data'.i18n, - style: textTheme!.bodyMedium!.copyWith(color: AppColors.gray8), + style: textTheme!.bodyMedium!.copyWith(color: context.textSecondary), ), SizedBox(height: defaultSize), Text( 'data_we_collect'.i18n, style: AppTextStyles.bodyMediumBold.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), SizedBox(height: defaultSize), Text( 'you_can_change_anytime'.i18n, - style: textTheme!.bodyMedium!.copyWith(color: AppColors.gray8), + style: textTheme!.bodyMedium!.copyWith(color: context.textSecondary), ), ], ), action: [ AppTextButton( label: 'dont_allow'.i18n, - textColor: AppColors.gray6, + textColor: context.textDisabled, onPressed: () { context.pop(); ref diff --git a/lib/features/home/provider/app_setting_notifier.dart b/lib/features/home/provider/app_setting_notifier.dart index f75e90cbce..b922acb553 100644 --- a/lib/features/home/provider/app_setting_notifier.dart +++ b/lib/features/home/provider/app_setting_notifier.dart @@ -10,6 +10,7 @@ import 'package:lantern/core/utils/storage_utils.dart'; import 'package:lantern/lantern/lantern_service.dart'; import 'package:lantern/lantern/lantern_service_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:window_manager/window_manager.dart'; part 'app_setting_notifier.g.dart'; @@ -20,14 +21,33 @@ class AppSettingNotifier extends _$AppSettingNotifier { @override AppSetting build() { _db = sl(); + final setting = _db.getAppSetting(); + + if (setting != null && setting.locale.isNotEmpty) { + updateToolbarThemeMode(); + return setting; + } + // First-time user → use device locale // First-time user or DB was wiped after env switch → use device locale final fallback = _detectDeviceLocale(); final initial = AppSetting(locale: fallback.toString()); _db.updateAppSetting(initial); + updateToolbarThemeMode(); _detectEnvironmentFromFile(); return initial; } + void updateToolbarThemeMode() { + final setting = _db.getAppSetting(); + final mode = setting?.themeMode ?? 'system'; + final modeEnum = resolveThemeMode(mode); + if (modeEnum == ThemeMode.system || modeEnum == ThemeMode.light) { + windowManager.setBrightness(Brightness.light); + } else { + windowManager.setBrightness(Brightness.dark); + } + } + Future update(AppSetting updated) async { state = updated; _db.updateAppSetting(updated); @@ -111,6 +131,19 @@ class AppSettingNotifier extends _$AppSettingNotifier { update(state.copyWith(onboardingCompleted: value)); } + void setThemeMode(String mode) { + update(state.copyWith(themeMode: mode)); + if (PlatformUtils.isDesktop) { + final modeEnum = resolveThemeMode(mode); + if (modeEnum == ThemeMode.system || modeEnum == ThemeMode.light) { + windowManager.setBrightness(Brightness.light); + } else { + windowManager.setBrightness(Brightness.dark); + } + } + } + + Future setEnvironment(bool isStaging) async { final env = isStaging ? 'stage' : 'prod'; update(state.copyWith(environment: env)); diff --git a/lib/features/home/provider/app_setting_notifier.g.dart b/lib/features/home/provider/app_setting_notifier.g.dart index 02ac8fb3d5..5b2f2c05cf 100644 --- a/lib/features/home/provider/app_setting_notifier.g.dart +++ b/lib/features/home/provider/app_setting_notifier.g.dart @@ -42,7 +42,7 @@ final class AppSettingNotifierProvider } String _$appSettingNotifierHash() => - r'49e35afce80b0b7b25c62d647cdf3924ee6cc49a'; + r'355d669c54fd042f337506102347b94dcf12c03f'; abstract class _$AppSettingNotifier extends $Notifier { AppSetting build(); diff --git a/lib/features/home/provider/home_notifier.g.dart b/lib/features/home/provider/home_notifier.g.dart index c36c756f93..ac185b25bf 100644 --- a/lib/features/home/provider/home_notifier.g.dart +++ b/lib/features/home/provider/home_notifier.g.dart @@ -33,7 +33,7 @@ final class HomeNotifierProvider HomeNotifier create() => HomeNotifier(); } -String _$homeNotifierHash() => r'8c1c7e93ef15d1bc9bb6a7adf7422e93423294b8'; +String _$homeNotifierHash() => r'acb597e5a3f386fddf86e5b3f3af742ffa93e2c7'; abstract class _$HomeNotifier extends $AsyncNotifier { FutureOr build(); diff --git a/lib/features/language/language.dart b/lib/features/language/language.dart index 243bcee5ff..dc8bdcfb0f 100644 --- a/lib/features/language/language.dart +++ b/lib/features/language/language.dart @@ -74,14 +74,13 @@ class LanguageListView extends HookConsumerWidget { AppTile( label: displayLanguage(langCode), onPressed: () => onLanguageTap(langCode), - trailing: Radio( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + trailing: AppRadioButton( value: langCode, groupValue: locale.toString(), onChanged: (value) { onLanguageTap(value!); }, - activeColor: AppColors.blue7, + // activeColor: AppColors.blue7, ), minHeight: 56, ), diff --git a/lib/features/logs/log_line.dart b/lib/features/logs/log_line.dart index eccfb24f5d..fff26b2bec 100644 --- a/lib/features/logs/log_line.dart +++ b/lib/features/logs/log_line.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:lantern/core/common/app_semantic_colors.dart'; import 'package:lantern/core/common/app_text_styles.dart'; import 'package:lantern/features/logs/parsed_log.dart'; @@ -15,7 +16,7 @@ class LogLineWidget extends StatelessWidget { return Text( line, style: AppTextStyles.monospace( - color: Colors.white, + color: context.textPrimary, ), ); } diff --git a/lib/features/logs/logs.dart b/lib/features/logs/logs.dart index d7f198c0a7..0b5189a2a4 100644 --- a/lib/features/logs/logs.dart +++ b/lib/features/logs/logs.dart @@ -80,9 +80,9 @@ class Logs extends HookConsumerWidget { Expanded( child: Container( decoration: ShapeDecoration( - color: AppColors.black1, + color: context.bgElevated, shape: RoundedRectangleBorder( - side: BorderSide(width: 1), + side: BorderSide(width: 1,color: context.borderDefault), borderRadius: BorderRadius.circular(16), ), ), diff --git a/lib/features/macos_extension/macos_extension_dialog.dart b/lib/features/macos_extension/macos_extension_dialog.dart index a6f3e91587..5c9643ab27 100644 --- a/lib/features/macos_extension/macos_extension_dialog.dart +++ b/lib/features/macos_extension/macos_extension_dialog.dart @@ -54,7 +54,7 @@ class _MacOSExtensionDialogState extends ConsumerState { actions: [ CloseButton(), ], - backgroundColor: AppColors.white, + backgroundColor: context.bgElevated, ), body: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -62,7 +62,7 @@ class _MacOSExtensionDialogState extends ConsumerState { AppImage(path: AppImagePaths.sysDialog), const SizedBox(height: 48.0), Text('enable_network_extension'.i18n, - style: textTheme.headlineSmall!.copyWith(color: AppColors.gray8), + style: textTheme.headlineSmall!.copyWith(color: context.textSecondary), textAlign: TextAlign.center), const SizedBox(height: 16.0), Padding( @@ -70,21 +70,21 @@ class _MacOSExtensionDialogState extends ConsumerState { child: Text( 'enable_network_extension_message'.i18n, style: textTheme.bodyLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), const SizedBox(height: 16.0), RichText( text: TextSpan( - style: textTheme.bodyLarge!.copyWith(color: AppColors.gray7), + style: textTheme.bodyLarge!.copyWith(color: context.textTertiary), children: [ TextSpan(text: 'click'.i18n), WidgetSpan(child: SizedBox(width: 4.0)), TextSpan( text: 'open_system_settings'.i18n, style: AppTextStyles.bodyLargeBold.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), WidgetSpan(child: SizedBox(width: 4.0)), diff --git a/lib/features/onboarding/onboarding.dart b/lib/features/onboarding/onboarding.dart index 46a7adf4b0..49bb8a9de8 100644 --- a/lib/features/onboarding/onboarding.dart +++ b/lib/features/onboarding/onboarding.dart @@ -45,15 +45,15 @@ class _OnboardingState extends ConsumerState { return Scaffold( appBar: AppBar( leading: const SizedBox.shrink(), - backgroundColor: AppColors.white, - title: const LanternLogo(), + backgroundColor: context.bgElevated, + title: LanternLogo(color: context.textPrimary), bottom: PreferredSize( preferredSize: Size.fromHeight(0), child: DividerSpace(padding: EdgeInsets.zero), ), ), body: Container( - color: AppColors.white, + color: context.bgElevated, padding: const EdgeInsets.symmetric(horizontal: 16.0), child: SafeArea( child: Column( @@ -76,7 +76,7 @@ class _OnboardingState extends ConsumerState { itemSpacing: 15, indicatorBorderWidth: 0.0, currentIndicatorColor: AppColors.blue3, - indicatorBackgroundColor: AppColors.gray3, + indicatorBackgroundColor: context.borderInput, enableAnimation: true, padding: EdgeInsets.only(bottom: 10.0), alignment: Alignment.bottomCenter, @@ -91,13 +91,14 @@ class _OnboardingState extends ConsumerState { children: [ AppImage( path: AppImagePaths.appIconSVG, + useThemeColor: false, ), SizedBox(height: 48), Text( 'welcome_to_lantern'.i18n, style: textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, - color: AppColors.gray8), + color: context.textSecondary), ), SizedBox(height: 16), Text('lantern_pro_tagline'.i18n) @@ -128,7 +129,7 @@ class _OnboardingState extends ConsumerState { SizedBox(height: 12.0), AppTextButton( label: 'skip_connect_now'.i18n, - textColor: AppColors.gray9, + textColor: context.textPrimary, onPressed: () { onboardingCompleted(); }, @@ -150,34 +151,34 @@ class _OnboardingState extends ConsumerState { Text( 'what_makes_lantern_different'.i18n, style: textTheme.headlineSmall!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), SizedBox(height: 8.0), Text( 'built_for_privacy_speed_freedom'.i18n, style: textTheme.bodyLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), SizedBox(height: 24.0), AppTile( icon: Padding( padding: const EdgeInsets.only(top: 5.0), - child: AppImage(path: AppImagePaths.smartRouteMode), + child: AppImage(path: AppImagePaths.smartRouteMode,useThemeColor: false,), ), label: '', titleAlignment: ListTileTitleAlignment.top, labelWidget: Text( 'smart_routing_mode'.i18n, style: textTheme.titleMedium!.copyWith( - color: AppColors.black, + color: context.textPrimary, ), ), subtitle: Text( 'region_specific_routing_description'.i18n, style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -185,20 +186,20 @@ class _OnboardingState extends ConsumerState { AppTile( icon: Padding( padding: const EdgeInsets.only(top: 5.0), - child: AppImage(path: AppImagePaths.advanceProtocol), + child: AppImage(path: AppImagePaths.advanceProtocol,useThemeColor: false), ), label: '', titleAlignment: ListTileTitleAlignment.top, labelWidget: Text( 'advanced_protocols'.i18n, style: textTheme.titleMedium!.copyWith( - color: AppColors.black, + color: context.textPrimary, ), ), subtitle: Text( 'advanced_protocols_description'.i18n, style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -206,20 +207,20 @@ class _OnboardingState extends ConsumerState { AppTile( icon: Padding( padding: const EdgeInsets.only(top: 5.0), - child: AppImage(path: AppImagePaths.privateServerIntro), + child: AppImage(path: AppImagePaths.privateServerIntro,useThemeColor: false), ), label: '', titleAlignment: ListTileTitleAlignment.top, labelWidget: Text( 'private_servers'.i18n, style: textTheme.titleMedium!.copyWith( - color: AppColors.black, + color: context.textPrimary, ), ), subtitle: Text( 'private_servers_description'.i18n, style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -227,20 +228,20 @@ class _OnboardingState extends ConsumerState { AppTile( icon: Padding( padding: const EdgeInsets.only(top: 5.0), - child: AppImage(path: AppImagePaths.nonProfit), + child: AppImage(path: AppImagePaths.nonProfit,useThemeColor: false), ), label: '', titleAlignment: ListTileTitleAlignment.top, labelWidget: Text( 'nonprofit_mission'.i18n, style: textTheme.titleMedium!.copyWith( - color: AppColors.black, + color: context.textPrimary, ), ), subtitle: Text( 'built_by_nonprofit'.i18n, style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -284,7 +285,7 @@ class _OnboardingState extends ConsumerState { Text( 'choose_your_routing_mode'.i18n, style: textTheme.headlineSmall!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), SizedBox(height: 24.0), @@ -328,11 +329,11 @@ class RouteModeContainer extends StatelessWidget { duration: Duration(milliseconds: 250), padding: EdgeInsets.all(16.0), decoration: BoxDecoration( - color: isSelected ? AppColors.blue1 : AppColors.gray1, + color: isSelected ? context.bgHover : context.bgElevated, borderRadius: BorderRadius.circular(16.0), border: isSelected - ? Border.all(color: AppColors.blue7, width: 3.0) - : Border.all(color: AppColors.gray2, width: 1.0), + ? Border.all(color: context.borderInputFocus, width: 3.0) + : Border.all(color: context.borderDefault, width: 1.0), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -347,7 +348,7 @@ class RouteModeContainer extends StatelessWidget { Text( title(), style: textTheme.titleMedium!.copyWith( - color: AppColors.black, + color: context.textPrimary, ), ), SizedBox(width: 8.0), @@ -356,13 +357,13 @@ class RouteModeContainer extends StatelessWidget { EdgeInsets.symmetric(horizontal: 10.0, vertical: 3.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.0), - border: Border.all(color: AppColors.blue4), - color: AppColors.blue2, + border: Border.all(color: context.statusInfoBorder), + color: context.statusInfoBg, ), child: Text( tags(), style: - textTheme.labelMedium!.copyWith(color: AppColors.blue8), + textTheme.labelMedium!.copyWith(color: context.statusInfoText), )) ], ), @@ -370,7 +371,7 @@ class RouteModeContainer extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 38), child: Text(description(), - style: textTheme.bodyMedium!.copyWith(color: AppColors.gray8)), + style: textTheme.bodyMedium!.copyWith(color: context.textSecondary)), ) ], ), diff --git a/lib/features/plans/feature_list.dart b/lib/features/plans/feature_list.dart index f363a91052..199d4491ed 100644 --- a/lib/features/plans/feature_list.dart +++ b/lib/features/plans/feature_list.dart @@ -53,7 +53,7 @@ class _FeatureTile extends StatelessWidget { children: [ AppImage( path: image, - color: AppColors.blue10, + color: context.textPrimary, height: 24, ), SizedBox(width: defaultSize), diff --git a/lib/features/plans/plan_item.dart b/lib/features/plans/plan_item.dart index 72a0e7d19d..56d4a1da44 100644 --- a/lib/features/plans/plan_item.dart +++ b/lib/features/plans/plan_item.dart @@ -2,7 +2,6 @@ import 'package:badges/badges.dart' as badges; import 'package:flutter/material.dart'; import 'package:lantern/core/extensions/plan.dart'; import 'package:lantern/core/models/plan_data.dart'; -import 'package:lantern/core/utils/decoration.dart'; import '../../core/common/common.dart'; @@ -35,16 +34,16 @@ class PlanItem extends StatelessWidget { badgeStyle: badges.BadgeStyle( shape: badges.BadgeShape.square, borderSide: BorderSide( - color: AppColors.yellow4, + color: context.statusWarningText, width: 1, ), borderRadius: BorderRadius.circular(16), - badgeColor: AppColors.yellow3, + badgeColor: context.statusWarningBgDot, padding: EdgeInsets.symmetric(horizontal: 10, vertical: 6), ), badgeContent: Text( 'best_value'.i18n, - style: textTheme.labelMedium, + style: textTheme.labelMedium!, ), child: GestureDetector( onTap: () { @@ -54,7 +53,7 @@ class PlanItem extends StatelessWidget { margin: EdgeInsets.only(top: 16), padding: EdgeInsets.symmetric(horizontal: defaultSize, vertical: 12), duration: Duration(milliseconds: 300), - decoration: planSelected ? selectedDecoration : unselectedDecoration, + decoration: getPlanDecoration(planSelected, context), child: Row( children: [ Column( @@ -62,7 +61,9 @@ class PlanItem extends StatelessWidget { children: [ Text( plan.description.toTitleCase(), - style: textTheme.titleMedium, + style: textTheme.titleMedium!.copyWith( + color: context.textPrimary, + ), ), if (referralMessage.isNotEmpty) Text( @@ -78,13 +79,13 @@ class PlanItem extends StatelessWidget { Text( plan.formattedYearlyPrice, style: textTheme.titleMedium!.copyWith( - color: AppColors.blue7, + color: context.textLink, ), ), Text( '${plan.formattedMonthlyPrice}/month', style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), ], @@ -92,7 +93,7 @@ class PlanItem extends StatelessWidget { Radio( value: true, groupValue: planSelected, - fillColor: WidgetStatePropertyAll(AppColors.gray9), + fillColor: WidgetStatePropertyAll(context.textPrimary), onChanged: (value) { onPressed.call(plan); }, @@ -103,4 +104,15 @@ class PlanItem extends StatelessWidget { ), ); } + + BoxDecoration getPlanDecoration(bool isSelected, BuildContext context) { + return BoxDecoration( + color: isSelected ? context.bgHover : context.bgElevated, + border: Border.all( + color: isSelected ? context.textLink : context.borderInput, + width: isSelected ? 3 : 1.5, + ), + borderRadius: BorderRadius.circular(16), + ); + } } diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 41a57df896..615fd0cf25 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -38,17 +38,17 @@ class _PlansState extends ConsumerState { Widget build(BuildContext context) { textTheme = Theme.of(context).textTheme; return BaseScreen( - backgroundColor: AppColors.white, + backgroundColor: context.bgElevated, padded: false, appBar: CustomAppBar( title: SizedBox( height: 20.h, child: LanternLogo( - color: AppColors.gray9, + color: context.textPrimary, isPro: true, ), ), - backgroundColor: AppColors.white, + backgroundColor: context.bgElevated, leading: IconButton( icon: Icon(Icons.close), onPressed: () { @@ -63,7 +63,10 @@ class _PlansState extends ConsumerState { ], ), title: "", - body: SafeArea(child: _buildBody()), + body: SafeArea( + bottom: !PlatformUtils.isIOS, + child: _buildBody(), + ), ); } @@ -84,7 +87,7 @@ class _PlansState extends ConsumerState { DividerSpace(padding: EdgeInsets.zero), Expanded( child: Container( - color: AppColors.gray1, + color: context.bgSurface, padding: EdgeInsets.symmetric( horizontal: context.isSmallDevice ? 0 : defaultSize), child: Column( @@ -139,7 +142,7 @@ class _PlansState extends ConsumerState { child: Text( 'subscription_renewal_info'.i18n, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), ), @@ -150,7 +153,7 @@ class _PlansState extends ConsumerState { AppTextButton( label: 'privacy_policy'.i18n, fontSize: 12, - textColor: AppColors.gray7, + textColor: context.textTertiary, onPressed: () { UrlUtils.openWithSystemBrowser( AppUrls.privacyPolicy); @@ -163,7 +166,7 @@ class _PlansState extends ConsumerState { AppTextButton( label: 'terms_of_service'.i18n, fontSize: 12, - textColor: AppColors.gray7, + textColor: context.textTertiary, onPressed: () { UrlUtils.openWithSystemBrowser( AppUrls.termsOfService); @@ -239,7 +242,7 @@ class _PlansState extends ConsumerState { SizedBox(height: defaultSize), Text('referral_code'.i18n, style: textTheme.headlineSmall!.copyWith( - color: AppColors.gray9, + color: context.textPrimary, )), SizedBox(height: 24), AppTextField( @@ -257,7 +260,7 @@ class _PlansState extends ConsumerState { AppTextButton( label: 'cancel'.i18n, underLine: false, - textColor: AppColors.gray6, + textColor: context.textDisabled, onPressed: () { appRouter.pop(); }, diff --git a/lib/features/plans/provider/payment_notifier.g.dart b/lib/features/plans/provider/payment_notifier.g.dart index f30196ea47..b999fa1da0 100644 --- a/lib/features/plans/provider/payment_notifier.g.dart +++ b/lib/features/plans/provider/payment_notifier.g.dart @@ -41,7 +41,7 @@ final class PaymentNotifierProvider } } -String _$paymentNotifierHash() => r'4348c0acda395c71a0951848746f98000316ef0f'; +String _$paymentNotifierHash() => r'593bf59110bb4eb70a843d440467573d9b5bb5cd'; abstract class _$PaymentNotifier extends $Notifier { void build(); diff --git a/lib/features/private_server/join_private_server.dart b/lib/features/private_server/join_private_server.dart index ee26ee3ba3..5bbab35be5 100644 --- a/lib/features/private_server/join_private_server.dart +++ b/lib/features/private_server/join_private_server.dart @@ -56,7 +56,7 @@ class _JoinPrivateServerState extends ConsumerState { child: Column(children: [ // SizedBox(height: 16), InfoRow( - backgroundColor: AppColors.yellow1, + backgroundColor: context.bgPromo, showLeadingIcon: false, text: '', child: Row( @@ -106,7 +106,7 @@ class _JoinPrivateServerState extends ConsumerState { child: Text( "how_server_appears".i18n, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray6, + color: context.textDisabled, ), ), ), @@ -185,7 +185,7 @@ class _JoinPrivateServerState extends ConsumerState { AppImage( path: AppImagePaths.security, height: 40, - color: AppColors.gray9, + color: context.textPrimary, ), SizedBox(height: 16), Text( @@ -273,7 +273,7 @@ class _JoinPrivateServerState extends ConsumerState { onPressed: () { appRouter.popUntilRoot(); }, - textColor: AppColors.gray6, + textColor: context.textDisabled, ), AppTextButton( label: "go_to_server_locations".i18n, diff --git a/lib/features/private_server/manage_private_server.dart b/lib/features/private_server/manage_private_server.dart index e197d0d2c1..cc68bf1833 100644 --- a/lib/features/private_server/manage_private_server.dart +++ b/lib/features/private_server/manage_private_server.dart @@ -52,7 +52,7 @@ class _ManagePrivateServerState extends ConsumerState { unselectedLabelColor: Colors.grey, labelStyle: textTheme!.titleSmall, indicator: BoxDecoration( - color: AppColors.blue2, + color: context.textLink, borderRadius: BorderRadius.circular(40), shape: BoxShape.rectangle, border: Border.all(color: AppColors.blue3, width: 1), @@ -110,7 +110,7 @@ class _ManagePrivateServerState extends ConsumerState { mainAxisSize: MainAxisSize.min, children: [ IconButton( - icon: Icon(Icons.delete_outline, color: AppColors.gray9), + icon: Icon(Icons.delete_outline, color: context.textPrimary), iconSize: 24, onPressed: () => showDeleteDialog(item.serverName), ), @@ -123,9 +123,9 @@ class _ManagePrivateServerState extends ConsumerState { label: 'share_access_key'.i18n, bgColor: AppColors.blue1, icon: AppImagePaths.shareV2, - iconColor: AppColors.gray9, + iconColor: context.textPrimary, showBorder: true, - textColor: AppColors.gray9, + textColor: context.textPrimary, onPressed: () => onTapShareAccessKey(item)), SizedBox(height: 16), } @@ -181,7 +181,7 @@ class _ManagePrivateServerState extends ConsumerState { action: [ AppTextButton( label: 'cancel'.i18n, - textColor: AppColors.gray6, + textColor: context.textDisabled, onPressed: () { appRouter.pop(); }, @@ -253,7 +253,7 @@ class _ManagePrivateServerState extends ConsumerState { action: [ AppTextButton( label: 'cancel', - textColor: AppColors.gray6, + textColor: context.textDisabled, onPressed: () { appRouter.pop(); }, @@ -292,7 +292,7 @@ class _ManagePrivateServerState extends ConsumerState { action: [ AppTextButton( label: 'cancel'.i18n, - textColor: AppColors.gray6, + textColor: context.textDisabled, onPressed: () { appRouter.pop(); }, diff --git a/lib/features/private_server/manually_server_setup.dart b/lib/features/private_server/manually_server_setup.dart index 9cfd9fd607..b79b80cf6c 100644 --- a/lib/features/private_server/manually_server_setup.dart +++ b/lib/features/private_server/manually_server_setup.dart @@ -6,7 +6,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/models/entity/private_server_entity.dart'; -import 'package:lantern/core/models/private_server_status.dart'; import 'package:lantern/features/private_server/provider/private_server_notifier.dart'; import '../../core/services/injection_container.dart'; @@ -62,7 +61,6 @@ class _ManuallyServerSetupState extends ConsumerState { SizedBox(height: 16), PrimaryButton( icon: AppImagePaths.github, - iconColor: AppColors.white, isTaller: true, label: 'view_instructions_github'.i18n, onPressed: () { @@ -99,7 +97,7 @@ class _ManuallyServerSetupState extends ConsumerState { child: Text( "how_server_appears".i18n, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray6, + color: context.textDisabled, ), ), ), @@ -225,7 +223,7 @@ class _ManuallyServerSetupState extends ConsumerState { onPressed: () { appRouter.popUntilRoot(); }, - textColor: AppColors.gray6, + textColor: context.textDisabled, ), AppTextButton( label: "connect_now".i18n, diff --git a/lib/features/private_server/private_server_add_billing.dart b/lib/features/private_server/private_server_add_billing.dart index f189e8272f..c55a8a8810 100644 --- a/lib/features/private_server/private_server_add_billing.dart +++ b/lib/features/private_server/private_server_add_billing.dart @@ -70,7 +70,7 @@ class PrivateServerAddBilling extends HookConsumerWidget { Center( child: AppImage( path: AppImagePaths.creditCard, - color: AppColors.gray9, + color: context.textPrimary, height: 30, ), ), @@ -87,14 +87,14 @@ class PrivateServerAddBilling extends HookConsumerWidget { textAlign: TextAlign.left, text: TextSpan( style: - textTheme.bodyMedium!.copyWith(color: AppColors.gray8), + textTheme.bodyMedium!.copyWith(color: context.textSecondary), text: '${'1'.i18n}. ', children: [ TextSpan(text: '${'tap'.i18n} '), TextSpan( text: '${'open_system_settings'.i18n} ', style: AppTextStyles.bodyMediumBold!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, )), TextSpan(text: 'below_to_go_to_do'.i18n), ], @@ -104,7 +104,7 @@ class PrivateServerAddBilling extends HookConsumerWidget { RichText( text: TextSpan( style: - textTheme.bodyMedium!.copyWith(color: AppColors.gray8), + textTheme.bodyMedium!.copyWith(color: context.textSecondary), text: '${'2'.i18n}. ', children: [ TextSpan(text: '${'add_payment_method'.i18n} '), @@ -115,7 +115,7 @@ class PrivateServerAddBilling extends HookConsumerWidget { RichText( text: TextSpan( style: - textTheme.bodyMedium!.copyWith(color: AppColors.gray8), + textTheme.bodyMedium!.copyWith(color: context.textSecondary), text: '${'3'.i18n}. ', children: [ TextSpan(text: '${'return_to_lantern'.i18n} '), @@ -130,7 +130,7 @@ class PrivateServerAddBilling extends HookConsumerWidget { PrimaryButton( isTaller: true, icon: AppImagePaths.outsideBrowser, - iconColor: AppColors.white, + iconColor: context.textInverse, label: 'open_system_settings'.i18n, onPressed: () { UrlUtils.openUrl(AppUrls.digitalOceanBillingUrl); diff --git a/lib/features/private_server/private_server_deploy.dart b/lib/features/private_server/private_server_deploy.dart index 61e84c4767..914e97e3c7 100644 --- a/lib/features/private_server/private_server_deploy.dart +++ b/lib/features/private_server/private_server_deploy.dart @@ -69,7 +69,7 @@ class _PrivateServerDeployState extends ConsumerState { child: Text( 'private_server_setup_in_progress'.i18n, style: textTheme!.bodyLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -106,7 +106,7 @@ class _PrivateServerDeployState extends ConsumerState { SizedBox(height: 16), Text( 'private_server_ready_message'.i18n.fill([widget.serverName]), - style: textTheme!.bodyMedium!.copyWith(color: AppColors.gray8), + style: textTheme!.bodyMedium!.copyWith(color: context.textSecondary), ), ], ), @@ -116,7 +116,7 @@ class _PrivateServerDeployState extends ConsumerState { onPressed: () { appRouter.popUntilRoot(); }, - textColor: AppColors.gray6, + textColor: context.textDisabled, ), AppTextButton( label: "go_to_server_locations".i18n, @@ -149,7 +149,7 @@ class _PrivateServerDeployState extends ConsumerState { SizedBox(height: 16), Text( 'server_setup_failed_message'.i18n, - style: textTheme!.bodyMedium!.copyWith(color: AppColors.gray8), + style: textTheme!.bodyMedium!.copyWith(color: context.textSecondary), ), ], ), @@ -159,7 +159,7 @@ class _PrivateServerDeployState extends ConsumerState { onPressed: () { appRouter.popUntilRoot(); }, - textColor: AppColors.gray6, + textColor: context.textDisabled, ), AppTextButton( label: "retry".i18n, diff --git a/lib/features/private_server/private_server_locations.dart b/lib/features/private_server/private_server_locations.dart index c23bf01259..25e6765f58 100644 --- a/lib/features/private_server/private_server_locations.dart +++ b/lib/features/private_server/private_server_locations.dart @@ -57,7 +57,7 @@ class _PrivateServerLocationState extends ConsumerState { child: Text( '${widget.provider.displayName} Location Options (${widget.location.length}) ', style: textTheme.labelLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), diff --git a/lib/features/private_server/private_server_setup.dart b/lib/features/private_server/private_server_setup.dart index 12cc7486aa..365247c98e 100644 --- a/lib/features/private_server/private_server_setup.dart +++ b/lib/features/private_server/private_server_setup.dart @@ -134,6 +134,7 @@ class _PrivateServerSetupState extends ConsumerState { path: AppImagePaths.serverRack, type: AssetType.svg, height: PlatformUtils.isDesktop ? 190.h : 160.h, + useThemeColor: false, ), const SizedBox(height: defaultSize), ProviderCarousel( diff --git a/lib/features/private_server/private_sever_details.dart b/lib/features/private_server/private_sever_details.dart index b125d3cfc5..5aed7ffa69 100644 --- a/lib/features/private_server/private_sever_details.dart +++ b/lib/features/private_server/private_sever_details.dart @@ -266,7 +266,7 @@ class _PrivateSeverDetailsState extends ConsumerState { child: Text( "how_server_appears".i18n, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray6, + color: context.textDisabled, ), ), ), diff --git a/lib/features/private_server/provider_card.dart b/lib/features/private_server/provider_card.dart index def5197a3e..f823c0475f 100644 --- a/lib/features/private_server/provider_card.dart +++ b/lib/features/private_server/provider_card.dart @@ -40,13 +40,13 @@ class ProviderCard extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - AppImage(path: icon, width: 20, height: 20), + AppImage(path: icon, width: 20, height: 20,useThemeColor: false,), const SizedBox(width: defaultSize), Expanded( child: Text( title, style: t.titleMedium?.copyWith( - color: AppColors.black1, + color: context.textPrimary, fontWeight: FontWeight.w600, // height: 1.50, ), @@ -107,6 +107,7 @@ class CheckmarkTile extends StatelessWidget { path: iconPath ?? AppImagePaths.checkmark, width: 24, height: 24, + useThemeColor: false, ), const SizedBox(width: 16), Expanded( @@ -115,7 +116,7 @@ class CheckmarkTile extends StatelessWidget { softWrap: true, overflow: TextOverflow.visible, style: t.bodyMedium?.copyWith( - color: AppColors.black1, + color: context.textPrimary, height: 1.64, ), ), @@ -134,7 +135,7 @@ class CheckmarkTile extends StatelessWidget { return Column( children: [ row, - Divider(height: 1, color: AppColors.gray2), + Divider(height: 1, color: context.borderDefault), ], ); } diff --git a/lib/features/private_server/provider_carousel.dart b/lib/features/private_server/provider_carousel.dart index d2158335c3..fbd83bb901 100644 --- a/lib/features/private_server/provider_carousel.dart +++ b/lib/features/private_server/provider_carousel.dart @@ -82,9 +82,9 @@ class ProviderCarousel extends HookConsumerWidget { child: Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( - color: AppColors.white, + color: context.bgElevated, shape: BoxShape.circle, - border: Border.all(color: AppColors.gray4, width: 1), + border: Border.all(color: context.textDisabled, width: 1), ), child: AppImage( path: AppImagePaths.arrowBack, @@ -103,9 +103,9 @@ class ProviderCarousel extends HookConsumerWidget { child: Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( - color: AppColors.white, + color: context.bgElevated, shape: BoxShape.circle, - border: Border.all(color: AppColors.gray4, width: 1), + border: Border.all(color: context.textDisabled, width: 1), ), child: AppImage( path: AppImagePaths.arrowForward, @@ -184,7 +184,7 @@ class _CarouselDots extends StatelessWidget { spacing: 8, children: List.generate(count, (i) { final active = i == current; - final fill = active ? AppColors.gray4 : AppColors.gray2; + final fill = active ? context.textDisabled : context.borderDefault; return Semantics( selected: active, label: 'Page ${i + 1} of $count', @@ -198,7 +198,7 @@ class _CarouselDots extends StatelessWidget { decoration: BoxDecoration( color: fill, borderRadius: BorderRadius.circular(100), - border: Border.all(color: AppColors.gray3, width: 1), + border: Border.all(color: context.borderInput, width: 1), ), ), ), @@ -225,7 +225,7 @@ class _ArrowButton extends StatelessWidget { @override Widget build(BuildContext context) { - final iconColor = enabled ? AppColors.black1 : AppColors.gray4; + final iconColor = enabled ? context.textPrimary : context.textDisabled; Widget icon = assetPath != null ? AppImage(path: assetPath!, width: 24, height: 24, color: iconColor) diff --git a/lib/features/setting/appearance.dart b/lib/features/setting/appearance.dart new file mode 100644 index 0000000000..678ffa2e9a --- /dev/null +++ b/lib/features/setting/appearance.dart @@ -0,0 +1,92 @@ +import 'package:auto_route/annotations.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:lantern/core/common/common.dart'; +import 'package:lantern/features/home/provider/app_setting_notifier.dart'; + +@RoutePage(name: 'Appearance') +class Appearance extends StatelessWidget { + const Appearance({super.key}); + + @override + Widget build(BuildContext context) { + return BaseScreen( + title: 'appearance'.i18n, + body: Card( + child: AppearanceListView(), + ), + ); + } +} + +class AppearanceListView extends ConsumerWidget { + final ScrollController? scrollController; + + const AppearanceListView({super.key, this.scrollController}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentMode = ref.watch(appSettingProvider).themeMode; + + final options = [ + ('system', 'system'.i18n,AppImagePaths.automatic), + ('light', 'light'.i18n,AppImagePaths.lightMode), + ('dark', 'dark'.i18n,AppImagePaths.darkMode), + ]; + + return ListView.separated( + controller: scrollController, + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: options.length, + separatorBuilder: (_, __) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: DividerSpace(), + ), + itemBuilder: (context, index) { + final (value, label,icon) = options[index]; + return AppTile( + icon: icon, + label: label, + minHeight: 56, + trailing: AppRadioButton( + value: value, + groupValue: currentMode, + onChanged: (selected) => _onSelect(selected!, ref, context), + ), + onPressed: () => _onSelect(value, ref, context), + ); + }, + ); + } + + void _onSelect(String mode, WidgetRef ref, BuildContext context) { + ref.read(appSettingProvider.notifier).setThemeMode(mode); + appRouter.maybePop(); + } +} + +void showAppearanceBottomSheet({required BuildContext context}) { + showAppBottomSheet( + context: context, + title: 'appearance'.i18n, + scrollControlDisabledMaxHeightRatio: 0.32.h, + builder: (context, scrollController) { + return Flexible( + child: AppearanceListView(scrollController: scrollController), + ); + }, + ); +} + +String appearanceModeLabel(String mode) { + switch (mode) { + case 'light': + return 'light'.i18n; + case 'dark': + return 'dark'.i18n; + default: + return 'system'.i18n; + } +} diff --git a/lib/features/setting/download_links.dart b/lib/features/setting/download_links.dart index 425d10ea4b..c5561f791c 100644 --- a/lib/features/setting/download_links.dart +++ b/lib/features/setting/download_links.dart @@ -22,83 +22,81 @@ class DownloadLinks extends StatelessWidget { Widget _buildBody(BuildContext buildContext) { final theme = Theme.of(buildContext).textTheme; - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Center( - child: AppImage( - path: AppImagePaths.globIllustration, - type: AssetType.png, - height: 180.h, - width: 180.w, - ), - ), - SizedBox(height: defaultSize), - Card( - child: AppTile( - icon: AppImagePaths.lanternLogoRounded, - trailing: AppImage(path: AppImagePaths.outsideBrowser), - label: 'lantern_io'.i18n, - onPressed: () { - UrlUtils.openUrl(AppUrls.lanternOfficial); - }, - ), + return ListView( + children: [ + Center( + child: AppImage( + path: AppImagePaths.globIllustration, + type: AssetType.png, + height: 180.h, + width: 180.w, ), - SizedBox(height: defaultSize), - Padding( - padding: const EdgeInsets.only(left: 16), - child: Text( - 'alternative_download_links'.i18n, - style: theme.labelLarge!.copyWith( - color: AppColors.gray8, - ), - ), + ), + SizedBox(height: defaultSize), + Card( + child: AppTile( + icon: AppImagePaths.lanternLogoRounded, + iconUseThemeColor: false, + trailing: AppImage(path: AppImagePaths.outsideBrowser), + label: 'lantern_io'.i18n, + onPressed: () { + UrlUtils.openUrl(AppUrls.lanternOfficial); + }, ), - SizedBox(height: 4.0), - Card( - child: Column( - children: [ - AppTile.link( - url: AppUrls.lanternGithub, - icon: AppImagePaths.github, - label: 'github_download_page'.i18n, - ), - DividerSpace(), - AppTile.link( - url: AppUrls.telegramBot, - icon: AppImagePaths.telegram, - label: 'telegram_bot'.i18n, - ), - ], + ), + SizedBox(height: defaultSize), + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + 'alternative_download_links'.i18n, + style: theme.labelLarge!.copyWith( + color: buildContext.textSecondary, ), ), - SizedBox(height: defaultSize), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - 'if_you_cannot_access_website'.i18n, - style: theme.bodyMedium!.copyWith( - color: AppColors.gray8, + ), + SizedBox(height: 4.0), + Card( + child: Column( + children: [ + AppTile.link( + url: AppUrls.lanternGithub, + icon: AppImagePaths.github, + label: 'github_download_page'.i18n, ), + DividerSpace(), + AppTile.link( + url: AppUrls.telegramBot, + icon: AppImagePaths.telegram, + label: 'telegram_bot'.i18n, + ), + ], + ), + ), + SizedBox(height: defaultSize), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'if_you_cannot_access_website'.i18n, + style: theme.bodyMedium!.copyWith( + color: buildContext.textSecondary, ), ), - SizedBox(height: defaultSize), - DividerSpace(padding: EdgeInsets.zero), - SizedBox(height: defaultSize), - Padding( - padding: const EdgeInsets.only(left: 16), - child: Text( - 'available_on'.i18n, - style: theme.labelLarge!.copyWith( - color: AppColors.gray8, - ), + ), + SizedBox(height: defaultSize), + DividerSpace(padding: EdgeInsets.zero), + SizedBox(height: defaultSize), + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + 'available_on'.i18n, + style: theme.labelLarge!.copyWith( + color: buildContext.textSecondary, ), ), - SizedBox(height: 4.0), - _availableRow(), - ], - ), + ), + SizedBox(height: 4.0), + _availableRow(), + ], ); } diff --git a/lib/features/setting/follow_us.dart b/lib/features/setting/follow_us.dart index b62b0ba839..d3f37e2e19 100644 --- a/lib/features/setting/follow_us.dart +++ b/lib/features/setting/follow_us.dart @@ -116,17 +116,4 @@ class FollowUsListView extends HookWidget { } } -void showFollowUsBottomSheet({required BuildContext context}) { - showAppBottomSheet( - context: context, - title: 'follow_us'.i18n, - scrollControlDisabledMaxHeightRatio: context.isSmallDevice ? 0.39.h : 0.3.h, - builder: (context, scrollController) { - return Flexible( - child: FollowUsListView( - scrollController: scrollController, - ), - ); - }, - ); -} + diff --git a/lib/features/setting/invite_friends.dart b/lib/features/setting/invite_friends.dart index a6c4857bd5..c0ddfcb4b1 100644 --- a/lib/features/setting/invite_friends.dart +++ b/lib/features/setting/invite_friends.dart @@ -21,19 +21,20 @@ class InviteFriends extends HookConsumerWidget { Widget _buildBody(String referralCode) { final isCopied = useState(false); - final textTheme = Theme.of(useContext()).textTheme; + final context = useContext(); + final textTheme = Theme.of(context).textTheme; return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Center(child: AppImage(path: AppImagePaths.startIllustration)), + Center(child: AppImage(path: AppImagePaths.startIllustration,useThemeColor: false,)), SizedBox(height: defaultSize), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( 'your_referral_code'.i18n, style: textTheme.labelLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -50,7 +51,7 @@ class InviteFriends extends HookConsumerWidget { firstChild: AppImage(path: AppImagePaths.copy), secondChild: Icon( Icons.check_circle, - color: AppColors.green7, + color: context.statusSuccessBg, ), ), label: referralCode, @@ -66,7 +67,7 @@ class InviteFriends extends HookConsumerWidget { Text( 'invite_friends_message'.i18n, style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), SizedBox(height: defaultSize), @@ -76,19 +77,19 @@ class InviteFriends extends HookConsumerWidget { TextSpan( text: '- ', style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), TextSpan( text: 'monthly_plan'.i18n, style: AppTextStyles.bodyMediumBold.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), TextSpan( text: ' ${'15_days_each'.i18n}', style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ], @@ -101,19 +102,19 @@ class InviteFriends extends HookConsumerWidget { TextSpan( text: '- ', style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), TextSpan( text: 'annual_plan'.i18n, style: AppTextStyles.bodyMediumBold.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), TextSpan( text: ' ${'1_month_each'.i18n}', style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ], @@ -126,19 +127,19 @@ class InviteFriends extends HookConsumerWidget { TextSpan( text: '- ', style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), TextSpan( text: 'two_year_plan'.i18n, style: AppTextStyles.bodyMediumBold.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), TextSpan( text: ' ${'2_month_each'.i18n}', style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ], @@ -148,7 +149,7 @@ class InviteFriends extends HookConsumerWidget { Text( 'referral_code_info'.i18n, style: textTheme.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ], diff --git a/lib/features/setting/setting.dart b/lib/features/setting/setting.dart index bf8baaa109..20751ce92c 100644 --- a/lib/features/setting/setting.dart +++ b/lib/features/setting/setting.dart @@ -11,8 +11,8 @@ import 'package:lantern/core/utils/pro_utils.dart'; import 'package:lantern/core/widgets/subscription_tags.dart'; import 'package:lantern/features/home/provider/app_setting_notifier.dart'; import 'package:lantern/features/home/provider/home_notifier.dart'; -import 'package:lantern/features/setting/follow_us.dart' - show showFollowUsBottomSheet; +import 'package:lantern/features/setting/appearance.dart' + show appearanceModeLabel, showAppearanceBottomSheet; import '../../core/services/injection_container.dart'; import '../../lantern/lantern_service_notifier.dart'; @@ -24,9 +24,7 @@ enum _SettingType { language, appearance, support, - followUs, getPro, - downloadLinks, checkForUpdates, browserUnbounded, } @@ -50,6 +48,7 @@ class _SettingState extends ConsumerState { localIsPro && (localUser?.legacyUserData.unpassRegistered ?? false); final isAuthenticated = appSetting.userLoggedIn || hasProSession; final locale = appSetting.locale; + final themeMode = appSetting.themeMode; final textTheme = Theme.of(context).textTheme; final isUserPro = ref.watch(isUserProProvider); final user = ref.watch(homeProvider).value; @@ -100,7 +99,7 @@ class _SettingState extends ConsumerState { : Text( email, style: textTheme.labelMedium!.copyWith( - color: AppColors.blue7, + color: context.textLink, ), ), onPressed: () => settingMenuTap(_SettingType.account), @@ -133,19 +132,23 @@ class _SettingState extends ConsumerState { trailing: Text( displayLanguage(locale), style: textTheme.titleMedium!.copyWith( - color: AppColors.blue7, + color: context.textLink, ), ), onPressed: () => settingMenuTap(_SettingType.language), ), DividerSpace(), - if (PlatformUtils.isDesktop) - AppTile( - label: 'check_for_updates'.i18n, - icon: AppImagePaths.update, - onPressed: () async => - await settingMenuTap(_SettingType.checkForUpdates), + AppTile( + label: 'appearance'.i18n, + icon: AppImagePaths.theme, + trailing: Text( + appearanceModeLabel(themeMode), + style: textTheme.titleMedium!.copyWith( + color: context.textLink, + ), ), + onPressed: () => settingMenuTap(_SettingType.appearance), + ), ], ), ), @@ -159,18 +162,15 @@ class _SettingState extends ConsumerState { icon: AppImagePaths.support, onPressed: () => settingMenuTap(_SettingType.support), ), - DividerSpace(), - AppTile( - label: 'download_links'.i18n, - icon: AppImagePaths.desktop, - onPressed: () => settingMenuTap(_SettingType.downloadLinks), - ), - DividerSpace(), - AppTile( - label: 'follow_us'.i18n, - icon: AppImagePaths.thumb, - onPressed: () => settingMenuTap(_SettingType.followUs), - ), + if (PlatformUtils.isDesktop) ...{ + DividerSpace(), + AppTile( + label: 'check_for_updates'.i18n, + icon: AppImagePaths.update, + onPressed: () async => + await settingMenuTap(_SettingType.checkForUpdates), + ), + }, DividerSpace(), AppTile( label: 'get_30_days_of_pro_free'.i18n, @@ -220,7 +220,7 @@ class _SettingState extends ConsumerState { child: Text( 'lantern_projects'.i18n, style: textTheme.labelLarge!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -229,12 +229,13 @@ class _SettingState extends ConsumerState { child: AppTile( minHeight: 72, icon: AppImagePaths.lanternLogoRounded, + iconUseThemeColor: false, trailing: AppImage(path: AppImagePaths.outsideBrowser), label: 'unbounded'.i18n, subtitle: Text( 'help_fight_global_internet_censorship'.i18n, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), onPressed: () { @@ -257,24 +258,19 @@ class _SettingState extends ConsumerState { appRouter.push(Language()); return; case _SettingType.appearance: - // TODO: Handle this case. - throw UnimplementedError(); - case _SettingType.support: - appRouter.push(Support()); - break; - case _SettingType.followUs: if (PlatformUtils.isDesktop) { - appRouter.push(FollowUs()); + appRouter.push(const Appearance()); return; } - showFollowUsBottomSheet(context: context); + showAppearanceBottomSheet(context: context); + break; + case _SettingType.support: + appRouter.push(Support()); break; + case _SettingType.getPro: appRouter.push(InviteFriends()); break; - case _SettingType.downloadLinks: - appRouter.push(DownloadLinks()); - break; case _SettingType.checkForUpdates: await checkForUpdates(); break; diff --git a/lib/features/setting/smart_routing.dart b/lib/features/setting/smart_routing.dart index f261dbd6f7..7c22d0acc2 100644 --- a/lib/features/setting/smart_routing.dart +++ b/lib/features/setting/smart_routing.dart @@ -50,7 +50,7 @@ class SmartRouting extends HookConsumerWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, letterSpacing: 0.0, ), ), @@ -68,7 +68,7 @@ class SmartRouting extends HookConsumerWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, letterSpacing: 0.0, ), ), diff --git a/lib/features/setting/vpn_setting.dart b/lib/features/setting/vpn_setting.dart index 8e50586808..b1bbab6e11 100644 --- a/lib/features/setting/vpn_setting.dart +++ b/lib/features/setting/vpn_setting.dart @@ -84,7 +84,7 @@ class VPNSetting extends HookConsumerWidget { subtitle: Text( 'only_active'.i18n, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, letterSpacing: 0.0, ), ), @@ -163,7 +163,7 @@ class VPNSetting extends HookConsumerWidget { maxFontSize: 12, maxLines: 2, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, letterSpacing: 0.0, ), ), diff --git a/lib/features/split_tunneling/apps_split_tunneling.dart b/lib/features/split_tunneling/apps_split_tunneling.dart index 434898256a..855c7de694 100644 --- a/lib/features/split_tunneling/apps_split_tunneling.dart +++ b/lib/features/split_tunneling/apps_split_tunneling.dart @@ -198,9 +198,9 @@ class AppRow extends HookConsumerWidget { fit: BoxFit.cover, ); } - return Icon(Icons.apps, size: 24, color: AppColors.gray6); + return Icon(Icons.apps, size: 24, color: context.textDisabled); }, - orElse: () => Icon(Icons.apps, size: 24, color: AppColors.gray6), + orElse: () => Icon(Icons.apps, size: 24, color: context.textDisabled), ); } @@ -220,7 +220,7 @@ class AppRow extends HookConsumerWidget { style: AppTextStyles.bodyMedium.copyWith( fontSize: 16, fontWeight: FontWeight.w400, - color: AppColors.gray9, + color: context.textPrimary, ), ), ), diff --git a/lib/features/split_tunneling/split_tunneling.dart b/lib/features/split_tunneling/split_tunneling.dart index 5747f6f779..aaa6c77ffd 100644 --- a/lib/features/split_tunneling/split_tunneling.dart +++ b/lib/features/split_tunneling/split_tunneling.dart @@ -41,14 +41,14 @@ class SplitTunneling extends HookConsumerWidget { tileTextStyle: AppTextStyles.bodyMedium.copyWith( fontWeight: FontWeight.w600, fontSize: 16, - color: AppColors.gray9, + color: context.textPrimary, ), subtitle: Text( 'add_apps_websites_bypass_vpn'.i18n, maxLines: 1, overflow: TextOverflow.ellipsis, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, letterSpacing: 0.0, ), ), diff --git a/lib/features/split_tunneling/split_tunneling_info.dart b/lib/features/split_tunneling/split_tunneling_info.dart index 7f5f693ef7..0d0ece223c 100644 --- a/lib/features/split_tunneling/split_tunneling_info.dart +++ b/lib/features/split_tunneling/split_tunneling_info.dart @@ -50,7 +50,7 @@ class SplitTunnelingInfo extends HookConsumerWidget { 'split_tunneling_description'.i18n, style: AppTextStyles.bodyLarge.copyWith( height: 1.625, - color: AppColors.gray9, + color: context.textPrimary, ), ), ), @@ -62,7 +62,7 @@ class SplitTunnelingInfo extends HookConsumerWidget { 'location_based_rules'.i18n, style: AppTextStyles.bodyLarge.copyWith( height: 1.625, - color: AppColors.gray9, + color: context.textPrimary, ), ), ), @@ -73,10 +73,10 @@ class SplitTunnelingInfo extends HookConsumerWidget { 'unblocked_sites_bypass'.i18n, ], textStyle: AppTextStyles.bodyMedium.copyWith( - color: AppColors.gray8, + color: context.textSecondary, height: 1.5, ), - bulletColor: AppColors.gray7, + bulletColor: context.textTertiary, ), SubsectionTitle(icon: "✅", text: 'uncensored_regions'.i18n), @@ -86,10 +86,10 @@ class SplitTunnelingInfo extends HookConsumerWidget { 'examples_of_bypassed_sites'.i18n, ], textStyle: AppTextStyles.bodyMedium.copyWith( - color: AppColors.gray8, + color: context.textSecondary, height: 1.5, ), - bulletColor: AppColors.gray7, + bulletColor: context.textTertiary, ), SizedBox(height: 16.0), LinkText( @@ -139,7 +139,7 @@ class SubsectionTitle extends StatelessWidget { ? Theme.of(context).textTheme.titleLarge! : AppTextStyles.labelMedium.copyWith( fontSize: 16, - color: AppColors.gray9, + color: context.textPrimary, fontWeight: FontWeight.w600, ); diff --git a/lib/features/split_tunneling/website_domain_input.dart b/lib/features/split_tunneling/website_domain_input.dart index 6d114c9bc8..045d884b5b 100644 --- a/lib/features/split_tunneling/website_domain_input.dart +++ b/lib/features/split_tunneling/website_domain_input.dart @@ -99,7 +99,7 @@ class WebsiteDomainInput extends HookConsumerWidget { ), AppTextButton( label: 'add'.i18n, - textColor: AppColors.black, + textColor: context.textPrimary, onPressed: validateAndExtractDomain, ), ], @@ -109,7 +109,7 @@ class WebsiteDomainInput extends HookConsumerWidget { child: Text( 'use_commas'.i18n, style: AppTextStyles.bodyMedium.copyWith( - color: AppColors.gray7, + color: context.textTertiary, height: 1.6, fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/features/split_tunneling/website_split_tunneling.dart b/lib/features/split_tunneling/website_split_tunneling.dart index 477c3a5173..972df1615d 100644 --- a/lib/features/split_tunneling/website_split_tunneling.dart +++ b/lib/features/split_tunneling/website_split_tunneling.dart @@ -56,7 +56,7 @@ class WebsiteSplitTunneling extends HookConsumerWidget { child: Text( 'no_websites_selected'.i18n, style: textTheme.bodyLarge!.copyWith( - color: AppColors.gray9, + color: context.textPrimary, ), ), ) @@ -99,7 +99,7 @@ class WebsiteRow extends StatelessWidget { contentPadding: EdgeInsets.only(left: 16), label: website.domain, tileTextStyle: AppTextStyles.labelLarge.copyWith( - color: AppColors.gray8, + color: context.textSecondary, fontSize: 14, fontWeight: FontWeight.w500, ), diff --git a/lib/features/support/app_version.dart b/lib/features/support/app_version.dart index 144953d249..6364671d10 100644 --- a/lib/features/support/app_version.dart +++ b/lib/features/support/app_version.dart @@ -16,11 +16,11 @@ class AppVersion extends StatelessWidget { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( - color: AppColors.gray1, + color: context.bgSurface, borderRadius: BorderRadius.circular(8), border: Border( - top: BorderSide(color: AppColors.gray2, width: 1), - bottom: BorderSide(color: AppColors.gray2, width: 1), + top: BorderSide(color: context.borderDefault, width: 1), + bottom: BorderSide(color: context.borderDefault, width: 1), ), ), child: Row( @@ -28,7 +28,7 @@ class AppVersion extends StatelessWidget { children: [ Text('lantern_version'.i18n, style: theme.bodyMedium), Text(label, - style: theme.titleSmall!.copyWith(color: AppColors.blue7)), + style: theme.titleSmall!.copyWith(color: context.textLink)), ], ), ); diff --git a/lib/features/support/support.dart b/lib/features/support/support.dart index 08c528e3b2..8facefa771 100644 --- a/lib/features/support/support.dart +++ b/lib/features/support/support.dart @@ -4,6 +4,8 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:intl/intl.dart'; import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/utils/route_utils.dart'; +import 'package:lantern/core/utils/screen_utils.dart'; +import 'package:lantern/features/setting/follow_us.dart' hide FollowUs; import 'package:lantern/features/support/app_version.dart'; @RoutePage(name: 'Support') @@ -21,6 +23,7 @@ class Support extends StatelessWidget { Center( child: AppImage( path: AppImagePaths.supportIllustration, + useThemeColor: false, type: AssetType.svg, height: 180.h, width: 180.w, @@ -91,6 +94,34 @@ class Support extends StatelessWidget { ), ), const SizedBox(height: defaultSize), + AppCard( + padding: EdgeInsets.zero, + child: Column( + children: [ + DividerSpace(), + AppTile( + label: 'download_links'.i18n, + icon: AppImagePaths.desktop, + onPressed: () { + appRouter.push(DownloadLinks()); + }, + ), + DividerSpace(), + AppTile( + label: 'follow_us'.i18n, + icon: AppImagePaths.thumb, + onPressed: () { + if (PlatformUtils.isDesktop) { + appRouter.push(FollowUs()); + return; + } + showFollowUsBottomSheet(context: context); + }, + ), + ], + ), + ), + const SizedBox(height: defaultSize), const AppVersion(), const SizedBox(height: size24), ], @@ -98,4 +129,19 @@ class Support extends StatelessWidget { ), ); } + + void showFollowUsBottomSheet({required BuildContext context}) { + showAppBottomSheet( + context: context, + title: 'follow_us'.i18n, + scrollControlDisabledMaxHeightRatio: context.isSmallDevice ? 0.39.h : 0.3.h, + builder: (context, scrollController) { + return Flexible( + child: FollowUsListView( + scrollController: scrollController, + ), + ); + }, + ); + } } diff --git a/lib/features/system_tray/provider/system_tray_notifier.g.dart b/lib/features/system_tray/provider/system_tray_notifier.g.dart index 7ecf37290f..463367cc03 100644 --- a/lib/features/system_tray/provider/system_tray_notifier.g.dart +++ b/lib/features/system_tray/provider/system_tray_notifier.g.dart @@ -34,7 +34,7 @@ final class SystemTrayNotifierProvider } String _$systemTrayNotifierHash() => - r'f3b5715f964e92493d2736fbd019bf4b9ed292c5'; + r'ab73408f744c3428316f2022851ddc955ea6cc9c'; abstract class _$SystemTrayNotifier extends $AsyncNotifier { FutureOr build(); diff --git a/lib/features/vpn/location_setting.dart b/lib/features/vpn/location_setting.dart index a23061e459..70be915849 100644 --- a/lib/features/vpn/location_setting.dart +++ b/lib/features/vpn/location_setting.dart @@ -55,7 +55,7 @@ class LocationSetting extends HookConsumerWidget { icon: flag.isEmpty ? AppImagePaths.location : Flag(countryCode: flag), actions: [ if (serverType == ServerLocationType.auto) - AppImage(path: AppImagePaths.blot), + AppImage(path: AppImagePaths.blot,color: context.statusWarningBgDot,), const SizedBox(width: 8), IconButton( onPressed: () => appRouter.push(const ServerSelection()), diff --git a/lib/features/vpn/server_desktop_view.dart b/lib/features/vpn/server_desktop_view.dart index 04da819b59..7c0290bb7b 100644 --- a/lib/features/vpn/server_desktop_view.dart +++ b/lib/features/vpn/server_desktop_view.dart @@ -32,13 +32,13 @@ class _ServerDesktopViewState extends State { turns: isExpanded ? .25 : 0.0, child: Icon( Icons.arrow_forward_ios_rounded, - color: AppColors.gray9, + color: context.textPrimary, size: 20, ), ), title: Text( 'Korea', - style: textTheme.bodyLarge!.copyWith(color: AppColors.gray9), + style: textTheme.bodyLarge!.copyWith(color: context.textPrimary), ), shape: RoundedRectangleBorder(side: BorderSide.none), leading: AppImage(path: AppImagePaths.location), @@ -49,10 +49,10 @@ class _ServerDesktopViewState extends State { contentPadding: EdgeInsets.only(left: 46), label: 'USA - New Jersey', tileTextStyle: - textTheme.bodyMedium!.copyWith(color: AppColors.gray9), + textTheme.bodyMedium!.copyWith(color: context.textPrimary), trailing: Radio( visualDensity: VisualDensity.compact, - activeColor: AppColors.gray9, + activeColor: context.textPrimary, value: true, groupValue: false, onChanged: (value) {}, diff --git a/lib/features/vpn/server_selection.dart b/lib/features/vpn/server_selection.dart index ff6211ce7b..bf0459e364 100644 --- a/lib/features/vpn/server_selection.dart +++ b/lib/features/vpn/server_selection.dart @@ -58,7 +58,7 @@ class _ServerSelectionState extends ConsumerState { child: Text( 'automatically_chooses_fastest_location'.i18n, style: _textTheme?.bodyMedium!.copyWith( - color: AppColors.gray8, + color: context.textSecondary, ), ), ), @@ -81,7 +81,7 @@ class _ServerSelectionState extends ConsumerState { padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( 'automatically_chooses_fastest_location'.i18n, - style: _textTheme?.bodyMedium!.copyWith(color: AppColors.gray8), + style: _textTheme?.bodyMedium!.copyWith(color: context.textSecondary), ), ), SizedBox(height: size24), @@ -99,7 +99,7 @@ class _ServerSelectionState extends ConsumerState { labelPadding: EdgeInsets.zero, indicatorPadding: EdgeInsets.symmetric(horizontal: size24), indicator: BoxDecoration( - color: AppColors.blue2, + color: context.textLink, borderRadius: BorderRadius.circular(40), shape: BoxShape.rectangle, border: Border.all(color: AppColors.blue3, width: 1), @@ -138,7 +138,7 @@ class _ServerSelectionState extends ConsumerState { padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( 'smart_location'.i18n, - style: _textTheme?.labelLarge!.copyWith(color: AppColors.gray8), + style: _textTheme?.labelLarge!.copyWith(color: context.textSecondary), ), ), AppCard( @@ -155,12 +155,12 @@ class _ServerSelectionState extends ConsumerState { : Text( protocol.capitalize, style: _textTheme!.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), trailing: Row( mainAxisSize: MainAxisSize.min, - children: [AppImage(path: AppImagePaths.blot)], + children: [AppImage(path: AppImagePaths.blot,color: context.statusWarningBgDot,)], ), ), ), @@ -189,7 +189,7 @@ class _ServerSelectionState extends ConsumerState { : Text( serverLocation.protocol.capitalize, style: _textTheme!.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ); case ServerLocationType.privateServer: @@ -201,7 +201,7 @@ class _ServerSelectionState extends ConsumerState { child: Text( serverLocation.displayName, style: _textTheme!.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), ), @@ -209,7 +209,7 @@ class _ServerSelectionState extends ConsumerState { Text( serverLocation.protocol.capitalize, style: _textTheme!.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), ], @@ -383,7 +383,7 @@ class _ServerLocationListViewState if (!widget.userPro) Positioned.fill( child: Container( - color: AppColors.white.withValues(alpha: 0.72), + color: context.bgElevated.withValues(alpha: 0.72), alignment: Alignment.center, ), ), @@ -496,7 +496,7 @@ class _CountryCityListViewState extends State<_CountryCityListView> { country, style: Theme.of( context, - ).textTheme.bodyLarge!.copyWith(color: AppColors.gray9), + ).textTheme.bodyLarge!.copyWith(color: context.textPrimary), ), onExpansionChanged: (expanded) { setState(() => _isExpanded = expanded); @@ -516,11 +516,11 @@ class _CountryCityListViewState extends State<_CountryCityListView> { maxLines: 1, style: Theme.of( context, - ).textTheme.labelMedium!.copyWith(color: AppColors.gray7), + ).textTheme.labelMedium!.copyWith(color: context.textTertiary), ), tileTextStyle: Theme.of( context, - ).textTheme.bodyMedium!.copyWith(color: AppColors.gray9), + ).textTheme.bodyMedium!.copyWith(color: context.textPrimary), onPressed: () => _onLocationSelected(context, loc), ); }).toList(), @@ -533,7 +533,7 @@ class _CountryCityListViewState extends State<_CountryCityListView> { trailing: AppImage( path: AppImagePaths.arrowForward, height: 20.0, - color: AppColors.gray9, + color: context.textPrimary, ), onPressed: () => _showCountryBottomSheet(context), ); @@ -605,7 +605,7 @@ class _PrivateServerLocationListViewState Text( 'no_private_server_setup_yet'.i18n, textAlign: TextAlign.center, - style: _textTheme!.titleSmall!.copyWith(color: AppColors.gray8), + style: _textTheme!.titleSmall!.copyWith(color: context.textSecondary), ), SizedBox(height: 16), PrimaryButton( @@ -662,7 +662,7 @@ class _PrivateServerLocationListViewState child: Text( '${server.serverLocationName.locationName} - ${server.externalIp}', style: _textTheme!.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), ), @@ -670,7 +670,7 @@ class _PrivateServerLocationListViewState Text( server.protocol.capitalize, style: _textTheme!.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), ], @@ -709,7 +709,7 @@ class _PrivateServerLocationListViewState child: Text( '${server.serverLocationName} - ${server.externalIp}', style: _textTheme!.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), ), diff --git a/lib/features/vpn/single_city_server_view.dart b/lib/features/vpn/single_city_server_view.dart index 803a3f6c43..899f9b368b 100644 --- a/lib/features/vpn/single_city_server_view.dart +++ b/lib/features/vpn/single_city_server_view.dart @@ -38,7 +38,7 @@ class _SingleCityServerViewState extends State { : Text( widget.location.protocol.capitalize, style: textTheme.labelMedium!.copyWith( - color: AppColors.gray7, + color: context.textTertiary, ), ), icon: Flag(countryCode: widget.location.countryCode), diff --git a/lib/features/vpn/vpn_status.dart b/lib/features/vpn/vpn_status.dart index 612628b917..f759984e12 100644 --- a/lib/features/vpn/vpn_status.dart +++ b/lib/features/vpn/vpn_status.dart @@ -26,6 +26,7 @@ class VpnStatus extends HookConsumerWidget { label: 'vpn_status'.i18n, value: vpnStatus.name.capitalize, icon: AppImagePaths.glob, + onTap: isExtensionNeeded(systemExtensionStatus) ? () { appRouter.push(const MacOSExtensionDialog()); @@ -33,7 +34,7 @@ class VpnStatus extends HookConsumerWidget { : null, actions: [ if (isExtensionNeeded(systemExtensionStatus)) - AppImage(path: AppImagePaths.warning, color: AppColors.red6) + AppImage(path: AppImagePaths.warning, color: context.borderError) else VPNStatusIndicator(status: vpnStatus), ], @@ -43,23 +44,23 @@ class VpnStatus extends HookConsumerWidget { if (isExtensionNeeded(systemExtensionStatus)) Text( 'network_extension_required'.i18n, - style: textTheme.titleMedium!.copyWith(color: AppColors.gray9), + style: textTheme.titleMedium!.copyWith(color: context.textPrimary), ) else Text(vpnStatus.name.capitalize, style: textTheme.titleMedium! - .copyWith(color: getStatusColor(vpnStatus))), + .copyWith(color: getStatusColor(vpnStatus, context))), if (vpnStatus == VPNStatus.connecting) AnimatedTextKit( animatedTexts: [ TyperAnimatedText( '... ', textStyle: - textTheme.titleMedium!.copyWith(color: AppColors.gray9), + textTheme.titleMedium!.copyWith(color: context.textPrimary), ), TyperAnimatedText('...', textStyle: textTheme.titleMedium! - .copyWith(color: AppColors.gray9)), + .copyWith(color: context.textPrimary)), ], repeatForever: true, ) @@ -77,10 +78,10 @@ class VpnStatus extends HookConsumerWidget { systemExtensionStatus.status != SystemExtensionStatus.activated); } - Color getStatusColor(VPNStatus vpnStatus) { + Color getStatusColor(VPNStatus vpnStatus, BuildContext context) { if (vpnStatus == VPNStatus.connected) { - return AppColors.green6; + return context.statusSuccessBorder; } - return AppColors.gray9; + return context.textPrimary; } } diff --git a/lib/features/vpn/vpn_switch.dart b/lib/features/vpn/vpn_switch.dart index 72fd13d9b9..fc8803617a 100644 --- a/lib/features/vpn/vpn_switch.dart +++ b/lib/features/vpn/vpn_switch.dart @@ -53,7 +53,7 @@ class VPNSwitch extends HookConsumerWidget { padding: const EdgeInsets.all(8.0), child: CircularProgressIndicator( strokeWidth: 8.r, - color: AppColors.gray1, + color: context.actionToggleKnobBg, ), ), ); @@ -65,7 +65,7 @@ class VPNSwitch extends HookConsumerWidget { }, child: Container( decoration: BoxDecoration( - color: AppColors.gray1, + color: context.actionToggleKnobBg, borderRadius: BorderRadius.circular(30.r), ), ), @@ -75,7 +75,7 @@ class VPNSwitch extends HookConsumerWidget { return Container( padding: EdgeInsets.all(5.r), decoration: BoxDecoration( - color: _wrapperColor(vpnStatus), + color: _wrapperColor(vpnStatus, context), borderRadius: BorderRadius.circular(50.r), ), child: child, @@ -106,19 +106,19 @@ class VPNSwitch extends HookConsumerWidget { ); } - Color _wrapperColor(VPNStatus vpnStatus) { + Color _wrapperColor(VPNStatus vpnStatus, BuildContext context) { switch (vpnStatus) { case VPNStatus.connected: - return AppColors.blue4; + return context.actionToggleBrandActiveBg; case VPNStatus.connecting: case VPNStatus.disconnected: - return AppColors.gray7; + return context.actionToggleDisabledBg; case VPNStatus.disconnecting: - return AppColors.gray7; + return context.textTertiary; case VPNStatus.missingPermission: - return AppColors.gray7; + return context.textTertiary; case VPNStatus.error: - return AppColors.gray7; + return context.textTertiary; } } } diff --git a/lib/lantern_app.dart b/lib/lantern_app.dart index 01a36fef07..252338408d 100644 --- a/lib/lantern_app.dart +++ b/lib/lantern_app.dart @@ -158,7 +158,8 @@ class _LanternAppState extends ConsumerState { @override Widget build(BuildContext context) { - final locale = ref.watch(appSettingProvider).locale; + final appSetting = ref.watch(appSettingProvider); + final locale = appSetting.locale; Localization.defaultLocale = locale; return GlobalLoaderOverlay( overlayColor: Colors.black.withOpacity(0.5), @@ -182,9 +183,8 @@ class _LanternAppState extends ConsumerState { locale: locale.toLocale, debugShowCheckedModeBanner: false, theme: AppTheme.appTheme(), - - themeMode: ThemeMode.light, darkTheme: AppTheme.darkTheme(), + themeMode: resolveThemeMode(appSetting.themeMode), supportedLocales: languages .map((lang) => Locale(lang.split('_').first, lang.split('_').last))