mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: custom font dropdown (#5502)
* fix: custom font dropdown * fix: dart analyzer
This commit is contained in:
parent
b6d873db1b
commit
6b46372aa0
@ -1,7 +1,11 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
||||||
import 'package:appflowy/shared/af_role_pb_extension.dart';
|
import 'package:appflowy/shared/af_role_pb_extension.dart';
|
||||||
|
import 'package:appflowy/shared/google_fonts_extension.dart';
|
||||||
import 'package:appflowy/util/font_family_extension.dart';
|
import 'package:appflowy/util/font_family_extension.dart';
|
||||||
import 'package:appflowy/workspace/application/appearance_defaults.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/appearance_cubit.dart';
|
||||||
@ -28,6 +32,7 @@ import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/th
|
|||||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
|
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/language.dart';
|
import 'package:flowy_infra/language.dart';
|
||||||
import 'package:flowy_infra/plugins/bloc/dynamic_plugin_bloc.dart';
|
import 'package:flowy_infra/plugins/bloc/dynamic_plugin_bloc.dart';
|
||||||
@ -40,8 +45,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
@ -836,52 +839,199 @@ class _SelectedModeIndicator extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FontSelectorDropdown extends StatelessWidget {
|
class _FontSelectorDropdown extends StatefulWidget {
|
||||||
const _FontSelectorDropdown();
|
const _FontSelectorDropdown();
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_FontSelectorDropdown> createState() => _FontSelectorDropdownState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FontSelectorDropdownState extends State<_FontSelectorDropdown> {
|
||||||
|
late final _options = [defaultFontFamily, ...GoogleFonts.asMap().keys];
|
||||||
|
final _focusNode = FocusNode();
|
||||||
|
final _controller = PopoverController();
|
||||||
|
final _scrollController = ScrollController();
|
||||||
|
|
||||||
|
void _scrollIfNeccessary() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
// Set scroll position to selected item.
|
||||||
|
final appearance = context.read<AppearanceSettingsCubit>().state;
|
||||||
|
const itemExtent = 32;
|
||||||
|
final index = _options.indexOf(appearance.font);
|
||||||
|
final newPosition = (index * itemExtent).toDouble();
|
||||||
|
if (_scrollController.offset != newPosition) {
|
||||||
|
_scrollController.jumpTo(newPosition);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.close();
|
||||||
|
_focusNode.dispose();
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final appearance = context.watch<AppearanceSettingsCubit>().state;
|
final appearance = context.watch<AppearanceSettingsCubit>().state;
|
||||||
return SettingsDropdown<String>(
|
return LayoutBuilder(
|
||||||
key: const Key('FontSelectorDropdown'),
|
builder: (context, constraints) => AppFlowyPopover(
|
||||||
actions: [
|
margin: EdgeInsets.zero,
|
||||||
GestureDetector(
|
controller: _controller,
|
||||||
behavior: HitTestBehavior.opaque,
|
skipTraversal: true,
|
||||||
onTap: () => context
|
triggerActions: PopoverTriggerFlags.none,
|
||||||
.read<AppearanceSettingsCubit>()
|
onClose: () {
|
||||||
.setFontFamily(defaultFontFamily),
|
_focusNode.unfocus();
|
||||||
child: SizedBox(
|
setState(() {});
|
||||||
height: 26,
|
},
|
||||||
child: FlowyHover(
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
resetHoverOnRebuild: false,
|
constraints: BoxConstraints(
|
||||||
child: Padding(
|
maxHeight: 150,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
maxWidth: constraints.maxWidth - 90,
|
||||||
child: Row(
|
),
|
||||||
children: [
|
decoration: BoxDecoration(
|
||||||
const FlowySvg(FlowySvgs.restore_s),
|
color: Theme.of(context).cardColor,
|
||||||
const HSpace(4),
|
borderRadius: const BorderRadius.all(Radius.circular(4.0)),
|
||||||
FlowyText.regular(LocaleKeys.settings_common_reset.tr()),
|
boxShadow: [
|
||||||
],
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.10),
|
||||||
|
blurRadius: 6,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
popupBuilder: (_) {
|
||||||
|
_scrollIfNeccessary();
|
||||||
|
return Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: ListView.separated(
|
||||||
|
controller: _scrollController,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
|
itemCount: _options.length,
|
||||||
|
separatorBuilder: (_, __) => const VSpace(4),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final font = _options[index];
|
||||||
|
final isSelected = appearance.font == font;
|
||||||
|
return SizedBox(
|
||||||
|
height: 28,
|
||||||
|
child: ListTile(
|
||||||
|
selected: isSelected,
|
||||||
|
dense: true,
|
||||||
|
hoverColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSurface
|
||||||
|
.withOpacity(0.12),
|
||||||
|
selectedTileColor:
|
||||||
|
Theme.of(context).colorScheme.primary.withOpacity(0.12),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
|
minTileHeight: 28,
|
||||||
|
onTap: () {
|
||||||
|
context
|
||||||
|
.read<AppearanceSettingsCubit>()
|
||||||
|
.setFontFamily(font);
|
||||||
|
|
||||||
|
// This is a workaround such that when dialog rebuilds due
|
||||||
|
// to font changing, the font selector won't retain focus.
|
||||||
|
_focusNode.parent?.requestFocus();
|
||||||
|
|
||||||
|
_controller.close();
|
||||||
|
},
|
||||||
|
title: Text(
|
||||||
|
font.fontFamilyDisplayName,
|
||||||
|
style: TextStyle(
|
||||||
|
color: AFThemeExtension.of(context).textColor,
|
||||||
|
fontFamily: getGoogleFontSafely(font).fontFamily,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing:
|
||||||
|
isSelected ? const FlowySvg(FlowySvgs.check_s) : null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TapRegion(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTapOutside: (_) {
|
||||||
|
_focusNode.unfocus();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: Listener(
|
||||||
|
onPointerDown: (_) {
|
||||||
|
_focusNode.requestFocus();
|
||||||
|
setState(() {});
|
||||||
|
_controller.show();
|
||||||
|
},
|
||||||
|
child: Focus(
|
||||||
|
focusNode: _focusNode,
|
||||||
|
includeSemantics: false,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: _focusNode.hasFocus
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
borderRadius: Corners.s8Border,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const HSpace(18),
|
||||||
|
Text(
|
||||||
|
appearance.font.fontFamilyDisplayName,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyLarge
|
||||||
|
?.copyWith(fontFamily: appearance.font),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
child: Icon(Icons.arrow_drop_down),
|
||||||
|
),
|
||||||
|
const HSpace(10),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const HSpace(16),
|
||||||
),
|
GestureDetector(
|
||||||
],
|
behavior: HitTestBehavior.opaque,
|
||||||
onChanged: (font) =>
|
onTap: () => context
|
||||||
context.read<AppearanceSettingsCubit>().setFontFamily(font),
|
.read<AppearanceSettingsCubit>()
|
||||||
selectedOption: appearance.font,
|
.setFontFamily(defaultFontFamily),
|
||||||
options: [defaultFontFamily, ...GoogleFonts.asMap().keys]
|
child: SizedBox(
|
||||||
.map(
|
height: 26,
|
||||||
(font) => buildDropdownMenuEntry<String>(
|
child: FlowyHover(
|
||||||
context,
|
resetHoverOnRebuild: false,
|
||||||
selectedValue: appearance.font,
|
child: Padding(
|
||||||
value: font,
|
padding:
|
||||||
label: font.fontFamilyDisplayName,
|
const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||||
fontFamily: font,
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const FlowySvg(FlowySvgs.restore_s),
|
||||||
|
const HSpace(4),
|
||||||
|
FlowyText.regular(
|
||||||
|
LocaleKeys.settings_common_reset.tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
],
|
||||||
.toList(),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:appflowy/shared/google_fonts_extension.dart';
|
|
||||||
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:appflowy/shared/google_fonts_extension.dart';
|
||||||
|
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
|
||||||
DropdownMenuEntry<T> buildDropdownMenuEntry<T>(
|
DropdownMenuEntry<T> buildDropdownMenuEntry<T>(
|
||||||
@ -33,7 +33,7 @@ DropdownMenuEntry<T> buildDropdownMenuEntry<T>(
|
|||||||
leadingIcon: leadingWidget,
|
leadingIcon: leadingWidget,
|
||||||
labelWidget: Padding(
|
labelWidget: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
child: FlowyText.medium(
|
child: FlowyText.regular(
|
||||||
label,
|
label,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
|
Loading…
Reference in New Issue
Block a user