feat: support scaling text on mobile (#4690)

This commit is contained in:
Lucas.Xu 2024-02-22 08:50:24 +07:00 committed by GitHub
parent f1831d07af
commit 7802c75d7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 218 additions and 78 deletions

View File

@ -51,4 +51,11 @@ class KVKeys {
static const String kAppflowyCloudBaseURL = 'kAppFlowyCloudBaseURL';
static const String kSupabaseURL = 'kSupbaseURL';
static const String kSupabaseAnonKey = 'kSupabaseAnonKey';
/// The key for saving the text scale factor.
///
/// The value is a double string.
/// The value range is from 0.8 to 1.0. If it's greater than 1.0, it will cause
/// the text to be too large and not aligned with the icon
static const String textScaleFactor = 'textScaleFactor';
}

View File

@ -1,5 +1,6 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/setting/appearance/rtl_setting.dart';
import 'package:appflowy/mobile/presentation/setting/appearance/text_scale_setting.dart';
import 'package:appflowy/mobile/presentation/setting/appearance/theme_setting.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -18,6 +19,7 @@ class AppearanceSettingGroup extends StatelessWidget {
settingItemList: const [
ThemeSetting(),
FontSetting(),
TextScaleSetting(),
RTLSetting(),
],
);

View File

@ -0,0 +1,66 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/plugins/document/presentation/more/font_size_slider.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../setting.dart';
const int _divisions = 4;
class TextScaleSetting extends StatelessWidget {
const TextScaleSetting({
super.key,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textScaleFactor =
context.watch<AppearanceSettingsCubit>().state.textScaleFactor;
return MobileSettingItem(
name: LocaleKeys.settings_appearance_fontScaleFactor.tr(),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
FlowyText(
// map the text scale factor to the 0-1
// 0.8 - 0.0
// 0.9 - 0.5
// 1.0 - 1.0
((_divisions + 1) * textScaleFactor - _divisions)
.toStringAsFixed(2),
color: theme.colorScheme.onSurface,
),
const Icon(Icons.chevron_right),
],
),
onTap: () {
showMobileBottomSheet(
context,
showHeader: true,
showDragHandle: true,
showDivider: false,
showCloseButton: false,
title: LocaleKeys.settings_appearance_fontScaleFactor.tr(),
builder: (context) {
return FontSizeStepper(
value: textScaleFactor,
minimumValue: 0.8,
maximumValue: 1.0,
divisions: _divisions,
onChanged: (newTextScaleFactor) {
context
.read<AppearanceSettingsCubit>()
.setTextScaleFactor(newTextScaleFactor);
},
);
},
);
},
);
}
}

View File

@ -1,6 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_configuration.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/align_toolbar_item/custom_text_align_command.dart';
@ -22,6 +19,8 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:collection/collection.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
final List<CommandShortcutEvent> commandShortcutEvents = [

View File

@ -7,6 +7,7 @@ import 'package:appflowy/plugins/document/presentation/more/cubit/document_appea
import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart';
import 'package:appflowy/util/google_font_family_extension.dart';
import 'package:appflowy/workspace/application/appearance_defaults.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:collection/collection.dart';
@ -83,6 +84,8 @@ class EditorStyleCustomizer {
),
),
textSpanDecorator: customizeAttributeDecorator,
textScaleFactor:
context.watch<AppearanceSettingsCubit>().state.textScaleFactor,
);
}
@ -131,6 +134,8 @@ class EditorStyleCustomizer {
textSpanDecorator: customizeAttributeDecorator,
mobileDragHandleBallSize: const Size.square(12.0),
magnifierSize: const Size(144, 96),
textScaleFactor:
context.watch<AppearanceSettingsCubit>().state.textScaleFactor,
);
}

View File

