Skip to content

Commit 60d9a6b

Browse files
authored
refactor: appflowy themes (#1567)
* refactor: appflowy themes * refactor: store ThemeData directly in cubit * refactor: remove textStyles * refactor: move AppTheme back into cubit
1 parent 67e350e commit 60d9a6b

File tree

7 files changed

+398
-376
lines changed

7 files changed

+398
-376
lines changed

frontend/app_flowy/lib/startup/tasks/app_widget.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ class ApplicationWidget extends StatelessWidget {
8282
builder: (context, state) => MaterialApp(
8383
builder: overlayManagerBuilder(),
8484
debugShowCheckedModeBanner: false,
85-
theme: state.theme.getThemeData(state.locale),
86-
darkTheme: state.darkTheme.getThemeData(state.locale),
85+
theme: state.lightTheme,
86+
darkTheme: state.darkTheme,
8787
themeMode: state.themeMode,
8888
localizationsDelegates: context.localizationDelegates +
8989
[AppFlowyEditorLocalizations.delegate],

frontend/app_flowy/lib/workspace/application/appearance.dart

Lines changed: 205 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import 'dart:async';
22

33
import 'package:app_flowy/user/application/user_settings_service.dart';
4+
import 'package:flowy_infra/size.dart';
45
import 'package:flowy_infra/theme.dart';
6+
import 'package:flowy_infra/theme_extension.dart';
57
import 'package:flowy_sdk/log.dart';
68
import 'package:flowy_sdk/protobuf/flowy-user/user_setting.pb.dart';
79
import 'package:flutter/material.dart';
@@ -11,7 +13,10 @@ import 'package:freezed_annotation/freezed_annotation.dart';
1113

1214
part '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].
1520
class 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
157145
class 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
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import 'package:flutter/material.dart';
2+
3+
import 'default_colorscheme.dart';
4+
5+
@immutable
6+
abstract class FlowyColorScheme {
7+
final Color surface;
8+
final Color hover;
9+
final Color selector;
10+
final Color red;
11+
final Color yellow;
12+
final Color green;
13+
final Color shader1;
14+
final Color shader2;
15+
final Color shader3;
16+
final Color shader4;
17+
final Color shader5;
18+
final Color shader6;
19+
final Color shader7;
20+
final Color bg1;
21+
final Color bg2;
22+
final Color bg3;
23+
final Color bg4;
24+
final Color tint1;
25+
final Color tint2;
26+
final Color tint3;
27+
final Color tint4;
28+
final Color tint5;
29+
final Color tint6;
30+
final Color tint7;
31+
final Color tint8;
32+
final Color tint9;
33+
final Color main1;
34+
final Color main2;
35+
final Color shadow;
36+
37+
const FlowyColorScheme({
38+
required this.surface,
39+
required this.hover,
40+
required this.selector,
41+
required this.red,
42+
required this.yellow,
43+
required this.green,
44+
required this.shader1,
45+
required this.shader2,
46+
required this.shader3,
47+
required this.shader4,
48+
required this.shader5,
49+
required this.shader6,
50+
required this.shader7,
51+
required this.bg1,
52+
required this.bg2,
53+
required this.bg3,
54+
required this.bg4,
55+
required this.tint1,
56+
required this.tint2,
57+
required this.tint3,
58+
required this.tint4,
59+
required this.tint5,
60+
required this.tint6,
61+
required this.tint7,
62+
required this.tint8,
63+
required this.tint9,
64+
required this.main1,
65+
required this.main2,
66+
required this.shadow,
67+
});
68+
69+
factory FlowyColorScheme.builtIn(String themeName, Brightness brightness) {
70+
switch (brightness) {
71+
case Brightness.light:
72+
return const DefaultColorScheme.light();
73+
case Brightness.dark:
74+
return const DefaultColorScheme.dark();
75+
}
76+
}
77+
78+
// factory FlowyColorScheme.fromJson(Map<String, dynamic> json, Brightness brightness) {
79+
// // load Json
80+
81+
// return FlowyColorScheme(brightness...);
82+
// }
83+
}

0 commit comments

Comments
 (0)