Major update 6 (#1456)

pull/1458/head
Ahmed Hnewa 1 year ago committed by GitHub
parent 9b380e4866
commit 6cf9cd0f0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      CHANGELOG.md
  2. 12
      example/lib/pages/home_page.dart
  3. 2
      flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart
  4. 2
      flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart
  5. 2
      flutter_quill_extensions/lib/embeds/toolbar/image_button.dart
  6. 2
      flutter_quill_extensions/lib/embeds/toolbar/media_button.dart
  7. 2
      flutter_quill_extensions/lib/embeds/toolbar/video_button.dart
  8. 2
      flutter_quill_extensions/pubspec.yaml
  9. 7
      lib/src/models/config/shared_configurations.dart
  10. 39
      lib/src/models/config/toolbar/buttons/link_style.dart
  11. 61
      lib/src/models/config/toolbar/buttons/search.dart
  12. 41
      lib/src/models/config/toolbar/buttons/select_header_style.dart
  13. 31
      lib/src/models/config/toolbar/configurations.dart
  14. 16
      lib/src/models/themes/quill_custom_button.dart
  15. 7
      lib/src/widgets/raw_editor/raw_editor.dart
  16. 2
      lib/src/widgets/toolbar/buttons/clear_format.dart
  17. 2
      lib/src/widgets/toolbar/buttons/color.dart
  18. 25
      lib/src/widgets/toolbar/buttons/font_family.dart
  19. 26
      lib/src/widgets/toolbar/buttons/font_size.dart
  20. 23
      lib/src/widgets/toolbar/buttons/history.dart
  21. 2
      lib/src/widgets/toolbar/buttons/indent.dart
  22. 193
      lib/src/widgets/toolbar/buttons/link_style.dart
  23. 72
      lib/src/widgets/toolbar/buttons/search.dart
  24. 187
      lib/src/widgets/toolbar/buttons/search/search.dart
  25. 81
      lib/src/widgets/toolbar/buttons/search/search_dialog.dart
  26. 31
      lib/src/widgets/toolbar/buttons/select_alignment.dart
  27. 151
      lib/src/widgets/toolbar/buttons/select_header_style.dart
  28. 29
      lib/src/widgets/toolbar/buttons/toggle_check_list.dart
  29. 18
      lib/src/widgets/toolbar/buttons/toggle_style.dart
  30. 9
      lib/src/widgets/toolbar/enum.dart
  31. 93
      lib/src/widgets/toolbar/toolbar.dart
  32. 2
      pubspec.yaml

@ -1,3 +1,10 @@
## [7.9.0]
- Buttons Improvemenets
- Refactor all the button configurations that used in `QuillToolbar.basic()` but there are still few lefts
- **Breaking change**: Remove some configurations from the QuillToolbar and move them to the new `QuillProvider`, please notice this is a development version and this might be changed in the next few days, the stable release will be ready in less than 3 weeks
- Update `flutter_quill_extensions` and it will be published into pub.dev soon.
- Allow you to customize the search dialog by custom callback with child builder
## [7.8.0]
- **Important note**: this is not test release yet, it works but need more test and changes and breaking changes, we don't have development version and it will help us if you try the latest version and report the issues in Github but if you want a stable version please use `7.4.16`. this refactoring process will not take long and should be done less than three weeks with the testing.
- We managed to refactor most of the buttons configurations and customizations in the `QuillProvider`, only three lefts then will start on refactoring the toolbar configurations

@ -262,7 +262,7 @@ class _HomePageState extends State<HomePage> {
webImagePickImpl: _webImagePickImpl,
),
showAlignmentButtons: true,
afterButtonPressed: _focusNode.requestFocus,
// afterButtonPressed: _focusNode.requestFocus,
);
}
if (_isDesktop()) {
@ -272,7 +272,7 @@ class _HomePageState extends State<HomePage> {
filePickImpl: openFileSystemPickerForDesktop,
),
showAlignmentButtons: true,
afterButtonPressed: _focusNode.requestFocus,
// afterButtonPressed: _focusNode.requestFocus,
);
}
return QuillToolbar.basic(
@ -288,7 +288,7 @@ class _HomePageState extends State<HomePage> {
// cameraPickSettingSelector: _selectCameraPickSetting,
),
showAlignmentButtons: true,
afterButtonPressed: _focusNode.requestFocus,
// afterButtonPressed: _focusNode.requestFocus,
);
}
@ -320,6 +320,12 @@ class _HomePageState extends State<HomePage> {
return SafeArea(
child: QuillProvider(
configurations: QuillConfigurations(
toolbarConfigurations: QuillToolbarConfigurations(
buttonOptions: QuillToolbarButtonOptions(
base: QuillToolbarBaseButtonOptions(
afterButtonPressed: _focusNode.requestFocus,
),
)),
editorConfigurations: const QuillEditorConfigurations(
placeholder: 'Add content',
// ignore: avoid_redundant_argument_values

@ -53,7 +53,7 @@ class CameraButton extends StatelessWidget {
final iconFillColor =
iconTheme?.iconUnselectedFillColor ?? (fillColor ?? theme.canvasColor);
return QuillIconButton(
return QuillToolbarIconButton(
icon: Icon(icon, size: iconSize, color: iconColor),
tooltip: tooltip,
highlightElevation: 0,

@ -34,7 +34,7 @@ class FormulaButton extends StatelessWidget {
final iconFillColor =
iconTheme?.iconUnselectedFillColor ?? (fillColor ?? theme.canvasColor);
return QuillIconButton(
return QuillToolbarIconButton(
icon: Icon(icon, size: iconSize, color: iconColor),
tooltip: tooltip,
highlightElevation: 0,

@ -51,7 +51,7 @@ class ImageButton extends StatelessWidget {
final iconFillColor =
iconTheme?.iconUnselectedFillColor ?? (fillColor ?? theme.canvasColor);
return QuillIconButton(
return QuillToolbarIconButton(
icon: Icon(icon, size: iconSize, color: iconColor),
tooltip: tooltip,
highlightElevation: 0,

@ -94,7 +94,7 @@ class MediaButton extends StatelessWidget {
final iconFillColor =
iconTheme?.iconUnselectedFillColor ?? fillColor ?? theme.canvasColor;
return QuillIconButton(
return QuillToolbarIconButton(
icon: Icon(icon, size: iconSize, color: iconColor),
tooltip: tooltip,
highlightElevation: 0,

@ -53,7 +53,7 @@ class VideoButton extends StatelessWidget {
final iconFillColor =
iconTheme?.iconUnselectedFillColor ?? (fillColor ?? theme.canvasColor);
return QuillIconButton(
return QuillToolbarIconButton(
icon: Icon(icon, size: iconSize, color: iconColor),
tooltip: tooltip,
highlightElevation: 0,

@ -19,7 +19,7 @@ dependencies:
flutter:
sdk: flutter
flutter_quill: ^7.5.0
flutter_quill: ^7.8.0
# In case you are working on changes for both libraries,
# flutter_quill:
# path: ~/development/playground/framework_based/flutter/flutter-quill

@ -1,7 +1,9 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart' show Color, Colors, Locale;
import './editor/configurations.dart' show QuillEditorConfigurations;
import './toolbar/configurations.dart' show QuillToolbarConfigurations;
import '../themes/quill_dialog_theme.dart';
import 'others/animations.dart';
export './others/animations.dart';
@ -11,6 +13,7 @@ export './others/animations.dart';
class QuillSharedConfigurations extends Equatable {
const QuillSharedConfigurations({
this.dialogBarrierColor = Colors.black54,
this.dialogTheme,
this.locale,
this.animationConfigurations = const QuillAnimationConfigurations(
checkBoxPointItem: false,
@ -22,6 +25,10 @@ class QuillSharedConfigurations extends Equatable {
/// The barrier color of the shown dialogs
final Color dialogBarrierColor;
/// The default dialog theme for all the dialogs for quill editor and
/// quill toolbar
final QuillDialogTheme? dialogTheme;
/// The locale to use for the editor and toolbar, defaults to system locale
/// More https://github.com/singerdmx/flutter-quill#translation
final Locale? locale;

@ -0,0 +1,39 @@
import 'package:flutter/widgets.dart' show Color;
import '../../../../widgets/toolbar/toolbar.dart';
import '../../../structs/link_dialog_action.dart';
import '../../../themes/quill_dialog_theme.dart';
class QuillToolbarLinkStyleButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {
const QuillToolbarLinkStyleButtonExtraOptions({
required super.controller,
required super.context,
required super.onPressed,
});
}
class QuillToolbarLinkStyleButtonOptions extends QuillToolbarBaseButtonOptions<
QuillToolbarLinkStyleButtonOptions,
QuillToolbarLinkStyleButtonExtraOptions> {
const QuillToolbarLinkStyleButtonOptions({
this.dialogTheme,
this.linkRegExp,
this.linkDialogAction,
this.dialogBarrierColor,
this.iconSize,
super.iconData,
super.globalIconSize,
super.afterButtonPressed,
super.tooltip,
super.iconTheme,
super.childBuilder,
super.controller,
});
final double? iconSize;
final QuillDialogTheme? dialogTheme;
final RegExp? linkRegExp;
final LinkDialogAction? linkDialogAction;
final Color? dialogBarrierColor;
}

@ -0,0 +1,61 @@
import 'package:flutter/widgets.dart' show Color;
import '../../../../../flutter_quill.dart';
class QuillToolbarSearchButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {
const QuillToolbarSearchButtonExtraOptions({
required super.controller,
required super.context,
required super.onPressed,
});
}
class QuillToolbarSearchButtonOptions extends QuillToolbarBaseButtonOptions {
const QuillToolbarSearchButtonOptions({
super.iconData,
super.controller,
super.childBuilder,
super.tooltip,
super.afterButtonPressed,
super.iconTheme,
this.dialogTheme,
this.iconSize,
this.dialogBarrierColor,
this.fillColor,
this.customOnPressedCallback,
});
final QuillDialogTheme? dialogTheme;
final double? iconSize;
/// By default will be [dialogBarrierColor] from [QuillSharedConfigurations]
final Color? dialogBarrierColor;
final Color? fillColor;
/// By default we will show simple search dialog ui
/// you can pass value to this callback to change this
final QuillToolbarSearchButtomOnPressedCallback? customOnPressedCallback;
}
typedef QuillToolbarSearchButtomOnPressedCallback = Future<void> Function(
QuillController controller,
);
// typedef QuillToolbarSearchButtonFindTextCallback = List<int> Function({
// required int index,
// required String text,
// required QuillController controller,
// required List<int> offsets,
// required bool wholeWord,
// required bool caseSensitive,
// bool moveToPosition,
// });
// typedef QuillToolbarSearchButtonMoveToPositionCallback = void Function({
// required int index,
// required String text,
// required QuillController controller,
// required List<int> offsets,
// });

@ -0,0 +1,41 @@
import 'package:flutter/widgets.dart' show Axis;
import '../../../../widgets/toolbar/toolbar.dart';
import '../../../documents/attribute.dart';
class QuillToolbarSelectHeaderStyleButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {
const QuillToolbarSelectHeaderStyleButtonExtraOptions({
required super.controller,
required super.context,
required super.onPressed,
});
}
class QuillToolbarSelectHeaderStyleButtonsOptions
extends QuillToolbarBaseButtonOptions<
QuillToolbarSelectHeaderStyleButtonsOptions,
QuillToolbarSelectHeaderStyleButtonExtraOptions> {
const QuillToolbarSelectHeaderStyleButtonsOptions({
super.afterButtonPressed,
super.childBuilder,
super.controller,
super.iconData,
super.iconTheme,
super.tooltip,
this.axis,
this.attributes = const [
Attribute.header,
Attribute.h1,
Attribute.h2,
Attribute.h3,
],
this.iconSize,
});
final List<Attribute> attributes;
/// By default we will the toolbar axis from [QuillToolbarConfigurations]
final Axis? axis;
final double? iconSize;
}

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart' show immutable;
import 'package:flutter/widgets.dart' show Axis;
import 'buttons/base.dart';
import 'buttons/clear_format.dart';
@ -8,17 +9,24 @@ import 'buttons/font_family.dart';
import 'buttons/font_size.dart';
import 'buttons/history.dart';
import 'buttons/indent.dart';
import 'buttons/link_style.dart';
import 'buttons/search.dart';
import 'buttons/select_alignment.dart';
import 'buttons/select_header_style.dart';
import 'buttons/toggle_check_list.dart';
import 'buttons/toggle_style.dart';
export './../../../widgets/toolbar/buttons/search/search_dialog.dart';
export './buttons/base.dart';
export './buttons/clear_format.dart';
export './buttons/color.dart';
export './buttons/font_family.dart';
export './buttons/font_size.dart';
export './buttons/history.dart';
export './buttons/link_style.dart';
export './buttons/search.dart';
export './buttons/select_alignment.dart';
export './buttons/select_header_style.dart';
export './buttons/toggle_check_list.dart';
export './buttons/toggle_style.dart';
@ -42,8 +50,10 @@ class QuillToolbarConfigurations extends Equatable {
this.multiRowsDisplay = true,
this.fontFamilyValues,
this.fontSizesValues,
this.axis = Axis.horizontal,
/// By default it will calculated based on the [baseOptions] iconSize
/// By default it will calculated based on the [globalIconSize] from
/// [base] in [QuillToolbarButtonOptions]
/// You can change it but the the change only apply if
/// the [multiRowsDisplay] is false, if [multiRowsDisplay] then the value
/// will be [kDefaultIconSize] * 2
@ -93,6 +103,11 @@ class QuillToolbarConfigurations extends Equatable {
/// ```
final Map<String, String>? fontSizesValues;
/// Toolbar axis
/// it will apply only for [QuillToolbar.basic]
/// we will update that logic soon
final Axis axis;
@override
List<Object?> get props => [
buttonOptions,
@ -100,6 +115,7 @@ class QuillToolbarConfigurations extends Equatable {
fontFamilyValues,
fontSizesValues,
toolbarSize,
axis,
];
}
@ -137,6 +153,10 @@ class QuillToolbarButtonOptions extends Equatable {
this.clearFormat = const QuillToolbarClearFormatButtonOptions(),
this.selectAlignmentButtons =
const QuillToolbarSelectAlignmentButtonOptions(),
this.search = const QuillToolbarSearchButtonOptions(),
this.selectHeaderStyleButtons =
const QuillToolbarSelectHeaderStyleButtonsOptions(),
this.linkStyle = const QuillToolbarLinkStyleButtonOptions(),
});
/// The base configurations for all the buttons which will apply to all
@ -173,6 +193,15 @@ class QuillToolbarButtonOptions extends Equatable {
/// and you have child builder
final QuillToolbarSelectAlignmentButtonOptions selectAlignmentButtons;
final QuillToolbarSearchButtonOptions search;
/// The reason we call this buttons in the end because this is responsible
/// for all the header style buttons and not just one, you still
/// can customize it and you also have child builder
final QuillToolbarSelectHeaderStyleButtonsOptions selectHeaderStyleButtons;
final QuillToolbarLinkStyleButtonOptions linkStyle;
@override
List<Object?> get props => [
base,

@ -1,17 +1,18 @@
import 'package:flutter/material.dart';
class QuillCustomButton {
import '../../widgets/toolbar/toolbar.dart';
class QuillCustomButton extends QuillToolbarBaseButtonOptions {
const QuillCustomButton({
this.icon,
super.iconData,
this.iconColor,
this.onTap,
this.tooltip,
super.tooltip,
this.iconSize,
this.child,
super.iconTheme,
});
///The icon widget
final IconData? icon;
///The icon color;
final Color? iconColor;
@ -21,6 +22,5 @@ class QuillCustomButton {
///The customButton placeholder
final Widget? child;
/// The button tooltip.
final String? tooltip;
final double? iconSize;
}

@ -50,7 +50,7 @@ import '../text_block.dart';
import '../text_line.dart';
import '../text_selection.dart';
import '../toolbar/buttons/link_style2.dart';
import '../toolbar/search_dialog.dart';
import '../toolbar/buttons/search/search_dialog.dart';
import 'raw_editor_state_selection_delegate_mixin.dart';
import 'raw_editor_state_text_input_client_mixin.dart';
@ -2628,7 +2628,10 @@ class _OpenSearchAction extends ContextAction<OpenSearchIntent> {
}
await showDialog<String>(
context: context,
builder: (_) => SearchDialog(controller: state.controller, text: ''),
builder: (_) => QuillToolbarSearchDialog(
controller: state.controller,
text: '',
),
);
}

@ -18,7 +18,7 @@ class QuillToolbarClearFormatButton extends StatelessWidget {
final QuillToolbarClearFormatButtonOptions options;
QuillController get controller {
return options.controller ?? _controller;
return _controller;
}
double _iconSize(BuildContext context) {

@ -100,7 +100,7 @@ class _QuillToolbarColorButtonState extends State<QuillToolbarColorButton> {
}
QuillController get controller {
return options.controller ?? widget.controller;
return widget.controller;
}
double get iconSize {

@ -38,12 +38,6 @@ class _QuillToolbarFontFamilyButtonState
return widget.options;
}
/// Since it's not safe to call anything related to the context in dispose
/// then we will save a reference to the [controller]
/// and update it in [didChangeDependencies]
/// and use it in dispose method
late QuillController _controller;
Style get _selectionStyle => controller.getSelectionStyle();
@override
@ -52,27 +46,14 @@ class _QuillToolbarFontFamilyButtonState
_initState();
}
Future<void> _initState() async {
if (isFlutterTest()) {
// We don't need to listen for changes in the tests
return;
}
await Future.delayed(Duration.zero);
setState(() {
void _initState() {
_currentValue = _defaultDisplayText;
});
controller.addListener(_didChangeEditingValue);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_controller = controller;
}
@override
void dispose() {
_controller.removeListener(_didChangeEditingValue);
controller.removeListener(_didChangeEditingValue);
super.dispose();
}
@ -128,7 +109,7 @@ class _QuillToolbarFontFamilyButtonState
}
QuillController get controller {
return options.controller ?? widget.controller;
return widget.controller;
}
double get iconSize {

@ -34,12 +34,6 @@ class _QuillToolbarFontSizeButtonState
return widget.options;
}
/// Since it's not safe to call anything related to the context in dispose
/// then we will save a reference to the [controller]
/// and update it in [didChangeDependencies]
/// and use it in dispose method
late QuillController _controller;
Map<String, String> get rawItemsMap {
final fontSizes = options.rawItemsMap ??
context.requireQuillToolbarConfigurations.fontSizesValues ??
@ -65,33 +59,21 @@ class _QuillToolbarFontSizeButtonState
_initState();
}
Future<void> _initState() async {
if (isFlutterTest()) {
return;
}
await Future.delayed(Duration.zero);
setState(() {
void _initState() {
_currentValue = _defaultDisplayText;
});
controller.addListener(_didChangeEditingValue);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_controller = controller;
}
@override
void dispose() {
_controller.removeListener(_didChangeEditingValue);
controller.removeListener(_didChangeEditingValue);
super.dispose();
}
@override
void didUpdateWidget(covariant QuillToolbarFontSizeButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.controller == controller) {
if (controller == oldWidget.controller) {
return;
}
controller
@ -119,7 +101,7 @@ class _QuillToolbarFontSizeButtonState
}
QuillController get controller {
return options.controller ?? widget.controller;
return widget.controller;
}
double get iconSize {

@ -1,19 +1,19 @@
import 'package:flutter/material.dart';
import '../../../../extensions.dart';
import '../../../../translations.dart';
import '../../../utils/extensions/build_context.dart';
import '../../../utils/extensions/quill_controller.dart';
import '../../controller.dart';
import '../toolbar.dart';
class QuillToolbarHistoryButton extends StatefulWidget {
const QuillToolbarHistoryButton({
required this.options,
required this.controller,
super.key,
});
final QuillToolbarHistoryButtonOptions options;
final QuillController controller;
@override
_QuillToolbarHistoryButtonState createState() =>
@ -29,7 +29,7 @@ class _QuillToolbarHistoryButtonState extends State<QuillToolbarHistoryButton> {
}
QuillController get controller {
return options.controller.notNull(context);
return widget.controller;
}
@override
@ -38,17 +38,12 @@ class _QuillToolbarHistoryButtonState extends State<QuillToolbarHistoryButton> {
_listenForChanges(); // Listen for changes and change it
}
Future<void> _listenForChanges() async {
if (isFlutterTest()) {
// We don't need to listen for changes in the tests
return;
}
await Future.delayed(Duration.zero); // Wait for the widget to built
void _listenForChanges() {
_updateCanPressed(); // Set the init state
// Listen for changes and change it
controller.changes.listen((event) async {
_updateCanPressed();
_updateCanPressedWithSetState();
});
}
@ -116,16 +111,18 @@ class _QuillToolbarHistoryButtonState extends State<QuillToolbarHistoryButton> {
);
}
void _updateCanPressed() {
void _updateCanPressedWithSetState() {
if (!mounted) return;
setState(() {
setState(_updateCanPressed);
}
void _updateCanPressed() {
if (options.isUndo) {
_canPressed = controller.hasUndo;
return;
}
_canPressed = controller.hasRedo;
});
}
void _updateHistory() {

@ -30,7 +30,7 @@ class _QuillToolbarIndentButtonState extends State<QuillToolbarIndentButton> {
}
QuillController get controller {
return options.controller ?? widget.controller;
return widget.controller;
}
double get iconSize {

@ -6,6 +6,7 @@ import '../../../models/structs/link_dialog_action.dart';
import '../../../models/themes/quill_dialog_theme.dart';
import '../../../models/themes/quill_icon_theme.dart';
import '../../../translations/toolbar.i18n.dart';
import '../../../utils/extensions/build_context.dart';
import '../../controller.dart';
import '../../link.dart';
import '../toolbar.dart';
@ -13,28 +14,21 @@ import '../toolbar.dart';
class QuillToolbarLinkStyleButton extends StatefulWidget {
const QuillToolbarLinkStyleButton({
required this.controller,
this.iconSize = kDefaultIconSize,
this.icon,
this.iconTheme,
this.dialogTheme,
this.afterButtonPressed,
this.tooltip,
this.linkRegExp,
this.linkDialogAction,
this.dialogBarrierColor = Colors.black54,
Key? key,
}) : super(key: key);
required this.options,
super.key,
});
final QuillController controller;
final IconData? icon;
final double iconSize;
final QuillIconTheme? iconTheme;
final QuillDialogTheme? dialogTheme;
final VoidCallback? afterButtonPressed;
final String? tooltip;
final RegExp? linkRegExp;
final LinkDialogAction? linkDialogAction;
final Color dialogBarrierColor;
// final IconData? icon;
// final double iconSize;
// final QuillIconTheme? iconTheme;
// final QuillDialogTheme? dialogTheme;
// final VoidCallback? afterButtonPressed;
// final String? tooltip;
// final RegExp? linkRegExp;
// final LinkDialogAction? linkDialogAction;
// final Color dialogBarrierColor;
final QuillToolbarLinkStyleButtonOptions options;
@override
_QuillToolbarLinkStyleButtonState createState() =>
@ -50,22 +44,68 @@ class _QuillToolbarLinkStyleButtonState
@override
void initState() {
super.initState();
widget.controller.addListener(_didChangeSelection);
controller.addListener(_didChangeSelection);
}
@override
void didUpdateWidget(covariant QuillToolbarLinkStyleButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != widget.controller) {
if (oldWidget.controller != controller) {
oldWidget.controller.removeListener(_didChangeSelection);
widget.controller.addListener(_didChangeSelection);
controller.addListener(_didChangeSelection);
}
}
@override
void dispose() {
super.dispose();
widget.controller.removeListener(_didChangeSelection);
controller.removeListener(_didChangeSelection);
}
QuillController get controller {
return widget.controller;
}
QuillToolbarLinkStyleButtonOptions get options {
return widget.options;
}
double get iconSize {
final baseFontSize = baseButtonExtraOptions.globalIconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize;
}
VoidCallback? get afterButtonPressed {
return options.afterButtonPressed ??
baseButtonExtraOptions.afterButtonPressed;
}
QuillIconTheme? get iconTheme {
return options.iconTheme ?? baseButtonExtraOptions.iconTheme;
}
QuillToolbarBaseButtonOptions get baseButtonExtraOptions {
return context.requireQuillToolbarBaseButtonOptions;
}
String get tooltip {
return options.tooltip ??
baseButtonExtraOptions.tooltip ??
'Insert URL'.i18n;
}
IconData get iconData {
return options.iconData ?? baseButtonExtraOptions.iconData ?? Icons.link;
}
Color get dialogBarrierColor {
return options.dialogBarrierColor ??
context.requireQuillSharedConfigurations.dialogBarrierColor;
}
RegExp get linkRegExp {
return options.linkRegExp ?? RegExp(r'https?://\S+');
}
@override
@ -73,87 +113,113 @@ class _QuillToolbarLinkStyleButtonState
final theme = Theme.of(context);
final isToggled = _getLinkAttributeValue() != null;
final pressedHandler = () => _openLinkDialog(context);
final childBuilder =
options.childBuilder ?? baseButtonExtraOptions.childBuilder;
if (childBuilder != null) {
return childBuilder(
QuillToolbarLinkStyleButtonOptions(
afterButtonPressed: afterButtonPressed,
controller: controller,
dialogBarrierColor: dialogBarrierColor,
dialogTheme: options.dialogTheme,
iconData: iconData,
iconSize: iconSize,
tooltip: tooltip,
linkDialogAction: options.linkDialogAction,
linkRegExp: linkRegExp,
iconTheme: iconTheme,
),
QuillToolbarLinkStyleButtonExtraOptions(
context: context,
controller: controller,
onPressed: () {
pressedHandler();
afterButtonPressed?.call();
},
),
);
}
return QuillToolbarIconButton(
tooltip: widget.tooltip,
tooltip: tooltip,
highlightElevation: 0,
hoverElevation: 0,
size: widget.iconSize * kIconButtonFactor,
size: iconSize * kIconButtonFactor,
icon: Icon(
widget.icon ?? Icons.link,
size: widget.iconSize,
iconData,
size: iconSize,
color: isToggled
? (widget.iconTheme?.iconSelectedColor ??
theme.primaryIconTheme.color)
: (widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color),
? (iconTheme?.iconSelectedColor ?? theme.primaryIconTheme.color)
: (iconTheme?.iconUnselectedColor ?? theme.iconTheme.color),
),
fillColor: isToggled
? (widget.iconTheme?.iconSelectedFillColor ??
Theme.of(context).primaryColor)
: (widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor),
borderRadius: widget.iconTheme?.borderRadius ?? 2,
? (iconTheme?.iconSelectedFillColor ?? Theme.of(context).primaryColor)
: (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor),
borderRadius: iconTheme?.borderRadius ?? 2,
onPressed: pressedHandler,
afterPressed: widget.afterButtonPressed,
afterPressed: afterButtonPressed,
);
}
void _openLinkDialog(BuildContext context) {
showDialog<_TextLink>(
Future<void> _openLinkDialog(BuildContext context) async {
// TODO: Add a custom call back to customize this just like in the search
// button
final value = await showDialog<_TextLink>(
context: context,
barrierColor: widget.dialogBarrierColor,
barrierColor: dialogBarrierColor,
builder: (ctx) {
final link = _getLinkAttributeValue();
final index = widget.controller.selection.start;
final index = controller.selection.start;
var text;
if (link != null) {
// text should be the link's corresponding text, not selection
final leaf =
widget.controller.document.querySegmentLeafNode(index).leaf;
final leaf = controller.document.querySegmentLeafNode(index).leaf;
if (leaf != null) {
text = leaf.toPlainText();
}
}
final len = widget.controller.selection.end - index;
text ??=
len == 0 ? '' : widget.controller.document.getPlainText(index, len);
final len = controller.selection.end - index;
text ??= len == 0 ? '' : controller.document.getPlainText(index, len);
return _LinkDialog(
dialogTheme: widget.dialogTheme,
dialogTheme: options.dialogTheme,
link: link,
text: text,
linkRegExp: widget.linkRegExp,
action: widget.linkDialogAction,
linkRegExp: linkRegExp,
action: options.linkDialogAction,
);
},
).then(
(value) {
if (value != null) _linkSubmitted(value);
},
);
if (value == null) {
return;
}
_linkSubmitted(value);
}
String? _getLinkAttributeValue() {
return widget.controller
.getSelectionStyle()
.attributes[Attribute.link.key]
?.value;
return controller.getSelectionStyle().attributes[Attribute.link.key]?.value;
}
void _linkSubmitted(_TextLink value) {
var index = widget.controller.selection.start;
var length = widget.controller.selection.end - index;
var index = controller.selection.start;
var length = controller.selection.end - index;
if (_getLinkAttributeValue() != null) {
// text should be the link's corresponding text, not selection
final leaf = widget.controller.document.querySegmentLeafNode(index).leaf;
final leaf = controller.document.querySegmentLeafNode(index).leaf;
if (leaf != null) {
final range = getLinkRange(leaf);
index = range.start;
length = range.end - range.start;
}
}
widget.controller.replaceText(index, length, value.text, null);
widget.controller
.formatText(index, value.text.length, LinkAttribute(value.link));
controller
..replaceText(index, length, value.text, null)
..formatText(
index,
value.text.length,
LinkAttribute(value.link),
);
}
}
@ -164,8 +230,7 @@ class _LinkDialog extends StatefulWidget {
this.text,
this.linkRegExp,
this.action,
Key? key,
}) : super(key: key);
});
final QuillDialogTheme? dialogTheme;
final String? link;

@ -1,72 +0,0 @@
import 'package:flutter/material.dart';
import '../../../models/themes/quill_dialog_theme.dart';
import '../../../models/themes/quill_icon_theme.dart';
import '../../controller.dart';
import '../search_dialog.dart';
import '../toolbar.dart';
class QuillToolbarSearchButton extends StatelessWidget {
const QuillToolbarSearchButton({
required this.icon,
required this.controller,
this.iconSize = kDefaultIconSize,
this.fillColor,
this.iconTheme,
this.dialogBarrierColor = Colors.black54,
this.dialogTheme,
this.afterButtonPressed,
this.tooltip,
Key? key,
}) : super(key: key);
final IconData icon;
final double iconSize;
final QuillController controller;
final Color? fillColor;
final Color dialogBarrierColor;
final QuillIconTheme? iconTheme;
final QuillDialogTheme? dialogTheme;
final VoidCallback? afterButtonPressed;
final String? tooltip;
@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 QuillToolbarIconButton(
tooltip: tooltip,
icon: Icon(icon, size: iconSize, color: iconColor),
highlightElevation: 0,
hoverElevation: 0,
size: iconSize * kIconButtonFactor,
fillColor: iconFillColor,
borderRadius: iconTheme?.borderRadius ?? 2,
onPressed: () => _onPressedHandler(context),
afterPressed: afterButtonPressed,
);
}
Future<void> _onPressedHandler(BuildContext context) async {
final value = await showDialog<String>(
barrierColor: dialogBarrierColor,
context: context,
builder: (_) => SearchDialog(
controller: controller,
dialogTheme: dialogTheme,
text: '',
),
);
_searchSubmitted(value);
}
void _searchSubmitted(String? value) {
// If we are doing nothing here then why we care about the result??
}
}

@ -0,0 +1,187 @@
import 'package:flutter/material.dart';
import '../../../../../translations.dart';
import '../../../../models/themes/quill_dialog_theme.dart';
import '../../../../models/themes/quill_icon_theme.dart';
import '../../../../utils/extensions/build_context.dart';
import '../../../controller.dart';
import '../../toolbar.dart';
class QuillToolbarSearchButton extends StatelessWidget {
const QuillToolbarSearchButton({
required QuillController controller,
required this.options,
super.key,
}) : _controller = controller;
final QuillController _controller;
final QuillToolbarSearchButtonOptions options;
QuillController get controller {
return _controller;
}
double _iconSize(BuildContext context) {
final baseFontSize = baseButtonExtraOptions(context).globalIconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize;
}
VoidCallback? _afterButtonPressed(BuildContext context) {
return options.afterButtonPressed ??
baseButtonExtraOptions(context).afterButtonPressed;
}
QuillIconTheme? _iconTheme(BuildContext context) {
return options.iconTheme ?? baseButtonExtraOptions(context).iconTheme;
}
QuillToolbarBaseButtonOptions baseButtonExtraOptions(BuildContext context) {
return context.requireQuillToolbarBaseButtonOptions;
}
IconData _iconData(BuildContext context) {
return options.iconData ??
baseButtonExtraOptions(context).iconData ??
Icons.search;
}
String _tooltip(BuildContext context) {
return options.tooltip ??
baseButtonExtraOptions(context).tooltip ??
('Search'.i18n);
}
Color _dialogBarrierColor(BuildContext context) {
return options.dialogBarrierColor ??
context.requireQuillSharedConfigurations.dialogBarrierColor;
}
QuillDialogTheme? _dialogTheme(BuildContext context) {
return options.dialogTheme ??
context.requireQuillSharedConfigurations.dialogTheme;
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final iconTheme = _iconTheme(context);
final tooltip = _tooltip(context);
final iconData = _iconData(context);
final iconSize = _iconSize(context);
final afterButtonPressed = _afterButtonPressed(context);
final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color;
final iconFillColor = iconTheme?.iconUnselectedFillColor ??
(options.fillColor ?? theme.canvasColor);
final childBuilder =
options.childBuilder ?? baseButtonExtraOptions(context).childBuilder;
if (childBuilder != null) {
return childBuilder(
QuillToolbarSearchButtonOptions(
afterButtonPressed: afterButtonPressed,
controller: controller,
dialogBarrierColor: _dialogBarrierColor(context),
dialogTheme: _dialogTheme(context),
fillColor: options.fillColor,
iconData: _iconData(context),
iconSize: _iconSize(context),
tooltip: _tooltip(context),
iconTheme: _iconTheme(context),
),
QuillToolbarSearchButtonExtraOptions(
controller: controller,
context: context,
onPressed: () {
_sharedOnPressed(context);
afterButtonPressed?.call();
},
),
);
}
return QuillToolbarIconButton(
tooltip: tooltip,
icon: Icon(
iconData,
size: iconSize,
color: iconColor,
),
highlightElevation: 0,
hoverElevation: 0,
size: iconSize * kIconButtonFactor,
fillColor: iconFillColor,
borderRadius: iconTheme?.borderRadius ?? 2,
onPressed: () => _sharedOnPressed(context),
afterPressed: afterButtonPressed,
);
}
Future<void> _sharedOnPressed(BuildContext context) async {
final customCallback = options.customOnPressedCallback;
if (customCallback != null) {
await customCallback(
controller,
);
return;
}
await showDialog<String>(
barrierColor: _dialogBarrierColor(context),
context: context,
builder: (_) => QuillToolbarSearchDialog(
controller: controller,
dialogTheme: _dialogTheme(context),
text: '',
),
);
}
// Those functions ((findText, moveToPosition)) are not ready yet.
// but consider moving them to a better place
// List<int> _findText({
// required int index,
// required String text,
// required QuillController controller,
// required List<int> offsets,
// required bool wholeWord,
// required bool caseSensitive,
// bool moveToPosition = true,
// }) {
// if (text.isEmpty) {
// return List.empty();
// }
// final newOffsets = controller.document.search(
// text,
// caseSensitive: caseSensitive,
// wholeWord: wholeWord,
// );
// index = 0; // TODO: This might need to be updated...
// if (offsets.isNotEmpty && moveToPosition) {
// _moveToPosition(
// index: index,
// text: text,
// controller: controller,
// offsets: offsets,
// );
// }
// return newOffsets;
// }
// void _moveToPosition({
// required int index,
// required String text,
// required QuillController controller,
// required List<int> offsets,
// }) {
// controller.updateSelection(
// TextSelection(
// baseOffset: offsets[index],
// extentOffset: offsets[index] + text.length,
// ),
// ChangeSource.LOCAL,
// );
// }
}

@ -1,27 +1,62 @@
import 'package:flutter/material.dart';
import '../../../translations.dart';
import '../../models/documents/document.dart';
import '../../models/themes/quill_dialog_theme.dart';
import '../controller.dart';
import '../../../../../translations.dart';
import '../../../../models/documents/document.dart';
import '../../../../models/themes/quill_dialog_theme.dart';
import '../../../controller.dart';
class SearchDialog extends StatefulWidget {
const SearchDialog({
@immutable
class QuillToolbarSearchDialogChildBuilderExtraOptions {
const QuillToolbarSearchDialogChildBuilderExtraOptions({
required this.onFindTextPressed,
required this.moveToNext,
required this.moveToPrevious,
required this.onTextChanged,
required this.onEditingComplete,
required this.text,
required this.textEditingController,
required this.offsets,
required this.index,
required this.caseSensitive,
required this.wholeWord,
});
final VoidCallback? onFindTextPressed;
final VoidCallback moveToNext;
final VoidCallback moveToPrevious;
final ValueChanged<String>? onTextChanged;
final VoidCallback? onEditingComplete;
final String text;
final TextEditingController textEditingController;
final List<int>? offsets;
final int index;
final bool caseSensitive;
final bool wholeWord;
}
typedef QuillToolbarSearchDialogChildBuilder = Widget Function(
QuillToolbarSearchDialogChildBuilderExtraOptions extraOptions,
);
class QuillToolbarSearchDialog extends StatefulWidget {
const QuillToolbarSearchDialog({
required this.controller,
this.dialogTheme,
this.text,
Key? key,
}) : super(key: key);
this.childBuilder,
super.key,
});
final QuillController controller;
final QuillDialogTheme? dialogTheme;
final String? text;
final QuillToolbarSearchDialogChildBuilder? childBuilder;
@override
_SearchDialogState createState() => _SearchDialogState();
_QuillToolbarSearchDialogState createState() =>
_QuillToolbarSearchDialogState();
}
class _SearchDialogState extends State<SearchDialog> {
class _QuillToolbarSearchDialogState extends State<QuillToolbarSearchDialog> {
late String _text;
late TextEditingController _controller;
late List<int>? _offsets;
@ -55,6 +90,25 @@ class _SearchDialogState extends State<SearchDialog> {
}
}
final childBuilder = widget.childBuilder;
if (childBuilder != null) {
return childBuilder(
QuillToolbarSearchDialogChildBuilderExtraOptions(
onFindTextPressed: _findText,
onEditingComplete: _findText,
onTextChanged: _textChanged,
caseSensitive: _caseSensitive,
textEditingController: _controller,
index: _index,
offsets: _offsets,
text: _text,
wholeWord: _wholeWord,
moveToNext: _moveToNext,
moveToPrevious: _moveToPosition,
),
);
}
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
@ -140,6 +194,7 @@ class _SearchDialogState extends State<SearchDialog> {
}
void _findText() {
_text = _controller.text;
if (_text.isEmpty) {
return;
}
@ -160,8 +215,10 @@ class _SearchDialogState extends State<SearchDialog> {
widget.controller.updateSelection(
TextSelection(
baseOffset: _offsets![_index],
extentOffset: _offsets![_index] + _text.length),
ChangeSource.LOCAL);
extentOffset: _offsets![_index] + _text.length,
),
ChangeSource.LOCAL,
);
}
void _moveToPrevious() {

@ -40,7 +40,7 @@ class _QuillToolbarSelectAlignmentButtonState
extends State<QuillToolbarSelectAlignmentButton> {
Attribute? _value;
Style get _selectionStyle => widget.controller.getSelectionStyle();
Style get _selectionStyle => controller.getSelectionStyle();
@override
void initState() {
@ -49,7 +49,7 @@ class _QuillToolbarSelectAlignmentButtonState
_value = _selectionStyle.attributes[Attribute.align.key] ??
Attribute.leftAlignment;
});
widget.controller.addListener(_didChangeEditingValue);
controller.addListener(_didChangeEditingValue);
}
QuillToolbarSelectAlignmentButtonOptions get options {
@ -57,7 +57,7 @@ class _QuillToolbarSelectAlignmentButtonState
}
QuillController get controller {
return options.controller ?? widget.controller;
return widget.controller;
}
double get iconSize {
@ -123,12 +123,6 @@ class _QuillToolbarSelectAlignmentButtonState
);
}
/// Since it's not safe to call anything related to the context in dispose
/// then we will save a reference to the [controller]
/// and update it in [didChangeDependencies]
/// and use it in dispose method
late QuillController _controller;
void _didChangeEditingValue() {
setState(() {
_value = _selectionStyle.attributes[Attribute.align.key] ??
@ -139,23 +133,17 @@ class _QuillToolbarSelectAlignmentButtonState
@override
void didUpdateWidget(covariant QuillToolbarSelectAlignmentButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != widget.controller) {
if (oldWidget.controller != controller) {
oldWidget.controller.removeListener(_didChangeEditingValue);
widget.controller.addListener(_didChangeEditingValue);
controller.addListener(_didChangeEditingValue);
_value = _selectionStyle.attributes[Attribute.align.key] ??
Attribute.leftAlignment;
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_controller = controller;
}
@override
void dispose() {
_controller.removeListener(_didChangeEditingValue);
controller.removeListener(_didChangeEditingValue);
super.dispose();
}
@ -207,7 +195,7 @@ class _QuillToolbarSelectAlignmentButtonState
if (childBuilder != null) {
throw UnsupportedError(
'Sorry but the `childBuilder` for the Select alignment button'
'Sorry but the `childBuilder` for the Select alignment buttons'
' is not supported. Yet but we will work on that soon.',
);
}
@ -245,11 +233,10 @@ class _QuillToolbarSelectAlignmentButtonState
: (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor),
onPressed: () {
_valueAttribute[index] == Attribute.leftAlignment
? widget.controller.formatSelection(
? controller.formatSelection(
Attribute.clone(Attribute.align, null),
)
: widget.controller
.formatSelection(_valueAttribute[index]);
: controller.formatSelection(_valueAttribute[index]);
afterButtonPressed?.call();
},
child: Icon(

@ -1,48 +1,34 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../../../extensions.dart';
import '../../../../translations.dart';
import '../../../models/documents/attribute.dart';
import '../../../models/documents/style.dart';
import '../../../models/themes/quill_icon_theme.dart';
import '../../../utils/widgets.dart';
import '../../../utils/extensions/build_context.dart';
import '../../controller.dart';
import '../toolbar.dart';
class QuillToolbarSelectHeaderStyleButton extends StatefulWidget {
const QuillToolbarSelectHeaderStyleButton({
class QuillToolbarSelectHeaderStyleButtons extends StatefulWidget {
const QuillToolbarSelectHeaderStyleButtons({
required this.controller,
this.axis = Axis.horizontal,
this.iconSize = kDefaultIconSize,
this.iconTheme,
this.attributes = const [
Attribute.header,
Attribute.h1,
Attribute.h2,
Attribute.h3,
],
this.afterButtonPressed,
this.tooltip,
Key? key,
}) : super(key: key);
required this.options,
super.key,
});
final QuillController controller;
final Axis axis;
final double iconSize;
final QuillIconTheme? iconTheme;
final List<Attribute> attributes;
final VoidCallback? afterButtonPressed;
final String? tooltip;
final QuillToolbarSelectHeaderStyleButtonsOptions options;
@override
_QuillToolbarSelectHeaderStyleButtonState createState() =>
_QuillToolbarSelectHeaderStyleButtonState();
_QuillToolbarSelectHeaderStyleButtonsState createState() =>
_QuillToolbarSelectHeaderStyleButtonsState();
}
class _QuillToolbarSelectHeaderStyleButtonState
extends State<QuillToolbarSelectHeaderStyleButton> {
class _QuillToolbarSelectHeaderStyleButtonsState
extends State<QuillToolbarSelectHeaderStyleButtons> {
Attribute? _selectedAttribute;
Style get _selectionStyle => widget.controller.getSelectionStyle();
Style get _selectionStyle => controller.getSelectionStyle();
final _valueToText = <Attribute, String>{
Attribute.header: 'N',
@ -57,61 +43,110 @@ class _QuillToolbarSelectHeaderStyleButtonState
setState(() {
_selectedAttribute = _getHeaderValue();
});
widget.controller.addListener(_didChangeEditingValue);
controller.addListener(_didChangeEditingValue);
}
QuillToolbarSelectHeaderStyleButtonsOptions get options {
return widget.options;
}
QuillController get controller {
return widget.controller;
}
double get iconSize {
final baseFontSize = baseButtonExtraOptions.globalIconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize;
}
VoidCallback? get afterButtonPressed {
return options.afterButtonPressed ??
baseButtonExtraOptions.afterButtonPressed;
}
QuillIconTheme? get iconTheme {
return options.iconTheme ?? baseButtonExtraOptions.iconTheme;
}
QuillToolbarBaseButtonOptions get baseButtonExtraOptions {
return context.requireQuillToolbarBaseButtonOptions;
}
String get tooltip {
return options.tooltip ??
baseButtonExtraOptions.tooltip ??
'Header style'.i18n;
}
Axis get axis {
return options.axis ?? context.requireQuillToolbarConfigurations.axis;
}
void _sharedOnPressed(Attribute attribute) {
final _attribute =
_selectedAttribute == attribute ? Attribute.header : attribute;
controller.formatSelection(_attribute);
afterButtonPressed?.call();
}
@override
Widget build(BuildContext context) {
assert(
widget.attributes.every((element) => _valueToText.keys.contains(element)),
options.attributes.every(
(element) => _valueToText.keys.contains(element),
),
'All attributes must be one of them: header, h1, h2 or h3',
);
final theme = Theme.of(context);
final style = TextStyle(
fontWeight: FontWeight.w600,
fontSize: widget.iconSize * 0.7,
fontSize: iconSize * 0.7,
);
final childBuilder =
options.childBuilder ?? baseButtonExtraOptions.childBuilder;
if (childBuilder != null) {
throw UnsupportedError(
'Sorry but the `childBuilder` for the Select header button'
' is not supported. Yet but we will work on that soon.',
);
}
final children = widget.attributes.map((attribute) {
final children = options.attributes.map((attribute) {
final isSelected = _selectedAttribute == attribute;
return Padding(
// ignore: prefer_const_constructors
padding: EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0),
// Do we really need to ignore (prefer_const_constructors)??
padding: EdgeInsets.symmetric(horizontal: !isWeb() ? 1.0 : 5.0),
child: ConstrainedBox(
constraints: BoxConstraints.tightFor(
width: widget.iconSize * kIconButtonFactor,
height: widget.iconSize * kIconButtonFactor,
width: iconSize * kIconButtonFactor,
height: iconSize * kIconButtonFactor,
),
child: UtilityWidgets.maybeTooltip(
message: widget.tooltip,
message: tooltip,
child: RawMaterialButton(
hoverElevation: 0,
highlightElevation: 0,
elevation: 0,
visualDensity: VisualDensity.compact,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
widget.iconTheme?.borderRadius ?? 2)),
borderRadius:
BorderRadius.circular(iconTheme?.borderRadius ?? 2)),
fillColor: isSelected
? (widget.iconTheme?.iconSelectedFillColor ??
? (iconTheme?.iconSelectedFillColor ??
Theme.of(context).primaryColor)
: (widget.iconTheme?.iconUnselectedFillColor ??
theme.canvasColor),
onPressed: () {
final _attribute = _selectedAttribute == attribute
? Attribute.header
: attribute;
widget.controller.formatSelection(_attribute);
widget.afterButtonPressed?.call();
},
: (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor),
onPressed: () => _sharedOnPressed(attribute),
child: Text(
_valueToText[attribute] ?? '',
style: style.copyWith(
color: isSelected
? (widget.iconTheme?.iconSelectedColor ??
? (iconTheme?.iconSelectedColor ??
theme.primaryIconTheme.color)
: (widget.iconTheme?.iconUnselectedColor ??
: (iconTheme?.iconUnselectedColor ??
theme.iconTheme.color),
),
),
@ -121,7 +156,7 @@ class _QuillToolbarSelectHeaderStyleButtonState
);
}).toList();
return widget.axis == Axis.horizontal
return axis == Axis.horizontal
? Row(
mainAxisSize: MainAxisSize.min,
children: children,
@ -139,10 +174,10 @@ class _QuillToolbarSelectHeaderStyleButtonState
}
Attribute<dynamic> _getHeaderValue() {
final attr = widget.controller.toolbarButtonToggler[Attribute.header.key];
final attr = controller.toolbarButtonToggler[Attribute.header.key];
if (attr != null) {
// checkbox tapping causes controller.selection to go to offset 0
widget.controller.toolbarButtonToggler.remove(Attribute.header.key);
controller.toolbarButtonToggler.remove(Attribute.header.key);
return attr;
}
return _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header;
@ -150,18 +185,18 @@ class _QuillToolbarSelectHeaderStyleButtonState
@override
void didUpdateWidget(
covariant QuillToolbarSelectHeaderStyleButton oldWidget) {
covariant QuillToolbarSelectHeaderStyleButtons oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != widget.controller) {
if (oldWidget.controller != controller) {
oldWidget.controller.removeListener(_didChangeEditingValue);
widget.controller.addListener(_didChangeEditingValue);
controller.addListener(_didChangeEditingValue);
_selectedAttribute = _getHeaderValue();
}
}
@override
void dispose() {
widget.controller.removeListener(_didChangeEditingValue);
controller.removeListener(_didChangeEditingValue);
super.dispose();
}
}

@ -31,18 +31,11 @@ class _QuillToolbarToggleCheckListButtonState
extends State<QuillToolbarToggleCheckListButton> {
bool? _isToggled;
/// Since it's not safe to call anything related to the context in dispose
/// then we will save a reference to the [controller]
/// and update it in [didChangeDependencies]
/// and use it in dispose method
late QuillController _controller;
Style get _selectionStyle => widget.controller.getSelectionStyle();
Style get _selectionStyle => controller.getSelectionStyle();
void _didChangeEditingValue() {
setState(() {
_isToggled =
_getIsToggled(widget.controller.getSelectionStyle().attributes);
_isToggled = _getIsToggled(controller.getSelectionStyle().attributes);
});
}
@ -50,17 +43,17 @@ class _QuillToolbarToggleCheckListButtonState
void initState() {
super.initState();
_isToggled = _getIsToggled(_selectionStyle.attributes);
widget.controller.addListener(_didChangeEditingValue);
controller.addListener(_didChangeEditingValue);
}
bool _getIsToggled(Map<String, Attribute> attrs) {
var attribute = widget.controller.toolbarButtonToggler[Attribute.list.key];
var attribute = controller.toolbarButtonToggler[Attribute.list.key];
if (attribute == null) {
attribute = attrs[Attribute.list.key];
} else {
// checkbox tapping causes controller.selection to go to offset 0
widget.controller.toolbarButtonToggler.remove(Attribute.list.key);
controller.toolbarButtonToggler.remove(Attribute.list.key);
}
if (attribute == null) {
@ -75,20 +68,14 @@ class _QuillToolbarToggleCheckListButtonState
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != controller) {
oldWidget.controller.removeListener(_didChangeEditingValue);
widget.controller.addListener(_didChangeEditingValue);
controller.addListener(_didChangeEditingValue);
_isToggled = _getIsToggled(_selectionStyle.attributes);
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_controller = controller;
}
@override
void dispose() {
_controller.removeListener(_didChangeEditingValue);
controller.removeListener(_didChangeEditingValue);
super.dispose();
}
@ -97,7 +84,7 @@ class _QuillToolbarToggleCheckListButtonState
}
QuillController get controller {
return options.controller ?? widget.controller;
return widget.controller;
}
double get iconSize {

@ -66,12 +66,6 @@ class QuillToolbarToggleStyleButton extends StatefulWidget {
class _QuillToolbarToggleStyleButtonState
extends State<QuillToolbarToggleStyleButton> {
/// Since it's not safe to call anything related to the context in dispose
/// then we will save a reference to the [controller]
/// and update it in [didChangeDependencies]
/// and use it in dispose method
late QuillController _controller;
bool? _isToggled;
Style get _selectionStyle => controller.getSelectionStyle();
@ -88,7 +82,7 @@ class _QuillToolbarToggleStyleButtonState
}
QuillController get controller {
return options.controller ?? widget.controller;
return widget.controller;
}
double get iconSize {
@ -236,20 +230,14 @@ class _QuillToolbarToggleStyleButtonState
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != controller) {
oldWidget.controller.removeListener(_didChangeEditingValue);
widget.controller.addListener(_didChangeEditingValue);
controller.addListener(_didChangeEditingValue);
_isToggled = _getIsToggled(_selectionStyle.attributes);
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_controller = controller;
}
@override
void dispose() {
_controller.removeListener(_didChangeEditingValue);
controller.removeListener(_didChangeEditingValue);
super.dispose();
}

@ -79,6 +79,9 @@ enum ToolbarButtons {
'You will find toolbarConfigurations and then buttons and pass a value'
' and change what you want, the tooltip for spesefic button for example')
direction,
@Deprecated('Please customize the button in the QuillProvider. '
'You will find toolbarConfigurations and then buttons and pass a value'
' and change what you want, the tooltip for spesefic button for example')
headerStyle,
@Deprecated('Please customize the button in the QuillProvider. '
'You will find toolbarConfigurations and then buttons and pass a value'
@ -108,6 +111,12 @@ enum ToolbarButtons {
'You will find toolbarConfigurations and then buttons and pass a value'
' and change what you want, the tooltip for spesefic button for example')
indentDecrease,
@Deprecated('Please customize the button in the QuillProvider. '
'You will find toolbarConfigurations and then buttons and pass a value'
' and change what you want, the tooltip for spesefic button for example')
link,
@Deprecated('Please customize the button in the QuillProvider. '
'You will find toolbarConfigurations and then buttons and pass a value'
' and change what you want, the tooltip for spesefic button for example')
search,
}

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:i18n_extension/i18n_widget.dart';
import '../../../flutter_quill.dart';
import '../../translations/toolbar.i18n.dart';
import '../../utils/extensions/build_context.dart';
import 'buttons/arrow_indicated_list.dart';
@ -18,7 +17,7 @@ export 'buttons/indent.dart';
export 'buttons/link_style.dart';
export 'buttons/link_style2.dart';
export 'buttons/quill_icon.dart';
export 'buttons/search.dart';
export 'buttons/search/search.dart';
export 'buttons/select_alignment.dart';
export 'buttons/select_header_style.dart';
export 'buttons/toggle_check_list.dart';
@ -38,7 +37,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
this.toolbarIconCrossAlignment = WrapCrossAlignment.center,
this.color,
this.customButtons = const [],
VoidCallback? afterButtonPressed,
this.sectionDividerColor,
this.sectionDividerSpace,
this.linkDialogAction,
@ -47,7 +45,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
}) : super(key: key);
factory QuillToolbar.basic({
Axis axis = Axis.horizontal,
double toolbarSectionSpacing = kToolbarSectionSpacing,
WrapAlignment toolbarIconAlignment = WrapAlignment.center,
WrapCrossAlignment toolbarIconCrossAlignment = WrapCrossAlignment.center,
@ -97,10 +94,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
///shown when embedding an image, for example
QuillDialogTheme? dialogTheme,
/// Callback to be called after any button on the toolbar is pressed.
/// Is called after whatever logic the button performs has run.
VoidCallback? afterButtonPressed,
///Map of tooltips for toolbar buttons
///
///The example is:
@ -113,6 +106,9 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
///```
///
/// To disable tooltips just pass empty map as well.
@Deprecated('This is deprecated and will no longer used. '
'to change the tooltips please pass them in the Quill toolbar button'
' configurations which exists in in the QuillProvider')
Map<ToolbarButtons, String>? tooltips,
/// The locale to use for the editor toolbar, defaults to system locale
@ -127,10 +123,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
/// The space occupied by toolbar divider
double? sectionDividerSpace,
/// Validate the legitimacy of hyperlinks
RegExp? linkRegExp,
LinkDialogAction? linkDialogAction,
Key? key,
}) {
final isButtonGroupShown = [
@ -157,40 +149,46 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
showLink || showSearchButton
];
//default button tooltips
final buttonTooltips = tooltips ??
<ToolbarButtons, String>{
ToolbarButtons.headerStyle: 'Header style'.i18n,
ToolbarButtons.link: 'Insert URL'.i18n,
ToolbarButtons.search: 'Search'.i18n,
};
return QuillToolbar(
key: key,
axis: axis,
color: color,
decoration: decoration,
toolbarSectionSpacing: toolbarSectionSpacing,
toolbarIconAlignment: toolbarIconAlignment,
toolbarIconCrossAlignment: toolbarIconCrossAlignment,
customButtons: customButtons,
afterButtonPressed: afterButtonPressed,
childrenBuilder: (context) {
final controller = context.requireQuillController;
final toolbarConfigurations = context.requireQuillToolbarConfigurations;
final toolbarIconSize =
final globalIconSize =
toolbarConfigurations.buttonOptions.base.globalIconSize;
final axis = toolbarConfigurations.axis;
if (tooltips != null) {
throw UnsupportedError(
'This is deprecated and will no longer used. to change '
'the tooltips please pass them in the Quill toolbar button'
'configurations which exists in in the QuillProvider',
);
}
return [
if (showUndo)
QuillToolbarHistoryButton(
options: toolbarConfigurations.buttonOptions.undoHistory,
controller:
toolbarConfigurations.buttonOptions.undoHistory.controller ??
context.requireQuillController,
),
if (showRedo)
QuillToolbarHistoryButton(
options: toolbarConfigurations.buttonOptions.redoHistory,
controller:
toolbarConfigurations.buttonOptions.redoHistory.controller ??
context.requireQuillController,
),
if (showFontFamily)
QuillToolbarFontFamilyButton(
@ -288,7 +286,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
),
if (embedButtons != null)
for (final builder in embedButtons)
builder(controller, toolbarIconSize, iconTheme, dialogTheme),
builder(controller, globalIconSize, iconTheme, dialogTheme),
if (showDividers &&
isButtonGroupShown[0] &&
(isButtonGroupShown[1] ||
@ -338,13 +336,15 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
space: sectionDividerSpace,
),
if (showHeaderStyle)
QuillToolbarSelectHeaderStyleButton(
tooltip: buttonTooltips[ToolbarButtons.headerStyle],
QuillToolbarSelectHeaderStyleButtons(
controller: controller,
axis: axis,
iconSize: toolbarIconSize,
iconTheme: iconTheme,
afterButtonPressed: afterButtonPressed,
options:
toolbarConfigurations.buttonOptions.selectHeaderStyleButtons,
// tooltip: buttonTooltips[ToolbarButtons.headerStyle],
// axis: axis,
// iconSize: toolbarIconSize,
// iconTheme: iconTheme,
// afterButtonPressed: afterButtonPressed,
),
if (showDividers &&
showHeaderStyle &&
@ -422,28 +422,13 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
color: sectionDividerColor, space: sectionDividerSpace),
if (showLink)
QuillToolbarLinkStyleButton(
tooltip: buttonTooltips[ToolbarButtons.link],
controller: controller,
iconSize: toolbarIconSize,
iconTheme: iconTheme,
dialogTheme: dialogTheme,
afterButtonPressed: afterButtonPressed,
linkRegExp: linkRegExp,
linkDialogAction: linkDialogAction,
dialogBarrierColor:
context.requireQuillSharedConfigurations.dialogBarrierColor,
options: toolbarConfigurations.buttonOptions.linkStyle,
),
if (showSearchButton)
QuillToolbarSearchButton(
icon: Icons.search,
iconSize: toolbarIconSize,
dialogBarrierColor:
context.requireQuillSharedConfigurations.dialogBarrierColor,
tooltip: buttonTooltips[ToolbarButtons.search],
controller: controller,
iconTheme: iconTheme,
dialogTheme: dialogTheme,
afterButtonPressed: afterButtonPressed,
options: toolbarConfigurations.buttonOptions.search,
),
if (customButtons.isNotEmpty)
if (showDividers)
@ -461,12 +446,16 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
] else ...[
CustomButton(
onPressed: customButton.onTap,
icon: customButton.icon,
icon: customButton.iconData ??
context.quillToolbarBaseButtonOptions?.iconData,
iconColor: customButton.iconColor,
iconSize: toolbarIconSize,
iconTheme: iconTheme,
afterButtonPressed: afterButtonPressed,
tooltip: customButton.tooltip,
iconSize: customButton.iconSize ?? globalIconSize,
iconTheme: iconTheme ??
context.quillToolbarBaseButtonOptions?.iconTheme,
afterButtonPressed: customButton.afterButtonPressed ??
context.quillToolbarBaseButtonOptions?.afterButtonPressed,
tooltip: customButton.tooltip ??
context.quillToolbarBaseButtonOptions?.tooltip,
),
],
];

@ -1,6 +1,6 @@
name: flutter_quill
description: A rich text editor built for the modern Android, iOS, web and desktop platforms. It is the WYSIWYG editor and a Quill component for Flutter.
version: 7.8.0
version: 7.9.0
homepage: https://1o24bbs.com/c/bulletjournal/108
repository: https://github.com/singerdmx/flutter-quill
topics:

Loading…
Cancel
Save