Code cleanup and fixes (#1486)

pull/1496/head v8.1.2
Ellet 1 year ago committed by GitHub
parent 3e9ab4bd4d
commit f7528d30a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      .github/ISSUE_TEMPLATE/issue-template.md
  2. 6
      CHANGELOG.md
  3. 6
      lib/src/models/config/others/animations.dart
  4. 63
      lib/src/models/config/toolbar/buttons/link_style2.dart
  5. 2
      lib/src/models/config/toolbar/buttons/select_alignment.dart
  6. 17
      lib/src/models/config/toolbar/buttons/select_header_style.dart
  7. 12
      lib/src/models/config/toolbar/configurations.dart
  8. 2
      lib/src/models/documents/nodes/block.dart
  9. 2
      lib/src/models/documents/nodes/container.dart
  10. 6
      lib/src/models/documents/nodes/leaf.dart
  11. 2
      lib/src/models/documents/nodes/line.dart
  12. 4
      lib/src/models/documents/nodes/node.dart
  13. 14
      lib/src/models/rules/insert.dart
  14. 8
      lib/src/test/widget_tester_extension.dart
  15. 15
      lib/src/utils/delta.dart
  16. 7
      lib/src/utils/experimental.dart
  17. 14
      lib/src/utils/string.dart
  18. 22
      lib/src/widgets/controller.dart
  19. 6
      lib/src/widgets/cursor.dart
  20. 129
      lib/src/widgets/default_styles.dart
  21. 53
      lib/src/widgets/delegate.dart
  22. 7
      lib/src/widgets/editor/editor.dart
  23. 9
      lib/src/widgets/embeds.dart
  24. 5
      lib/src/widgets/keyboard_listener.dart
  25. 8
      lib/src/widgets/proxy.dart
  26. 9
      lib/src/widgets/quill_single_child_scroll_view.dart
  27. 15
      lib/src/widgets/raw_editor/raw_editor.dart
  28. 4
      lib/src/widgets/style_widgets/bullet_point.dart
  29. 9
      lib/src/widgets/style_widgets/checkbox_point.dart
  30. 8
      lib/src/widgets/style_widgets/number_point.dart
  31. 84
      lib/src/widgets/text_block.dart
  32. 160
      lib/src/widgets/text_line.dart
  33. 6
      lib/src/widgets/text_selection.dart
  34. 4
      lib/src/widgets/toolbar/buttons/arrow_indicated_list.dart
  35. 18
      lib/src/widgets/toolbar/buttons/link_style.dart
  36. 186
      lib/src/widgets/toolbar/buttons/link_style2.dart
  37. 64
      lib/src/widgets/toolbar/buttons/select_alignment.dart
  38. 52
      lib/src/widgets/toolbar/buttons/select_header_style.dart
  39. 162
      lib/src/widgets/toolbar/toolbar.dart
  40. 6
      pubspec.yaml
  41. 2
      test/bug_fix_test.dart

@ -1,20 +1,16 @@
--- ---
name: Issue template name: Issue template
about: Common things to fill about: Common things to fill
title: "[Web] or [Mobile] or [Desktop]" title: "Issue in [Mobile, Desktop, Web]"
labels: '' labels: ''
assignees: '' assignees: ''
--- ---
My issue is about [Web]
My issue is about [Mobile]
My issue is about [Desktop]
I have tried running `example` directory successfully before creating an issue here.
Please note that we are using <b>latest</b> flutter version in stable channel on branch master. If you are using beta or master channel, or you are not using <b>latest</b> flutter version in stable channel, you may experience error. Please note that we are using <b>latest</b> flutter version in stable channel on branch master. If you are using beta or master channel, or you are not using <b>latest</b> flutter version in stable channel, you may experience error.
<!-- Did you try the running `example` directory?? How does it goes -->
<!-- Please explain how to encounter the issue in details if possible --> <!-- Please explain how to encounter the issue in details if possible -->
<!-- Don't forgot to mention the platform you are testing in --> <!-- Don't forgot to mention the platform you are testing in -->

@ -1,3 +1,9 @@
## [8.1.2]
- Fix bug that is related to the regexp of the insert link dialog
- Require dart 3 as minimum
- Code cleanup
- Add spacer widget between each button in the `QuillToolbar`
## [8.1.1] ## [8.1.1]
- Fix null error in line.dart [#1487](https://github.com/singerdmx/flutter-quill/issues/1487) - Fix null error in line.dart [#1487](https://github.com/singerdmx/flutter-quill/issues/1487)

@ -1,10 +1,8 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart' show immutable; import 'package:meta/meta.dart' show experimental, immutable;
import '../../../utils/experimental.dart';
@immutable @immutable
@Experimental('This class might removed') @experimental
class QuillAnimationConfigurations extends Equatable { class QuillAnimationConfigurations extends Equatable {
const QuillAnimationConfigurations({ const QuillAnimationConfigurations({
required this.checkBoxPointItem, required this.checkBoxPointItem,

@ -0,0 +1,63 @@
import 'package:flutter/widgets.dart';
import '../../../themes/quill_dialog_theme.dart';
import 'base.dart';
class QuillToolbarLinkStyleButton2ExtraOptions
extends QuillToolbarBaseButtonExtraOptions {
const QuillToolbarLinkStyleButton2ExtraOptions({
required super.controller,
required super.context,
required super.onPressed,
});
}
class QuillToolbarLinkStyleButton2Options extends QuillToolbarBaseButtonOptions<
QuillToolbarLinkStyleButton2Options,
QuillToolbarLinkStyleButton2ExtraOptions> {
const QuillToolbarLinkStyleButton2Options({
this.iconSize,
this.dialogTheme,
this.constraints,
this.addLinkLabel,
this.editLinkLabel,
this.linkColor,
this.validationMessage,
this.buttonSize,
this.dialogBarrierColor,
this.childrenSpacing = 16.0,
this.autovalidateMode = AutovalidateMode.disabled,
super.iconData,
super.afterButtonPressed,
super.tooltip,
super.iconTheme,
super.childBuilder,
super.controller,
});
final double? iconSize;
final QuillDialogTheme? dialogTheme;
/// The constrains for dialog.
final BoxConstraints? constraints;
/// The text of label in link add mode.
final String? addLinkLabel;
/// The text of label in link edit mode.
final String? editLinkLabel;
/// The color of URL.
final Color? linkColor;
/// The margin between child widgets in the dialog.
final double childrenSpacing;
final AutovalidateMode autovalidateMode;
final String? validationMessage;
/// The size of dialog buttons.
final Size? buttonSize;
final Color? dialogBarrierColor;
}

@ -19,6 +19,8 @@ class QuillToolbarSelectAlignmentButtonOptions
this.tooltips, this.tooltips,
this.iconSize, this.iconSize,
super.afterButtonPressed, super.afterButtonPressed,
/// This will called on every select alignment button
super.childBuilder, super.childBuilder,
super.controller, super.controller,
super.iconTheme, super.iconTheme,

@ -20,20 +20,21 @@ class QuillToolbarSelectHeaderStyleButtonsOptions
super.afterButtonPressed, super.afterButtonPressed,
super.childBuilder, super.childBuilder,
super.controller, super.controller,
super.iconData,
super.iconTheme, super.iconTheme,
super.tooltip, super.tooltip,
this.axis, this.axis,
this.attributes = const [ this.attributes,
Attribute.header,
Attribute.h1,
Attribute.h2,
Attribute.h3,
],
this.iconSize, this.iconSize,
}); });
final List<Attribute> attributes; /// Default value:
/// const [
/// Attribute.header,
/// Attribute.h1,
/// Attribute.h2,
/// Attribute.h3,
/// ]
final List<Attribute>? attributes;
/// By default we will the toolbar axis from [QuillToolbarConfigurations] /// By default we will the toolbar axis from [QuillToolbarConfigurations]
final Axis? axis; final Axis? axis;

@ -1,7 +1,7 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart' show immutable; import 'package:flutter/foundation.dart' show immutable;
import 'package:flutter/widgets.dart' import 'package:flutter/widgets.dart'
show Axis, Color, Decoration, WrapAlignment, WrapCrossAlignment; show Axis, Color, Decoration, Widget, WrapAlignment, WrapCrossAlignment;
import '../../../widgets/embeds.dart'; import '../../../widgets/embeds.dart';
import '../../structs/link_dialog_action.dart'; import '../../structs/link_dialog_action.dart';
@ -107,6 +107,7 @@ class QuillToolbarConfigurations extends Equatable {
this.color, this.color,
this.sectionDividerColor, this.sectionDividerColor,
this.sectionDividerSpace, this.sectionDividerSpace,
this.spacerWidget,
/// By default it will calculated based on the [globalIconSize] from /// By default it will calculated based on the [globalIconSize] from
/// [base] in [QuillToolbarButtonOptions] /// [base] in [QuillToolbarButtonOptions]
@ -130,6 +131,15 @@ class QuillToolbarConfigurations extends Equatable {
/// If you want change spesefic buttons or all of them /// If you want change spesefic buttons or all of them
/// then you came to the right place /// then you came to the right place
final QuillToolbarButtonOptions buttonOptions; final QuillToolbarButtonOptions buttonOptions;
/// A widget that will placed between each button in the toolbar
/// can be used as a spacer
/// it will not used before the first button
/// it will not used after the last button
/// it will also not used in the toolbar dividers
/// Default value will be [SizedBox.shrink()]
/// some widgets like the header styles will be considered as one widget
final Widget? spacerWidget;
final bool multiRowsDisplay; final bool multiRowsDisplay;
/// By default it will be /// By default it will be

@ -13,7 +13,7 @@ import 'node.dart';
/// - Text Alignment /// - Text Alignment
/// - Text Direction /// - Text Direction
/// - Code Block /// - Code Block
class Block extends Container<Line?> { base class Block extends Container<Line?> {
/// Creates new unmounted [Block]. /// Creates new unmounted [Block].
@override @override
Node newInstance() => Block(); Node newInstance() => Block();

@ -14,7 +14,7 @@ import 'node.dart';
/// ///
/// Most of the operation handling logic is implemented by [Line] /// Most of the operation handling logic is implemented by [Line]
/// and [QuillText]. /// and [QuillText].
abstract class Container<T extends Node?> extends Node { abstract base class Container<T extends Node?> extends Node {
final LinkedList<Node> _children = LinkedList<Node>(); final LinkedList<Node> _children = LinkedList<Node>();
/// List of children. /// List of children.

@ -8,7 +8,7 @@ import 'line.dart';
import 'node.dart'; import 'node.dart';
/// A leaf in Quill document tree. /// A leaf in Quill document tree.
abstract class Leaf extends Node { abstract base class Leaf extends Node {
/// Creates a new [Leaf] with specified [data]. /// Creates a new [Leaf] with specified [data].
factory Leaf(Object data) { factory Leaf(Object data) {
if (data is Embeddable) { if (data is Embeddable) {
@ -216,7 +216,7 @@ abstract class Leaf extends Node {
/// The reason we are renamed quill Text to [QuillText] so it doesn't /// The reason we are renamed quill Text to [QuillText] so it doesn't
/// conflict with the one from the widgets, material or cupertino library /// conflict with the one from the widgets, material or cupertino library
/// ///
class QuillText extends Leaf { base class QuillText extends Leaf {
QuillText([String text = '']) QuillText([String text = ''])
: assert(!text.contains('\n')), : assert(!text.contains('\n')),
super.val(text); super.val(text);
@ -249,7 +249,7 @@ class QuillText extends Leaf {
/// necessarily mean the embed will look according to that style. For instance, /// necessarily mean the embed will look according to that style. For instance,
/// applying "bold" style to an image gives no effect, while adding a "link" to /// applying "bold" style to an image gives no effect, while adding a "link" to
/// an image actually makes the image react to user's action. /// an image actually makes the image react to user's action.
class Embed extends Leaf { base class Embed extends Leaf {
Embed(Embeddable data) : super.val(data); Embed(Embeddable data) : super.val(data);
// Refer to https://www.fileformat.info/info/unicode/char/fffc/index.htm // Refer to https://www.fileformat.info/info/unicode/char/fffc/index.htm

@ -19,7 +19,7 @@ import 'node.dart';
/// ///
/// When a line contains an embed, it fully occupies the line, no other embeds /// When a line contains an embed, it fully occupies the line, no other embeds
/// or text nodes are allowed. /// or text nodes are allowed.
class Line extends Container<Leaf?> { base class Line extends Container<Leaf?> {
@override @override
Leaf get defaultChild => QuillText(); Leaf get defaultChild => QuillText();

@ -17,7 +17,7 @@ import 'line.dart';
/// ///
/// The current parent node is exposed by the [parent] property. A node is /// The current parent node is exposed by the [parent] property. A node is
/// considered [mounted] when the [parent] property is not `null`. /// considered [mounted] when the [parent] property is not `null`.
abstract class Node extends LinkedListEntry<Node> { abstract base class Node extends LinkedListEntry<Node> {
/// Current parent of this node. May be null if this node is not mounted. /// Current parent of this node. May be null if this node is not mounted.
Container? parent; Container? parent;
@ -127,7 +127,7 @@ abstract class Node extends LinkedListEntry<Node> {
} }
/// Root node of document tree. /// Root node of document tree.
class Root extends Container<Container<Node?>> { base class Root extends Container<Container<Node?>> {
@override @override
Node newInstance() => Root(); Node newInstance() => Root();

@ -358,14 +358,14 @@ class AutoFormatMultipleLinksRule extends InsertRule {
// TODO: You might want to rename those but everywhere even in // TODO: You might want to rename those but everywhere even in
// flutter_quill_extensions // flutter_quill_extensions
static const _oneLinePattern = static const _oneLineLinkPattern =
r'^https?:\/\/[\w\-]+(\.[\w\-]+)*(:\d+)?(\/.*)?$'; r'^https?:\/\/[\w\-]+(\.[\w\-]+)*(:\d+)?(\/.*)?$';
static const _detectLinkPattern = static const _detectLinkPattern =
r'https?:\/\/[\w\-]+(\.[\w\-]+)*(:\d+)?(\/[^\s]*)?'; r'https?:\/\/[\w\-]+(\.[\w\-]+)*(:\d+)?(\/[^\s]*)?';
/// It requires a valid link in one link /// It requires a valid link in one link
static final oneLineRegExp = RegExp( static final oneLineLinkRegExp = RegExp(
_oneLinePattern, _oneLineLinkPattern,
caseSensitive: false, caseSensitive: false,
); );
@ -375,10 +375,10 @@ class AutoFormatMultipleLinksRule extends InsertRule {
_detectLinkPattern, _detectLinkPattern,
caseSensitive: false, caseSensitive: false,
); );
@Deprecated( // @Deprecated(
'Please use [linkRegExp1] or [linkRegExp2]', // 'Please use [linkRegExp1] or [linkRegExp2]',
) // )
static final linkRegExp = oneLineRegExp; static final linkRegExp = oneLineLinkRegExp;
@override @override
Delta? applyRule( Delta? applyRule(

@ -11,10 +11,10 @@ extension QuillEnterText on WidgetTester {
return TestAsyncUtils.guard(() async { return TestAsyncUtils.guard(() async {
final editor = state<QuillEditorState>( final editor = state<QuillEditorState>(
find.descendant( find.descendant(
of: finder, of: finder,
matching: matching: find.byType(QuillEditor, skipOffstage: finder.skipOffstage),
find.byType(QuillEditor, skipOffstage: finder.skipOffstage), matchRoot: true,
matchRoot: true), ),
); );
editor.widget.focusNode.requestFocus(); editor.widget.focusNode.requestFocus();
await pump(); await pump();

@ -1,13 +1,20 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui'; import 'dart:ui';
import 'package:meta/meta.dart' show immutable;
import '../models/documents/attribute.dart'; import '../models/documents/attribute.dart';
import '../models/documents/nodes/node.dart'; import '../models/documents/nodes/node.dart';
import '../models/quill_delta.dart'; import '../models/quill_delta.dart';
// Diff between two texts - old text and new text // Diff between two texts - old text and new text
@immutable
class Diff { class Diff {
Diff(this.start, this.deleted, this.inserted); const Diff({
required this.start,
required this.deleted,
required this.inserted,
});
// Start index in old text at which changes begin. // Start index in old text at which changes begin.
final int start; final int start;
@ -37,7 +44,11 @@ Diff getDiff(String oldText, String newText, int cursorPosition) {
start++) {} start++) {}
final deleted = (start >= end) ? '' : oldText.substring(start, end); final deleted = (start >= end) ? '' : oldText.substring(start, end);
final inserted = newText.substring(start, end + delta); final inserted = newText.substring(start, end + delta);
return Diff(start, deleted, inserted); return Diff(
start: start,
deleted: deleted,
inserted: inserted,
);
} }
int getPositionDelta(Delta user, Delta actual) { int getPositionDelta(Delta user, Delta actual) {

@ -1,7 +0,0 @@
import 'package:flutter/foundation.dart' show immutable;
@immutable
class Experimental {
const Experimental([this.reason = 'Experimental feature']);
final String reason;
}

@ -19,20 +19,6 @@ Map<String, String> parseKeyValuePairs(String s, Set<String> targetKeys) {
return result; return result;
} }
@Deprecated('Use replaceStyleStringWithSize instead')
String replaceStyleString(
String s,
double width,
double height,
) {
return replaceStyleStringWithSize(
s,
width: width,
height: height,
isMobile: true,
);
}
String replaceStyleStringWithSize( String replaceStyleStringWithSize(
String s, { String s, {
required double width, required double width,

@ -333,18 +333,23 @@ class QuillController extends ChangeNotifier {
void moveCursorToStart() { void moveCursorToStart() {
updateSelection( updateSelection(
const TextSelection.collapsed(offset: 0), ChangeSource.LOCAL); const TextSelection.collapsed(offset: 0),
ChangeSource.LOCAL,
);
} }
void moveCursorToPosition(int position) { void moveCursorToPosition(int position) {
updateSelection( updateSelection(
TextSelection.collapsed(offset: position), ChangeSource.LOCAL); TextSelection.collapsed(offset: position),
ChangeSource.LOCAL,
);
} }
void moveCursorToEnd() { void moveCursorToEnd() {
updateSelection( updateSelection(
TextSelection.collapsed(offset: plainTextEditingValue.text.length), TextSelection.collapsed(offset: plainTextEditingValue.text.length),
ChangeSource.LOCAL); ChangeSource.LOCAL,
);
} }
void updateSelection(TextSelection textSelection, ChangeSource source) { void updateSelection(TextSelection textSelection, ChangeSource source) {
@ -358,9 +363,12 @@ class QuillController extends ChangeNotifier {
} }
textSelection = selection.copyWith( textSelection = selection.copyWith(
baseOffset: delta.transformPosition(selection.baseOffset, force: false), baseOffset: delta.transformPosition(selection.baseOffset, force: false),
extentOffset: extentOffset: delta.transformPosition(
delta.transformPosition(selection.extentOffset, force: false)); selection.extentOffset,
force: false,
),
);
if (selection != textSelection) { if (selection != textSelection) {
_updateSelection(textSelection, source); _updateSelection(textSelection, source);
} }

@ -257,7 +257,11 @@ class CursorPainter {
/// [offset] is global top left (x, y) of text line /// [offset] is global top left (x, y) of text line
/// [position] is relative (x) in text line /// [position] is relative (x) in text line
void paint( void paint(
Canvas canvas, Offset offset, TextPosition position, bool lineHasEmbed) { Canvas canvas,
Offset offset,
TextPosition position,
bool lineHasEmbed,
) {
// relative (x, y) to global offset // relative (x, y) to global offset
var relativeCaretOffset = editable!.getOffsetForCaret(position, prototype); var relativeCaretOffset = editable!.getOffsetForCaret(position, prototype);
if (lineHasEmbed && relativeCaretOffset == Offset.zero) { if (lineHasEmbed && relativeCaretOffset == Offset.zero) {

@ -240,22 +240,30 @@ class DefaultStyles {
const VerticalSpacing(0, 0), const VerticalSpacing(0, 0),
null), null),
h3: DefaultTextBlockStyle( h3: DefaultTextBlockStyle(
defaultTextStyle.style.copyWith( defaultTextStyle.style.copyWith(
fontSize: 20, fontSize: 20,
color: defaultTextStyle.style.color!.withOpacity(0.70), color: defaultTextStyle.style.color!.withOpacity(0.70),
height: 1.25, height: 1.25,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
decoration: TextDecoration.none, decoration: TextDecoration.none,
), ),
const VerticalSpacing(8, 0), const VerticalSpacing(8, 0),
const VerticalSpacing(0, 0), const VerticalSpacing(0, 0),
null), null,
),
paragraph: DefaultTextBlockStyle(baseStyle, const VerticalSpacing(0, 0), paragraph: DefaultTextBlockStyle(baseStyle, const VerticalSpacing(0, 0),
const VerticalSpacing(0, 0), null), const VerticalSpacing(0, 0), null),
bold: const TextStyle(fontWeight: FontWeight.bold), bold: const TextStyle(fontWeight: FontWeight.bold),
subscript: const TextStyle(fontFeatures: [FontFeature.subscripts()]), subscript: const TextStyle(
superscript: fontFeatures: [
const TextStyle(fontFeatures: [FontFeature.superscripts()]), FontFeature.subscripts(),
],
),
superscript: const TextStyle(
fontFeatures: [
FontFeature.superscripts(),
],
),
italic: const TextStyle(fontStyle: FontStyle.italic), italic: const TextStyle(fontStyle: FontStyle.italic),
small: const TextStyle(fontSize: 12), small: const TextStyle(fontSize: 12),
underline: const TextStyle(decoration: TextDecoration.underline), underline: const TextStyle(decoration: TextDecoration.underline),
@ -288,16 +296,22 @@ class DefaultStyles {
const VerticalSpacing(0, 0), const VerticalSpacing(0, 0),
null), null),
lists: DefaultListBlockStyle( lists: DefaultListBlockStyle(
baseStyle, baseSpacing, const VerticalSpacing(0, 6), null, null), baseStyle,
baseSpacing,
const VerticalSpacing(0, 6),
null,
null,
),
quote: DefaultTextBlockStyle( quote: DefaultTextBlockStyle(
TextStyle(color: baseStyle.color!.withOpacity(0.6)), TextStyle(color: baseStyle.color!.withOpacity(0.6)),
baseSpacing, baseSpacing,
const VerticalSpacing(6, 2), const VerticalSpacing(6, 2),
BoxDecoration( BoxDecoration(
border: Border( border: Border(
left: BorderSide(width: 4, color: Colors.grey.shade300), left: BorderSide(width: 4, color: Colors.grey.shade300),
), ),
)), ),
),
code: DefaultTextBlockStyle( code: DefaultTextBlockStyle(
TextStyle( TextStyle(
color: Colors.blue.shade900.withOpacity(0.9), color: Colors.blue.shade900.withOpacity(0.9),
@ -312,11 +326,23 @@ class DefaultStyles {
borderRadius: BorderRadius.circular(2), borderRadius: BorderRadius.circular(2),
)), )),
indent: DefaultTextBlockStyle( indent: DefaultTextBlockStyle(
baseStyle, baseSpacing, const VerticalSpacing(0, 6), null), baseStyle,
align: DefaultTextBlockStyle(baseStyle, const VerticalSpacing(0, 0), baseSpacing,
const VerticalSpacing(0, 0), null), const VerticalSpacing(0, 6),
leading: DefaultTextBlockStyle(baseStyle, const VerticalSpacing(0, 0), null,
const VerticalSpacing(0, 0), null), ),
align: DefaultTextBlockStyle(
baseStyle,
const VerticalSpacing(0, 0),
const VerticalSpacing(0, 0),
null,
),
leading: DefaultTextBlockStyle(
baseStyle,
const VerticalSpacing(0, 0),
const VerticalSpacing(0, 0),
null,
),
sizeSmall: const TextStyle(fontSize: 10), sizeSmall: const TextStyle(fontSize: 10),
sizeLarge: const TextStyle(fontSize: 18), sizeLarge: const TextStyle(fontSize: 18),
sizeHuge: const TextStyle(fontSize: 22)); sizeHuge: const TextStyle(fontSize: 22));
@ -324,29 +350,30 @@ class DefaultStyles {
DefaultStyles merge(DefaultStyles other) { DefaultStyles merge(DefaultStyles other) {
return DefaultStyles( return DefaultStyles(
h1: other.h1 ?? h1, h1: other.h1 ?? h1,
h2: other.h2 ?? h2, h2: other.h2 ?? h2,
h3: other.h3 ?? h3, h3: other.h3 ?? h3,
paragraph: other.paragraph ?? paragraph, paragraph: other.paragraph ?? paragraph,
bold: other.bold ?? bold, bold: other.bold ?? bold,
subscript: other.subscript ?? subscript, subscript: other.subscript ?? subscript,
superscript: other.superscript ?? superscript, superscript: other.superscript ?? superscript,
italic: other.italic ?? italic, italic: other.italic ?? italic,
small: other.small ?? small, small: other.small ?? small,
underline: other.underline ?? underline, underline: other.underline ?? underline,
strikeThrough: other.strikeThrough ?? strikeThrough, strikeThrough: other.strikeThrough ?? strikeThrough,
inlineCode: other.inlineCode ?? inlineCode, inlineCode: other.inlineCode ?? inlineCode,
link: other.link ?? link, link: other.link ?? link,
color: other.color ?? color, color: other.color ?? color,
placeHolder: other.placeHolder ?? placeHolder, placeHolder: other.placeHolder ?? placeHolder,
lists: other.lists ?? lists, lists: other.lists ?? lists,
quote: other.quote ?? quote, quote: other.quote ?? quote,
code: other.code ?? code, code: other.code ?? code,
indent: other.indent ?? indent, indent: other.indent ?? indent,
align: other.align ?? align, align: other.align ?? align,
leading: other.leading ?? leading, leading: other.leading ?? leading,
sizeSmall: other.sizeSmall ?? sizeSmall, sizeSmall: other.sizeSmall ?? sizeSmall,
sizeLarge: other.sizeLarge ?? sizeLarge, sizeLarge: other.sizeLarge ?? sizeLarge,
sizeHuge: other.sizeHuge ?? sizeHuge); sizeHuge: other.sizeHuge ?? sizeHuge,
);
} }
} }

@ -314,8 +314,10 @@ class EditorTextSelectionGestureDetectorBuilder {
void onDragSelectionUpdate( void onDragSelectionUpdate(
//DragStartDetails startDetails, //DragStartDetails startDetails,
DragUpdateDetails updateDetails) { DragUpdateDetails updateDetails) {
renderEditor!.extendSelection(updateDetails.globalPosition, renderEditor!.extendSelection(
cause: SelectionChangedCause.drag); updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
} }
/// Handler for [EditorTextSelectionGestureDetector.onDragSelectionEnd]. /// Handler for [EditorTextSelectionGestureDetector.onDragSelectionEnd].
@ -341,29 +343,30 @@ class EditorTextSelectionGestureDetectorBuilder {
/// the handlers provided by this builder. /// the handlers provided by this builder.
/// ///
/// The [child] or its subtree should contain [EditableText]. /// The [child] or its subtree should contain [EditableText].
Widget build( Widget build({
{required HitTestBehavior behavior, required HitTestBehavior behavior,
required Widget child, required Widget child,
Key? key, Key? key,
bool detectWordBoundary = true}) { bool detectWordBoundary = true,
}) {
return EditorTextSelectionGestureDetector( return EditorTextSelectionGestureDetector(
key: key, key: key,
onTapDown: onTapDown, onTapDown: onTapDown,
onForcePressStart: onForcePressStart: delegate.forcePressEnabled ? onForcePressStart : null,
delegate.forcePressEnabled ? onForcePressStart : null, onForcePressEnd: delegate.forcePressEnabled ? onForcePressEnd : null,
onForcePressEnd: delegate.forcePressEnabled ? onForcePressEnd : null, onSingleTapUp: onSingleTapUp,
onSingleTapUp: onSingleTapUp, onSingleTapCancel: onSingleTapCancel,
onSingleTapCancel: onSingleTapCancel, onSingleLongTapStart: onSingleLongTapStart,
onSingleLongTapStart: onSingleLongTapStart, onSingleLongTapMoveUpdate: onSingleLongTapMoveUpdate,
onSingleLongTapMoveUpdate: onSingleLongTapMoveUpdate, onSingleLongTapEnd: onSingleLongTapEnd,
onSingleLongTapEnd: onSingleLongTapEnd, onDoubleTapDown: onDoubleTapDown,
onDoubleTapDown: onDoubleTapDown, onSecondarySingleTapUp: onSecondarySingleTapUp,
onSecondarySingleTapUp: onSecondarySingleTapUp, onDragSelectionStart: onDragSelectionStart,
onDragSelectionStart: onDragSelectionStart, onDragSelectionUpdate: onDragSelectionUpdate,
onDragSelectionUpdate: onDragSelectionUpdate, onDragSelectionEnd: onDragSelectionEnd,
onDragSelectionEnd: onDragSelectionEnd, behavior: behavior,
behavior: behavior, detectWordBoundary: detectWordBoundary,
detectWordBoundary: detectWordBoundary, child: child,
child: child); );
} }
} }

@ -1,10 +1,5 @@
import 'dart:math' as math; import 'dart:math' as math;
// ignore: unnecessary_import
// import 'dart:typed_data';
// The project maanged to compiled successfully without the import
// do we still need this import (dart:typed_data)??
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
@ -1495,7 +1490,7 @@ class RenderEditor extends RenderEditableContainerBox
} }
} }
class QuillVerticalCaretMovementRun extends Iterator<TextPosition> { class QuillVerticalCaretMovementRun implements Iterator<TextPosition> {
QuillVerticalCaretMovementRun._( QuillVerticalCaretMovementRun._(
this._editor, this._editor,
this._currentTextPosition, this._currentTextPosition,

@ -29,7 +29,8 @@ abstract class EmbedBuilder {
} }
typedef EmbedButtonBuilder = Widget Function( typedef EmbedButtonBuilder = Widget Function(
QuillController controller, QuillController controller,
double toolbarIconSize, double toolbarIconSize,
QuillIconTheme? iconTheme, QuillIconTheme? iconTheme,
QuillDialogTheme? dialogTheme); QuillDialogTheme? dialogTheme,
);

@ -44,8 +44,9 @@ class QuillKeyboardListenerState extends State<QuillKeyboardListener> {
final QuillPressedKeys _pressedKeys = QuillPressedKeys(); final QuillPressedKeys _pressedKeys = QuillPressedKeys();
bool _keyEvent(KeyEvent event) { bool _keyEvent(KeyEvent event) {
_pressedKeys _pressedKeys._updatePressedKeys(
._updatePressedKeys(HardwareKeyboard.instance.logicalKeysPressed); HardwareKeyboard.instance.logicalKeysPressed,
);
return false; return false;
} }

@ -6,8 +6,12 @@ import 'package:flutter/widgets.dart';
import 'box.dart'; import 'box.dart';
class BaselineProxy extends SingleChildRenderObjectWidget { class BaselineProxy extends SingleChildRenderObjectWidget {
const BaselineProxy({Key? key, Widget? child, this.textStyle, this.padding}) const BaselineProxy({
: super(key: key, child: child); Key? key,
Widget? child,
this.textStyle,
this.padding,
}) : super(key: key, child: child);
final TextStyle? textStyle; final TextStyle? textStyle;
final EdgeInsets? padding; final EdgeInsets? padding;

@ -14,10 +14,10 @@ class QuillSingleChildScrollView extends StatelessWidget {
const QuillSingleChildScrollView({ const QuillSingleChildScrollView({
required this.controller, required this.controller,
required this.viewportBuilder, required this.viewportBuilder,
Key? key, super.key,
this.physics, this.physics,
this.restorationId, this.restorationId,
}) : super(key: key); });
/// An object that can be used to control the position to which this scroll /// An object that can be used to control the position to which this scroll
/// view is scrolled. /// view is scrolled.
@ -48,7 +48,10 @@ class QuillSingleChildScrollView extends StatelessWidget {
AxisDirection _getDirection(BuildContext context) { AxisDirection _getDirection(BuildContext context) {
return getAxisDirectionFromAxisReverseAndDirectionality( return getAxisDirectionFromAxisReverseAndDirectionality(
context, Axis.vertical, false); context,
Axis.vertical,
false,
);
} }
@override @override

@ -437,9 +437,6 @@ class RawEditorState extends EditorState
// in the web browser, but we do unfocus for all other kinds of events. // in the web browser, but we do unfocus for all other kinds of events.
switch (event.kind) { switch (event.kind) {
case ui.PointerDeviceKind.touch: case ui.PointerDeviceKind.touch:
// if (isWeb()) {
// widget.focusNode.unfocus();
// }
break; break;
case ui.PointerDeviceKind.mouse: case ui.PointerDeviceKind.mouse:
case ui.PointerDeviceKind.stylus: case ui.PointerDeviceKind.stylus:
@ -449,7 +446,7 @@ class RawEditorState extends EditorState
break; break;
case ui.PointerDeviceKind.trackpad: case ui.PointerDeviceKind.trackpad:
throw UnimplementedError( throw UnimplementedError(
'Unexpected pointer down event for trackpad', 'Unexpected pointer down event for trackpad.',
); );
} }
break; break;
@ -461,7 +458,7 @@ class RawEditorState extends EditorState
default: default:
throw UnsupportedError( throw UnsupportedError(
'The platform ${defaultTargetPlatform.name} is not supported in the' 'The platform ${defaultTargetPlatform.name} is not supported in the'
' _defaultOnTapOutside', ' _defaultOnTapOutside()',
); );
} }
} }
@ -474,8 +471,11 @@ class RawEditorState extends EditorState
var _doc = controller.document; var _doc = controller.document;
if (_doc.isEmpty() && widget.placeholder != null) { if (_doc.isEmpty() && widget.placeholder != null) {
final raw = widget.placeholder?.replaceAll(r'"', '\\"'); final raw = widget.placeholder?.replaceAll(r'"', '\\"');
_doc = Document.fromJson(jsonDecode( _doc = Document.fromJson(
'[{"attributes":{"placeholder":true},"insert":"$raw\\n"}]')); jsonDecode(
'[{"attributes":{"placeholder":true},"insert":"$raw\\n"}]',
),
);
} }
Widget child = CompositedTransformTarget( Widget child = CompositedTransformTarget(
@ -1435,7 +1435,6 @@ class RawEditorState extends EditorState
@override @override
void requestKeyboard() { void requestKeyboard() {
if (controller.skipRequestKeyboard) { if (controller.skipRequestKeyboard) {
// TODO: There is a bug, requestKeyboard is being called 2-4 times!
// and that just by one simple change // and that just by one simple change
controller.skipRequestKeyboard = false; controller.skipRequestKeyboard = false;
return; return;

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class QuillBulletPoint extends StatelessWidget { class QuillEditorBulletPoint extends StatelessWidget {
const QuillBulletPoint({ const QuillEditorBulletPoint({
required this.style, required this.style,
required this.width, required this.width,
this.padding = 0, this.padding = 0,

@ -3,8 +3,8 @@ import 'package:flutter_animate/flutter_animate.dart';
import '../../utils/extensions/build_context.dart'; import '../../utils/extensions/build_context.dart';
class CheckboxPoint extends StatefulWidget { class QuillEditorCheckboxPoint extends StatefulWidget {
const CheckboxPoint({ const QuillEditorCheckboxPoint({
required this.size, required this.size,
required this.value, required this.value,
required this.enabled, required this.enabled,
@ -20,10 +20,11 @@ class CheckboxPoint extends StatefulWidget {
final QuillCheckboxBuilder? uiBuilder; final QuillCheckboxBuilder? uiBuilder;
@override @override
_CheckboxPointState createState() => _CheckboxPointState(); _QuillEditorCheckboxPointState createState() =>
_QuillEditorCheckboxPointState();
} }
class _CheckboxPointState extends State<CheckboxPoint> { class _QuillEditorCheckboxPointState extends State<QuillEditorCheckboxPoint> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final uiBuilder = widget.uiBuilder; final uiBuilder = widget.uiBuilder;

@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import '../../models/documents/attribute.dart'; import '../../models/documents/attribute.dart';
import '../text_block.dart'; import '../text_block.dart';
class QuillNumberPoint extends StatelessWidget { class QuillEditorNumberPoint extends StatelessWidget {
const QuillNumberPoint({ const QuillEditorNumberPoint({
required this.index, required this.index,
required this.indentLevelCounts, required this.indentLevelCounts,
required this.count, required this.count,
@ -13,8 +13,8 @@ class QuillNumberPoint extends StatelessWidget {
required this.attrs, required this.attrs,
this.withDot = true, this.withDot = true,
this.padding = 0.0, this.padding = 0.0,
Key? key, super.key,
}) : super(key: key); });
final int index; final int index;
final Map<int?, int> indentLevelCounts; final Map<int?, int> indentLevelCounts;

@ -146,7 +146,13 @@ class EditableTextBlock extends StatelessWidget {
index++; index++;
final editableTextLine = EditableTextLine( final editableTextLine = EditableTextLine(
line, line,
_buildLeading(context, line, index, indentLevelCounts, count), _buildLeading(
context: context,
line: line,
index: index,
indentLevelCounts: indentLevelCounts,
count: count,
),
TextLine( TextLine(
line: line, line: line,
textDirection: textDirection, textDirection: textDirection,
@ -194,14 +200,19 @@ class EditableTextBlock extends StatelessWidget {
} }
} }
Widget? _buildLeading(BuildContext context, Line line, int index, Widget? _buildLeading({
Map<int, int> indentLevelCounts, int count) { required BuildContext context,
required Line line,
required int index,
required Map<int, int> indentLevelCounts,
required int count,
}) {
final defaultStyles = QuillStyles.getStyles(context, false)!; final defaultStyles = QuillStyles.getStyles(context, false)!;
final fontSize = defaultStyles.paragraph?.style.fontSize ?? 16; final fontSize = defaultStyles.paragraph?.style.fontSize ?? 16;
final attrs = line.style.attributes; final attrs = line.style.attributes;
if (attrs[Attribute.list.key] == Attribute.ol) { if (attrs[Attribute.list.key] == Attribute.ol) {
return QuillNumberPoint( return QuillEditorNumberPoint(
index: index, index: index,
indentLevelCounts: indentLevelCounts, indentLevelCounts: indentLevelCounts,
count: count, count: count,
@ -213,7 +224,7 @@ class EditableTextBlock extends StatelessWidget {
} }
if (attrs[Attribute.list.key] == Attribute.ul) { if (attrs[Attribute.list.key] == Attribute.ul) {
return QuillBulletPoint( return QuillEditorBulletPoint(
style: style:
defaultStyles.leading!.style.copyWith(fontWeight: FontWeight.bold), defaultStyles.leading!.style.copyWith(fontWeight: FontWeight.bold),
width: fontSize * 2, width: fontSize * 2,
@ -223,7 +234,7 @@ class EditableTextBlock extends StatelessWidget {
if (attrs[Attribute.list.key] == Attribute.checked || if (attrs[Attribute.list.key] == Attribute.checked ||
attrs[Attribute.list.key] == Attribute.unchecked) { attrs[Attribute.list.key] == Attribute.unchecked) {
return CheckboxPoint( return QuillEditorCheckboxPoint(
size: fontSize, size: fontSize,
value: attrs[Attribute.list.key] == Attribute.checked, value: attrs[Attribute.list.key] == Attribute.checked,
enabled: !readOnly, enabled: !readOnly,
@ -233,7 +244,7 @@ class EditableTextBlock extends StatelessWidget {
} }
if (attrs.containsKey(Attribute.codeBlock.key) && if (attrs.containsKey(Attribute.codeBlock.key) &&
context.requireQuillEditorElementOptions.codeBlock.enableLineNumbers) { context.requireQuillEditorElementOptions.codeBlock.enableLineNumbers) {
return QuillNumberPoint( return QuillEditorNumberPoint(
index: index, index: index,
indentLevelCounts: indentLevelCounts, indentLevelCounts: indentLevelCounts,
count: count, count: count,
@ -278,7 +289,11 @@ class EditableTextBlock extends StatelessWidget {
} }
VerticalSpacing _getSpacingForLine( VerticalSpacing _getSpacingForLine(
Line node, int index, int count, DefaultStyles? defaultStyles) { Line node,
int index,
int count,
DefaultStyles? defaultStyles,
) {
var top = 0.0, bottom = 0.0; var top = 0.0, bottom = 0.0;
final attrs = block.style.attributes; final attrs = block.style.attributes;
@ -301,7 +316,7 @@ class EditableTextBlock extends StatelessWidget {
throw 'Invalid level $level'; throw 'Invalid level $level';
} }
} else { } else {
late VerticalSpacing lineSpacing; final VerticalSpacing lineSpacing;
if (attrs.containsKey(Attribute.blockQuote.key)) { if (attrs.containsKey(Attribute.blockQuote.key)) {
lineSpacing = defaultStyles!.quote!.lineSpacing; lineSpacing = defaultStyles!.quote!.lineSpacing;
} else if (attrs.containsKey(Attribute.indent.key)) { } else if (attrs.containsKey(Attribute.indent.key)) {
@ -500,12 +515,18 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
TextSelectionPoint getBaseEndpointForSelection(TextSelection selection) { TextSelectionPoint getBaseEndpointForSelection(TextSelection selection) {
if (selection.isCollapsed) { if (selection.isCollapsed) {
return TextSelectionPoint( return TextSelectionPoint(
Offset(0, preferredLineHeight(selection.extent)) + Offset(0, preferredLineHeight(selection.extent)) +
getOffsetForCaret(selection.extent), getOffsetForCaret(selection.extent),
null); null,
);
} }
final baseNode = container.queryChild(selection.start, false).node; final baseNode = container
.queryChild(
selection.start,
false,
)
.node;
var baseChild = firstChild; var baseChild = firstChild;
while (baseChild != null) { while (baseChild != null) {
if (baseChild.container == baseNode) { if (baseChild.container == baseNode) {
@ -516,19 +537,26 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
assert(baseChild != null); assert(baseChild != null);
final basePoint = baseChild!.getBaseEndpointForSelection( final basePoint = baseChild!.getBaseEndpointForSelection(
localSelection(baseChild.container, selection, true)); localSelection(
baseChild.container,
selection,
true,
),
);
return TextSelectionPoint( return TextSelectionPoint(
basePoint.point + (baseChild.parentData as BoxParentData).offset, basePoint.point + (baseChild.parentData as BoxParentData).offset,
basePoint.direction); basePoint.direction,
);
} }
@override @override
TextSelectionPoint getExtentEndpointForSelection(TextSelection selection) { TextSelectionPoint getExtentEndpointForSelection(TextSelection selection) {
if (selection.isCollapsed) { if (selection.isCollapsed) {
return TextSelectionPoint( return TextSelectionPoint(
Offset(0, preferredLineHeight(selection.extent)) + Offset(0, preferredLineHeight(selection.extent)) +
getOffsetForCaret(selection.extent), getOffsetForCaret(selection.extent),
null); null,
);
} }
final extentNode = container.queryChild(selection.end, false).node; final extentNode = container.queryChild(selection.end, false).node;
@ -543,10 +571,16 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
assert(extentChild != null); assert(extentChild != null);
final extentPoint = extentChild!.getExtentEndpointForSelection( final extentPoint = extentChild!.getExtentEndpointForSelection(
localSelection(extentChild.container, selection, true)); localSelection(
extentChild.container,
selection,
true,
),
);
return TextSelectionPoint( return TextSelectionPoint(
extentPoint.point + (extentChild.parentData as BoxParentData).offset, extentPoint.point + (extentChild.parentData as BoxParentData).offset,
extentPoint.direction); extentPoint.direction,
);
} }
@override @override
@ -576,8 +610,10 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
offset.translate(decorationPadding.left, decorationPadding.top); offset.translate(decorationPadding.left, decorationPadding.top);
_painter!.paint(context.canvas, decorationOffset, filledConfiguration); _painter!.paint(context.canvas, decorationOffset, filledConfiguration);
if (debugSaveCount != context.canvas.getSaveCount()) { if (debugSaveCount != context.canvas.getSaveCount()) {
throw '${_decoration.runtimeType} painter had mismatching save and ' throw StateError(
'restore calls.'; '${_decoration.runtimeType} painter had mismatching save and '
'restore calls.',
);
} }
if (decoration.isComplex) { if (decoration.isComplex) {
context.setIsComplexHint(); context.setIsComplexHint();

@ -43,8 +43,8 @@ class TextLine extends StatefulWidget {
this.customStyleBuilder, this.customStyleBuilder,
this.customRecognizerBuilder, this.customRecognizerBuilder,
this.customLinkPrefixes = const <String>[], this.customLinkPrefixes = const <String>[],
Key? key, super.key,
}) : super(key: key); });
final Line line; final Line line;
final TextDirection? textDirection; final TextDirection? textDirection;
@ -88,8 +88,8 @@ class _TextLineState extends State<TextLine> {
// In editing mode it depends on the platform: // In editing mode it depends on the platform:
// Desktop platforms (macos, linux, windows): // Desktop platforms (macOS, Linux, Windows):
// only allow Meta(Control)+Click combinations // only allow Meta (Control) + Click combinations
if (isDesktop()) { if (isDesktop()) {
return _metaOrControlPressed; return _metaOrControlPressed;
} }
@ -581,21 +581,22 @@ class EditableTextLine extends RenderObjectWidget {
} }
} }
enum TextLineSlot { LEADING, BODY } enum TextLineSlot { leading, body }
class RenderEditableTextLine extends RenderEditableBox { class RenderEditableTextLine extends RenderEditableBox {
/// Creates new editable paragraph render box. /// Creates new editable paragraph render box.
RenderEditableTextLine( RenderEditableTextLine(
this.line, this.line,
this.textDirection, this.textDirection,
this.textSelection, this.textSelection,
this.enableInteractiveSelection, this.enableInteractiveSelection,
this.hasFocus, this.hasFocus,
this.devicePixelRatio, this.devicePixelRatio,
this.padding, this.padding,
this.color, this.color,
this.cursorCont, this.cursorCont,
this.inlineCodeStyle); this.inlineCodeStyle,
);
RenderBox? _leading; RenderBox? _leading;
RenderContentProxyBox? _body; RenderContentProxyBox? _body;
@ -715,11 +716,11 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
void setLeading(RenderBox? l) { void setLeading(RenderBox? l) {
_leading = _updateChild(_leading, l, TextLineSlot.LEADING); _leading = _updateChild(_leading, l, TextLineSlot.leading);
} }
void setBody(RenderContentProxyBox? b) { void setBody(RenderContentProxyBox? b) {
_body = _updateChild(_body, b, TextLineSlot.BODY) as RenderContentProxyBox?; _body = _updateChild(_body, b, TextLineSlot.body) as RenderContentProxyBox?;
} }
void setInlineCodeStyle(InlineCodeStyle newStyle) { void setInlineCodeStyle(InlineCodeStyle newStyle) {
@ -744,7 +745,10 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
RenderBox? _updateChild( RenderBox? _updateChild(
RenderBox? old, RenderBox? newChild, TextLineSlot slot) { RenderBox? old,
RenderBox? newChild,
TextLineSlot slot,
) {
if (old != null) { if (old != null) {
dropChild(old); dropChild(old);
children.remove(slot); children.remove(slot);
@ -800,8 +804,9 @@ class RenderEditableTextLine extends RenderEditableBox {
assert(boxes.isNotEmpty); assert(boxes.isNotEmpty);
final targetBox = first ? boxes.first : boxes.last; final targetBox = first ? boxes.first : boxes.last;
return TextSelectionPoint( return TextSelectionPoint(
Offset(first ? targetBox.start : targetBox.end, targetBox.bottom), Offset(first ? targetBox.start : targetBox.end, targetBox.bottom),
targetBox.direction); targetBox.direction,
);
} }
@override @override
@ -814,9 +819,12 @@ class RenderEditableTextLine extends RenderEditableBox {
.where((element) => element.top < lineDy && element.bottom > lineDy) .where((element) => element.top < lineDy && element.bottom > lineDy)
.toList(growable: false); .toList(growable: false);
return TextRange( return TextRange(
start: start: getPositionForOffset(
getPositionForOffset(Offset(lineBoxes.first.left, lineDy)).offset, Offset(lineBoxes.first.left, lineDy),
end: getPositionForOffset(Offset(lineBoxes.last.right, lineDy)).offset); ).offset,
end: getPositionForOffset(
Offset(lineBoxes.last.right, lineDy),
).offset);
} }
@override @override
@ -886,7 +894,9 @@ class RenderEditableTextLine extends RenderEditableBox {
/// of the cursor for iOS is approximate and obtained through an eyeball /// of the cursor for iOS is approximate and obtained through an eyeball
/// comparison. /// comparison.
void _computeCaretPrototype() { void _computeCaretPrototype() {
if (isAppleOS()) { // If the cursor is taller only on iOS and not AppleOS then we should check
// only for iOS instead of AppleOS (macOS for example)
if (isIOS()) {
_caretPrototype = Rect.fromLTWH(0, 0, cursorWidth, cursorHeight + 2); _caretPrototype = Rect.fromLTWH(0, 0, cursorWidth, cursorHeight + 2);
} else { } else {
_caretPrototype = Rect.fromLTWH(0, 2, cursorWidth, cursorHeight - 4.0); _caretPrototype = Rect.fromLTWH(0, 2, cursorWidth, cursorHeight - 4.0);
@ -1090,8 +1100,13 @@ class RenderEditableTextLine extends RenderEditableBox {
} else { } else {
final parentData = _leading!.parentData as BoxParentData; final parentData = _leading!.parentData as BoxParentData;
final effectiveOffset = offset + parentData.offset; final effectiveOffset = offset + parentData.offset;
context.paintChild(_leading!, context.paintChild(
Offset(size.width - _leading!.size.width, effectiveOffset.dy)); _leading!,
Offset(
size.width - _leading!.size.width,
effectiveOffset.dy,
),
);
} }
} }
@ -1106,18 +1121,29 @@ class RenderEditableTextLine extends RenderEditableBox {
continue; continue;
} }
final textRange = TextSelection( final textRange = TextSelection(
baseOffset: item.offset, extentOffset: item.offset + item.length); baseOffset: item.offset,
extentOffset: item.offset + item.length,
);
final rects = _body!.getBoxesForSelection(textRange); final rects = _body!.getBoxesForSelection(textRange);
final paint = Paint()..color = inlineCodeStyle.backgroundColor!; final paint = Paint()..color = inlineCodeStyle.backgroundColor!;
for (final box in rects) { for (final box in rects) {
final rect = box.toRect().translate(0, 1).shift(effectiveOffset); final rect = box.toRect().translate(0, 1).shift(effectiveOffset);
if (inlineCodeStyle.radius == null) { if (inlineCodeStyle.radius == null) {
final paintRect = Rect.fromLTRB( final paintRect = Rect.fromLTRB(
rect.left - 2, rect.top, rect.right + 2, rect.bottom); rect.left - 2,
rect.top,
rect.right + 2,
rect.bottom,
);
context.canvas.drawRect(paintRect, paint); context.canvas.drawRect(paintRect, paint);
} else { } else {
final paintRect = RRect.fromLTRBR(rect.left - 2, rect.top, final paintRect = RRect.fromLTRBR(
rect.right + 2, rect.bottom, inlineCodeStyle.radius!); rect.left - 2,
rect.top,
rect.right + 2,
rect.bottom,
inlineCodeStyle.radius!,
);
context.canvas.drawRRect(paintRect, paint); context.canvas.drawRRect(paintRect, paint);
} }
} }
@ -1154,10 +1180,20 @@ class RenderEditableTextLine extends RenderEditableBox {
if (line.isEmpty && if (line.isEmpty &&
textSelection.baseOffset <= line.offset && textSelection.baseOffset <= line.offset &&
textSelection.extentOffset > line.offset) { textSelection.extentOffset > line.offset) {
final lineHeight = final lineHeight = preferredLineHeight(
preferredLineHeight(TextPosition(offset: line.offset)); TextPosition(
_selectedRects offset: line.offset,
?.add(TextBox.fromLTRBD(0, 0, 3, lineHeight, textDirection)); ),
);
_selectedRects?.add(
TextBox.fromLTRBD(
0,
0,
3,
lineHeight,
textDirection,
),
);
} }
_paintSelection(context, effectiveOffset); _paintSelection(context, effectiveOffset);
@ -1179,12 +1215,18 @@ class RenderEditableTextLine extends RenderEditableBox {
? TextPosition( ? TextPosition(
offset: cursorCont.floatingCursorTextPosition.value!.offset - offset: cursorCont.floatingCursorTextPosition.value!.offset -
line.documentOffset, line.documentOffset,
affinity: cursorCont.floatingCursorTextPosition.value!.affinity) affinity: cursorCont.floatingCursorTextPosition.value!.affinity,
)
: TextPosition( : TextPosition(
offset: textSelection.extentOffset - line.documentOffset, offset: textSelection.extentOffset - line.documentOffset,
affinity: textSelection.base.affinity); affinity: textSelection.base.affinity,
);
_cursorPainter.paint( _cursorPainter.paint(
context.canvas, effectiveOffset, position, lineHasEmbed); context.canvas,
effectiveOffset,
position,
lineHasEmbed,
);
} }
@override @override
@ -1192,29 +1234,35 @@ class RenderEditableTextLine extends RenderEditableBox {
if (_leading != null) { if (_leading != null) {
final childParentData = _leading!.parentData as BoxParentData; final childParentData = _leading!.parentData as BoxParentData;
final isHit = result.addWithPaintOffset( final isHit = result.addWithPaintOffset(
offset: childParentData.offset, offset: childParentData.offset,
position: position, position: position,
hitTest: (result, transformed) { hitTest: (result, transformed) {
assert(transformed == position - childParentData.offset); assert(transformed == position - childParentData.offset);
return _leading!.hitTest(result, position: transformed); return _leading!.hitTest(result, position: transformed);
}); },
);
if (isHit) return true; if (isHit) return true;
} }
if (_body == null) return false; if (_body == null) return false;
final parentData = _body!.parentData as BoxParentData; final parentData = _body!.parentData as BoxParentData;
return result.addWithPaintOffset( return result.addWithPaintOffset(
offset: parentData.offset, offset: parentData.offset,
position: position, position: position,
hitTest: (result, position) { hitTest: (result, position) {
return _body!.hitTest(result, position: position); return _body!.hitTest(result, position: position);
}); },
);
} }
@override @override
Rect getLocalRectForCaret(TextPosition position) { Rect getLocalRectForCaret(TextPosition position) {
final caretOffset = getOffsetForCaret(position); final caretOffset = getOffsetForCaret(position);
var rect = var rect = Rect.fromLTWH(
Rect.fromLTWH(0, 0, cursorWidth, cursorHeight).shift(caretOffset); 0,
0,
cursorWidth,
cursorHeight,
).shift(caretOffset);
final cursorOffset = cursorCont.style.offset; final cursorOffset = cursorCont.style.offset;
// Add additional cursor offset (generally only if on iOS). // Add additional cursor offset (generally only if on iOS).
if (cursorOffset != null) rect = rect.shift(cursorOffset); if (cursorOffset != null) rect = rect.shift(cursorOffset);
@ -1272,16 +1320,16 @@ class _TextLineElement extends RenderObjectElement {
@override @override
void mount(Element? parent, dynamic newSlot) { void mount(Element? parent, dynamic newSlot) {
super.mount(parent, newSlot); super.mount(parent, newSlot);
_mountChild(widget.leading, TextLineSlot.LEADING); _mountChild(widget.leading, TextLineSlot.leading);
_mountChild(widget.body, TextLineSlot.BODY); _mountChild(widget.body, TextLineSlot.body);
} }
@override @override
void update(EditableTextLine newWidget) { void update(EditableTextLine newWidget) {
super.update(newWidget); super.update(newWidget);
assert(widget == newWidget); assert(widget == newWidget);
_updateChild(widget.leading, TextLineSlot.LEADING); _updateChild(widget.leading, TextLineSlot.leading);
_updateChild(widget.body, TextLineSlot.BODY); _updateChild(widget.body, TextLineSlot.body);
} }
@override @override
@ -1318,10 +1366,10 @@ class _TextLineElement extends RenderObjectElement {
void _updateRenderObject(RenderBox? child, TextLineSlot? slot) { void _updateRenderObject(RenderBox? child, TextLineSlot? slot) {
switch (slot) { switch (slot) {
case TextLineSlot.LEADING: case TextLineSlot.leading:
renderObject.setLeading(child); renderObject.setLeading(child);
break; break;
case TextLineSlot.BODY: case TextLineSlot.body:
renderObject.setBody(child as RenderContentProxyBox?); renderObject.setBody(child as RenderContentProxyBox?);
break; break;
default: default:

@ -842,8 +842,10 @@ class _EditorTextSelectionGestureDetectorState
void _handleDragUpdate(DragUpdateDetails details) { void _handleDragUpdate(DragUpdateDetails details) {
_lastDragUpdateDetails = details; _lastDragUpdateDetails = details;
_dragUpdateThrottleTimer ??= _dragUpdateThrottleTimer ??= Timer(
Timer(const Duration(milliseconds: 50), _handleDragUpdateThrottled); const Duration(milliseconds: 50),
_handleDragUpdateThrottled,
);
} }
/// Drag updates are being throttled to avoid excessive text layouts in text /// Drag updates are being throttled to avoid excessive text layouts in text

@ -10,8 +10,8 @@ class QuillToolbarArrowIndicatedButtonList extends StatefulWidget {
const QuillToolbarArrowIndicatedButtonList({ const QuillToolbarArrowIndicatedButtonList({
required this.axis, required this.axis,
required this.buttons, required this.buttons,
Key? key, super.key,
}) : super(key: key); });
final Axis axis; final Axis axis;
final List<Widget> buttons; final List<Widget> buttons;

@ -95,14 +95,13 @@ class _QuillToolbarLinkStyleButtonState
context.requireQuillSharedConfigurations.dialogBarrierColor; context.requireQuillSharedConfigurations.dialogBarrierColor;
} }
RegExp get linkRegExp { RegExp? get linkRegExp {
return options.linkRegExp ?? RegExp(r'https?://\S+'); return options.linkRegExp;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isToggled = _getLinkAttributeValue() != null; final isToggled = _getLinkAttributeValue() != null;
final pressedHandler = () => _openLinkDialog(context);
final childBuilder = final childBuilder =
options.childBuilder ?? baseButtonExtraOptions.childBuilder; options.childBuilder ?? baseButtonExtraOptions.childBuilder;
@ -124,7 +123,7 @@ class _QuillToolbarLinkStyleButtonState
context: context, context: context,
controller: controller, controller: controller,
onPressed: () { onPressed: () {
pressedHandler(); _openLinkDialog(context);
afterButtonPressed?.call(); afterButtonPressed?.call();
}, },
), ),
@ -144,10 +143,10 @@ class _QuillToolbarLinkStyleButtonState
: (iconTheme?.iconUnselectedColor ?? theme.iconTheme.color), : (iconTheme?.iconUnselectedColor ?? theme.iconTheme.color),
), ),
fillColor: isToggled fillColor: isToggled
? (iconTheme?.iconSelectedFillColor ?? Theme.of(context).primaryColor) ? (iconTheme?.iconSelectedFillColor ?? theme.primaryColor)
: (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor), : (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor),
borderRadius: iconTheme?.borderRadius ?? 2, borderRadius: iconTheme?.borderRadius ?? 2,
onPressed: pressedHandler, onPressed: () => _openLinkDialog(context),
afterPressed: afterButtonPressed, afterPressed: afterButtonPressed,
); );
} }
@ -236,7 +235,11 @@ class _LinkDialog extends StatefulWidget {
class _LinkDialogState extends State<_LinkDialog> { class _LinkDialogState extends State<_LinkDialog> {
late String _link; late String _link;
late String _text; late String _text;
late RegExp linkRegExp;
RegExp get linkRegExp {
return widget.linkRegExp ?? AutoFormatMultipleLinksRule.oneLineLinkRegExp;
}
late TextEditingController _linkController; late TextEditingController _linkController;
late TextEditingController _textController; late TextEditingController _textController;
@ -245,7 +248,6 @@ class _LinkDialogState extends State<_LinkDialog> {
super.initState(); super.initState();
_link = widget.link ?? ''; _link = widget.link ?? '';
_text = widget.text ?? ''; _text = widget.text ?? '';
linkRegExp = widget.linkRegExp ?? AutoFormatMultipleLinksRule.oneLineRegExp;
_linkController = TextEditingController(text: _link); _linkController = TextEditingController(text: _link);
_textController = TextEditingController(text: _text); _textController = TextEditingController(text: _text);
} }

@ -5,9 +5,11 @@ import 'package:url_launcher/link.dart';
import '../../../../extensions.dart' import '../../../../extensions.dart'
show UtilityWidgets, AutoFormatMultipleLinksRule; show UtilityWidgets, AutoFormatMultipleLinksRule;
import '../../../../translations.dart'; import '../../../../translations.dart';
import '../../../models/config/toolbar/buttons/link_style2.dart';
import '../../../models/documents/attribute.dart'; import '../../../models/documents/attribute.dart';
import '../../../models/themes/quill_dialog_theme.dart'; import '../../../models/themes/quill_dialog_theme.dart';
import '../../../models/themes/quill_icon_theme.dart'; import '../../../models/themes/quill_icon_theme.dart';
import '../../../utils/extensions/build_context.dart';
import '../../controller.dart'; import '../../controller.dart';
import '../../link.dart'; import '../../link.dart';
import '../base_toolbar.dart'; import '../base_toolbar.dart';
@ -16,60 +18,20 @@ import '../base_toolbar.dart';
/// customization /// customization
/// and uses dialog similar to one which is used on [http://quilljs.com]. /// and uses dialog similar to one which is used on [http://quilljs.com].
class QuillToolbarLinkStyleButton2 extends StatefulWidget { class QuillToolbarLinkStyleButton2 extends StatefulWidget {
const QuillToolbarLinkStyleButton2({ QuillToolbarLinkStyleButton2({
required this.controller, required this.controller,
this.icon, required this.options,
this.iconSize = kDefaultIconSize, super.key,
this.iconTheme, }) : assert(options.addLinkLabel == null ||
this.dialogTheme, (options.addLinkLabel?.isNotEmpty ?? true)),
this.afterButtonPressed, assert(options.editLinkLabel == null ||
this.tooltip, (options.editLinkLabel?.isNotEmpty ?? true)),
this.constraints, assert(options.childrenSpacing > 0),
this.addLinkLabel, assert(options.validationMessage == null ||
this.editLinkLabel, (options.validationMessage?.isNotEmpty ?? true));
this.linkColor,
this.childrenSpacing = 16.0,
this.autovalidateMode = AutovalidateMode.disabled,
this.validationMessage,
this.buttonSize,
this.dialogBarrierColor = Colors.black54,
Key? key,
}) : assert(addLinkLabel == null || addLinkLabel.length > 0),
assert(editLinkLabel == null || editLinkLabel.length > 0),
assert(childrenSpacing > 0),
assert(validationMessage == null || validationMessage.length > 0),
super(key: key);
final QuillController controller; final QuillController controller;
final IconData? icon; final QuillToolbarLinkStyleButton2Options options;
final double iconSize;
final QuillIconTheme? iconTheme;
final QuillDialogTheme? dialogTheme;
final VoidCallback? afterButtonPressed;
final String? tooltip;
/// The constrains for dialog.
final BoxConstraints? constraints;
/// The text of label in link add mode.
final String? addLinkLabel;
/// The text of label in link edit mode.
final String? editLinkLabel;
/// The color of URL.
final Color? linkColor;
/// The margin between child widgets in the dialog.
final double childrenSpacing;
final AutovalidateMode autovalidateMode;
final String? validationMessage;
/// The size of dialog buttons.
final Size? buttonSize;
final Color dialogBarrierColor;
@override @override
State<QuillToolbarLinkStyleButton2> createState() => State<QuillToolbarLinkStyleButton2> createState() =>
@ -99,30 +61,101 @@ class _QuillToolbarLinkStyleButton2State
} }
} }
QuillController get controller {
return widget.controller;
}
QuillToolbarLinkStyleButton2Options 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;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final childBuilder =
options.childBuilder ?? baseButtonExtraOptions.childBuilder;
if (childBuilder != null) {
return childBuilder(
QuillToolbarLinkStyleButton2Options(
iconData: iconData,
addLinkLabel: options.addLinkLabel,
afterButtonPressed: options.afterButtonPressed,
autovalidateMode: options.autovalidateMode,
buttonSize: options.buttonSize,
childrenSpacing: options.childrenSpacing,
dialogBarrierColor: dialogBarrierColor,
dialogTheme: options.dialogTheme,
iconSize: iconSize,
constraints: options.constraints,
tooltip: tooltip,
iconTheme: iconTheme,
editLinkLabel: options.editLinkLabel,
validationMessage: options.validationMessage,
linkColor: options.linkColor,
),
QuillToolbarLinkStyleButton2ExtraOptions(
controller: controller,
context: context,
onPressed: () {
_openLinkDialog();
afterButtonPressed?.call();
},
),
);
}
final theme = Theme.of(context); final theme = Theme.of(context);
final isToggled = _getLinkAttributeValue() != null; final isToggled = _getLinkAttributeValue() != null;
return QuillToolbarIconButton( return QuillToolbarIconButton(
tooltip: widget.tooltip, tooltip: tooltip,
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: widget.iconSize * kIconButtonFactor, size: iconSize * kIconButtonFactor,
icon: Icon( icon: Icon(
widget.icon ?? Icons.link, iconData,
size: widget.iconSize, size: iconSize,
color: isToggled color: isToggled
? (widget.iconTheme?.iconSelectedColor ?? ? (iconTheme?.iconSelectedColor ?? theme.primaryIconTheme.color)
theme.primaryIconTheme.color) : (iconTheme?.iconUnselectedColor ?? theme.iconTheme.color),
: (widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color),
), ),
fillColor: isToggled fillColor: isToggled
? (widget.iconTheme?.iconSelectedFillColor ?? ? (iconTheme?.iconSelectedFillColor ?? theme.primaryColor)
Theme.of(context).primaryColor) : (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor),
: (widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor), borderRadius: iconTheme?.borderRadius ?? 2,
borderRadius: widget.iconTheme?.borderRadius ?? 2,
onPressed: _openLinkDialog, onPressed: _openLinkDialog,
afterPressed: widget.afterButtonPressed, afterPressed: afterButtonPressed,
); );
} }
@ -131,19 +164,19 @@ class _QuillToolbarLinkStyleButton2State
final textLink = await showDialog<QuillTextLink>( final textLink = await showDialog<QuillTextLink>(
context: context, context: context,
barrierColor: widget.dialogBarrierColor, barrierColor: dialogBarrierColor,
builder: (_) => LinkStyleDialog( builder: (_) => LinkStyleDialog(
dialogTheme: widget.dialogTheme, dialogTheme: options.dialogTheme,
text: initialTextLink.text, text: initialTextLink.text,
link: initialTextLink.link, link: initialTextLink.link,
constraints: widget.constraints, constraints: options.constraints,
addLinkLabel: widget.addLinkLabel, addLinkLabel: options.addLinkLabel,
editLinkLabel: widget.editLinkLabel, editLinkLabel: options.editLinkLabel,
linkColor: widget.linkColor, linkColor: options.linkColor,
childrenSpacing: widget.childrenSpacing, childrenSpacing: options.childrenSpacing,
autovalidateMode: widget.autovalidateMode, autovalidateMode: options.autovalidateMode,
validationMessage: widget.validationMessage, validationMessage: options.validationMessage,
buttonSize: widget.buttonSize, buttonSize: options.buttonSize,
), ),
); );
@ -166,7 +199,7 @@ class _QuillToolbarLinkStyleButton2State
class LinkStyleDialog extends StatefulWidget { class LinkStyleDialog extends StatefulWidget {
const LinkStyleDialog({ const LinkStyleDialog({
Key? key, super.key,
this.text, this.text,
this.link, this.link,
this.dialogTheme, this.dialogTheme,
@ -183,8 +216,7 @@ class LinkStyleDialog extends StatefulWidget {
}) : assert(addLinkLabel == null || addLinkLabel.length > 0), }) : assert(addLinkLabel == null || addLinkLabel.length > 0),
assert(editLinkLabel == null || editLinkLabel.length > 0), assert(editLinkLabel == null || editLinkLabel.length > 0),
assert(childrenSpacing > 0), assert(childrenSpacing > 0),
assert(validationMessage == null || validationMessage.length > 0), assert(validationMessage == null || validationMessage.length > 0);
super(key: key);
final String? text; final String? text;
final String? link; final String? link;
@ -387,7 +419,7 @@ class _LinkStyleDialogState extends State<LinkStyleDialog> {
String? _validateLink(String? value) { String? _validateLink(String? value) {
if ((value?.isEmpty ?? false) || if ((value?.isEmpty ?? false) ||
!AutoFormatMultipleLinksRule.oneLineRegExp.hasMatch(value!)) { !AutoFormatMultipleLinksRule.oneLineLinkRegExp.hasMatch(value!)) {
return widget.validationMessage ?? 'That is not a valid URL'; return widget.validationMessage ?? 'That is not a valid URL';
} }

@ -60,18 +60,18 @@ class _QuillToolbarSelectAlignmentButtonState
return widget.controller; return widget.controller;
} }
double get iconSize { double get _iconSize {
final baseFontSize = baseButtonExtraOptions.globalIconSize; final baseFontSize = baseButtonExtraOptions.globalIconSize;
final iconSize = options.iconSize; final iconSize = options.iconSize;
return iconSize ?? baseFontSize; return iconSize ?? baseFontSize;
} }
VoidCallback? get afterButtonPressed { VoidCallback? get _afterButtonPressed {
return options.afterButtonPressed ?? return options.afterButtonPressed ??
baseButtonExtraOptions.afterButtonPressed; baseButtonExtraOptions.afterButtonPressed;
} }
QuillIconTheme? get iconTheme { QuillIconTheme? get _iconTheme {
return options.iconTheme ?? baseButtonExtraOptions.iconTheme; return options.iconTheme ?? baseButtonExtraOptions.iconTheme;
} }
@ -191,25 +191,42 @@ class _QuillToolbarSelectAlignmentButtonState
final childBuilder = final childBuilder =
options.childBuilder ?? baseButtonExtraOptions.childBuilder; options.childBuilder ?? baseButtonExtraOptions.childBuilder;
if (childBuilder != null) { void _sharedOnPressed(int index) {
throw UnsupportedError( _valueAttribute[index] == Attribute.leftAlignment
'Sorry but the `childBuilder` for the Select alignment buttons' ? controller.formatSelection(
' is not supported. Yet but we will work on that soon.', Attribute.clone(Attribute.align, null),
); )
: controller.formatSelection(_valueAttribute[index]);
_afterButtonPressed?.call();
} }
final theme = Theme.of(context);
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: List.generate(buttonCount, (index) { children: List.generate(buttonCount, (index) {
if (childBuilder != null) {
return childBuilder(
QuillToolbarSelectAlignmentButtonOptions(
afterButtonPressed: _afterButtonPressed,
iconSize: _iconSize,
iconTheme: _iconTheme,
tooltips: _tooltips,
iconsData: _iconsData,
),
QuillToolbarSelectAlignmentButtonExtraOptions(
context: context,
controller: controller,
onPressed: () => _sharedOnPressed(index),
),
);
}
final theme = Theme.of(context);
return Padding( return Padding(
padding: widget.padding ?? padding: widget.padding ??
const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0), const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints.tightFor( constraints: BoxConstraints.tightFor(
width: iconSize * kIconButtonFactor, width: _iconSize * kIconButtonFactor,
height: iconSize * kIconButtonFactor, height: _iconSize * kIconButtonFactor,
), ),
child: UtilityWidgets.maybeTooltip( child: UtilityWidgets.maybeTooltip(
message: _valueString[index] == Attribute.leftAlignment.value message: _valueString[index] == Attribute.leftAlignment.value
@ -226,19 +243,12 @@ class _QuillToolbarSelectAlignmentButtonState
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: borderRadius:
BorderRadius.circular(iconTheme?.borderRadius ?? 2)), BorderRadius.circular(_iconTheme?.borderRadius ?? 2)),
fillColor: _valueToText[_value] == _valueString[index] fillColor: _valueToText[_value] == _valueString[index]
? (iconTheme?.iconSelectedFillColor ?? ? (_iconTheme?.iconSelectedFillColor ?? theme.primaryColor)
Theme.of(context).primaryColor) : (_iconTheme?.iconUnselectedFillColor ??
: (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor), theme.canvasColor),
onPressed: () { onPressed: () => _sharedOnPressed(index),
_valueAttribute[index] == Attribute.leftAlignment
? controller.formatSelection(
Attribute.clone(Attribute.align, null),
)
: controller.formatSelection(_valueAttribute[index]);
afterButtonPressed?.call();
},
child: Icon( child: Icon(
_valueString[index] == Attribute.leftAlignment.value _valueString[index] == Attribute.leftAlignment.value
? _iconsData.leftAlignment ? _iconsData.leftAlignment
@ -248,11 +258,11 @@ class _QuillToolbarSelectAlignmentButtonState
Attribute.rightAlignment.value Attribute.rightAlignment.value
? _iconsData.rightAlignment ? _iconsData.rightAlignment
: _iconsData.justifyAlignment, : _iconsData.justifyAlignment,
size: iconSize, size: _iconSize,
color: _valueToText[_value] == _valueString[index] color: _valueToText[_value] == _valueString[index]
? (iconTheme?.iconSelectedColor ?? ? (_iconTheme?.iconSelectedColor ??
theme.primaryIconTheme.color) theme.primaryIconTheme.color)
: (iconTheme?.iconUnselectedColor ?? : (_iconTheme?.iconUnselectedColor ??
theme.iconTheme.color), theme.iconTheme.color),
), ),
), ),

@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../extensions.dart'; import '../../../../extensions.dart';
@ -90,10 +91,20 @@ class _QuillToolbarSelectHeaderStyleButtonsState
afterButtonPressed?.call(); afterButtonPressed?.call();
} }
List<Attribute> get _attrbuites {
return options.attributes ??
const [
Attribute.header,
Attribute.h1,
Attribute.h2,
Attribute.h3,
];
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert( assert(
options.attributes.every( _attrbuites.every(
(element) => _valueToText.keys.contains(element), (element) => _valueToText.keys.contains(element),
), ),
'All attributes must be one of them: header, h1, h2 or h3', 'All attributes must be one of them: header, h1, h2 or h3',
@ -107,19 +118,28 @@ class _QuillToolbarSelectHeaderStyleButtonsState
final childBuilder = final childBuilder =
options.childBuilder ?? baseButtonExtraOptions.childBuilder; options.childBuilder ?? baseButtonExtraOptions.childBuilder;
if (childBuilder != null) { final children = _attrbuites.map((attribute) {
throw UnsupportedError( if (childBuilder != null) {
'Sorry but the `childBuilder` for the Select header button' return childBuilder(
' is not supported. Yet but we will work on that soon.', QuillToolbarSelectHeaderStyleButtonsOptions(
); afterButtonPressed: afterButtonPressed,
} attributes: _attrbuites,
final theme = Theme.of(context); axis: axis,
iconSize: iconSize,
final children = options.attributes.map((attribute) { iconTheme: iconTheme,
tooltip: tooltip,
),
QuillToolbarSelectHeaderStyleButtonExtraOptions(
controller: controller,
context: context,
onPressed: () => _sharedOnPressed(attribute),
),
);
}
final theme = Theme.of(context);
final isSelected = _selectedAttribute == attribute; final isSelected = _selectedAttribute == attribute;
return Padding( return Padding(
// Do we really need to ignore (prefer_const_constructors)?? padding: const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0),
padding: EdgeInsets.symmetric(horizontal: !isWeb() ? 1.0 : 5.0),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints.tightFor( constraints: BoxConstraints.tightFor(
width: iconSize * kIconButtonFactor, width: iconSize * kIconButtonFactor,
@ -136,12 +156,14 @@ class _QuillToolbarSelectHeaderStyleButtonsState
borderRadius: borderRadius:
BorderRadius.circular(iconTheme?.borderRadius ?? 2)), BorderRadius.circular(iconTheme?.borderRadius ?? 2)),
fillColor: isSelected fillColor: isSelected
? (iconTheme?.iconSelectedFillColor ?? ? (iconTheme?.iconSelectedFillColor ?? theme.primaryColor)
Theme.of(context).primaryColor)
: (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor), : (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor),
onPressed: () => _sharedOnPressed(attribute), onPressed: () => _sharedOnPressed(attribute),
child: Text( child: Text(
_valueToText[attribute] ?? '', _valueToText[attribute] ??
(throw ArgumentError.notNull(
'attrbuite',
)),
style: style.copyWith( style: style.copyWith(
color: isSelected color: isSelected
? (iconTheme?.iconSelectedColor ?? ? (iconTheme?.iconSelectedColor ??

@ -70,36 +70,47 @@ class QuillToolbar extends StatelessWidget {
final axis = toolbarConfigurations.axis; final axis = toolbarConfigurations.axis;
final globalController = context.requireQuillController; final globalController = context.requireQuillController;
final spacerWidget =
configurations.spacerWidget ?? const SizedBox.shrink();
return [ return [
if (configurations.showUndo) if (configurations.showUndo) ...[
QuillToolbarHistoryButton( QuillToolbarHistoryButton(
options: toolbarConfigurations.buttonOptions.undoHistory, options: toolbarConfigurations.buttonOptions.undoHistory,
controller: toolbarConfigurations controller: toolbarConfigurations
.buttonOptions.undoHistory.controller ?? .buttonOptions.undoHistory.controller ??
globalController, globalController,
), ),
if (configurations.showRedo) spacerWidget,
],
if (configurations.showRedo) ...[
QuillToolbarHistoryButton( QuillToolbarHistoryButton(
options: toolbarConfigurations.buttonOptions.redoHistory, options: toolbarConfigurations.buttonOptions.redoHistory,
controller: toolbarConfigurations controller: toolbarConfigurations
.buttonOptions.redoHistory.controller ?? .buttonOptions.redoHistory.controller ??
globalController, globalController,
), ),
if (configurations.showFontFamily) spacerWidget,
],
if (configurations.showFontFamily) ...[
QuillToolbarFontFamilyButton( QuillToolbarFontFamilyButton(
options: toolbarConfigurations.buttonOptions.fontFamily, options: toolbarConfigurations.buttonOptions.fontFamily,
controller: toolbarConfigurations controller: toolbarConfigurations
.buttonOptions.fontFamily.controller ?? .buttonOptions.fontFamily.controller ??
globalController, globalController,
), ),
if (configurations.showFontSize) spacerWidget,
],
if (configurations.showFontSize) ...[
QuillToolbarFontSizeButton( QuillToolbarFontSizeButton(
options: toolbarConfigurations.buttonOptions.fontSize, options: toolbarConfigurations.buttonOptions.fontSize,
controller: toolbarConfigurations controller: toolbarConfigurations
.buttonOptions.fontFamily.controller ?? .buttonOptions.fontFamily.controller ??
globalController, globalController,
), ),
if (configurations.showBoldButton) spacerWidget,
],
if (configurations.showBoldButton) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.bold, attribute: Attribute.bold,
options: toolbarConfigurations.buttonOptions.bold, options: toolbarConfigurations.buttonOptions.bold,
@ -107,7 +118,9 @@ class QuillToolbar extends StatelessWidget {
toolbarConfigurations.buttonOptions.bold.controller ?? toolbarConfigurations.buttonOptions.bold.controller ??
globalController, globalController,
), ),
if (configurations.showSubscript) spacerWidget,
],
if (configurations.showSubscript) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.subscript, attribute: Attribute.subscript,
options: toolbarConfigurations.buttonOptions.subscript, options: toolbarConfigurations.buttonOptions.subscript,
@ -115,7 +128,9 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.subscript.controller ?? .buttonOptions.subscript.controller ??
globalController, globalController,
), ),
if (configurations.showSuperscript) spacerWidget,
],
if (configurations.showSuperscript) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.superscript, attribute: Attribute.superscript,
options: toolbarConfigurations.buttonOptions.superscript, options: toolbarConfigurations.buttonOptions.superscript,
@ -123,7 +138,9 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.superscript.controller ?? .buttonOptions.superscript.controller ??
globalController, globalController,
), ),
if (configurations.showItalicButton) spacerWidget,
],
if (configurations.showItalicButton) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.italic, attribute: Attribute.italic,
options: toolbarConfigurations.buttonOptions.italic, options: toolbarConfigurations.buttonOptions.italic,
@ -131,7 +148,9 @@ class QuillToolbar extends StatelessWidget {
toolbarConfigurations.buttonOptions.italic.controller ?? toolbarConfigurations.buttonOptions.italic.controller ??
globalController, globalController,
), ),
if (configurations.showSmallButton) spacerWidget,
],
if (configurations.showSmallButton) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.small, attribute: Attribute.small,
options: toolbarConfigurations.buttonOptions.small, options: toolbarConfigurations.buttonOptions.small,
@ -139,7 +158,9 @@ class QuillToolbar extends StatelessWidget {
toolbarConfigurations.buttonOptions.small.controller ?? toolbarConfigurations.buttonOptions.small.controller ??
globalController, globalController,
), ),
if (configurations.showUnderLineButton) spacerWidget,
],
if (configurations.showUnderLineButton) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.underline, attribute: Attribute.underline,
options: toolbarConfigurations.buttonOptions.underLine, options: toolbarConfigurations.buttonOptions.underLine,
@ -147,7 +168,9 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.underLine.controller ?? .buttonOptions.underLine.controller ??
globalController, globalController,
), ),
if (configurations.showStrikeThrough) spacerWidget,
],
if (configurations.showStrikeThrough) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.strikeThrough, attribute: Attribute.strikeThrough,
options: toolbarConfigurations.buttonOptions.strikeThrough, options: toolbarConfigurations.buttonOptions.strikeThrough,
@ -155,7 +178,9 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.strikeThrough.controller ?? .buttonOptions.strikeThrough.controller ??
globalController, globalController,
), ),
if (configurations.showInlineCode) spacerWidget,
],
if (configurations.showInlineCode) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.inlineCode, attribute: Attribute.inlineCode,
options: toolbarConfigurations.buttonOptions.inlineCode, options: toolbarConfigurations.buttonOptions.inlineCode,
@ -163,23 +188,31 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.inlineCode.controller ?? .buttonOptions.inlineCode.controller ??
globalController, globalController,
), ),
if (configurations.showColorButton) spacerWidget,
],
if (configurations.showColorButton) ...[
QuillToolbarColorButton( QuillToolbarColorButton(
controller: controller, controller: controller,
isBackground: false, isBackground: false,
options: toolbarConfigurations.buttonOptions.color, options: toolbarConfigurations.buttonOptions.color,
), ),
if (configurations.showBackgroundColorButton) spacerWidget,
],
if (configurations.showBackgroundColorButton) ...[
QuillToolbarColorButton( QuillToolbarColorButton(
options: toolbarConfigurations.buttonOptions.backgroundColor, options: toolbarConfigurations.buttonOptions.backgroundColor,
controller: controller, controller: controller,
isBackground: true, isBackground: true,
), ),
if (configurations.showClearFormat) spacerWidget,
],
if (configurations.showClearFormat) ...[
QuillToolbarClearFormatButton( QuillToolbarClearFormatButton(
controller: controller, controller: controller,
options: toolbarConfigurations.buttonOptions.clearFormat, options: toolbarConfigurations.buttonOptions.clearFormat,
), ),
spacerWidget,
],
if (theEmbedButtons != null) if (theEmbedButtons != null)
for (final builder in theEmbedButtons) for (final builder in theEmbedButtons)
builder( builder(
@ -199,7 +232,7 @@ class QuillToolbar extends StatelessWidget {
color: configurations.sectionDividerColor, color: configurations.sectionDividerColor,
space: configurations.sectionDividerSpace, space: configurations.sectionDividerSpace,
), ),
if (configurations.showAlignmentButtons) if (configurations.showAlignmentButtons) ...[
QuillToolbarSelectAlignmentButton( QuillToolbarSelectAlignmentButton(
controller: controller, controller: controller,
options: toolbarConfigurations options: toolbarConfigurations
@ -216,7 +249,9 @@ class QuillToolbar extends StatelessWidget {
showRightAlignment: configurations.showRightAlignment, showRightAlignment: configurations.showRightAlignment,
showJustifyAlignment: configurations.showJustifyAlignment, showJustifyAlignment: configurations.showJustifyAlignment,
), ),
if (configurations.showDirection) spacerWidget,
],
if (configurations.showDirection) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.rtl, attribute: Attribute.rtl,
options: toolbarConfigurations.buttonOptions.direction, options: toolbarConfigurations.buttonOptions.direction,
@ -224,6 +259,8 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.direction.controller ?? .buttonOptions.direction.controller ??
context.requireQuillController, context.requireQuillController,
), ),
spacerWidget,
],
if (configurations.showDividers && if (configurations.showDividers &&
isButtonGroupShown[1] && isButtonGroupShown[1] &&
(isButtonGroupShown[2] || (isButtonGroupShown[2] ||
@ -235,12 +272,14 @@ class QuillToolbar extends StatelessWidget {
color: configurations.sectionDividerColor, color: configurations.sectionDividerColor,
space: configurations.sectionDividerSpace, space: configurations.sectionDividerSpace,
), ),
if (configurations.showHeaderStyle) if (configurations.showHeaderStyle) ...[
QuillToolbarSelectHeaderStyleButtons( QuillToolbarSelectHeaderStyleButtons(
controller: controller, controller: controller,
options: toolbarConfigurations options: toolbarConfigurations
.buttonOptions.selectHeaderStyleButtons, .buttonOptions.selectHeaderStyleButtons,
), ),
spacerWidget,
],
if (configurations.showDividers && if (configurations.showDividers &&
configurations.showHeaderStyle && configurations.showHeaderStyle &&
isButtonGroupShown[2] && isButtonGroupShown[2] &&
@ -252,7 +291,7 @@ class QuillToolbar extends StatelessWidget {
color: configurations.sectionDividerColor, color: configurations.sectionDividerColor,
space: configurations.sectionDividerSpace, space: configurations.sectionDividerSpace,
), ),
if (configurations.showListNumbers) if (configurations.showListNumbers) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.ol, attribute: Attribute.ol,
options: toolbarConfigurations.buttonOptions.listNumbers, options: toolbarConfigurations.buttonOptions.listNumbers,
@ -260,7 +299,9 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.listNumbers.controller ?? .buttonOptions.listNumbers.controller ??
globalController, globalController,
), ),
if (configurations.showListBullets) spacerWidget,
],
if (configurations.showListBullets) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.ul, attribute: Attribute.ul,
options: toolbarConfigurations.buttonOptions.listBullets, options: toolbarConfigurations.buttonOptions.listBullets,
@ -268,14 +309,18 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.listBullets.controller ?? .buttonOptions.listBullets.controller ??
globalController, globalController,
), ),
if (configurations.showListCheck) spacerWidget,
],
if (configurations.showListCheck) ...[
QuillToolbarToggleCheckListButton( QuillToolbarToggleCheckListButton(
options: toolbarConfigurations.buttonOptions.toggleCheckList, options: toolbarConfigurations.buttonOptions.toggleCheckList,
controller: toolbarConfigurations controller: toolbarConfigurations
.buttonOptions.toggleCheckList.controller ?? .buttonOptions.toggleCheckList.controller ??
globalController, globalController,
), ),
if (configurations.showCodeBlock) spacerWidget,
],
if (configurations.showCodeBlock) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
attribute: Attribute.codeBlock, attribute: Attribute.codeBlock,
options: toolbarConfigurations.buttonOptions.codeBlock, options: toolbarConfigurations.buttonOptions.codeBlock,
@ -283,15 +328,18 @@ class QuillToolbar extends StatelessWidget {
.buttonOptions.codeBlock.controller ?? .buttonOptions.codeBlock.controller ??
globalController, globalController,
), ),
spacerWidget,
],
if (configurations.showDividers && if (configurations.showDividers &&
isButtonGroupShown[3] && isButtonGroupShown[3] &&
(isButtonGroupShown[4] || isButtonGroupShown[5])) (isButtonGroupShown[4] || isButtonGroupShown[5])) ...[
QuillToolbarDivider( QuillToolbarDivider(
axis, axis,
color: configurations.sectionDividerColor, color: configurations.sectionDividerColor,
space: configurations.sectionDividerSpace, space: configurations.sectionDividerSpace,
), ),
if (configurations.showQuote) ],
if (configurations.showQuote) ...[
QuillToolbarToggleStyleButton( QuillToolbarToggleStyleButton(
options: toolbarConfigurations.buttonOptions.quote, options: toolbarConfigurations.buttonOptions.quote,
controller: controller:
@ -299,7 +347,9 @@ class QuillToolbar extends StatelessWidget {
globalController, globalController,
attribute: Attribute.blockQuote, attribute: Attribute.blockQuote,
), ),
if (configurations.showIndent) spacerWidget,
],
if (configurations.showIndent) ...[
QuillToolbarIndentButton( QuillToolbarIndentButton(
controller: toolbarConfigurations controller: toolbarConfigurations
.buttonOptions.indentIncrease.controller ?? .buttonOptions.indentIncrease.controller ??
@ -307,7 +357,9 @@ class QuillToolbar extends StatelessWidget {
isIncrease: true, isIncrease: true,
options: toolbarConfigurations.buttonOptions.indentIncrease, options: toolbarConfigurations.buttonOptions.indentIncrease,
), ),
if (configurations.showIndent) spacerWidget,
],
if (configurations.showIndent) ...[
QuillToolbarIndentButton( QuillToolbarIndentButton(
controller: toolbarConfigurations controller: toolbarConfigurations
.buttonOptions.indentDecrease.controller ?? .buttonOptions.indentDecrease.controller ??
@ -315,6 +367,8 @@ class QuillToolbar extends StatelessWidget {
isIncrease: false, isIncrease: false,
options: toolbarConfigurations.buttonOptions.indentDecrease, options: toolbarConfigurations.buttonOptions.indentDecrease,
), ),
spacerWidget,
],
if (configurations.showDividers && if (configurations.showDividers &&
isButtonGroupShown[4] && isButtonGroupShown[4] &&
isButtonGroupShown[5]) isButtonGroupShown[5])
@ -323,45 +377,51 @@ class QuillToolbar extends StatelessWidget {
color: configurations.sectionDividerColor, color: configurations.sectionDividerColor,
space: configurations.sectionDividerSpace, space: configurations.sectionDividerSpace,
), ),
if (configurations.showLink) if (configurations.showLink) ...[
QuillToolbarLinkStyleButton( QuillToolbarLinkStyleButton(
controller: controller, controller: controller,
options: toolbarConfigurations.buttonOptions.linkStyle, options: toolbarConfigurations.buttonOptions.linkStyle,
), ),
if (configurations.showSearchButton) spacerWidget,
],
if (configurations.showSearchButton) ...[
QuillToolbarSearchButton( QuillToolbarSearchButton(
controller: controller, controller: controller,
options: toolbarConfigurations.buttonOptions.search, options: toolbarConfigurations.buttonOptions.search,
), ),
if (configurations.customButtons.isNotEmpty) spacerWidget,
],
if (configurations.customButtons.isNotEmpty) ...[
if (configurations.showDividers) if (configurations.showDividers)
QuillToolbarDivider( QuillToolbarDivider(
axis, axis,
color: configurations.sectionDividerColor, color: configurations.sectionDividerColor,
space: configurations.sectionDividerSpace, space: configurations.sectionDividerSpace,
), ),
for (final customButton in configurations.customButtons) for (final customButton in configurations.customButtons)
if (customButton.child != null) ...[ if (customButton.child != null) ...[
InkWell( InkWell(
onTap: customButton.onTap, onTap: customButton.onTap,
child: customButton.child, child: customButton.child,
), ),
] else ...[ ] else ...[
CustomButton( CustomButton(
onPressed: customButton.onTap, onPressed: customButton.onTap,
icon: customButton.iconData ?? icon: customButton.iconData ??
context.quillToolbarBaseButtonOptions?.iconData, context.quillToolbarBaseButtonOptions?.iconData,
iconColor: customButton.iconColor, iconColor: customButton.iconColor,
iconSize: customButton.iconSize ?? globalIconSize, iconSize: customButton.iconSize ?? globalIconSize,
iconTheme: iconTheme: context
context.requireQuillToolbarBaseButtonOptions.iconTheme, .requireQuillToolbarBaseButtonOptions.iconTheme,
afterButtonPressed: customButton.afterButtonPressed ?? afterButtonPressed: customButton.afterButtonPressed ??
context context.quillToolbarBaseButtonOptions
.quillToolbarBaseButtonOptions?.afterButtonPressed, ?.afterButtonPressed,
tooltip: customButton.tooltip ?? tooltip: customButton.tooltip ??
context.quillToolbarBaseButtonOptions?.tooltip, context.quillToolbarBaseButtonOptions?.tooltip,
), ),
], ],
spacerWidget,
],
]; ];
}, },
), ),

@ -1,6 +1,6 @@
name: flutter_quill 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. 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: 8.1.1 version: 8.1.2
homepage: https://1o24bbs.com/c/bulletjournal/108 homepage: https://1o24bbs.com/c/bulletjournal/108
repository: https://github.com/singerdmx/flutter-quill repository: https://github.com/singerdmx/flutter-quill
topics: topics:
@ -17,7 +17,8 @@ platforms:
windows: windows:
environment: environment:
sdk: ">=2.17.0 <4.0.0" sdk: '>=3.1.3 <4.0.0'
# sdk: ">=2.17.0 <4.0.0"
flutter: ">=3.10.0" flutter: ">=3.10.0"
dependencies: dependencies:
@ -40,5 +41,6 @@ dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
meta: ^1.9.1
flutter: null flutter: null

@ -130,7 +130,7 @@ void main() {
controller.formatSelection(Attribute.unchecked); controller.formatSelection(Attribute.unchecked);
editor.focusNode.unfocus(); editor.focusNode.unfocus();
await tester.pump(); await tester.pump();
await tester.tap(find.byType(CheckboxPoint)); await tester.tap(find.byType(QuillEditorCheckboxPoint));
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
}); });
}); });

Loading…
Cancel
Save