From cd05dfaeb8ec3d3a860ba0bc8ccc563728d7eba8 Mon Sep 17 00:00:00 2001 From: Till Friebe Date: Sat, 27 Mar 2021 18:06:58 +0100 Subject: [PATCH 1/4] Avoid redundant argument values (#114) If it's ok, I would spend the day enabling linter rules so that the codebase is more compliant with [Effective Dart](https://dart.dev/guides/language/effective-dart) and thus more readable for new developers. --- analysis_options.yaml | 1 + example/lib/pages/home_page.dart | 1 - example/lib/pages/read_only_page.dart | 1 - lib/models/documents/document.dart | 6 +++--- lib/models/quill_delta.dart | 2 +- lib/widgets/controller.dart | 1 - lib/widgets/editor.dart | 4 +--- lib/widgets/raw_editor.dart | 2 -- lib/widgets/text_block.dart | 2 +- lib/widgets/toolbar.dart | 3 --- 10 files changed, 7 insertions(+), 16 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 57f3028e..33b1634a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -8,3 +8,4 @@ analyzer: linter: rules: - avoid_print + - avoid_redundant_argument_values diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart index 2e044fd7..9e91cde4 100644 --- a/example/lib/pages/home_page.dart +++ b/example/lib/pages/home_page.dart @@ -110,7 +110,6 @@ class _HomePageState extends State { autoFocus: false, readOnly: false, placeholder: 'Add content', - enableInteractiveSelection: true, expands: false, padding: EdgeInsets.zero, customStyles: DefaultStyles( diff --git a/example/lib/pages/read_only_page.dart b/example/lib/pages/read_only_page.dart index 8f03d45c..6e2ec0e0 100644 --- a/example/lib/pages/read_only_page.dart +++ b/example/lib/pages/read_only_page.dart @@ -42,7 +42,6 @@ class _ReadOnlyPageState extends State { focusNode: _focusNode, autoFocus: true, readOnly: !_edit, - enableInteractiveSelection: true, expands: false, padding: EdgeInsets.zero, ), diff --git a/lib/models/documents/document.dart b/lib/models/documents/document.dart index d52e89a4..126ff32b 100644 --- a/lib/models/documents/document.dart +++ b/lib/models/documents/document.dart @@ -183,7 +183,7 @@ class Document { bool nextOpIsImage = i + 1 < ops.length && ops[i + 1].isInsert && ops[i + 1].data is! String; if (nextOpIsImage && !(op.data as String).endsWith('\n')) { - res.push(Operation.insert('\n', null)); + res.push(Operation.insert('\n')); } // Currently embed is equivalent to image and hence `is! String` bool opInsertImage = op.isInsert && op.data is! String; @@ -193,7 +193,7 @@ class Document { (ops[i + 1].data as String).startsWith('\n'); if (opInsertImage && (i + 1 == ops.length - 1 || !nextOpIsLineBreak)) { // automatically append '\n' for image - res.push(Operation.insert('\n', null)); + res.push(Operation.insert('\n')); } } @@ -213,7 +213,7 @@ class Document { _history.clear(); } - String toPlainText() => _root.children.map((e) => e.toPlainText()).join(''); + String toPlainText() => _root.children.map((e) => e.toPlainText()).join(); void _loadDocument(Delta doc) { assert((doc.last.data as String).endsWith('\n')); diff --git a/lib/models/quill_delta.dart b/lib/models/quill_delta.dart index bced9b88..a1fe1f0f 100644 --- a/lib/models/quill_delta.dart +++ b/lib/models/quill_delta.dart @@ -520,7 +520,7 @@ class Delta { if (op.isInsert) { inverted.delete(op.length!); } else if (op.isRetain && op.isPlain) { - inverted.retain(op.length!, null); + inverted.retain(op.length!); baseIndex += op.length!; } else if (op.isDelete || (op.isRetain && op.isNotPlain)) { final length = op.length!; diff --git a/lib/widgets/controller.dart b/lib/widgets/controller.dart index 29206059..f613af69 100644 --- a/lib/widgets/controller.dart +++ b/lib/widgets/controller.dart @@ -31,7 +31,6 @@ class QuillController extends ChangeNotifier { TextEditingValue get plainTextEditingValue => TextEditingValue( text: document.toPlainText(), selection: selection, - composing: TextRange.empty, ); Style getSelectionStyle() { diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index 305a58c8..f3d3bd1a 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -200,7 +200,6 @@ class QuillEditor extends StatefulWidget { focusNode: FocusNode(), autoFocus: true, readOnly: readOnly, - enableInteractiveSelection: true, expands: false, padding: EdgeInsets.zero); } @@ -726,8 +725,7 @@ class RenderEditor extends RenderEditableContainerBox ); if (position.offset - word.start <= 1) { _handleSelectionChange( - TextSelection.collapsed( - offset: word.start, affinity: TextAffinity.downstream), + TextSelection.collapsed(offset: word.start), cause, ); } else { diff --git a/lib/widgets/raw_editor.dart b/lib/widgets/raw_editor.dart index 04b4aaf8..11c9c568 100644 --- a/lib/widgets/raw_editor.dart +++ b/lib/widgets/raw_editor.dart @@ -382,8 +382,6 @@ class RawEditorState extends EditorState TextInputConfiguration( inputType: TextInputType.multiline, readOnly: widget.readOnly, - obscureText: false, - autocorrect: true, inputAction: TextInputAction.newline, keyboardAppearance: widget.keyboardAppearance, textCapitalization: widget.textCapitalization, diff --git a/lib/widgets/text_block.dart b/lib/widgets/text_block.dart index 4d2a5cc3..1b085957 100644 --- a/lib/widgets/text_block.dart +++ b/lib/widgets/text_block.dart @@ -623,7 +623,7 @@ class _NumberPoint extends StatelessWidget { n = (n / 26).floor(); } - return result.toString().split('').reversed.join(''); + return result.toString().split('').reversed.join(); } String _intToRoman(int input) { diff --git a/lib/widgets/toolbar.dart b/lib/widgets/toolbar.dart index cb23a19e..0859cad3 100644 --- a/lib/widgets/toolbar.dart +++ b/lib/widgets/toolbar.dart @@ -525,7 +525,6 @@ class _ImageButtonState extends State { Future _pickImageWeb() async { _paths = (await FilePicker.platform.pickFiles( type: _pickingType, - allowMultiple: false, allowedExtensions: (_extension?.isNotEmpty ?? false) ? _extension?.replaceAll(' ', '').split(',') : null, @@ -1163,7 +1162,6 @@ class QuillIconButton extends StatelessWidget { child: RawMaterialButton( visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)), - padding: EdgeInsets.zero, fillColor: fillColor, elevation: 0, hoverElevation: hoverElevation, @@ -1209,7 +1207,6 @@ class _QuillDropdownButtonState extends State> { child: RawMaterialButton( visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)), - padding: EdgeInsets.zero, fillColor: widget.fillColor, elevation: 0, hoverElevation: widget.hoverElevation, From 25f1582f70f11c5e0ff643f3f33b5a84e7cdbbab Mon Sep 17 00:00:00 2001 From: Till Friebe Date: Sat, 27 Mar 2021 18:08:16 +0100 Subject: [PATCH 2/4] Always put required named parameters first (#115) --- analysis_options.yaml | 1 + lib/widgets/default_styles.dart | 2 +- lib/widgets/editor.dart | 4 +- lib/widgets/responsive_widget.dart | 12 +-- lib/widgets/text_block.dart | 6 +- lib/widgets/text_line.dart | 17 ++-- lib/widgets/text_selection.dart | 6 +- lib/widgets/toolbar.dart | 127 +++++++++++++++-------------- 8 files changed, 89 insertions(+), 86 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 33b1634a..451c1799 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,5 +7,6 @@ analyzer: unsafe_html: ignore linter: rules: + - always_put_required_named_parameters_first - avoid_print - avoid_redundant_argument_values diff --git a/lib/widgets/default_styles.dart b/lib/widgets/default_styles.dart index bbe8db10..30b6b7fe 100644 --- a/lib/widgets/default_styles.dart +++ b/lib/widgets/default_styles.dart @@ -6,9 +6,9 @@ class QuillStyles extends InheritedWidget { final DefaultStyles data; QuillStyles({ - Key? key, required this.data, required Widget child, + Key? key, }) : super(key: key, child: child); @override diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index f3d3bd1a..ef2221c0 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -176,14 +176,14 @@ class QuillEditor extends StatefulWidget { required this.scrollable, required this.padding, required this.autoFocus, - this.showCursor, required this.readOnly, + required this.expands, + this.showCursor, this.placeholder, this.enableInteractiveSelection = true, this.minHeight, this.maxHeight, this.customStyles, - required this.expands, this.textCapitalization = TextCapitalization.sentences, this.keyboardAppearance = Brightness.light, this.scrollPhysics, diff --git a/lib/widgets/responsive_widget.dart b/lib/widgets/responsive_widget.dart index 806883ec..dbfec8d5 100644 --- a/lib/widgets/responsive_widget.dart +++ b/lib/widgets/responsive_widget.dart @@ -5,12 +5,12 @@ class ResponsiveWidget extends StatelessWidget { final Widget? mediumScreen; final Widget? smallScreen; - const ResponsiveWidget( - {Key? key, - required this.largeScreen, - this.mediumScreen, - this.smallScreen}) - : super(key: key); + const ResponsiveWidget({ + required this.largeScreen, + this.mediumScreen, + this.smallScreen, + Key? key, + }) : super(key: key); static bool isSmallScreen(BuildContext context) { return MediaQuery.of(context).size.width < 800; diff --git a/lib/widgets/text_block.dart b/lib/widgets/text_block.dart index 1b085957..ef226b1e 100644 --- a/lib/widgets/text_block.dart +++ b/lib/widgets/text_block.dart @@ -253,11 +253,11 @@ class EditableTextBlock extends StatelessWidget { class RenderEditableTextBlock extends RenderEditableContainerBox implements RenderEditableBox { RenderEditableTextBlock({ - List? children, required Block block, required TextDirection textDirection, required EdgeInsetsGeometry padding, required Decoration decoration, + List? children, ImageConfiguration configuration = ImageConfiguration.empty, EdgeInsets contentPadding = EdgeInsets.zero, }) : _decoration = decoration, @@ -558,7 +558,6 @@ class _NumberPoint extends StatelessWidget { final double padding; const _NumberPoint({ - Key? key, required this.index, required this.indentLevelCounts, required this.count, @@ -567,6 +566,7 @@ class _NumberPoint extends StatelessWidget { required this.attrs, this.withDot = true, this.padding = 0.0, + Key? key, }) : super(key: key); @override @@ -656,9 +656,9 @@ class _BulletPoint extends StatelessWidget { final double width; const _BulletPoint({ - Key? key, required this.style, required this.width, + Key? key, }) : super(key: key); @override diff --git a/lib/widgets/text_line.dart b/lib/widgets/text_line.dart index 0da1e64f..82418ca3 100644 --- a/lib/widgets/text_line.dart +++ b/lib/widgets/text_line.dart @@ -27,13 +27,13 @@ class TextLine extends StatelessWidget { final EmbedBuilder embedBuilder; final DefaultStyles styles; - const TextLine( - {Key? key, - required this.line, - this.textDirection, - required this.embedBuilder, - required this.styles}) - : super(key: key); + const TextLine({ + required this.line, + required this.embedBuilder, + required this.styles, + this.textDirection, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -839,7 +839,8 @@ class _TextLineElement extends RenderObjectElement { } @override - void moveRenderObjectChild(RenderObject child, dynamic oldSlot, dynamic newSlot) { + void moveRenderObjectChild( + RenderObject child, dynamic oldSlot, dynamic newSlot) { throw UnimplementedError(); } diff --git a/lib/widgets/text_selection.dart b/lib/widgets/text_selection.dart index 4d9cb564..99f381fa 100644 --- a/lib/widgets/text_selection.dart +++ b/lib/widgets/text_selection.dart @@ -245,7 +245,6 @@ class EditorTextSelectionOverlay { class _TextSelectionHandleOverlay extends StatefulWidget { const _TextSelectionHandleOverlay({ - Key? key, required this.selection, required this.position, required this.startHandleLayerLink, @@ -255,6 +254,7 @@ class _TextSelectionHandleOverlay extends StatefulWidget { required this.onSelectionHandleTapped, required this.selectionControls, this.dragStartBehavior = DragStartBehavior.start, + Key? key, }) : super(key: key); final TextSelection selection; @@ -468,7 +468,7 @@ class _TextSelectionHandleOverlayState class EditorTextSelectionGestureDetector extends StatefulWidget { const EditorTextSelectionGestureDetector({ - Key? key, + required this.child, this.onTapDown, this.onForcePressStart, this.onForcePressEnd, @@ -482,7 +482,7 @@ class EditorTextSelectionGestureDetector extends StatefulWidget { this.onDragSelectionUpdate, this.onDragSelectionEnd, this.behavior, - required this.child, + Key? key, }) : super(key: key); final GestureTapDownCallback? onTapDown; diff --git a/lib/widgets/toolbar.dart b/lib/widgets/toolbar.dart index 0859cad3..8eeb35ae 100644 --- a/lib/widgets/toolbar.dart +++ b/lib/widgets/toolbar.dart @@ -25,9 +25,9 @@ class InsertEmbedButton extends StatelessWidget { final IconData icon; const InsertEmbedButton({ - Key? key, required this.controller, required this.icon, + Key? key, }) : super(key: key); @override @@ -56,9 +56,9 @@ class LinkStyleButton extends StatefulWidget { final IconData? icon; const LinkStyleButton({ - Key? key, required this.controller, this.icon, + Key? key, }) : super(key: key); @override @@ -183,11 +183,11 @@ class ToggleStyleButton extends StatefulWidget { final ToggleStyleButtonBuilder childBuilder; ToggleStyleButton({ - Key? key, required this.attribute, required this.icon, required this.controller, this.childBuilder = defaultToggleStyleButtonBuilder, + Key? key, }) : super(key: key); @override @@ -267,11 +267,11 @@ class ToggleCheckListButton extends StatefulWidget { final Attribute attribute; ToggleCheckListButton({ - Key? key, required this.icon, required this.controller, - this.childBuilder = defaultToggleStyleButtonBuilder, required this.attribute, + this.childBuilder = defaultToggleStyleButtonBuilder, + Key? key, }) : super(key: key); @override @@ -371,7 +371,7 @@ Widget defaultToggleStyleButtonBuilder( class SelectHeaderStyleButton extends StatefulWidget { final QuillController controller; - const SelectHeaderStyleButton({Key? key, required this.controller}) + const SelectHeaderStyleButton({required this.controller, Key? key}) : super(key: key); @override @@ -494,14 +494,14 @@ class ImageButton extends StatefulWidget { final ImageSource imageSource; - ImageButton( - {Key? key, - required this.icon, - required this.controller, - required this.imageSource, - this.onImagePickCallback, - this.imagePickImpl}) - : super(key: key); + ImageButton({ + required this.icon, + required this.controller, + required this.imageSource, + this.onImagePickCallback, + this.imagePickImpl, + Key? key, + }) : super(key: key); @override _ImageButtonState createState() => _ImageButtonState(); @@ -600,12 +600,12 @@ class ColorButton extends StatefulWidget { final bool background; final QuillController controller; - ColorButton( - {Key? key, - required this.icon, - required this.controller, - required this.background}) - : super(key: key); + ColorButton({ + required this.icon, + required this.controller, + required this.background, + Key? key, + }) : super(key: key); @override _ColorButtonState createState() => _ColorButtonState(); @@ -738,12 +738,12 @@ class HistoryButton extends StatefulWidget { final bool undo; final QuillController controller; - HistoryButton( - {Key? key, - required this.icon, - required this.controller, - required this.undo}) - : super(key: key); + HistoryButton({ + required this.icon, + required this.controller, + required this.undo, + Key? key, + }) : super(key: key); @override _HistoryButtonState createState() => _HistoryButtonState(); @@ -810,12 +810,12 @@ class IndentButton extends StatefulWidget { final QuillController controller; final bool isIncrease; - IndentButton( - {Key? key, - required this.icon, - required this.controller, - required this.isIncrease}) - : super(key: key); + IndentButton({ + required this.icon, + required this.controller, + required this.isIncrease, + Key? key, + }) : super(key: key); @override _IndentButtonState createState() => _IndentButtonState(); @@ -865,7 +865,7 @@ class ClearFormatButton extends StatefulWidget { final QuillController controller; - ClearFormatButton({Key? key, required this.icon, required this.controller}) + ClearFormatButton({required this.icon, required this.controller, Key? key}) : super(key: key); @override @@ -896,30 +896,31 @@ class _ClearFormatButtonState extends State { class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { final List children; - const QuillToolbar({Key? key, required this.children}) : super(key: key); - - factory QuillToolbar.basic( - {Key? key, - required QuillController controller, - double toolbarIconSize = 18.0, - bool showBoldButton = true, - bool showItalicButton = true, - bool showUnderLineButton = true, - bool showStrikeThrough = true, - bool showColorButton = true, - bool showBackgroundColorButton = true, - bool showClearFormat = true, - bool showHeaderStyle = true, - bool showListNumbers = true, - bool showListBullets = true, - bool showListCheck = true, - bool showCodeBlock = true, - bool showQuote = true, - bool showIndent = true, - bool showLink = true, - bool showHistory = true, - bool showHorizontalRule = false, - OnImagePickCallback? onImagePickCallback}) { + const QuillToolbar({required this.children, Key? key}) : super(key: key); + + factory QuillToolbar.basic({ + required QuillController controller, + double toolbarIconSize = 18.0, + bool showBoldButton = true, + bool showItalicButton = true, + bool showUnderLineButton = true, + bool showStrikeThrough = true, + bool showColorButton = true, + bool showBackgroundColorButton = true, + bool showClearFormat = true, + bool showHeaderStyle = true, + bool showListNumbers = true, + bool showListBullets = true, + bool showListCheck = true, + bool showCodeBlock = true, + bool showQuote = true, + bool showIndent = true, + bool showLink = true, + bool showHistory = true, + bool showHorizontalRule = false, + OnImagePickCallback? onImagePickCallback, + Key? key, + }) { iconSize = toolbarIconSize; return QuillToolbar(key: key, children: [ Visibility( @@ -1146,13 +1147,13 @@ class QuillIconButton extends StatelessWidget { final double highlightElevation; const QuillIconButton({ - Key? key, required this.onPressed, this.icon, this.size = 40, this.fillColor, this.hoverElevation = 1, this.highlightElevation = 1, + Key? key, }) : super(key: key); @override @@ -1184,15 +1185,15 @@ class QuillDropdownButton extends StatefulWidget { final ValueChanged onSelected; const QuillDropdownButton({ - Key? key, - this.height = 40, - this.fillColor, - this.hoverElevation = 1, - this.highlightElevation = 1, required this.child, required this.initialValue, required this.items, required this.onSelected, + this.height = 40, + this.fillColor, + this.hoverElevation = 1, + this.highlightElevation = 1, + Key? key, }) : super(key: key); @override From ace4fd4deb929e37128f78252a9d5310dafe7d33 Mon Sep 17 00:00:00 2001 From: Xin Yao Date: Sat, 27 Mar 2021 10:46:52 -0700 Subject: [PATCH 3/4] Fix selection not working --- lib/widgets/toolbar.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/widgets/toolbar.dart b/lib/widgets/toolbar.dart index 8eeb35ae..514946a3 100644 --- a/lib/widgets/toolbar.dart +++ b/lib/widgets/toolbar.dart @@ -1248,6 +1248,7 @@ class _QuillDropdownButtonState extends State> { // if (widget.onCanceled != null) widget.onCanceled(); return null; } + widget.onSelected(newValue); }); } From 1cf37c1824197f4907cf820705de07cf92c77b2d Mon Sep 17 00:00:00 2001 From: Till Friebe Date: Sat, 27 Mar 2021 18:57:40 +0100 Subject: [PATCH 4/4] Rebuild editor when keyboard is already open (#111) * Rebuild editor when keyboard is already open If the keyboard is already open, but the editor thinks that the keyboard is not open, the text will not be updated when writing. This can easily happen if one has a `TabBarView` with two children, each with an `QuillEditor`, see the code for an example:
Example ```dart import 'package:flutter/material.dart'; import 'package:flutter_quill/widgets/controller.dart'; import 'package:flutter_quill/widgets/editor.dart'; void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { late QuillController _controller1; late QuillController _controller2; @override void initState() { _controller1 = QuillController.basic(); _controller2 = QuillController.basic(); super.initState(); } @override Widget build(BuildContext context) { return MaterialApp( home: DefaultTabController( length: 2, child: Scaffold( appBar: AppBar( title: Text('Flutter Quill tabs demo'), bottom: TabBar( tabs: [ Tab(text: 'First'), Tab(text: 'Second'), ], ), ), body: TabBarView( children: [ QuillEditor.basic(controller: _controller1, readOnly: false), QuillEditor.basic(controller: _controller2, readOnly: false), ], ), ), ), ); } }
Video
* Add documentation comment for getOffsetToRevealCursor * Set initial keyboard visibility --- lib/widgets/editor.dart | 5 +++++ lib/widgets/raw_editor.dart | 1 + 2 files changed, 6 insertions(+) diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index ef2221c0..0913ffe0 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -864,6 +864,11 @@ class RenderEditor extends RenderEditableContainerBox ); } + /// Returns the y-offset of the editor at which [selection] is visible. + /// + /// The offset is the distance from the top of the editor and is the minimum + /// from the current scroll position until [selection] becomes visible. + /// Returns null if [selection] is already visible. double? getOffsetToRevealCursor( double viewportHeight, double scrollOffset, double offsetInViewport) { List endpoints = getEndpointsForSelection(selection); diff --git a/lib/widgets/raw_editor.dart b/lib/widgets/raw_editor.dart index 11c9c568..73a17e25 100644 --- a/lib/widgets/raw_editor.dart +++ b/lib/widgets/raw_editor.dart @@ -700,6 +700,7 @@ class RawEditorState extends EditorState _keyboardVisible = true; } else { _keyboardVisibilityController = KeyboardVisibilityController(); + _keyboardVisible = _keyboardVisibilityController!.isVisible; _keyboardVisibilitySubscription = _keyboardVisibilityController?.onChange.listen((bool visible) { _keyboardVisible = visible;