@ -78,13 +78,15 @@ class DocumentAppearanceCubit extends Cubit<DocumentAppearance> {
? Color(int.parse(selectionColorString))
: null;
final textScaleFactor = prefs.getDouble(KVKeys.textScaleFactor) ?? 1.0;
if (isClosed) {
return;
}
emit(
state.copyWith(
fontSize: fontSize,
fontSize: fontSize * textScaleFactor,
fontFamily: fontFamily,
cursorColor: cursorColor,
selectionColor: selectionColor,

View File

@ -1,51 +1,71 @@
import 'package:flutter/material.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
class FontSizeStepper extends StatelessWidget {
const FontSizeStepper({super.key});
class FontSizeStepper extends StatefulWidget {
const FontSizeStepper({
super.key,
required this.minimumValue,
required this.maximumValue,
required this.value,
required this.divisions,
required this.onChanged,
});
final double minimumValue;
final double maximumValue;
final double value;
final ValueChanged<double> onChanged;
final int divisions;
@override
State<FontSizeStepper> createState() => _FontSizeStepperState();
}
class _FontSizeStepperState extends State<FontSizeStepper> {
late double _value = widget.value.clamp(
widget.minimumValue,
widget.maximumValue,
);
@override
Widget build(BuildContext context) {
return BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
builder: (context, state) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
children: [
const FlowyText('A', fontSize: 14),
const HSpace(6),
Expanded(
child: SliderTheme(
data: Theme.of(context).sliderTheme.copyWith(
showValueIndicator: ShowValueIndicator.never,
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 8,
),
overlayShape: const RoundSliderOverlayShape(
overlayRadius: 16,
),
),
child: Slider(
value: state.fontSize,
min: 10,
max: 24,
divisions: 8,
onChanged: (fontSize) => context
.read<DocumentAppearanceCubit>()
.syncFontSize(fontSize),
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
children: [
const FlowyText('A', fontSize: 14),
const HSpace(6),
Expanded(
child: SliderTheme(
data: Theme.of(context).sliderTheme.copyWith(
showValueIndicator: ShowValueIndicator.never,
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 8,
),
overlayShape: const RoundSliderOverlayShape(
overlayRadius: 16,
),
),
),
child: Slider(
value: _value,
min: widget.minimumValue,
max: widget.maximumValue,
divisions: widget.divisions,
onChanged: (value) {
setState(() {
_value = value;
});
widget.onChanged(value);
},
),
const HSpace(6),
const FlowyText('A', fontSize: 20),
],
),
),
);
},
const HSpace(6),
const FlowyText('A', fontSize: 20),
],
),
);
}
}

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/presentation/more/font_size_slider.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
@ -9,6 +8,8 @@ import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DocumentMoreButton extends StatelessWidget {
const DocumentMoreButton({super.key});
@ -24,7 +25,23 @@ class DocumentMoreButton extends StatelessWidget {
direction: PopoverDirection.leftWithCenterAligned,
constraints: const BoxConstraints(maxHeight: 40, maxWidth: 240),
offset: const Offset(-10, 0),
popupBuilder: (context) => const FontSizeStepper(),
popupBuilder: (context) {
return BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
builder: (context, state) {
return FontSizeStepper(
minimumValue: 10,
maximumValue: 24,
value: state.fontSize,
divisions: 8,
onChanged: (newFontSize) {
context
.read<DocumentAppearanceCubit>()
.syncFontSize(newFontSize);
},
);
},
);
},
child: FlowyButton(
text: FlowyText.regular(
LocaleKeys.moreAction_fontSize.tr(),

View File

@ -1,8 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/startup/startup.dart';
@ -21,6 +18,8 @@ import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
@ -186,7 +185,7 @@ class _ApplicationWidgetState extends State<ApplicationWidget> {
// use the 1.0 as the textScaleFactor to avoid the text size
// affected by the system setting.
data: MediaQuery.of(context).copyWith(
textScaler: const TextScaler.linear(1),
textScaler: TextScaler.linear(state.textScaleFactor),
),
child: overlayManagerBuilder(context, child),
),
@ -238,24 +237,11 @@ class AppGlobals {
}
class ApplicationBlocObserver extends BlocObserver {
@override
void onTransition(Bloc bloc, Transition transition) {
// Log.debug("[current]: ${transition.currentState} \n\n[next]: ${transition.nextState}");
// Log.debug("${transition.nextState}");
super.onTransition(bloc, transition);
}
@override
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
Log.debug(error);
super.onError(bloc, error, stackTrace);
}
// @override
// void onEvent(Bloc bloc, Object? event) {
// Log.debug("$event");
// super.onEvent(bloc, event);
// }
}
Future<AppTheme> appTheme(String themeName) async {

View File

@ -1,5 +1,7 @@
import 'dart:async';
import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/user_settings_service.dart';
import 'package:appflowy/util/color_to_hex_string.dart';
@ -58,12 +60,38 @@ class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
appearanceSettings.documentSetting.selectionColor,
),
),
1.0,
),
);
) {
readTextScaleFactor();
}
final AppearanceSettingsPB _appearanceSettings;
final DateTimeSettingsPB _dateTimeSettings;
Future<void> setTextScaleFactor(double textScaleFactor) async {
// only saved in local storage, this value is not synced across devices
await getIt<KeyValueStorage>().set(
KVKeys.textScaleFactor,
textScaleFactor.toString(),
);
// don't allow the text scale factor to be greater than 1.0, it will cause
// ui issues
emit(state.copyWith(textScaleFactor: textScaleFactor.clamp(0.7, 1.0)));
}
Future<void> readTextScaleFactor() async {
final textScaleFactor = await getIt<KeyValueStorage>().getWithFormat(
KVKeys.textScaleFactor,
(value) => double.parse(value),
);
textScaleFactor.fold(
() => emit(state.copyWith(textScaleFactor: 1.0)),
(value) => emit(state.copyWith(textScaleFactor: value.clamp(0.7, 1.0))),
);
}
/// Update selected theme in the user's settings and emit an updated state
/// with the AppTheme named [themeName].
Future<void> setTheme(String themeName) async {
@ -347,6 +375,7 @@ class AppearanceSettingsState with _$AppearanceSettingsState {
required String timezoneId,
required Color? documentCursorColor,
required Color? documentSelectionColor,
required double textScaleFactor,
}) = _AppearanceSettingsState;
factory AppearanceSettingsState.initial(
@ -364,6 +393,7 @@ class AppearanceSettingsState with _$AppearanceSettingsState {
String timezoneId,
Color? documentCursorColor,
Color? documentSelectionColor,
double textScaleFactor,
) {
return AppearanceSettingsState(
appTheme: appTheme,
@ -380,6 +410,7 @@ class AppearanceSettingsState with _$AppearanceSettingsState {
timezoneId: timezoneId,
documentCursorColor: documentCursorColor,
documentSelectionColor: documentSelectionColor,
textScaleFactor: textScaleFactor,
);
}

View File

@ -49,20 +49,24 @@ class FlowySvg extends StatelessWidget {
@override
Widget build(BuildContext context) {
final iconColor = color ?? Theme.of(context).iconTheme.color;
final textScaleFactor = MediaQuery.textScalerOf(context).scale(1);
return SizedBox(
width: size?.width,
height: size?.height,
child: SvgPicture.asset(
_normalized(),
return Transform.scale(
scale: textScaleFactor,
child: SizedBox(
width: size?.width,
height: size?.height,
colorFilter: iconColor != null && blendMode != null
? ColorFilter.mode(
iconColor,
blendMode!,
)
: null,
child: SvgPicture.asset(
_normalized(),
width: size?.width,
height: size?.height,
colorFilter: iconColor != null && blendMode != null
? ColorFilter.mode(
iconColor,
blendMode!,
)
: null,
),
),
);
}

View File

@ -53,8 +53,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: f38328d
resolved-ref: f38328d9e52be89b8036ae0ad3460ce9d6cc5be7
ref: "1715ed4"
resolved-ref: "1715ed45490e0a432fa1bbfcb2f1693471632ff7"
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
version: "2.3.2"

View File

@ -165,7 +165,7 @@ dependency_overrides:
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: "f38328d"
ref: "1715ed4"
uuid: ^4.1.0

View File

@ -333,6 +333,7 @@
"dark": "Dark Mode",
"system": "Adapt to System"
},
"fontScaleFactor": "Font Scale Factor",
"documentSettings": {
"cursorColor": "Document cursor color",
"selectionColor": "Document selection color",