refactor: use dependency injection to adapt the theme on mobile. (#3957)

This commit is contained in:
Lucas.Xu 2023-11-20 10:35:13 +08:00 committed by GitHub
parent 8179419f8b
commit 6f83f41c2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 599 additions and 551 deletions

View File

@ -1,275 +0,0 @@
// ThemeData in mobile
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:flowy_infra/colorscheme/colorscheme.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
const _primaryColor = Color(0xFF2DA2F6); //primary 100
const _onBackgroundColor = Color(0xff2F3030); // text/title color
const _onSurfaceColor = Color(0xff676666); // text/body color
const _onSecondaryColor = Color(0xFFC5C7CB); // text/body2 color
// TODO(yijing): improve theme color before release
ThemeData getMobileThemeData(
Brightness brightness,
FlowyColorScheme theme,
String fontFamily,
String monospaceFontFamily,
) {
fontFamily = GoogleFonts.getFont(fontFamily).fontFamily ?? fontFamily;
final mobileColorTheme = (brightness == Brightness.light)
? ColorScheme(
brightness: brightness,
primary: _primaryColor,
onPrimary: Colors.white,
// TODO(yijing): add color later
secondary: Colors.white,
onSecondary: _onSecondaryColor,
error: const Color(0xffFB006D),
onError: const Color(0xffFB006D),
background: Colors.white,
onBackground: _onBackgroundColor,
outline: const Color(0xffBDC0C5), //caption
outlineVariant: const Color(0xffCBD5E0).withOpacity(0.24),
//Snack bar
surface: Colors.white,
onSurface: _onSurfaceColor, // text/body color
surfaceVariant: const Color.fromARGB(255, 216, 216, 216),
)
: ColorScheme(
brightness: brightness,
primary: _primaryColor,
onPrimary: Colors.white,
// TODO(yijing): add color later
secondary: Colors.black,
onSecondary: Colors.white,
error: const Color(0xffFB006D),
onError: const Color(0xffFB006D),
background: const Color(0xff1C1C1E), // BG/Secondary color
onBackground: Colors.white,
outline: const Color(0xff96989C), //caption
outlineVariant: Colors.black,
//Snack bar
surface: const Color(0xff2F3030),
onSurface: const Color(0xffC5C6C7), // text/body color
);
return ThemeData(
// color
primaryColor: mobileColorTheme.primary, //primary 100
primaryColorLight: const Color(0xFF57B5F8), //primary 80
dividerColor: mobileColorTheme.outline, //caption
hintColor: mobileColorTheme.outline,
disabledColor: mobileColorTheme.outline,
scaffoldBackgroundColor: mobileColorTheme.background,
appBarTheme: AppBarTheme(
foregroundColor: mobileColorTheme.onBackground,
backgroundColor: mobileColorTheme.background,
centerTitle: false,
titleTextStyle: TextStyle(
color: mobileColorTheme.onBackground,
fontSize: 18,
fontWeight: FontWeight.w600,
letterSpacing: 0.05,
),
shadowColor: mobileColorTheme.outlineVariant,
),
radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return mobileColorTheme.primary;
}
return mobileColorTheme.outline;
}),
),
// button
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
fixedSize: MaterialStateProperty.all(const Size.fromHeight(48)),
elevation: MaterialStateProperty.all(0),
textStyle: MaterialStateProperty.all(
const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
shadowColor: MaterialStateProperty.all(null),
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return const Color(0xFF57B5F8);
}
return mobileColorTheme.primary;
},
),
foregroundColor: MaterialStateProperty.all(Colors.white),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
foregroundColor: MaterialStateProperty.all(
mobileColorTheme.onBackground,
),
backgroundColor: MaterialStateProperty.all(mobileColorTheme.background),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
side: MaterialStateProperty.all(
BorderSide(
color: mobileColorTheme.outline,
width: 0.5,
),
),
padding: MaterialStateProperty.all(
const EdgeInsets.symmetric(horizontal: 8, vertical: 12),
),
// splash color
overlayColor: MaterialStateProperty.all(
Colors.grey[100],
),
),
),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
TextStyle(
fontFamily: fontFamily,
),
),
),
),
// text
fontFamily: fontFamily,
textTheme: TextTheme(
displayLarge: const TextStyle(
color: Color(0xFF57B5F8),
fontSize: 32,
fontWeight: FontWeight.w700,
height: 1.20,
letterSpacing: 0.16,
),
displayMedium: TextStyle(
fontFamily: fontFamily,
color: mobileColorTheme.onBackground,
fontSize: 32,
fontWeight: FontWeight.w600,
height: 1.20,
letterSpacing: 0.16,
),
// H1 Semi 26
displaySmall: TextStyle(
color: mobileColorTheme.onBackground,
fontWeight: FontWeight.w600,
height: 1.10,
letterSpacing: 0.13,
),
// body2 14 Regular
bodyMedium: TextStyle(
fontFamily: fontFamily,
color: mobileColorTheme.onBackground,
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.20,
letterSpacing: 0.07,
),
// Trash empty title
labelLarge: TextStyle(
color: mobileColorTheme.onBackground,
fontSize: 22,
fontWeight: FontWeight.w600,
letterSpacing: -0.3,
),
// setting item title
labelMedium: TextStyle(
color: mobileColorTheme.onSurface,
fontSize: 18,
fontWeight: FontWeight.w500,
),
// setting group title
labelSmall: TextStyle(
color: mobileColorTheme.onBackground,
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0.05,
),
),
inputDecorationTheme: InputDecorationTheme(
contentPadding: const EdgeInsets.all(8),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
width: 2,
color: _primaryColor,
),
borderRadius: BorderRadius.all(Radius.circular(6)),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(color: mobileColorTheme.error),
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: mobileColorTheme.error),
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: mobileColorTheme.outline,
),
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
),
colorScheme: mobileColorTheme,
indicatorColor: Colors.blue,
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,
textColor: theme.text,
greyHover: theme.hoverBG1,
greySelect: theme.bg3,
lightGreyHover: theme.hoverBG3,
toggleOffFill: theme.shader5,
progressBarBGColor: theme.progressBarBGColor,
toggleButtonBGColor: theme.toggleButtonBGColor,
calendarWeekendBGColor: theme.calendarWeekendBGColor,
gridRowCountColor: theme.gridRowCountColor,
code: getFontStyle(
fontFamily: monospaceFontFamily,
fontColor: theme.shader3,
),
callout: getFontStyle(
fontFamily: fontFamily,
fontSize: FontSizes.s11,
fontColor: theme.shader3,
),
calloutBGColor: theme.hoverBG3,
tableCellBGColor: theme.surface,
caption: getFontStyle(
fontFamily: fontFamily,
fontSize: FontSizes.s11,
fontWeight: FontWeight.w400,
fontColor: theme.hint,
),
),
],
);
}

