11import 'dart:async' ;
22
33import 'package:app_flowy/user/application/user_settings_service.dart' ;
4+ import 'package:flowy_infra/size.dart' ;
45import 'package:flowy_infra/theme.dart' ;
6+ import 'package:flowy_infra/theme_extension.dart' ;
57import 'package:flowy_sdk/log.dart' ;
68import 'package:flowy_sdk/protobuf/flowy-user/user_setting.pb.dart' ;
79import 'package:flutter/material.dart' ;
@@ -11,7 +13,10 @@ import 'package:freezed_annotation/freezed_annotation.dart';
1113
1214part 'appearance.freezed.dart' ;
1315
14- /// [AppearanceSettingsCubit] is used to modify the appear setting of AppFlowy application. Includes the [Locale] and [AppTheme] .
16+ const _white = Color (0xFFFFFFFF );
17+
18+ /// [AppearanceSettingsCubit] is used to modify the appearance of AppFlowy.
19+ /// It includes the [AppTheme] , [ThemeMode] , [TextStyles] and [Locale] .
1520class AppearanceSettingsCubit extends Cubit <AppearanceSettingsState > {
1621 final AppearanceSettingsPB _setting;
1722
@@ -25,40 +30,23 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
2530 setting.locale,
2631 ));
2732
28- /// Updates the current theme and notify the listeners the theme was changed.
29- /// Do nothing if the passed in themeType equal to the current theme type.
30- // void setTheme(Brightness brightness) {
31- // if (state.theme.brightness == brightness) {
32- // return;
33- // }
34-
35- // _setting.theme = themeTypeToString(brightness);
36- // _saveAppearanceSettings();
37-
38- // emit(state.copyWith(
39- // theme: AppTheme.fromBrightness(
40- // brightness: _setting.themeMode,
41- // font: state.theme.font,
42- // monospaceFont: state.theme.monospaceFont,
43- // ),
44- // ));
45- // }
46-
47- /// Updates the current theme and notify the listeners the theme was changed.
48- /// Do nothing if the passed in themeType equal to the current theme type.
49- void setThemeMode (ThemeMode themeMode) {
50- if (state.themeMode == themeMode) {
51- return ;
52- }
33+ /// Update selected theme in the user's settings and emit an updated state
34+ /// with the AppTheme named [themeName] .
35+ void setTheme (String themeName) {
36+ _setting.theme = themeName;
37+ _saveAppearanceSettings ();
38+ emit (state.copyWith (appTheme: AppTheme .fromName (themeName: themeName)));
39+ }
5340
41+ /// Update the theme mode in the user's settings and emit an updated state.
42+ void setThemeMode (ThemeMode themeMode) {
5443 _setting.themeMode = _themeModeToPB (themeMode);
5544 _saveAppearanceSettings ();
56-
5745 emit (state.copyWith (themeMode: themeMode));
5846 }
5947
60- /// Updates the current locale and notify the listeners the locale was changed
61- /// Fallback to [en] locale If the newLocale is not supported.
48+ /// Updates the current locale and notify the listeners the locale was
49+ /// changed. Fallback to [en] locale if [ newLocale] is not supported.
6250 void setLocale (BuildContext context, Locale newLocale) {
6351 if (! context.supportedLocales.contains (newLocale)) {
6452 Log .warn ("Unsupported locale: $newLocale , Fallback to locale: en" );
@@ -106,8 +94,8 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
10694 return _setting.settingKeyValue[key];
10795 }
10896
109- /// Called when the application launch .
110- /// Uses the device locale when open the application for the first time
97+ /// Called when the application launches .
98+ /// Uses the device locale when the application is opened for the first time.
11199 void readLocaleWhenAppLaunch (BuildContext context) {
112100 if (_setting.resetToDefault) {
113101 _setting.resetToDefault = false ;
@@ -155,32 +143,200 @@ ThemeModePB _themeModeToPB(ThemeMode themeMode) {
155143
156144@freezed
157145class AppearanceSettingsState with _$AppearanceSettingsState {
146+ const AppearanceSettingsState ._();
147+
158148 const factory AppearanceSettingsState ({
159- required AppTheme theme,
160- required AppTheme darkTheme,
149+ required AppTheme appTheme,
161150 required ThemeMode themeMode,
151+ required String font,
152+ required String monospaceFont,
162153 required Locale locale,
163154 }) = _AppearanceSettingsState ;
164155
165156 factory AppearanceSettingsState .initial (
166157 String themeName,
167- ThemeModePB themeMode ,
158+ ThemeModePB themeModePB ,
168159 String font,
169160 String monospaceFont,
170- LocaleSettingsPB locale,
171- ) =>
172- AppearanceSettingsState (
173- theme: AppTheme .fromBrightness (
174- brightness: Brightness .light,
175- font: font,
176- monospaceFont: monospaceFont,
177- ),
178- darkTheme: AppTheme .fromBrightness (
179- brightness: Brightness .dark,
180- font: font,
181- monospaceFont: monospaceFont,
182- ),
183- themeMode: _themeModeFromPB (themeMode),
184- locale: Locale (locale.languageCode, locale.countryCode),
161+ LocaleSettingsPB localePB,
162+ ) {
163+ return AppearanceSettingsState (
164+ appTheme: AppTheme .fromName (themeName: themeName),
165+ font: font,
166+ monospaceFont: monospaceFont,
167+ themeMode: _themeModeFromPB (themeModePB),
168+ locale: Locale (localePB.languageCode, localePB.countryCode),
169+ );
170+ }
171+
172+ ThemeData get lightTheme => _getThemeData (Brightness .light);
173+ ThemeData get darkTheme => _getThemeData (Brightness .dark);
174+
175+ ThemeData _getThemeData (Brightness brightness) {
176+ // Poppins and SF Mono are not well supported in some languages, so use the
177+ // built-in font for the following languages.
178+ final useBuiltInFontLanguages = [
179+ const Locale ('zh' , 'CN' ),
180+ const Locale ('zh' , 'TW' ),
181+ ];
182+ String fontFamily = font;
183+ String monospaceFontFamily = monospaceFont;
184+ if (useBuiltInFontLanguages.contains (locale)) {
185+ fontFamily = '' ;
186+ monospaceFontFamily = '' ;
187+ }
188+
189+ final theme = brightness == Brightness .light
190+ ? appTheme.lightTheme
191+ : appTheme.darkTheme;
192+
193+ return ThemeData (
194+ brightness: brightness,
195+ textTheme:
196+ _getTextTheme (fontFamily: fontFamily, fontColor: theme.shader1),
197+ textSelectionTheme: TextSelectionThemeData (
198+ cursorColor: theme.main2,
199+ selectionHandleColor: theme.main2,
200+ ),
201+ primaryIconTheme: IconThemeData (color: theme.hover),
202+ iconTheme: IconThemeData (color: theme.shader1),
203+ scrollbarTheme: ScrollbarThemeData (
204+ thumbColor: MaterialStateProperty .all (Colors .transparent),
205+ ),
206+ materialTapTargetSize: MaterialTapTargetSize .shrinkWrap,
207+ canvasColor: theme.shader6,
208+ dividerColor: theme.shader6,
209+ hintColor: theme.shader3,
210+ disabledColor: theme.shader4,
211+ highlightColor: theme.main1,
212+ indicatorColor: theme.main1,
213+ toggleableActiveColor: theme.main1,
214+ colorScheme: ColorScheme (
215+ brightness: brightness,
216+ primary: theme.main1,
217+ onPrimary: _white,
218+ primaryContainer: theme.main2,
219+ onPrimaryContainer: _white,
220+ secondary: theme.hover,
221+ onSecondary: theme.shader1,
222+ secondaryContainer: theme.selector,
223+ onSecondaryContainer: theme.shader1,
224+ background: theme.surface,
225+ onBackground: theme.shader1,
226+ surface: theme.surface,
227+ onSurface: theme.shader1,
228+ onError: theme.shader7,
229+ error: theme.red,
230+ outline: theme.shader4,
231+ surfaceVariant: theme.bg1,
232+ shadow: theme.shadow,
233+ ),
234+ extensions: [
235+ AFThemeExtension (
236+ warning: theme.yellow,
237+ success: theme.green,
238+ tint1: theme.tint1,
239+ tint2: theme.tint2,
240+ tint3: theme.tint3,
241+ tint4: theme.tint4,
242+ tint5: theme.tint5,
243+ tint6: theme.tint6,
244+ tint7: theme.tint7,
245+ tint8: theme.tint8,
246+ tint9: theme.tint9,
247+ greyHover: theme.bg2,
248+ greySelect: theme.bg3,
249+ lightGreyHover: theme.shader6,
250+ toggleOffFill: theme.shader5,
251+ code: _getFontStyle (
252+ fontFamily: monospaceFontFamily,
253+ fontColor: theme.shader3,
254+ ),
255+ callout: _getFontStyle (
256+ fontFamily: fontFamily,
257+ fontSize: FontSizes .s11,
258+ fontColor: theme.shader3,
259+ ),
260+ caption: _getFontStyle (
261+ fontFamily: fontFamily,
262+ fontSize: FontSizes .s11,
263+ fontWeight: FontWeight .w400,
264+ fontColor: theme.shader3,
265+ ),
266+ )
267+ ],
268+ );
269+ }
270+
271+ TextStyle _getFontStyle ({
272+ String ? fontFamily,
273+ double ? fontSize,
274+ FontWeight ? fontWeight,
275+ Color ? fontColor,
276+ double ? letterSpacing,
277+ double ? lineHeight,
278+ }) =>
279+ TextStyle (
280+ fontFamily: fontFamily,
281+ fontSize: fontSize ?? FontSizes .s12,
282+ color: fontColor,
283+ fontWeight: fontWeight ?? FontWeight .w500,
284+ fontFamilyFallback: const ["Noto Color Emoji" ],
285+ letterSpacing: (fontSize ?? FontSizes .s12) * (letterSpacing ?? 0.005 ),
286+ height: lineHeight,
185287 );
288+
289+ TextTheme _getTextTheme (
290+ {required String fontFamily, required Color fontColor}) {
291+ return TextTheme (
292+ displayLarge: _getFontStyle (
293+ fontFamily: fontFamily,
294+ fontSize: FontSizes .s32,
295+ fontColor: fontColor,
296+ fontWeight: FontWeight .w600,
297+ lineHeight: 42.0 ,
298+ ), // h2
299+ displayMedium: _getFontStyle (
300+ fontFamily: fontFamily,
301+ fontSize: FontSizes .s24,
302+ fontColor: fontColor,
303+ fontWeight: FontWeight .w600,
304+ lineHeight: 34.0 ,
305+ ), // h3
306+ displaySmall: _getFontStyle (
307+ fontFamily: fontFamily,
308+ fontSize: FontSizes .s20,
309+ fontColor: fontColor,
310+ fontWeight: FontWeight .w600,
311+ lineHeight: 28.0 ,
312+ ), // h4
313+ titleLarge: _getFontStyle (
314+ fontFamily: fontFamily,
315+ fontSize: FontSizes .s18,
316+ fontColor: fontColor,
317+ fontWeight: FontWeight .w600,
318+ ), // title
319+ titleMedium: _getFontStyle (
320+ fontFamily: fontFamily,
321+ fontSize: FontSizes .s16,
322+ fontColor: fontColor,
323+ fontWeight: FontWeight .w600,
324+ ), // heading
325+ titleSmall: _getFontStyle (
326+ fontFamily: fontFamily,
327+ fontSize: FontSizes .s14,
328+ fontColor: fontColor,
329+ fontWeight: FontWeight .w600,
330+ ), // subheading
331+ bodyMedium: _getFontStyle (
332+ fontFamily: fontFamily,
333+ fontColor: fontColor,
334+ ), // body-regular
335+ bodySmall: _getFontStyle (
336+ fontFamily: fontFamily,
337+ fontColor: fontColor,
338+ fontWeight: FontWeight .w400,
339+ ), // body-thin
340+ );
341+ }
186342}
0 commit comments