|
@@ -1,7 +1,9 @@
|
|
|
import 'dart:async';
|
|
|
|
|
|
import 'package:app_flowy/user/application/user_settings_service.dart';
|
|
|
+import 'package:flowy_infra/size.dart';
|
|
|
import 'package:flowy_infra/theme.dart';
|
|
|
+import 'package:flowy_infra/theme_extension.dart';
|
|
|
import 'package:flowy_sdk/log.dart';
|
|
|
import 'package:flowy_sdk/protobuf/flowy-user/user_setting.pb.dart';
|
|
|
import 'package:flutter/material.dart';
|
|
@@ -11,7 +13,10 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
|
|
|
|
|
part 'appearance.freezed.dart';
|
|
|
|
|
|
-/// [AppearanceSettingsCubit] is used to modify the appear setting of AppFlowy application. Includes the [Locale] and [AppTheme].
|
|
|
+const _white = Color(0xFFFFFFFF);
|
|
|
+
|
|
|
+/// [AppearanceSettingsCubit] is used to modify the appearance of AppFlowy.
|
|
|
+/// It includes the [AppTheme], [ThemeMode], [TextStyles] and [Locale].
|
|
|
class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
|
|
|
final AppearanceSettingsPB _setting;
|
|
|
|
|
@@ -25,40 +30,23 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
|
|
|
setting.locale,
|
|
|
));
|
|
|
|
|
|
- /// Updates the current theme and notify the listeners the theme was changed.
|
|
|
- /// Do nothing if the passed in themeType equal to the current theme type.
|
|
|
- // void setTheme(Brightness brightness) {
|
|
|
- // if (state.theme.brightness == brightness) {
|
|
|
- // return;
|
|
|
- // }
|
|
|
-
|
|
|
- // _setting.theme = themeTypeToString(brightness);
|
|
|
- // _saveAppearanceSettings();
|
|
|
-
|
|
|
- // emit(state.copyWith(
|
|
|
- // theme: AppTheme.fromBrightness(
|
|
|
- // brightness: _setting.themeMode,
|
|
|
- // font: state.theme.font,
|
|
|
- // monospaceFont: state.theme.monospaceFont,
|
|
|
- // ),
|
|
|
- // ));
|
|
|
- // }
|
|
|
-
|
|
|
- /// Updates the current theme and notify the listeners the theme was changed.
|
|
|
- /// Do nothing if the passed in themeType equal to the current theme type.
|
|
|
- void setThemeMode(ThemeMode themeMode) {
|
|
|
- if (state.themeMode == themeMode) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ /// Update selected theme in the user's settings and emit an updated state
|
|
|
+ /// with the AppTheme named [themeName].
|
|
|
+ void setTheme(String themeName) {
|
|
|
+ _setting.theme = themeName;
|
|
|
+ _saveAppearanceSettings();
|
|
|
+ emit(state.copyWith(appTheme: AppTheme.fromName(themeName: themeName)));
|
|
|
+ }
|
|
|
|
|
|
+ /// Update the theme mode in the user's settings and emit an updated state.
|
|
|
+ void setThemeMode(ThemeMode themeMode) {
|
|
|
_setting.themeMode = _themeModeToPB(themeMode);
|
|
|
_saveAppearanceSettings();
|
|
|
-
|
|
|
emit(state.copyWith(themeMode: themeMode));
|
|
|
}
|
|
|
|
|
|
- /// Updates the current locale and notify the listeners the locale was changed
|
|
|
- /// Fallback to [en] locale If the newLocale is not supported.
|
|
|
+ /// Updates the current locale and notify the listeners the locale was
|
|
|
+ /// changed. Fallback to [en] locale if [newLocale] is not supported.
|
|
|
void setLocale(BuildContext context, Locale newLocale) {
|
|
|
if (!context.supportedLocales.contains(newLocale)) {
|
|
|
Log.warn("Unsupported locale: $newLocale, Fallback to locale: en");
|
|
@@ -106,8 +94,8 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
|
|
|
return _setting.settingKeyValue[key];
|
|
|
}
|
|
|
|
|
|
- /// Called when the application launch.
|
|
|
- /// Uses the device locale when open the application for the first time
|
|
|
+ /// Called when the application launches.
|
|
|
+ /// Uses the device locale when the application is opened for the first time.
|
|
|
void readLocaleWhenAppLaunch(BuildContext context) {
|
|
|
if (_setting.resetToDefault) {
|
|
|
_setting.resetToDefault = false;
|
|
@@ -155,32 +143,200 @@ ThemeModePB _themeModeToPB(ThemeMode themeMode) {
|
|
|
|
|
|
@freezed
|
|
|
class AppearanceSettingsState with _$AppearanceSettingsState {
|
|
|
+ const AppearanceSettingsState._();
|
|
|
+
|
|
|
const factory AppearanceSettingsState({
|
|
|
- required AppTheme theme,
|
|
|
- required AppTheme darkTheme,
|
|
|
+ required AppTheme appTheme,
|
|
|
required ThemeMode themeMode,
|
|
|
+ required String font,
|
|
|
+ required String monospaceFont,
|
|
|
required Locale locale,
|
|
|
}) = _AppearanceSettingsState;
|
|
|
|
|
|
factory AppearanceSettingsState.initial(
|
|
|
String themeName,
|
|
|
- ThemeModePB themeMode,
|
|
|
+ ThemeModePB themeModePB,
|
|
|
String font,
|
|
|
String monospaceFont,
|
|
|
- LocaleSettingsPB locale,
|
|
|
- ) =>
|
|
|
- AppearanceSettingsState(
|
|
|
- theme: AppTheme.fromBrightness(
|
|
|
- brightness: Brightness.light,
|
|
|
- font: font,
|
|
|
- monospaceFont: monospaceFont,
|
|
|
- ),
|
|
|
- darkTheme: AppTheme.fromBrightness(
|
|
|
- brightness: Brightness.dark,
|
|
|
- font: font,
|
|
|
- monospaceFont: monospaceFont,
|
|
|
- ),
|
|
|
- themeMode: _themeModeFromPB(themeMode),
|
|
|
- locale: Locale(locale.languageCode, locale.countryCode),
|
|
|
+ LocaleSettingsPB localePB,
|
|
|
+ ) {
|
|
|
+ return AppearanceSettingsState(
|
|
|
+ appTheme: AppTheme.fromName(themeName: themeName),
|
|
|
+ font: font,
|
|
|
+ monospaceFont: monospaceFont,
|
|
|
+ themeMode: _themeModeFromPB(themeModePB),
|
|
|
+ locale: Locale(localePB.languageCode, localePB.countryCode),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ ThemeData get lightTheme => _getThemeData(Brightness.light);
|
|
|
+ ThemeData get darkTheme => _getThemeData(Brightness.dark);
|
|
|
+
|
|
|
+ ThemeData _getThemeData(Brightness brightness) {
|
|
|
+ // Poppins and SF Mono are not well supported in some languages, so use the
|
|
|
+ // built-in font for the following languages.
|
|
|
+ final useBuiltInFontLanguages = [
|
|
|
+ const Locale('zh', 'CN'),
|
|
|
+ const Locale('zh', 'TW'),
|
|
|
+ ];
|
|
|
+ String fontFamily = font;
|
|
|
+ String monospaceFontFamily = monospaceFont;
|
|
|
+ if (useBuiltInFontLanguages.contains(locale)) {
|
|
|
+ fontFamily = '';
|
|
|
+ monospaceFontFamily = '';
|
|
|
+ }
|
|
|
+
|
|
|
+ final theme = brightness == Brightness.light
|
|
|
+ ? appTheme.lightTheme
|
|
|
+ : appTheme.darkTheme;
|
|
|
+
|
|
|
+ return ThemeData(
|
|
|
+ brightness: brightness,
|
|
|
+ textTheme:
|
|
|
+ _getTextTheme(fontFamily: fontFamily, fontColor: theme.shader1),
|
|
|
+ textSelectionTheme: TextSelectionThemeData(
|
|
|
+ cursorColor: theme.main2,
|
|
|
+ selectionHandleColor: theme.main2,
|
|
|
+ ),
|
|
|
+ primaryIconTheme: IconThemeData(color: theme.hover),
|
|
|
+ iconTheme: IconThemeData(color: theme.shader1),
|
|
|
+ scrollbarTheme: ScrollbarThemeData(
|
|
|
+ thumbColor: MaterialStateProperty.all(Colors.transparent),
|
|
|
+ ),
|
|
|
+ materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
|
+ canvasColor: theme.shader6,
|
|
|
+ dividerColor: theme.shader6,
|
|
|
+ hintColor: theme.shader3,
|
|
|
+ disabledColor: theme.shader4,
|
|
|
+ highlightColor: theme.main1,
|
|
|
+ indicatorColor: theme.main1,
|
|
|
+ toggleableActiveColor: theme.main1,
|
|
|
+ colorScheme: ColorScheme(
|
|
|
+ brightness: brightness,
|
|
|
+ primary: theme.main1,
|
|
|
+ onPrimary: _white,
|
|
|
+ primaryContainer: theme.main2,
|
|
|
+ onPrimaryContainer: _white,
|
|
|
+ secondary: theme.hover,
|
|
|
+ onSecondary: theme.shader1,
|
|
|
+ secondaryContainer: theme.selector,
|
|
|
+ onSecondaryContainer: theme.shader1,
|
|
|
+ background: theme.surface,
|
|
|
+ onBackground: theme.shader1,
|
|
|
+ surface: theme.surface,
|
|
|
+ onSurface: theme.shader1,
|
|
|
+ onError: theme.shader7,
|
|
|
+ error: theme.red,
|
|
|
+ outline: theme.shader4,
|
|
|
+ surfaceVariant: theme.bg1,
|
|
|
+ shadow: theme.shadow,
|
|
|
+ ),
|
|
|
+ extensions: [
|
|
|
+ AFThemeExtension(
|
|
|
+ warning: theme.yellow,
|
|
|
+ success: theme.green,
|
|
|
+ tint1: theme.tint1,
|
|
|
+ tint2: theme.tint2,
|
|
|
+ tint3: theme.tint3,
|
|
|
+ tint4: theme.tint4,
|
|
|
+ tint5: theme.tint5,
|
|
|
+ tint6: theme.tint6,
|
|
|
+ tint7: theme.tint7,
|
|
|
+ tint8: theme.tint8,
|
|
|
+ tint9: theme.tint9,
|
|
|
+ greyHover: theme.bg2,
|
|
|
+ greySelect: theme.bg3,
|
|
|
+ lightGreyHover: theme.shader6,
|
|
|
+ toggleOffFill: theme.shader5,
|
|
|
+ code: _getFontStyle(
|
|
|
+ fontFamily: monospaceFontFamily,
|
|
|
+ fontColor: theme.shader3,
|
|
|
+ ),
|
|
|
+ callout: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: FontSizes.s11,
|
|
|
+ fontColor: theme.shader3,
|
|
|
+ ),
|
|
|
+ caption: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: FontSizes.s11,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ fontColor: theme.shader3,
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ TextStyle _getFontStyle({
|
|
|
+ String? fontFamily,
|
|
|
+ double? fontSize,
|
|
|
+ FontWeight? fontWeight,
|
|
|
+ Color? fontColor,
|
|
|
+ double? letterSpacing,
|
|
|
+ double? lineHeight,
|
|
|
+ }) =>
|
|
|
+ TextStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: fontSize ?? FontSizes.s12,
|
|
|
+ color: fontColor,
|
|
|
+ fontWeight: fontWeight ?? FontWeight.w500,
|
|
|
+ fontFamilyFallback: const ["Noto Color Emoji"],
|
|
|
+ letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005),
|
|
|
+ height: lineHeight,
|
|
|
);
|
|
|
+
|
|
|
+ TextTheme _getTextTheme(
|
|
|
+ {required String fontFamily, required Color fontColor}) {
|
|
|
+ return TextTheme(
|
|
|
+ displayLarge: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: FontSizes.s32,
|
|
|
+ fontColor: fontColor,
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
+ lineHeight: 42.0,
|
|
|
+ ), // h2
|
|
|
+ displayMedium: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: FontSizes.s24,
|
|
|
+ fontColor: fontColor,
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
+ lineHeight: 34.0,
|
|
|
+ ), // h3
|
|
|
+ displaySmall: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: FontSizes.s20,
|
|
|
+ fontColor: fontColor,
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
+ lineHeight: 28.0,
|
|
|
+ ), // h4
|
|
|
+ titleLarge: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: FontSizes.s18,
|
|
|
+ fontColor: fontColor,
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
+ ), // title
|
|
|
+ titleMedium: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: FontSizes.s16,
|
|
|
+ fontColor: fontColor,
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
+ ), // heading
|
|
|
+ titleSmall: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontSize: FontSizes.s14,
|
|
|
+ fontColor: fontColor,
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
+ ), // subheading
|
|
|
+ bodyMedium: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontColor: fontColor,
|
|
|
+ ), // body-regular
|
|
|
+ bodySmall: _getFontStyle(
|
|
|
+ fontFamily: fontFamily,
|
|
|
+ fontColor: fontColor,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ), // body-thin
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|