View File

@ -27,8 +27,8 @@ class MobileHomePageHeader extends StatelessWidget {
child: BlocBuilder<SettingsUserViewBloc, SettingsUserState>(
builder: (context, state) {
final userIcon = state.userProfile.iconUrl;
return SizedBox(
height: 48,
return ConstrainedBox(
constraints: const BoxConstraints(minHeight: 48),
child: Row(
children: [
FlowyButton(
@ -61,13 +61,14 @@ class MobileHomePageHeader extends StatelessWidget {
const HSpace(12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
const FlowyText.medium(
'AppFlowy',
fontSize: 18,
),
const VSpace(4),
FlowyText.regular(
userProfile.email.isNotEmpty
? userProfile.email

View File

@ -133,7 +133,7 @@ class _MobileRecentViewState extends State<MobileRecentView> {
child: FlowyText(
view.name,
maxLines: 2,
fontSize: 16.0,
fontSize: 14.0,
overflow: TextOverflow.ellipsis,
),
),

View File

@ -9,6 +9,22 @@ import 'package:flowy_infra/theme_extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
List<MobileToolbarItem> getMobileToolbarItems() {
return [
customTextDecorationMobileToolbarItem,
buildTextAndBackgroundColorMobileToolbarItem(),
mobileAddBlockToolbarItem,
mobileConvertBlockToolbarItem,
linkMobileToolbarItem,
imageMobileToolbarItem,
mobileAlignToolbarItem,
mobileIndentToolbarItem,
mobileOutdentToolbarItem,
undoMobileToolbarItem,
redoMobileToolbarItem,
];
}
Map<String, BlockComponentBuilder> getEditorBuilderMap({
required BuildContext context,
required EditorState editorState,

View File

@ -266,22 +266,16 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
_setInitialSelection(editorScrollController);
if (PlatformExtension.isMobile) {
final theme = Theme.of(context);
return MobileToolbarV2(
toolbarHeight: 48.0,
backgroundColor: theme.colorScheme.background,
foregroundColor: theme.colorScheme.onSurface,
iconColor: theme.iconTheme.color ?? theme.colorScheme.onSurface,
tabBarSelectedBackgroundColor: theme.colorScheme.background,
tabBarSelectedForegroundColor: theme.colorScheme.onSurface,
editorState: editorState,
toolbarItems: [
customTextDecorationMobileToolbarItem,
buildTextAndBackgroundColorMobileToolbarItem(),
mobileAddBlockToolbarItem,
mobileConvertBlockToolbarItem,
linkMobileToolbarItem,
imageMobileToolbarItem,
mobileAlignToolbarItem,
mobileIndentToolbarItem,
mobileOutdentToolbarItem,
undoMobileToolbarItem,
redoMobileToolbarItem,
],
toolbarItems: getMobileToolbarItems(),
child: Column(
children: [
Expanded(

View File

@ -581,14 +581,20 @@ class DeleteCoverButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final fillColor = PlatformExtension.isDesktopOrWeb
? Theme.of(context).colorScheme.surface.withOpacity(0.5)
: Theme.of(context).colorScheme.onSurfaceVariant;
final svgColor = PlatformExtension.isDesktopOrWeb
? Theme.of(context).colorScheme.tertiary
: Theme.of(context).colorScheme.onPrimary;
return FlowyIconButton(
hoverColor: Theme.of(context).colorScheme.surface,
fillColor: Theme.of(context).colorScheme.surface.withOpacity(0.5),
fillColor: fillColor,
iconPadding: const EdgeInsets.all(5),
width: 28,
icon: FlowySvg(
FlowySvgs.delete_s,
color: Theme.of(context).colorScheme.tertiary,
color: svgColor,
),
onPressed: onTap,
);

View File

@ -1,10 +1,12 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
final customTextDecorationMobileToolbarItem = MobileToolbarItem.withMenu(
itemIconBuilder: (_, __, ___) => const AFMobileIcon(
afMobileIcons: AFMobileIcons.textDecoration,
itemIconBuilder: (_, __, ___) => const FlowySvg(
FlowySvgs.text_s,
size: Size.square(24),
),
itemMenuBuilder: (_, editorState, __) {
final selection = editorState.selection;
@ -82,6 +84,7 @@ class _TextDecorationMenuState extends State<_TextDecorationMenu> {
return MobileToolbarItemMenuBtn(
icon: AFMobileIcon(
afMobileIcons: currentDecoration.icon,
color: MobileToolbarTheme.of(context).iconColor,
),
label: FlowyText(currentDecoration.label),
isSelected: isSelected,

View File

@ -17,9 +17,13 @@ import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/user/application/user_listener.dart';
import 'package:appflowy/user/application/user_service.dart';
import 'package:appflowy/user/presentation/router.dart';
import 'package:appflowy/util/platform_extension.dart';
import 'package:appflowy/workspace/application/edit_panel/edit_panel_bloc.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/notifications/notification_action_bloc.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/desktop_appearance.dart';
import 'package:appflowy/workspace/application/settings/appearance/mobile_appearance.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/application/user/prelude.dart';
@ -102,6 +106,11 @@ void _resolveCommonService(
getIt.registerFactory<ClipboardService>(
() => ClipboardService(),
);
// theme
getIt.registerFactory<BaseAppearance>(
() => PlatformExtension.isMobile ? MobileAppearance() : DesktopAppearance(),
);
}
void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {

View File

@ -1,25 +1,20 @@
import 'dart:async';
import 'package:appflowy/mobile/application/mobile_theme_data.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/user_settings_service.dart';
import 'package:appflowy/util/platform_extension.dart';
import 'package:appflowy/workspace/application/appearance_defaults.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-user/date_time.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:google_fonts/google_fonts.dart';
part 'appearance_cubit.freezed.dart';
const _white = Color(0xFFFFFFFF);
/// [AppearanceSettingsCubit] is used to modify the appearance of AppFlowy.
/// It includes:
/// - [AppTheme]
@ -349,251 +344,11 @@ class AppearanceSettingsState with _$AppearanceSettingsState {
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;
final colorScheme = ColorScheme(
brightness: brightness,
primary: theme.primary,
onPrimary: theme.onPrimary,
primaryContainer: theme.main2,
onPrimaryContainer: _white,
// page title hover color
secondary: theme.hoverBG1,
onSecondary: theme.shader1,
// setting value hover color
secondaryContainer: theme.selector,
onSecondaryContainer: theme.topbarBg,
tertiary: theme.shader7,
// Editor: toolbarColor
onTertiary: theme.toolbarColor,
tertiaryContainer: theme.questionBubbleBG,
background: theme.surface,
onBackground: theme.text,
surface: theme.surface,
// text&icon color when it is hovered
onSurface: theme.hoverFG,
// grey hover color
inverseSurface: theme.hoverBG3,
onError: theme.onPrimary,
error: theme.red,
outline: theme.shader4,
surfaceVariant: theme.sidebarBg,
shadow: theme.shadow,
);
const Set<MaterialState> scrollbarInteractiveStates = <MaterialState>{
MaterialState.pressed,
MaterialState.hovered,
MaterialState.dragged,
};
if (PlatformExtension.isMobile) {
// Mobile version has only one theme(light mode) for now.
// The desktop theme and the mobile theme are independent.
final mobileThemeData = getMobileThemeData(
brightness,
theme,
fontFamily,
monospaceFontFamily,
);
return mobileThemeData;
}
// Due to Desktop version has multiple themes, it relies on the current theme to build the ThemeData
final desktopThemeData = ThemeData(
brightness: brightness,
dialogBackgroundColor: theme.surface,
textTheme: getTextTheme(fontFamily: fontFamily, fontColor: theme.text),
textSelectionTheme: TextSelectionThemeData(
cursorColor: theme.main2,
selectionHandleColor: theme.main2,
),
iconTheme: IconThemeData(color: theme.icon),
tooltipTheme: TooltipThemeData(
textStyle: getFontStyle(
fontFamily: fontFamily,
fontSize: FontSizes.s11,
fontWeight: FontWeight.w400,
fontColor: theme.surface,
),
),
scaffoldBackgroundColor: theme.surface,
snackBarTheme: SnackBarThemeData(
backgroundColor: colorScheme.primary,
contentTextStyle: TextStyle(color: colorScheme.onSurface),
),
scrollbarTheme: ScrollbarThemeData(
thumbColor: MaterialStateProperty.resolveWith((states) {
if (states.any(scrollbarInteractiveStates.contains)) {
return theme.shader7;
}
return theme.shader5;
}),
thickness: MaterialStateProperty.resolveWith((states) {
if (states.any(scrollbarInteractiveStates.contains)) {
return 4;
}
return 3.0;
}),
crossAxisMargin: 0.0,
mainAxisMargin: 6.0,
radius: Corners.s10Radius,
),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
//dropdown menu color
canvasColor: theme.surface,
dividerColor: theme.divider,
hintColor: theme.hint,
//action item hover color
hoverColor: theme.hoverBG2,
disabledColor: theme.shader4,
highlightColor: theme.main1,
indicatorColor: theme.main1,
cardColor: theme.input,
colorScheme: colorScheme,
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,
textColor: theme.text,
greyHover: theme.hoverBG1,
greySelect: theme.bg3,
lightGreyHover: theme.hoverBG3,
toggleOffFill: theme.shader5,
progressBarBGColor: theme.progressBarBGColor,
toggleButtonBGColor: theme.toggleButtonBGColor,
calendarWeekendBGColor: theme.calendarWeekendBGColor,
gridRowCountColor: theme.gridRowCountColor,
code: getFontStyle(
fontFamily: monospaceFontFamily,
fontColor: theme.shader3,
),
callout: getFontStyle(
fontFamily: fontFamily,
fontSize: FontSizes.s11,
fontColor: theme.shader3,
),
calloutBGColor: theme.hoverBG3,
tableCellBGColor: theme.surface,
caption: getFontStyle(
fontFamily: fontFamily,
fontSize: FontSizes.s11,
fontWeight: FontWeight.w400,
fontColor: theme.hint,
),
),
],
);
return desktopThemeData;
}
}
TextStyle getFontStyle({
required String fontFamily,
double? fontSize,
FontWeight? fontWeight,
Color? fontColor,
double? letterSpacing,
double? lineHeight,
}) {
try {
return GoogleFonts.getFont(
fontFamily,
fontSize: fontSize ?? FontSizes.s12,
color: fontColor,
fontWeight: fontWeight ?? FontWeight.w500,
letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005),
height: lineHeight,
);
} catch (e) {
return 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,
return getIt<BaseAppearance>().getThemeData(
appTheme,
brightness,
font,
monospaceFont,
);
}
}
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
);
}

View File

@ -0,0 +1,107 @@
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
abstract class BaseAppearance {
final white = const Color(0xFFFFFFFF);
final Set<MaterialState> scrollbarInteractiveStates = <MaterialState>{
MaterialState.pressed,
MaterialState.hovered,
MaterialState.dragged,
};
TextStyle getFontStyle({
required String fontFamily,
double? fontSize,
FontWeight? fontWeight,
Color? fontColor,
double? letterSpacing,
double? lineHeight,
}) {
try {
return GoogleFonts.getFont(
fontFamily,
fontSize: fontSize ?? FontSizes.s12,
color: fontColor,
fontWeight: fontWeight ?? FontWeight.w500,
letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005),
height: lineHeight,
);
} catch (e) {
return TextStyle(
fontFamily: fontFamily,
fontSize: fontSize ?? FontSizes.s12,
color: fontColor,
fontWeight: fontWeight ?? FontWeight.w500,
fontFamilyFallback: const ['Poppins'],
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
);
}
ThemeData getThemeData(
AppTheme appTheme,
Brightness brightness,
String fontFamily,
String codeFontFamily,
);
}

View File

@ -0,0 +1,150 @@
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flutter/material.dart';
class DesktopAppearance extends BaseAppearance {
@override
ThemeData getThemeData(
AppTheme appTheme,
Brightness brightness,
String fontFamily,
String codeFontFamily,
) {
assert(fontFamily.isNotEmpty);
assert(codeFontFamily.isNotEmpty);
final theme = brightness == Brightness.light
? appTheme.lightTheme
: appTheme.darkTheme;
final colorScheme = ColorScheme(
brightness: brightness,
primary: theme.primary,
onPrimary: theme.onPrimary,
primaryContainer: theme.main2,
onPrimaryContainer: white,
// page title hover color
secondary: theme.hoverBG1,
onSecondary: theme.shader1,
// setting value hover color
secondaryContainer: theme.selector,
onSecondaryContainer: theme.topbarBg,
tertiary: theme.shader7,
// Editor: toolbarColor
onTertiary: theme.toolbarColor,
tertiaryContainer: theme.questionBubbleBG,
background: theme.surface,
onBackground: theme.text,
surface: theme.surface,
// text&icon color when it is hovered
onSurface: theme.hoverFG,
// grey hover color
inverseSurface: theme.hoverBG3,
onError: theme.onPrimary,
error: theme.red,
outline: theme.shader4,
surfaceVariant: theme.sidebarBg,
shadow: theme.shadow,
);
// Due to Desktop version has multiple themes, it relies on the current theme to build the ThemeData
return ThemeData(
brightness: brightness,
dialogBackgroundColor: theme.surface,
textTheme: getTextTheme(
fontFamily: fontFamily,
fontColor: theme.text,
),
textSelectionTheme: TextSelectionThemeData(
cursorColor: theme.main2,
selectionHandleColor: theme.main2,
),
iconTheme: IconThemeData(color: theme.icon),
tooltipTheme: TooltipThemeData(
textStyle: getFontStyle(
fontFamily: fontFamily,
fontSize: FontSizes.s11,
fontWeight: FontWeight.w400,
fontColor: theme.surface,
),
),
scaffoldBackgroundColor: theme.surface,
snackBarTheme: SnackBarThemeData(
backgroundColor: colorScheme.primary,
contentTextStyle: TextStyle(color: colorScheme.onSurface),
),
scrollbarTheme: ScrollbarThemeData(
thumbColor: MaterialStateProperty.resolveWith((states) {
if (states.any(scrollbarInteractiveStates.contains)) {
return theme.shader7;
}
return theme.shader5;
}),
thickness: MaterialStateProperty.resolveWith((states) {
if (states.any(scrollbarInteractiveStates.contains)) {
return 4;
}
return 3.0;
}),
crossAxisMargin: 0.0,
mainAxisMargin: 6.0,
radius: Corners.s10Radius,
),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
//dropdown menu color
canvasColor: theme.surface,
dividerColor: theme.divider,
hintColor: theme.hint,
//action item hover color
hoverColor: theme.hoverBG2,
disabledColor: theme.shader4,
highlightColor: theme.main1,
indicatorColor: theme.main1,
cardColor: theme.input,
colorScheme: colorScheme,
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,
textColor: theme.text,
greyHover: theme.hoverBG1,
greySelect: theme.bg3,
lightGreyHover: theme.hoverBG3,
toggleOffFill: theme.shader5,
progressBarBGColor: theme.progressBarBGColor,
toggleButtonBGColor: theme.toggleButtonBGColor,
calendarWeekendBGColor: theme.calendarWeekendBGColor,
gridRowCountColor: theme.gridRowCountColor,
code: getFontStyle(
fontFamily: codeFontFamily,
fontColor: theme.shader3,
),
callout: getFontStyle(
fontFamily: fontFamily,
fontSize: FontSizes.s11,
fontColor: theme.shader3,
),
calloutBGColor: theme.hoverBG3,
tableCellBGColor: theme.surface,
caption: getFontStyle(
fontFamily: fontFamily,
fontSize: FontSizes.s11,
fontWeight: FontWeight.w400,
fontColor: theme.hint,
),
),
],
);
}
}

View File

@ -0,0 +1,282 @@
// ThemeData in mobile
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flutter/material.dart';
class MobileAppearance extends BaseAppearance {
static const _primaryColor = Color(0xFF2DA2F6); //primary 100
static const _onBackgroundColor = Color(0xff2F3030); // text/title color
static const _onSurfaceColor = Color(0xff676666); // text/body color
static const _onSecondaryColor = Color(0xFFC5C7CB); // text/body2 color
@override
ThemeData getThemeData(
AppTheme appTheme,
Brightness brightness,
String fontFamily,
String codeFontFamily,
) {
assert(fontFamily.isNotEmpty);
assert(codeFontFamily.isNotEmpty);
final fontStyle = getFontStyle(
fontFamily: fontFamily,
);
final codeFontStyle = getFontStyle(
fontFamily: codeFontFamily,
);
final theme = brightness == Brightness.light
? appTheme.lightTheme
: appTheme.darkTheme;
final colorTheme = brightness == Brightness.light
? ColorScheme(
brightness: brightness,
primary: _primaryColor,
onPrimary: Colors.white,
// TODO(yijing): add color later
secondary: Colors.white,
onSecondary: _onSecondaryColor,
error: const Color(0xffFB006D),
onError: const Color(0xffFB006D),
background: Colors.white,
onBackground: _onBackgroundColor,
outline: const Color(0xffE3E3E3), //caption
// outline: const Color(0xffBDC0C5), //caption
outlineVariant: const Color(0xffCBD5E0).withOpacity(0.24),
//Snack bar
surface: Colors.white,
onSurface: _onSurfaceColor, // text/body color
surfaceVariant: const Color.fromARGB(255, 216, 216, 216),
)
: ColorScheme(
brightness: brightness,
primary: _primaryColor,
onPrimary: Colors.white,
// TODO(yijing): add color later
secondary: Colors.black,
onSecondary: Colors.white,
error: const Color(0xffFB006D),
onError: const Color(0xffFB006D),
background: const Color(0xff1C1C1E), // BG/Secondary color
onBackground: Colors.white,
outline: const Color(0xff96989C), //caption
outlineVariant: Colors.black,
//Snack bar
surface: const Color(0xff2F3030),
onSurface: const Color(0xffC5C6C7), // text/body color
);
return ThemeData(
// color
primaryColor: colorTheme.primary, //primary 100
primaryColorLight: const Color(0xFF57B5F8), //primary 80
dividerColor: colorTheme.outline, //caption
hintColor: colorTheme.outline,
disabledColor: colorTheme.outline,
scaffoldBackgroundColor: colorTheme.background,
appBarTheme: AppBarTheme(
foregroundColor: colorTheme.onBackground,
backgroundColor: colorTheme.background,
centerTitle: false,
titleTextStyle: TextStyle(
color: colorTheme.onBackground,
fontSize: 18,
fontWeight: FontWeight.w600,
letterSpacing: 0.05,
),
shadowColor: colorTheme.outlineVariant,
),
radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return colorTheme.primary;
}
return colorTheme.outline;
}),
),
// button
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
fixedSize: MaterialStateProperty.all(const Size.fromHeight(48)),
elevation: MaterialStateProperty.all(0),
textStyle: MaterialStateProperty.all(
const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
shadowColor: MaterialStateProperty.all(null),
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return const Color(0xFF57B5F8);
}
return colorTheme.primary;
},
),
foregroundColor: MaterialStateProperty.all(Colors.white),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
foregroundColor: MaterialStateProperty.all(
colorTheme.onBackground,
),
backgroundColor: MaterialStateProperty.all(colorTheme.background),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
side: MaterialStateProperty.all(
BorderSide(
color: colorTheme.outline,
width: 0.5,
),
),
padding: MaterialStateProperty.all(
const EdgeInsets.symmetric(horizontal: 8, vertical: 12),
),
// splash color
overlayColor: MaterialStateProperty.all(
Colors.grey[100],
),
),
),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
fontStyle,
),
),
),
// text
fontFamily: fontStyle.fontFamily,
textTheme: TextTheme(
displayLarge: const TextStyle(
color: Color(0xFF57B5F8),
fontSize: 32,
fontWeight: FontWeight.w700,
height: 1.20,
letterSpacing: 0.16,
),
displayMedium: fontStyle.copyWith(
color: colorTheme.onBackground,
fontSize: 32,
fontWeight: FontWeight.w600,
height: 1.20,
letterSpacing: 0.16,
),
// H1 Semi 26
displaySmall: fontStyle.copyWith(
color: colorTheme.onBackground,
fontWeight: FontWeight.w600,
height: 1.10,
letterSpacing: 0.13,
),
// body2 14 Regular
bodyMedium: fontStyle.copyWith(
color: colorTheme.onBackground,
fontSize: 14,
fontWeight: FontWeight.w400,
// height: 1.2,
letterSpacing: 0.07,
),
// Trash empty title
labelLarge: fontStyle.copyWith(
color: colorTheme.onBackground,
fontSize: 22,
fontWeight: FontWeight.w600,
letterSpacing: -0.3,
),
// setting item title
labelMedium: fontStyle.copyWith(
color: colorTheme.onSurface,
fontSize: 18,
fontWeight: FontWeight.w500,
),
// setting group title
labelSmall: fontStyle.copyWith(
color: colorTheme.onBackground,
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0.05,
),
),
inputDecorationTheme: InputDecorationTheme(
contentPadding: const EdgeInsets.all(8),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
width: 2,
color: _primaryColor,
),
borderRadius: BorderRadius.all(Radius.circular(6)),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(color: colorTheme.error),
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: colorTheme.error),
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: colorTheme.outline,
),
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
),
colorScheme: colorTheme,
indicatorColor: Colors.blue,
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,
textColor: theme.text,
greyHover: theme.hoverBG1,
greySelect: theme.bg3,
lightGreyHover: theme.hoverBG3,
toggleOffFill: theme.shader5,
progressBarBGColor: theme.progressBarBGColor,
toggleButtonBGColor: theme.toggleButtonBGColor,
calendarWeekendBGColor: theme.calendarWeekendBGColor,
gridRowCountColor: theme.gridRowCountColor,
code: codeFontStyle.copyWith(
color: theme.shader3,
),
callout: fontStyle.copyWith(
fontSize: FontSizes.s11,
color: theme.shader3,
),
calloutBGColor: theme.hoverBG3,
tableCellBGColor: theme.surface,
caption: fontStyle.copyWith(
fontSize: FontSizes.s11,
fontWeight: FontWeight.w400,
color: theme.hint,
),
),
],
);
}
}

View File

@ -54,8 +54,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "2617f7"
resolved-ref: "2617f766a5a4aa83c9f4d5c4d7221c12dbe23b66"
ref: ec3ecf5
resolved-ref: ec3ecf58a0de453561d30e809c6dc1eed3d1aa9a
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
version: "2.0.0-beta.1"

View File

@ -47,7 +47,7 @@ dependencies:
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: '2617f7'
ref: 'ec3ecf5'
appflowy_popover:
path: packages/appflowy_popover