From a707918f7f98612d274f969d4cef53a6467d1cb0 Mon Sep 17 00:00:00 2001 From: Garv Shah <64292655+garv-shah@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:14:40 +1000 Subject: [PATCH] Added Formula Button (for maths support) (#900) * added basic math keyboard * added formula to sample data! * created new variable for showFormulaButton --- example/assets/sample_data.json | 7 +- .../models/documents/nodes/embeddable.dart | 3 + .../widgets/embeds/default_embed_builder.dart | 20 ++++++ lib/src/widgets/toolbar.dart | 14 ++++ lib/src/widgets/toolbar/formula_button.dart | 69 +++++++++++++++++++ pubspec.yaml | 1 + 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 lib/src/widgets/toolbar/formula_button.dart diff --git a/example/assets/sample_data.json b/example/assets/sample_data.json index 467bcadd..0e7f9e8d 100644 --- a/example/assets/sample_data.json +++ b/example/assets/sample_data.json @@ -536,5 +536,10 @@ }, { "insert": "\n" + }, + { + "insert": { + "formula": "x=\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}" + } } -] \ No newline at end of file +] diff --git a/lib/src/models/documents/nodes/embeddable.dart b/lib/src/models/documents/nodes/embeddable.dart index 61d5e115..b2db0abc 100644 --- a/lib/src/models/documents/nodes/embeddable.dart +++ b/lib/src/models/documents/nodes/embeddable.dart @@ -38,6 +38,9 @@ class BlockEmbed extends Embeddable { static const String videoType = 'video'; static BlockEmbed video(String videoUrl) => BlockEmbed(videoType, videoUrl); + static const String formulaType = 'formula'; + static BlockEmbed formula(String formula) => BlockEmbed(formulaType, formula); + static const String customType = 'custom'; static BlockEmbed custom(CustomBlockEmbed customBlock) => BlockEmbed(customType, customBlock.toJsonString()); diff --git a/lib/src/widgets/embeds/default_embed_builder.dart b/lib/src/widgets/embeds/default_embed_builder.dart index 0eb22b5a..50f8f037 100644 --- a/lib/src/widgets/embeds/default_embed_builder.dart +++ b/lib/src/widgets/embeds/default_embed_builder.dart @@ -1,7 +1,9 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:gallery_saver/gallery_saver.dart'; +import 'package:math_keyboard/math_keyboard.dart'; import 'package:tuple/tuple.dart'; import '../../models/documents/attribute.dart'; @@ -154,6 +156,24 @@ Widget defaultEmbedBuilder( readOnly: readOnly, onVideoInit: onVideoInit, ); + case BlockEmbed.formulaType: + final mathController = MathFieldEditingController(); + + return Focus( + onFocusChange: (hasFocus) { + if (hasFocus) { + // If the MathField is tapped, hides the built in keyboard + SystemChannels.textInput.invokeMethod('TextInput.hide'); + print(mathController.currentEditingValue()); + } + }, + child: MathField( + controller: mathController, + variables: const ['x', 'y', 'z'], + onChanged: (value) {}, + onSubmitted: (value) {}, + ), + ); default: throw UnimplementedError( 'Embeddable type "${node.value.type}" is not supported by default ' diff --git a/lib/src/widgets/toolbar.dart b/lib/src/widgets/toolbar.dart index 198b6dff..40cffdf0 100644 --- a/lib/src/widgets/toolbar.dart +++ b/lib/src/widgets/toolbar.dart @@ -14,6 +14,7 @@ import 'toolbar/arrow_indicated_button_list.dart'; import 'toolbar/camera_button.dart'; import 'toolbar/clear_format_button.dart'; import 'toolbar/color_button.dart'; +import 'toolbar/formula_button.dart'; import 'toolbar/history_button.dart'; import 'toolbar/image_button.dart'; import 'toolbar/image_video_utils.dart'; @@ -109,6 +110,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { bool multiRowsDisplay = true, bool showImageButton = true, bool showVideoButton = true, + bool showFormulaButton = false, bool showCameraButton = true, bool showDirection = false, bool showSearchButton = true, @@ -364,6 +366,18 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { webVideoPickImpl: webVideoPickImpl, iconTheme: iconTheme, ), + if (showFormulaButton) + FormulaButton( + icon: Icons.functions, + iconSize: toolbarIconSize, + controller: controller, + onImagePickCallback: onImagePickCallback, + filePickImpl: filePickImpl, + webImagePickImpl: webImagePickImpl, + mediaPickSettingSelector: mediaPickSettingSelector, + iconTheme: iconTheme, + dialogTheme: dialogTheme, + ), if (showDividers && isButtonGroupShown[0] && (isButtonGroupShown[1] || diff --git a/lib/src/widgets/toolbar/formula_button.dart b/lib/src/widgets/toolbar/formula_button.dart new file mode 100644 index 00000000..7c7fc9b3 --- /dev/null +++ b/lib/src/widgets/toolbar/formula_button.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; + +import '../../models/documents/nodes/embeddable.dart'; +import '../../models/themes/quill_dialog_theme.dart'; +import '../../models/themes/quill_icon_theme.dart'; +import '../controller.dart'; +import '../toolbar.dart'; + +class FormulaButton extends StatelessWidget { + const FormulaButton({ + required this.icon, + required this.controller, + this.iconSize = kDefaultIconSize, + this.onImagePickCallback, + this.fillColor, + this.filePickImpl, + this.webImagePickImpl, + this.mediaPickSettingSelector, + this.iconTheme, + this.dialogTheme, + Key? key, + }) : super(key: key); + + final IconData icon; + final double iconSize; + + final Color? fillColor; + + final QuillController controller; + + final OnImagePickCallback? onImagePickCallback; + + final WebImagePickImpl? webImagePickImpl; + + final FilePickImpl? filePickImpl; + + final MediaPickSettingSelector? mediaPickSettingSelector; + + final QuillIconTheme? iconTheme; + + final QuillDialogTheme? dialogTheme; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; + final iconFillColor = + iconTheme?.iconUnselectedFillColor ?? (fillColor ?? theme.canvasColor); + + return QuillIconButton( + icon: Icon(icon, size: iconSize, color: iconColor), + highlightElevation: 0, + hoverElevation: 0, + size: iconSize * 1.77, + fillColor: iconFillColor, + borderRadius: iconTheme?.borderRadius ?? 2, + onPressed: () => _onPressedHandler(context), + ); + } + + Future _onPressedHandler(BuildContext context) async { + final index = controller.selection.baseOffset; + final length = controller.selection.extentOffset - index; + + controller.replaceText(index, length, BlockEmbed.formula(''), null); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index e70dc559..e3fb5f21 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,7 @@ dependencies: gallery_saver: ^2.3.2 device_info_plus: ^4.0.0 platform: ^3.1.0 + math_keyboard: ^0.1.6 dev_dependencies: flutter_test: