Merge branch 'singerdmx:master' into feature/allowShortcutOverride

pull/2089/head
DeveloperBiz 8 months ago committed by GitHub
commit 4665bfdfb8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 44
      CHANGELOG.md
  2. 2
      CHANGELOG_DATA.json
  3. 15
      README.md
  4. 44
      dart_quill_delta/CHANGELOG.md
  5. 2
      dart_quill_delta/pubspec.yaml
  6. 2
      doc/attribute_introduction.md
  7. 1
      doc/configurations/custom_buttons.md
  8. 6
      doc/custom_embed_blocks.md
  9. 4
      doc/translation.md
  10. 7
      example/lib/screens/quill/my_quill_editor.dart
  11. 6
      example/lib/screens/quill/my_quill_toolbar.dart
  12. 3
      example/lib/screens/quill/quill_screen.dart
  13. 10
      example/lib/screens/simple/simple_screen.dart
  14. 44
      flutter_quill_extensions/CHANGELOG.md
  15. 11
      flutter_quill_extensions/README.md
  16. 2
      flutter_quill_extensions/pubspec.yaml
  17. 44
      flutter_quill_test/CHANGELOG.md
  18. 2
      flutter_quill_test/pubspec.yaml
  19. 2
      lib/src/common/structs/horizontal_spacing.dart
  20. 2
      lib/src/common/structs/vertical_spacing.dart
  21. 4
      lib/src/controller/provider.dart
  22. 97
      lib/src/controller/quill_controller.dart
  23. 13
      lib/src/controller/quill_controller_configurations.dart
  24. 13
      lib/src/delta/delta_diff.dart
  25. 20
      lib/src/document/document.dart
  26. 28
      lib/src/document/nodes/line.dart
  27. 14
      lib/src/editor/config/editor_configurations.dart
  28. 106
      lib/src/editor/editor.dart
  29. 26
      lib/src/editor/provider.dart
  30. 7
      lib/src/editor/raw_editor/config/raw_editor_configurations.dart
  31. 10
      lib/src/editor/raw_editor/raw_editor.dart
  32. 17
      lib/src/editor/raw_editor/raw_editor_state.dart
  33. 9
      lib/src/editor/raw_editor/raw_editor_state_selection_delegate_mixin.dart
  34. 5
      lib/src/editor/raw_editor/raw_editor_state_text_input_client_mixin.dart
  35. 46
      lib/src/editor/widgets/default_styles.dart
  36. 5
      lib/src/toolbar/base_toolbar.dart
  37. 7
      lib/src/toolbar/config/simple_toolbar_configurations.dart
  38. 537
      lib/src/toolbar/simple_toolbar.dart
  39. 2
      pubspec.yaml
  40. 14
      test/bug_fix_test.dart
  41. 190
      test/controller/controller_clipboard_test.dart
  42. 32
      test/controller/controller_test.dart
  43. 54
      test/document/document_test.dart
  44. 15
      test/editor/editor_test.dart

@ -4,6 +4,50 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 10.1.6
* fixed #1295 Double click to select text sometimes doesn't work. by @li8607 in https://github.com/singerdmx/flutter-quill/pull/2086
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.5...v10.1.6
## 10.1.5
* ref: add `VerticalSpacing.zero` and `HorizontalSpacing.zero` named constants by @adil192 in https://github.com/singerdmx/flutter-quill/pull/2083
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.4...v10.1.5
## 10.1.4
* Fix: collectStyles for lists and alignments by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2082
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.3...v10.1.4
## 10.1.3
* Move Controller outside of configurations data class by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2078
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.2...v10.1.3
## 10.1.2
* Fix Multiline paste with attributes and embeds by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2074
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.1...v10.1.2
## 10.1.1
* Toolbar dividers fixes + Docs updates by @troyanskiy in https://github.com/singerdmx/flutter-quill/pull/2071
## New Contributors
* @troyanskiy made their first contribution in https://github.com/singerdmx/flutter-quill/pull/2071
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.0...v10.1.1
## 10.1.0 ## 10.1.0
* Feat: support for customize copy and cut Embeddables to Clipboard by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2067 * Feat: support for customize copy and cut Embeddables to Clipboard by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2067

File diff suppressed because one or more lines are too long

@ -94,8 +94,9 @@ dependencies:
```yaml ```yaml
dependencies: dependencies:
flutter_quill: flutter_quill:
git: https://github.com/singerdmx/flutter-quill.git git:
ref: v<latest-version-here> url: https://github.com/singerdmx/flutter-quill.git
ref: v<latest-version-here>
``` ```
> [!TIP] > [!TIP]
@ -129,16 +130,18 @@ Instantiate a controller:
QuillController _controller = QuillController.basic(); QuillController _controller = QuillController.basic();
``` ```
Use the `QuillEditor`, and `QuillToolbar` widgets, Use the `QuillEditor`, and `QuillSimpleToolbar` widgets,
and attach the `QuillController` to them: and attach the `QuillController` to them:
```dart ```dart
QuillToolbar.simple( QuillSimpleToolbar(
configurations: QuillSimpleToolbarConfigurations(controller: _controller), controller: _controller,
configurations: QuillSimpleToolbarConfigurations(),
), ),
Expanded( Expanded(
child: QuillEditor.basic( child: QuillEditor.basic(
configurations: QuillEditorConfigurations(controller: _controller), controller: _controller,
configurations: const QuillEditorConfigurations(),
), ),
) )
``` ```

@ -4,6 +4,50 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 10.1.6
* fixed #1295 Double click to select text sometimes doesn't work. by @li8607 in https://github.com/singerdmx/flutter-quill/pull/2086
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.5...v10.1.6
## 10.1.5
* ref: add `VerticalSpacing.zero` and `HorizontalSpacing.zero` named constants by @adil192 in https://github.com/singerdmx/flutter-quill/pull/2083
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.4...v10.1.5
## 10.1.4
* Fix: collectStyles for lists and alignments by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2082
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.3...v10.1.4
## 10.1.3
* Move Controller outside of configurations data class by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2078
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.2...v10.1.3
## 10.1.2
* Fix Multiline paste with attributes and embeds by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2074
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.1...v10.1.2
## 10.1.1
* Toolbar dividers fixes + Docs updates by @troyanskiy in https://github.com/singerdmx/flutter-quill/pull/2071
## New Contributors
* @troyanskiy made their first contribution in https://github.com/singerdmx/flutter-quill/pull/2071
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.0...v10.1.1
## 10.1.0 ## 10.1.0
* Feat: support for customize copy and cut Embeddables to Clipboard by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2067 * Feat: support for customize copy and cut Embeddables to Clipboard by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2067

@ -1,6 +1,6 @@
name: dart_quill_delta name: dart_quill_delta
description: A port of quill-js-delta from typescript to dart description: A port of quill-js-delta from typescript to dart
version: 10.1.0 version: 10.1.6
homepage: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/ homepage: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/
repository: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/ repository: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -85,8 +85,8 @@ On `QuillEditor` or `QuillEditorConfigurations` **doesn't exist** a param that l
##### The editor ##### The editor
```dart ```dart
QuillEditor.basic( QuillEditor.basic(
controller: controller,
configurations: QuillEditorConfigurations( configurations: QuillEditorConfigurations(
controller: controller,
customStyleBuilder: (Attribute<dynamic> attribute) { customStyleBuilder: (Attribute<dynamic> attribute) {
if (attribute.key.equals(highlightKey)) { if (attribute.key.equals(highlightKey)) {
return TextStyle(color: Colors.black, backgroundColor: Colors.yellow); return TextStyle(color: Colors.black, backgroundColor: Colors.yellow);

@ -22,6 +22,7 @@ Each `QuillCustomButton` is used as part of the `customButtons` option as follow
```dart ```dart
QuillToolbar.simple( QuillToolbar.simple(
controller: _controller,
configurations: QuillSimpleToolbarConfigurations( configurations: QuillSimpleToolbarConfigurations(
customButtons: [ customButtons: [
QuillToolbarCustomButtonOptions( QuillToolbarCustomButtonOptions(

@ -98,10 +98,8 @@ Future<void> _addEditNote(BuildContext context, {Document? document}) async {
], ],
), ),
content: QuillEditor.basic( content: QuillEditor.basic(
configurations: const QuillEditorConfigurations( controller: quillEditorController,
controller: quillEditorController, configurations: const QuillEditorConfigurations(),
readOnly: false,
),
), ),
), ),
); );

@ -6,8 +6,8 @@ with:
```dart ```dart
QuillToolbar.simple( QuillToolbar.simple(
controller: _controller,
configurations: QuillSimpleToolbarConfigurations( configurations: QuillSimpleToolbarConfigurations(
controller: _controller,
sharedConfigurations: const QuillSharedConfigurations( sharedConfigurations: const QuillSharedConfigurations(
locale: Locale('de'), locale: Locale('de'),
), ),
@ -15,8 +15,8 @@ QuillToolbar.simple(
), ),
Expanded( Expanded(
child: QuillEditor.basic( child: QuillEditor.basic(
controller: _controller,
configurations: QuillEditorConfigurations( configurations: QuillEditorConfigurations(
controller: _controller,
sharedConfigurations: const QuillSharedConfigurations( sharedConfigurations: const QuillSharedConfigurations(
locale: Locale('de'), locale: Locale('de'),
), ),

@ -18,12 +18,14 @@ import 'embeds/timestamp_embed.dart';
class MyQuillEditor extends StatelessWidget { class MyQuillEditor extends StatelessWidget {
const MyQuillEditor({ const MyQuillEditor({
required this.controller,
required this.configurations, required this.configurations,
required this.scrollController, required this.scrollController,
required this.focusNode, required this.focusNode,
super.key, super.key,
}); });
final QuillController controller;
final QuillEditorConfigurations configurations; final QuillEditorConfigurations configurations;
final ScrollController scrollController; final ScrollController scrollController;
final FocusNode focusNode; final FocusNode focusNode;
@ -34,6 +36,7 @@ class MyQuillEditor extends StatelessWidget {
return QuillEditor( return QuillEditor(
scrollController: scrollController, scrollController: scrollController,
focusNode: focusNode, focusNode: focusNode,
controller: controller,
configurations: configurations.copyWith( configurations: configurations.copyWith(
elementOptions: const QuillEditorElementOptions( elementOptions: const QuillEditorElementOptions(
codeBlock: QuillEditorCodeBlockElementOptions( codeBlock: QuillEditorCodeBlockElementOptions(
@ -51,9 +54,9 @@ class MyQuillEditor extends StatelessWidget {
height: 1.15, height: 1.15,
fontWeight: FontWeight.w300, fontWeight: FontWeight.w300,
), ),
const HorizontalSpacing(0, 0), HorizontalSpacing.zero,
const VerticalSpacing(16, 0), const VerticalSpacing(16, 0),
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
sizeSmall: defaultTextStyle.style.copyWith(fontSize: 9), sizeSmall: defaultTextStyle.style.copyWith(fontSize: 9),

@ -203,8 +203,12 @@ class MyQuillToolbar extends StatelessWidget {
); );
} }
return QuillToolbar.simple( return QuillToolbar.simple(
controller: controller,
/// configurations parameter:
/// Optional: if not provided will use the configuration set when the controller was instantiated.
/// Override: Provide parameter here to override the default configuration - useful if configuration will change.
configurations: QuillSimpleToolbarConfigurations( configurations: QuillSimpleToolbarConfigurations(
controller: controller,
showAlignmentButtons: true, showAlignmentButtons: true,
multiRowsDisplay: true, multiRowsDisplay: true,
fontFamilyValues: { fontFamilyValues: {

@ -33,6 +33,7 @@ class QuillScreen extends StatefulWidget {
} }
class _QuillScreenState extends State<QuillScreen> { class _QuillScreenState extends State<QuillScreen> {
/// Instantiate the controller
final _controller = QuillController.basic(); final _controller = QuillController.basic();
final _editorFocusNode = FocusNode(); final _editorFocusNode = FocusNode();
final _editorScrollController = ScrollController(); final _editorScrollController = ScrollController();
@ -101,9 +102,9 @@ class _QuillScreenState extends State<QuillScreen> {
builder: (context) { builder: (context) {
return Expanded( return Expanded(
child: MyQuillEditor( child: MyQuillEditor(
controller: _controller,
configurations: QuillEditorConfigurations( configurations: QuillEditorConfigurations(
sharedConfigurations: _sharedConfigurations, sharedConfigurations: _sharedConfigurations,
controller: _controller,
), ),
scrollController: _editorScrollController, scrollController: _editorScrollController,
focusNode: _editorFocusNode, focusNode: _editorFocusNode,

@ -18,14 +18,14 @@ class _SimpleScreenState extends State<SimpleScreen> {
body: Column( body: Column(
children: [ children: [
QuillToolbar.simple( QuillToolbar.simple(
configurations: controller: _controller,
QuillSimpleToolbarConfigurations(controller: _controller), configurations: const QuillSimpleToolbarConfigurations(),
), ),
Expanded( Expanded(
child: QuillEditor.basic( child: QuillEditor.basic(
configurations: QuillEditorConfigurations( controller: _controller,
controller: _controller, configurations: const QuillEditorConfigurations(
padding: const EdgeInsets.all(16), padding: EdgeInsets.all(16),
), ),
), ),
), ),

@ -4,6 +4,50 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 10.1.6
* fixed #1295 Double click to select text sometimes doesn't work. by @li8607 in https://github.com/singerdmx/flutter-quill/pull/2086
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.5...v10.1.6
## 10.1.5
* ref: add `VerticalSpacing.zero` and `HorizontalSpacing.zero` named constants by @adil192 in https://github.com/singerdmx/flutter-quill/pull/2083
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.4...v10.1.5
## 10.1.4
* Fix: collectStyles for lists and alignments by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2082
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.3...v10.1.4
## 10.1.3
* Move Controller outside of configurations data class by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2078
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.2...v10.1.3
## 10.1.2
* Fix Multiline paste with attributes and embeds by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2074
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.1...v10.1.2
## 10.1.1
* Toolbar dividers fixes + Docs updates by @troyanskiy in https://github.com/singerdmx/flutter-quill/pull/2071
## New Contributors
* @troyanskiy made their first contribution in https://github.com/singerdmx/flutter-quill/pull/2071
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.0...v10.1.1
## 10.1.0 ## 10.1.0
* Feat: support for customize copy and cut Embeddables to Clipboard by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2067 * Feat: support for customize copy and cut Embeddables to Clipboard by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2067

@ -50,9 +50,10 @@ dependencies:
```yaml ```yaml
dependencies: dependencies:
flutter_quill_extensions: flutter_quill_extensions:
git: https://github.com/singerdmx/flutter-quill.git git:
path: flutter_quill_extensions url: https://github.com/singerdmx/flutter-quill.git
ref: v<latest-version-here> ref: v<latest-version-here>
path: flutter_quill_extensions
``` ```
## 🛠 Platform Specific Configurations ## 🛠 Platform Specific Configurations
@ -103,8 +104,8 @@ Set the `embedBuilders` and `embedToolbar` params in configurations of `QuillEdi
**Quill Toolbar**: **Quill Toolbar**:
```dart ```dart
QuillToolbar( QuillToolbar.simple(
configurations: QuillToolbarConfigurations( configurations: QuillSimpleToolbarConfigurations(
embedButtons: FlutterQuillEmbeds.toolbarButtons(), embedButtons: FlutterQuillEmbeds.toolbarButtons(),
), ),
), ),

@ -1,6 +1,6 @@
name: flutter_quill_extensions name: flutter_quill_extensions
description: Embed extensions for flutter_quill including image, video, formula and etc. description: Embed extensions for flutter_quill including image, video, formula and etc.
version: 10.1.0 version: 10.1.6
homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/ homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/
repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/ repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -4,6 +4,50 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 10.1.6
* fixed #1295 Double click to select text sometimes doesn't work. by @li8607 in https://github.com/singerdmx/flutter-quill/pull/2086
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.5...v10.1.6
## 10.1.5
* ref: add `VerticalSpacing.zero` and `HorizontalSpacing.zero` named constants by @adil192 in https://github.com/singerdmx/flutter-quill/pull/2083
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.4...v10.1.5
## 10.1.4
* Fix: collectStyles for lists and alignments by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2082
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.3...v10.1.4
## 10.1.3
* Move Controller outside of configurations data class by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2078
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.2...v10.1.3
## 10.1.2
* Fix Multiline paste with attributes and embeds by @AtlasAutocode in https://github.com/singerdmx/flutter-quill/pull/2074
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.1...v10.1.2
## 10.1.1
* Toolbar dividers fixes + Docs updates by @troyanskiy in https://github.com/singerdmx/flutter-quill/pull/2071
## New Contributors
* @troyanskiy made their first contribution in https://github.com/singerdmx/flutter-quill/pull/2071
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v10.1.0...v10.1.1
## 10.1.0 ## 10.1.0
* Feat: support for customize copy and cut Embeddables to Clipboard by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2067 * Feat: support for customize copy and cut Embeddables to Clipboard by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/2067

@ -1,6 +1,6 @@
name: flutter_quill_test name: flutter_quill_test
description: Test utilities for flutter_quill which includes methods to simplify interacting with the editor in test cases. description: Test utilities for flutter_quill which includes methods to simplify interacting with the editor in test cases.
version: 10.1.0 version: 10.1.6
homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/ homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/
repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/ repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -9,4 +9,6 @@ class HorizontalSpacing {
final double left; final double left;
final double right; final double right;
static const zero = HorizontalSpacing(0, 0);
} }

@ -9,4 +9,6 @@ class VerticalSpacing {
final double top; final double top;
final double bottom; final double bottom;
static const zero = VerticalSpacing(0, 0);
} }

@ -6,12 +6,16 @@ import 'quill_controller.dart';
extension QuillControllerExt on BuildContext { extension QuillControllerExt on BuildContext {
QuillController? get quilController { QuillController? get quilController {
// ignore: deprecated_member_use_from_same_package
return quillSimpleToolbarConfigurations?.controller ?? return quillSimpleToolbarConfigurations?.controller ??
// ignore: deprecated_member_use_from_same_package
quillEditorConfigurations?.controller; quillEditorConfigurations?.controller;
} }
QuillController get requireQuillController { QuillController get requireQuillController {
// ignore: deprecated_member_use_from_same_package
return quillSimpleToolbarConfigurations?.controller ?? return quillSimpleToolbarConfigurations?.controller ??
// ignore: deprecated_member_use_from_same_package
quillEditorConfigurations?.controller ?? quillEditorConfigurations?.controller ??
(throw ArgumentError( (throw ArgumentError(
'The quill provider is required, you must only call requireQuillController inside the QuillToolbar and QuillEditor')); 'The quill provider is required, you must only call requireQuillController inside the QuillToolbar and QuillEditor'));

@ -17,7 +17,9 @@ import '../document/nodes/embeddable.dart';
import '../document/nodes/leaf.dart'; import '../document/nodes/leaf.dart';
import '../document/structs/doc_change.dart'; import '../document/structs/doc_change.dart';
import '../document/style.dart'; import '../document/style.dart';
import '../editor/config/editor_configurations.dart';
import '../editor_toolbar_controller_shared/clipboard/clipboard_service_provider.dart'; import '../editor_toolbar_controller_shared/clipboard/clipboard_service_provider.dart';
import '../toolbar/config/simple_toolbar_configurations.dart';
import 'quill_controller_configurations.dart'; import 'quill_controller_configurations.dart';
typedef ReplaceTextCallback = bool Function(int index, int len, Object? data); typedef ReplaceTextCallback = bool Function(int index, int len, Object? data);
@ -39,19 +41,36 @@ class QuillController extends ChangeNotifier {
_selection = selection; _selection = selection;
factory QuillController.basic( factory QuillController.basic(
{QuillControllerConfigurations configurations = {QuillControllerConfigurations configurations =
const QuillControllerConfigurations(), const QuillControllerConfigurations(),
FocusNode? editorFocusNode}) { FocusNode? editorFocusNode}) =>
return QuillController( QuillController(
configurations: configurations, configurations: configurations,
editorFocusNode: editorFocusNode, editorFocusNode: editorFocusNode,
document: Document(), document: Document(),
selection: const TextSelection.collapsed(offset: 0), selection: const TextSelection.collapsed(offset: 0),
); );
}
final QuillControllerConfigurations configurations; final QuillControllerConfigurations configurations;
/// Editor configurations
///
/// Caches configuration set in QuillEditor ctor.
QuillEditorConfigurations? _editorConfigurations;
QuillEditorConfigurations get editorConfigurations =>
_editorConfigurations ?? const QuillEditorConfigurations();
set editorConfigurations(QuillEditorConfigurations? value) =>
_editorConfigurations = value;
/// Toolbar configurations
///
/// Caches configuration set in QuillSimpleToolbar ctor.
QuillSimpleToolbarConfigurations? _toolbarConfigurations;
QuillSimpleToolbarConfigurations get toolbarConfigurations =>
_toolbarConfigurations ?? const QuillSimpleToolbarConfigurations();
set toolbarConfigurations(QuillSimpleToolbarConfigurations? value) =>
_toolbarConfigurations = value;
/// Document managed by this controller. /// Document managed by this controller.
Document _document; Document _document;
@ -476,10 +495,13 @@ class QuillController extends ChangeNotifier {
/// Clipboard caches last copy to allow paste with styles. Static to allow paste between multiple instances of editor. /// Clipboard caches last copy to allow paste with styles. Static to allow paste between multiple instances of editor.
static String _pastePlainText = ''; static String _pastePlainText = '';
static Delta _pasteDelta = Delta();
static List<OffsetValue> _pasteStyleAndEmbed = <OffsetValue>[]; static List<OffsetValue> _pasteStyleAndEmbed = <OffsetValue>[];
String get pastePlainText => _pastePlainText; String get pastePlainText => _pastePlainText;
Delta get pasteDelta => _pasteDelta;
List<OffsetValue> get pasteStyleAndEmbed => _pasteStyleAndEmbed; List<OffsetValue> get pasteStyleAndEmbed => _pasteStyleAndEmbed;
bool readOnly; bool readOnly;
/// Used to give focus to the editor following a toolbar action /// Used to give focus to the editor following a toolbar action
@ -495,9 +517,17 @@ class QuillController extends ChangeNotifier {
bool clipboardSelection(bool copy) { bool clipboardSelection(bool copy) {
copiedImageUrl = null; copiedImageUrl = null;
_pastePlainText = getPlainText();
/// Get the text for the selected region and expand the content of Embedded objects.
_pastePlainText = document.getPlainText(
selection.start, selection.end - selection.start, editorConfigurations);
/// Get the internal representation so it can be pasted into a QuillEditor with style retained.
_pasteStyleAndEmbed = getAllIndividualSelectionStylesAndEmbed(); _pasteStyleAndEmbed = getAllIndividualSelectionStylesAndEmbed();
/// Get the deltas for the selection so they can be pasted into a QuillEditor with styles and embeds retained.
_pasteDelta = document.toDelta().slice(selection.start, selection.end);
if (!selection.isCollapsed) { if (!selection.isCollapsed) {
Clipboard.setData(ClipboardData(text: _pastePlainText)); Clipboard.setData(ClipboardData(text: _pastePlainText));
if (!copy) { if (!copy) {
@ -538,28 +568,7 @@ class QuillController extends ChangeNotifier {
// See https://github.com/flutter/flutter/issues/11427 // See https://github.com/flutter/flutter/issues/11427
final plainTextClipboardData = final plainTextClipboardData =
await Clipboard.getData(Clipboard.kTextPlain); await Clipboard.getData(Clipboard.kTextPlain);
if (plainTextClipboardData != null) { if (pasteUsingPlainOrDelta(plainTextClipboardData?.text)) {
final lines = plainTextClipboardData.text!.split('\n');
for (var i = 0; i < lines.length; ++i) {
final line = lines[i];
if (line.isNotEmpty) {
replaceTextWithEmbeds(
selection.start,
selection.end - selection.start,
line,
TextSelection.collapsed(offset: selection.start + line.length),
);
}
if (i != lines.length - 1) {
document.insert(selection.extentOffset, '\n');
_updateSelection(
TextSelection.collapsed(
offset: selection.extentOffset + 1,
),
insertNewline: true,
);
}
}
updateEditor?.call(); updateEditor?.call();
return true; return true;
} }
@ -572,6 +581,28 @@ class QuillController extends ChangeNotifier {
return false; return false;
} }
/// Internal method to allow unit testing
bool pasteUsingPlainOrDelta(String? clipboardText) {
if (clipboardText != null) {
/// Internal copy-paste preserves styles and embeds
if (clipboardText == _pastePlainText &&
_pastePlainText.isNotEmpty &&
_pasteDelta.isNotEmpty) {
replaceText(selection.start, selection.end - selection.start,
_pasteDelta, TextSelection.collapsed(offset: selection.end));
} else {
replaceText(
selection.start,
selection.end - selection.start,
clipboardText,
TextSelection.collapsed(
offset: selection.end + clipboardText.length));
}
return true;
}
return false;
}
void _pasteUsingDelta(Delta deltaFromClipboard) { void _pasteUsingDelta(Delta deltaFromClipboard) {
replaceText( replaceText(
selection.start, selection.start,

@ -1,6 +1,17 @@
import '../editor/config/editor_configurations.dart'
show QuillEditorConfigurations;
class QuillControllerConfigurations { class QuillControllerConfigurations {
const QuillControllerConfigurations( const QuillControllerConfigurations(
{this.onClipboardPaste, this.requireScriptFontFeatures = false}); {@Deprecated(
'This parameter is not used and will be removed in future versions.')
this.editorConfigurations,
this.onClipboardPaste,
this.requireScriptFontFeatures = false});
@Deprecated(
'This parameter is not used and will be removed in future versions.')
final QuillEditorConfigurations? editorConfigurations;
/// Callback when the user pastes and data has not already been processed /// Callback when the user pastes and data has not already been processed
/// ///

@ -70,19 +70,18 @@ int getPositionDelta(Delta user, Delta actual) {
); );
} }
if (userOperation.key == actualOperation.key) { if (userOperation.key == actualOperation.key) {
/// Insertions must update diff allowing for type mismatch of Operation
if (userOperation.key == Operation.insertKey) {
if (userOperation.data is Delta && actualOperation.data is String) {
diff += actualOperation.length!;
}
}
continue; continue;
} else if (userOperation.isInsert && actualOperation.isRetain) { } else if (userOperation.isInsert && actualOperation.isRetain) {
diff -= userOperation.length!; diff -= userOperation.length!;
} else if (userOperation.isDelete && actualOperation.isRetain) { } else if (userOperation.isDelete && actualOperation.isRetain) {
diff += userOperation.length!; diff += userOperation.length!;
} else if (userOperation.isRetain && actualOperation.isInsert) { } else if (userOperation.isRetain && actualOperation.isInsert) {
String? operationTxt = '';
if (actualOperation.data is String) {
operationTxt = actualOperation.data as String?;
}
if (operationTxt!.startsWith('\n')) {
continue;
}
diff += actualOperation.length!; diff += actualOperation.length!;
} }
} }

@ -6,6 +6,7 @@ import '../../quill_delta.dart';
import '../common/structs/offset_value.dart'; import '../common/structs/offset_value.dart';
import '../common/structs/segment_leaf_node.dart'; import '../common/structs/segment_leaf_node.dart';
import '../delta/delta_x.dart'; import '../delta/delta_x.dart';
import '../editor/config/editor_configurations.dart';
import '../editor/embed/embed_editor_builder.dart'; import '../editor/embed/embed_editor_builder.dart';
import '../rules/rule.dart'; import '../rules/rule.dart';
import 'attribute.dart'; import 'attribute.dart';
@ -191,17 +192,28 @@ class Document {
res = queryChild(--index); res = queryChild(--index);
} }
// //
final style = (res.node as Line).collectStyle(res.offset, 0); var style = (res.node as Line).collectStyle(res.offset, 0);
final remove = <Attribute>{}; final remove = <Attribute>{};
final add = <String, Attribute>{};
for (final attr in style.attributes.values) { for (final attr in style.attributes.values) {
if (!Attribute.inlineKeys.contains(attr.key)) { if (!Attribute.inlineKeys.contains(attr.key)) {
if (!current.containsKey(attr.key)) { if (!current.containsKey(attr.key)) {
remove.add(attr); remove.add(attr);
} else {
/// Trap for type of block attribute is changing
final curAttr = current.attributes[attr.key];
if (curAttr!.value != attr.value) {
remove.add(attr);
add[curAttr.key] = curAttr;
}
} }
} }
} }
if (remove.isNotEmpty) { if (remove.isNotEmpty) {
return style.removeAll(remove); style = style.removeAll(remove);
}
if (add.isNotEmpty) {
style.attributes.addAll(add);
} }
return style; return style;
} }
@ -239,9 +251,9 @@ class Document {
} }
/// Returns plain text within the specified text range. /// Returns plain text within the specified text range.
String getPlainText(int index, int len) { String getPlainText(int index, int len, [QuillEditorConfigurations? config]) {
final res = queryChild(index); final res = queryChild(index);
return (res.node as Line).getPlainText(res.offset, len); return (res.node as Line).getPlainText(res.offset, len, config);
} }
/// Returns [Line] located at specified character [offset]. /// Returns [Line] located at specified character [offset].

@ -4,6 +4,7 @@ import 'package:collection/collection.dart';
import '../../../../quill_delta.dart'; import '../../../../quill_delta.dart';
import '../../common/structs/offset_value.dart'; import '../../common/structs/offset_value.dart';
import '../../editor/config/editor_configurations.dart';
import '../../editor/embed/embed_editor_builder.dart'; import '../../editor/embed/embed_editor_builder.dart';
import '../../editor_toolbar_controller_shared/copy_cut_service/copy_cut_service_provider.dart'; import '../../editor_toolbar_controller_shared/copy_cut_service/copy_cut_service_provider.dart';
import '../attribute.dart'; import '../attribute.dart';
@ -512,14 +513,17 @@ base class Line extends QuillContainer<Leaf?> {
} }
/// Returns plain text within the specified text range. /// Returns plain text within the specified text range.
String getPlainText(int offset, int len) { String getPlainText(int offset, int len,
[QuillEditorConfigurations? config]) {
final plainText = StringBuffer(); final plainText = StringBuffer();
_getPlainText(offset, len, plainText); _getPlainText(offset, len, plainText, config);
return plainText.toString(); return plainText.toString();
} }
int _getNodeText(Leaf node, StringBuffer buffer, int offset, int remaining) { int _getNodeText(Leaf node, StringBuffer buffer, int offset, int remaining,
final text = node.toPlainText(); QuillEditorConfigurations? config) {
final text =
node.toPlainText(config?.embedBuilders, config?.unknownEmbedBuilder);
if (text == Embed.kObjectReplacementCharacter) { if (text == Embed.kObjectReplacementCharacter) {
final embed = node.value as Embeddable; final embed = node.value as Embeddable;
final provider = CopyCutServiceProvider.instance; final provider = CopyCutServiceProvider.instance;
@ -539,12 +543,19 @@ base class Line extends QuillContainer<Leaf?> {
return remaining - node.length; return remaining - node.length;
} }
/// Text for clipboard will expand the content of Embed nodes
if (node is Embed && config != null) {
buffer.write(text);
return remaining - 1;
}
final end = math.min(offset + remaining, text.length); final end = math.min(offset + remaining, text.length);
buffer.write(text.substring(offset, end)); buffer.write(text.substring(offset, end));
return remaining - (end - offset); return remaining - (end - offset);
} }
int _getPlainText(int offset, int len, StringBuffer plainText) { int _getPlainText(int offset, int len, StringBuffer plainText,
QuillEditorConfigurations? config) {
var len0 = len; var len0 = len;
final data = queryChild(offset, false); final data = queryChild(offset, false);
var node = data.node as Leaf?; var node = data.node as Leaf?;
@ -555,11 +566,12 @@ base class Line extends QuillContainer<Leaf?> {
plainText.write('\n'); plainText.write('\n');
len0 -= 1; len0 -= 1;
} else { } else {
len0 = _getNodeText(node, plainText, offset - node.offset, len0); len0 =
_getNodeText(node, plainText, offset - node.offset, len0, config);
while (!node!.isLast && len0 > 0) { while (!node!.isLast && len0 > 0) {
node = node.next as Leaf; node = node.next as Leaf;
len0 = _getNodeText(node, plainText, 0, len0); len0 = _getNodeText(node, plainText, 0, len0, config);
} }
if (len0 > 0) { if (len0 > 0) {
@ -570,7 +582,7 @@ base class Line extends QuillContainer<Leaf?> {
} }
if (len0 > 0 && nextLine != null) { if (len0 > 0 && nextLine != null) {
len0 = nextLine!._getPlainText(0, len0, plainText); len0 = nextLine!._getPlainText(0, len0, plainText, config);
} }
} }

@ -24,7 +24,9 @@ class QuillEditorConfigurations extends Equatable {
/// Important note for the maintainers /// Important note for the maintainers
/// When editing this class please update the [copyWith] function too. /// When editing this class please update the [copyWith] function too.
const QuillEditorConfigurations({ const QuillEditorConfigurations({
required this.controller, @Deprecated(
'controller should be passed directly to the editor - this parameter will be removed in future versions.')
this.controller,
this.sharedConfigurations = const QuillSharedConfigurations(), this.sharedConfigurations = const QuillSharedConfigurations(),
this.scrollable = true, this.scrollable = true,
this.padding = EdgeInsets.zero, this.padding = EdgeInsets.zero,
@ -86,7 +88,8 @@ class QuillEditorConfigurations extends Equatable {
final QuillSharedConfigurations sharedConfigurations; final QuillSharedConfigurations sharedConfigurations;
final QuillController controller; @Deprecated('controller will be removed in future versions.')
final QuillController? controller;
/// The text placeholder in the quill editor /// The text placeholder in the quill editor
final String? placeholder; final String? placeholder;
@ -97,7 +100,8 @@ class QuillEditorConfigurations extends Equatable {
/// by any shortcut or keyboard operation. The text is still selectable. /// by any shortcut or keyboard operation. The text is still selectable.
/// ///
/// Defaults to `false`. Must not be `null`. /// Defaults to `false`. Must not be `null`.
bool get readOnly => controller.readOnly; // ignore: deprecated_member_use_from_same_package
bool get readOnly => controller?.readOnly != false;
/// Override [readOnly] for checkbox. /// Override [readOnly] for checkbox.
/// ///
@ -381,7 +385,8 @@ class QuillEditorConfigurations extends Equatable {
@override @override
List<Object?> get props => [ List<Object?> get props => [
placeholder, placeholder,
controller.readOnly, // ignore: deprecated_member_use_from_same_package
controller?.readOnly,
]; ];
// We might use code generator like freezed but sometimes it can be limited // We might use code generator like freezed but sometimes it can be limited
@ -445,6 +450,7 @@ class QuillEditorConfigurations extends Equatable {
}) { }) {
return QuillEditorConfigurations( return QuillEditorConfigurations(
sharedConfigurations: sharedConfigurations ?? this.sharedConfigurations, sharedConfigurations: sharedConfigurations ?? this.sharedConfigurations,
// ignore: deprecated_member_use_from_same_package
controller: controller ?? this.controller, controller: controller ?? this.controller,
placeholder: placeholder ?? this.placeholder, placeholder: placeholder ?? this.placeholder,
checkBoxReadOnly: checkBoxReadOnly ?? this.checkBoxReadOnly, checkBoxReadOnly: checkBoxReadOnly ?? this.checkBoxReadOnly,

@ -10,6 +10,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../common/utils/platform.dart'; import '../common/utils/platform.dart';
import '../controller/quill_controller.dart';
import '../document/attribute.dart'; import '../document/attribute.dart';
import '../document/document.dart'; import '../document/document.dart';
import '../document/nodes/container.dart' as container_node; import '../document/nodes/container.dart' as container_node;
@ -120,36 +121,80 @@ abstract class RenderAbstractEditor implements TextLayoutMetrics {
} }
class QuillEditor extends StatefulWidget { class QuillEditor extends StatefulWidget {
const QuillEditor({ /// Quick start guide:
required this.configurations, ///
required this.focusNode, /// Instantiate a controller:
required this.scrollController, /// QuillController _controller = QuillController.basic();
super.key, ///
}); /// Connect the controller to the `QuillEditor` and `QuillSimpleToolbar` widgets.
/// QuillSimpleToolbar(
/// controller: _controller,
/// configurations: const QuillSimpleToolbarConfigurations(),
/// ),
/// Expanded(
/// child: QuillEditor.basic(
/// controller: _controller,
/// configurations: const QuillEditorConfigurations(),
/// ),
/// ),
///
factory QuillEditor({
required FocusNode focusNode,
required ScrollController scrollController,
/// Controller and configurations are required
///
/// Prefer: use controller and pass QuillEditorConfigurations in constructor for controller (using QuillControllerConfigurations).
/// Backward compatibility: use configurations and pass QuillController in constructor for configurations. (Will be removed in future versions.)
QuillController? controller,
QuillEditorConfigurations? configurations,
}) {
// ignore: deprecated_member_use_from_same_package
controller ??= configurations?.controller;
assert(controller != null,
'controller required. Provide controller directly (preferred) or indirectly through configurations (not recommended - will be removed in future versions).');
controller ??= QuillController(
document: Document(),
selection: const TextSelection.collapsed(offset: 0));
//
controller
..editorConfigurations = configurations
..editorFocusNode = focusNode;
//
return QuillEditor._(
focusNode: focusNode,
scrollController: scrollController,
controller: controller);
}
const QuillEditor._(
{required this.focusNode,
required this.scrollController,
required this.controller});
factory QuillEditor.basic({ factory QuillEditor.basic({
/// The controller for the quill editor widget of flutter quill
QuillController? controller,
/// The configurations for the quill editor widget of flutter quill /// The configurations for the quill editor widget of flutter quill
required QuillEditorConfigurations configurations, QuillEditorConfigurations? configurations,
FocusNode? focusNode, FocusNode? focusNode,
ScrollController? scrollController, ScrollController? scrollController,
}) { }) {
return QuillEditor( return QuillEditor(
scrollController: scrollController ?? ScrollController(), scrollController: scrollController ?? ScrollController(),
focusNode: focusNode ?? FocusNode(), focusNode: focusNode ?? FocusNode(),
configurations: configurations.copyWith( controller: controller,
textSelectionThemeData: configurations.textSelectionThemeData, configurations: configurations?.copyWith(),
autoFocus: configurations.autoFocus,
expands: configurations.expands,
padding: configurations.padding,
keyboardAppearance: configurations.keyboardAppearance,
embedBuilders: configurations.embedBuilders,
editorKey: configurations.editorKey,
),
); );
} }
/// The controller for the quill editor widget of flutter quill
final QuillController controller;
/// The configurations for the quill editor widget of flutter quill /// The configurations for the quill editor widget of flutter quill
final QuillEditorConfigurations configurations; QuillEditorConfigurations get configurations =>
controller.editorConfigurations;
/// Controls whether this editor has keyboard focus. /// Controls whether this editor has keyboard focus.
final FocusNode focusNode; final FocusNode focusNode;
@ -167,14 +212,13 @@ class QuillEditorState extends State<QuillEditor>
late EditorTextSelectionGestureDetectorBuilder late EditorTextSelectionGestureDetectorBuilder
_selectionGestureDetectorBuilder; _selectionGestureDetectorBuilder;
QuillEditorConfigurations get configurations { QuillController get controller => widget.controller;
return widget.configurations;
} QuillEditorConfigurations get configurations => widget.configurations;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_editorKey = configurations.editorKey ?? GlobalKey<EditorState>(); _editorKey = configurations.editorKey ?? GlobalKey<EditorState>();
_selectionGestureDetectorBuilder = _selectionGestureDetectorBuilder =
_QuillEditorSelectionGestureDetectorBuilder( _QuillEditorSelectionGestureDetectorBuilder(
@ -182,9 +226,7 @@ class QuillEditorState extends State<QuillEditor>
configurations.detectWordBoundary, configurations.detectWordBoundary,
); );
final focusNode = final focusNode = widget.focusNode;
widget.configurations.controller.editorFocusNode ?? widget.focusNode;
widget.configurations.controller.editorFocusNode = focusNode;
if (configurations.autoFocus) { if (configurations.autoFocus) {
focusNode.requestFocus(); focusNode.requestFocus();
@ -240,13 +282,13 @@ class QuillEditorState extends State<QuillEditor>
final child = FlutterQuillLocalizationsWidget( final child = FlutterQuillLocalizationsWidget(
child: QuillEditorProvider( child: QuillEditorProvider(
editorConfigurations: configurations, controller: controller,
child: QuillEditorBuilderWidget( child: QuillEditorBuilderWidget(
builder: configurations.builder, builder: configurations.builder,
child: QuillRawEditor( child: QuillRawEditor(
key: _editorKey, key: _editorKey,
controller: controller,
configurations: QuillRawEditorConfigurations( configurations: QuillRawEditorConfigurations(
controller: configurations.controller,
focusNode: widget.focusNode, focusNode: widget.focusNode,
scrollController: widget.scrollController, scrollController: widget.scrollController,
scrollable: configurations.scrollable, scrollable: configurations.scrollable,
@ -254,7 +296,7 @@ class QuillEditorState extends State<QuillEditor>
configurations.enableMarkdownStyleConversion, configurations.enableMarkdownStyleConversion,
scrollBottomInset: configurations.scrollBottomInset, scrollBottomInset: configurations.scrollBottomInset,
padding: configurations.padding, padding: configurations.padding,
readOnly: configurations.readOnly, readOnly: controller.readOnly,
checkBoxReadOnly: configurations.checkBoxReadOnly, checkBoxReadOnly: configurations.checkBoxReadOnly,
disableClipboard: configurations.disableClipboard, disableClipboard: configurations.disableClipboard,
placeholder: configurations.placeholder, placeholder: configurations.placeholder,
@ -444,19 +486,19 @@ class _QuillEditorSelectionGestureDetectorBuilder
} }
bool _isPositionSelected(TapUpDetails details) { bool _isPositionSelected(TapUpDetails details) {
if (_state.configurations.controller.document.isEmpty()) { if (_state.controller.document.isEmpty()) {
return false; return false;
} }
final pos = renderEditor!.getPositionForOffset(details.globalPosition); final pos = renderEditor!.getPositionForOffset(details.globalPosition);
final result = editor!.widget.configurations.controller.document final result =
.querySegmentLeafNode(pos.offset); editor!.widget.controller.document.querySegmentLeafNode(pos.offset);
final line = result.line; final line = result.line;
if (line == null) { if (line == null) {
return false; return false;
} }
final segmentLeaf = result.leaf; final segmentLeaf = result.leaf;
if (segmentLeaf == null && line.length == 1) { if (segmentLeaf == null && line.length == 1) {
editor!.widget.configurations.controller.updateSelection( editor!.widget.controller.updateSelection(
TextSelection.collapsed(offset: pos.offset), TextSelection.collapsed(offset: pos.offset),
ChangeSource.local, ChangeSource.local,
); );
@ -1060,7 +1102,7 @@ class RenderEditor extends RenderEditableContainerBox
TextSelection selectWordAtPosition(TextPosition position) { TextSelection selectWordAtPosition(TextPosition position) {
final word = getWordBoundary(position); final word = getWordBoundary(position);
// When long-pressing past the end of the text, we want a collapsed cursor. // When long-pressing past the end of the text, we want a collapsed cursor.
if (position.offset >= word.end) { if (position.offset > word.end) {
return TextSelection.fromPosition(position); return TextSelection.fromPosition(position);
} }
return TextSelection(baseOffset: word.start, extentOffset: word.end); return TextSelection(baseOffset: word.start, extentOffset: word.end);

@ -2,15 +2,32 @@ import 'package:flutter/foundation.dart' show debugPrint, kDebugMode;
import 'package:flutter/widgets.dart' import 'package:flutter/widgets.dart'
show BuildContext, InheritedWidget, Widget; show BuildContext, InheritedWidget, Widget;
import '../controller/quill_controller.dart';
import 'config/editor_configurations.dart'; import 'config/editor_configurations.dart';
class QuillEditorProvider extends InheritedWidget { class QuillEditorProvider extends InheritedWidget {
const QuillEditorProvider({ QuillEditorProvider({
required super.child, required super.child,
required this.editorConfigurations,
/// Controller and configurations are required but should only be provided from one.
///
/// Passing the controller as part of configurations is being deprecated and will be removed in the future.
/// Prefer: use controller and set QuillEditorConfigurations in the controller.
/// Current: use configurations and pass QuillController in constructor for configurations.
QuillController? controller,
@Deprecated(
'editorConfigurations are no longer needed and will be removed in future versions. Set configurations in the controller')
QuillEditorConfigurations? editorConfigurations,
super.key, super.key,
}); }) : editorConfigurations = editorConfigurations ??
controller?.editorConfigurations ??
const QuillEditorConfigurations(),
controller = controller ??
// ignore: deprecated_member_use_from_same_package
editorConfigurations?.controller ??
QuillController.basic();
final QuillController controller;
final QuillEditorConfigurations editorConfigurations; final QuillEditorConfigurations editorConfigurations;
@override @override
@ -52,8 +69,9 @@ class QuillEditorProvider extends InheritedWidget {
required QuillEditorProvider value, required QuillEditorProvider value,
required Widget child, required Widget child,
}) { }) {
value.controller.editorConfigurations = value.editorConfigurations;
return QuillEditorProvider( return QuillEditorProvider(
editorConfigurations: value.editorConfigurations, controller: value.controller,
child: child, child: child,
); );
} }

@ -40,7 +40,6 @@ import '../../../toolbar/theme/quill_dialog_theme.dart';
@immutable @immutable
class QuillRawEditorConfigurations extends Equatable { class QuillRawEditorConfigurations extends Equatable {
const QuillRawEditorConfigurations({ const QuillRawEditorConfigurations({
required this.controller,
required this.focusNode, required this.focusNode,
required this.scrollController, required this.scrollController,
required this.scrollBottomInset, required this.scrollBottomInset,
@ -49,6 +48,9 @@ class QuillRawEditorConfigurations extends Equatable {
required this.selectionCtrls, required this.selectionCtrls,
required this.embedBuilder, required this.embedBuilder,
required this.autoFocus, required this.autoFocus,
@Deprecated(
'controller should be passed directly to the editor - this parameter will be removed in future versions.')
this.controller,
this.showCursor = true, this.showCursor = true,
this.scrollable = true, this.scrollable = true,
this.padding = EdgeInsets.zero, this.padding = EdgeInsets.zero,
@ -93,7 +95,8 @@ class QuillRawEditorConfigurations extends Equatable {
}); });
/// Controls the document being edited. /// Controls the document being edited.
final QuillController controller; @Deprecated('controller will be removed in future versions.')
final QuillController? controller;
/// Controls whether this editor has keyboard focus. /// Controls whether this editor has keyboard focus.
final FocusNode focusNode; final FocusNode focusNode;

@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart'
immutable; immutable;
import '../../common/structs/offset_value.dart'; import '../../common/structs/offset_value.dart';
import '../../controller/quill_controller.dart';
import '../editor.dart'; import '../editor.dart';
import '../widgets/text/text_selection.dart'; import '../widgets/text/text_selection.dart';
import 'config/raw_editor_configurations.dart'; import 'config/raw_editor_configurations.dart';
@ -20,8 +21,14 @@ import 'raw_editor_state.dart';
class QuillRawEditor extends StatefulWidget { class QuillRawEditor extends StatefulWidget {
QuillRawEditor({ QuillRawEditor({
required this.configurations, required this.configurations,
controller,
super.key, super.key,
}) : assert( }) :
// ignore: deprecated_member_use_from_same_package
assert((controller ?? configurations.controller) != null),
// ignore: deprecated_member_use_from_same_package
controller = controller ?? configurations.controller,
assert(
configurations.maxHeight == null || configurations.maxHeight! > 0, configurations.maxHeight == null || configurations.maxHeight! > 0,
'maxHeight cannot be null'), 'maxHeight cannot be null'),
assert( assert(
@ -33,6 +40,7 @@ class QuillRawEditor extends StatefulWidget {
configurations.maxHeight! >= configurations.minHeight!, configurations.maxHeight! >= configurations.minHeight!,
'maxHeight cannot be null'); 'maxHeight cannot be null');
final QuillController controller;
final QuillRawEditorConfigurations configurations; final QuillRawEditorConfigurations configurations;
@override @override

@ -77,7 +77,7 @@ class QuillRawEditorState extends EditorState
// Cursors // Cursors
late CursorCont _cursorCont; late CursorCont _cursorCont;
QuillController get controller => widget.configurations.controller; QuillController get controller => widget.controller;
// Focus // Focus
bool _didAutoFocus = false; bool _didAutoFocus = false;
@ -1150,7 +1150,7 @@ class QuillRawEditorState extends EditorState
} else if (attrs.containsKey(Attribute.align.key)) { } else if (attrs.containsKey(Attribute.align.key)) {
return defaultStyles!.align!.horizontalSpacing; return defaultStyles!.align!.horizontalSpacing;
} }
return const HorizontalSpacing(0, 0); return HorizontalSpacing.zero;
} }
VerticalSpacing _getVerticalSpacingForBlock( VerticalSpacing _getVerticalSpacingForBlock(
@ -1167,7 +1167,7 @@ class QuillRawEditorState extends EditorState
} else if (attrs.containsKey(Attribute.align.key)) { } else if (attrs.containsKey(Attribute.align.key)) {
return defaultStyles!.align!.verticalSpacing; return defaultStyles!.align!.verticalSpacing;
} }
return const VerticalSpacing(0, 0); return VerticalSpacing.zero;
} }
void _didChangeTextEditingValueListener() { void _didChangeTextEditingValueListener() {
@ -1279,9 +1279,8 @@ class QuillRawEditorState extends EditorState
_cursorCont.show.value = widget.configurations.showCursor; _cursorCont.show.value = widget.configurations.showCursor;
_cursorCont.style = widget.configurations.cursorStyle; _cursorCont.style = widget.configurations.cursorStyle;
if (controller != oldWidget.configurations.controller) { if (controller != oldWidget.controller) {
oldWidget.configurations.controller oldWidget.controller.removeListener(_didChangeTextEditingValue);
.removeListener(_didChangeTextEditingValue);
controller.addListener(_didChangeTextEditingValue); controller.addListener(_didChangeTextEditingValue);
updateRemoteValueIfNeeded(); updateRemoteValueIfNeeded();
} }
@ -1298,7 +1297,7 @@ class QuillRawEditorState extends EditorState
updateKeepAlive(); updateKeepAlive();
} }
if (controller.selection != oldWidget.configurations.controller.selection) { if (controller.selection != oldWidget.controller.selection) {
_selectionOverlay?.update(textEditingValue); _selectionOverlay?.update(textEditingValue);
} }
@ -1353,7 +1352,7 @@ class QuillRawEditorState extends EditorState
/// operating on stale data. /// operating on stale data.
void _markNeedsBuild() { void _markNeedsBuild() {
if (_dirty) { if (_dirty) {
// No need to rebuilt if it already darty // No need to rebuilt if it already dirty
return; return;
} }
setState(() { setState(() {
@ -1631,7 +1630,7 @@ class QuillRawEditorState extends EditorState
final QuillEditorTextBoundary boundary; final QuillEditorTextBoundary boundary;
// final TextEditingValue textEditingValue = // final TextEditingValue textEditingValue =
// _textEditingValueforTextLayoutMetrics; // _textEditingValueForTextLayoutMetrics;
atomicTextBoundary = QuillEditorCharacterBoundary(textEditingValue); atomicTextBoundary = QuillEditorCharacterBoundary(textEditingValue);
// This isn't enough. Newline characters. // This isn't enough. Newline characters.
boundary = QuillEditorExpandedTextBoundary( boundary = QuillEditorExpandedTextBoundary(

@ -11,22 +11,21 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState
implements TextSelectionDelegate { implements TextSelectionDelegate {
@override @override
TextEditingValue get textEditingValue { TextEditingValue get textEditingValue {
return widget.configurations.controller.plainTextEditingValue; return widget.controller.plainTextEditingValue;
} }
set textEditingValue(TextEditingValue value) { set textEditingValue(TextEditingValue value) {
final cursorPosition = value.selection.extentOffset; final cursorPosition = value.selection.extentOffset;
final oldText = widget.configurations.controller.document.toPlainText(); final oldText = widget.controller.document.toPlainText();
final newText = value.text; final newText = value.text;
final diff = getDiff(oldText, newText, cursorPosition); final diff = getDiff(oldText, newText, cursorPosition);
if (diff.deleted == '' && diff.inserted == '') { if (diff.deleted == '' && diff.inserted == '') {
// Only changing selection range // Only changing selection range
widget.configurations.controller widget.controller.updateSelection(value.selection, ChangeSource.local);
.updateSelection(value.selection, ChangeSource.local);
return; return;
} }
widget.configurations.controller.replaceTextWithEmbeds( widget.controller.replaceTextWithEmbeds(
diff.start, diff.deleted.length, diff.inserted, value.selection); diff.start, diff.deleted.length, diff.inserted, value.selection);
} }

@ -198,10 +198,9 @@ mixin RawEditorStateTextInputClientMixin on EditorState
final cursorPosition = value.selection.extentOffset; final cursorPosition = value.selection.extentOffset;
final diff = getDiff(oldText, text, cursorPosition); final diff = getDiff(oldText, text, cursorPosition);
if (diff.deleted.isEmpty && diff.inserted.isEmpty) { if (diff.deleted.isEmpty && diff.inserted.isEmpty) {
widget.configurations.controller widget.controller.updateSelection(value.selection, ChangeSource.local);
.updateSelection(value.selection, ChangeSource.local);
} else { } else {
widget.configurations.controller.replaceText( widget.controller.replaceText(
diff.start, diff.start,
diff.deleted.length, diff.deleted.length,
diff.inserted, diff.inserted,

@ -271,7 +271,7 @@ class DefaultStyles {
), ),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(16, 0), const VerticalSpacing(16, 0),
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null), null),
h2: DefaultTextBlockStyle( h2: DefaultTextBlockStyle(
defaultTextStyle.style.copyWith( defaultTextStyle.style.copyWith(
@ -284,7 +284,7 @@ class DefaultStyles {
), ),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(8, 0), const VerticalSpacing(8, 0),
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null), null),
h3: DefaultTextBlockStyle( h3: DefaultTextBlockStyle(
defaultTextStyle.style.copyWith( defaultTextStyle.style.copyWith(
@ -297,7 +297,7 @@ class DefaultStyles {
), ),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(8, 0), const VerticalSpacing(8, 0),
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
h4: DefaultTextBlockStyle( h4: DefaultTextBlockStyle(
@ -311,7 +311,7 @@ class DefaultStyles {
), ),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(6, 0), const VerticalSpacing(6, 0),
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
h5: DefaultTextBlockStyle( h5: DefaultTextBlockStyle(
@ -325,7 +325,7 @@ class DefaultStyles {
), ),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(6, 0), const VerticalSpacing(6, 0),
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
h6: DefaultTextBlockStyle( h6: DefaultTextBlockStyle(
@ -339,42 +339,42 @@ class DefaultStyles {
), ),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(4, 0), const VerticalSpacing(4, 0),
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
lineHeightNormal: DefaultTextBlockStyle( lineHeightNormal: DefaultTextBlockStyle(
baseStyle.copyWith(height: 1.15), baseStyle.copyWith(height: 1.15),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
lineHeightTight: DefaultTextBlockStyle( lineHeightTight: DefaultTextBlockStyle(
baseStyle.copyWith(height: 1.30), baseStyle.copyWith(height: 1.30),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
lineHeightOneAndHalf: DefaultTextBlockStyle( lineHeightOneAndHalf: DefaultTextBlockStyle(
baseStyle.copyWith(height: 1.55), baseStyle.copyWith(height: 1.55),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
lineHeightDouble: DefaultTextBlockStyle( lineHeightDouble: DefaultTextBlockStyle(
baseStyle.copyWith(height: 2), baseStyle.copyWith(height: 2),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
paragraph: DefaultTextBlockStyle( paragraph: DefaultTextBlockStyle(
baseStyle, baseStyle,
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
bold: const TextStyle(fontWeight: FontWeight.bold), bold: const TextStyle(fontWeight: FontWeight.bold),
@ -422,8 +422,8 @@ class DefaultStyles {
color: Colors.grey.withOpacity(0.6), color: Colors.grey.withOpacity(0.6),
), ),
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null), null),
lists: DefaultListBlockStyle( lists: DefaultListBlockStyle(
baseStyle, baseStyle,
@ -453,7 +453,7 @@ class DefaultStyles {
), ),
baseHorizontalSpacing, baseHorizontalSpacing,
baseVerticalSpacing, baseVerticalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
BoxDecoration( BoxDecoration(
color: Colors.grey.shade50, color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(2), borderRadius: BorderRadius.circular(2),
@ -468,15 +468,15 @@ class DefaultStyles {
align: DefaultTextBlockStyle( align: DefaultTextBlockStyle(
baseStyle, baseStyle,
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
leading: DefaultTextBlockStyle( leading: DefaultTextBlockStyle(
baseStyle, baseStyle,
baseHorizontalSpacing, baseHorizontalSpacing,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
const VerticalSpacing(0, 0), VerticalSpacing.zero,
null, null,
), ),
sizeSmall: const TextStyle(fontSize: 10), sizeSmall: const TextStyle(fontSize: 10),

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import '../../flutter_quill.dart' import '../../flutter_quill.dart'
show QuillToolbarProvider, kDefaultToolbarSize; show QuillToolbarProvider, kDefaultToolbarSize;
import '../controller/quill_controller.dart';
import '../l10n/widgets/localizations.dart'; import '../l10n/widgets/localizations.dart';
import 'config/simple_toolbar_configurations.dart'; import 'config/simple_toolbar_configurations.dart';
import 'config/toolbar_configurations.dart'; import 'config/toolbar_configurations.dart';
@ -40,8 +41,10 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
}); });
static QuillSimpleToolbar simple( static QuillSimpleToolbar simple(
{required QuillSimpleToolbarConfigurations configurations}) { {QuillController? controller,
QuillSimpleToolbarConfigurations? configurations}) {
return QuillSimpleToolbar( return QuillSimpleToolbar(
controller: controller,
configurations: configurations, configurations: configurations,
); );
} }

@ -80,7 +80,9 @@ enum SearchButtonType {
@immutable @immutable
class QuillSimpleToolbarConfigurations extends QuillSharedToolbarProperties { class QuillSimpleToolbarConfigurations extends QuillSharedToolbarProperties {
const QuillSimpleToolbarConfigurations({ const QuillSimpleToolbarConfigurations({
required this.controller, @Deprecated(
'controller should be passed directly to the toolbar - this parameter will be removed in future versions.')
this.controller,
super.sharedConfigurations, super.sharedConfigurations,
super.toolbarSectionSpacing = kToolbarSectionSpacing, super.toolbarSectionSpacing = kToolbarSectionSpacing,
super.toolbarIconAlignment = WrapAlignment.center, super.toolbarIconAlignment = WrapAlignment.center,
@ -166,7 +168,8 @@ class QuillSimpleToolbarConfigurations extends QuillSharedToolbarProperties {
final Map<String, String>? fontFamilyValues; final Map<String, String>? fontFamilyValues;
final QuillController controller; @Deprecated('controller will be removed in future versions.')
final QuillController? controller;
/// By default it will be /// By default it will be
/// ```dart /// ```dart

@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../controller/quill_controller.dart';
import '../document/attribute.dart'; import '../document/attribute.dart';
import '../document/document.dart';
import 'base_toolbar.dart'; import 'base_toolbar.dart';
import 'buttons/alignment/select_alignment_buttons.dart'; import 'buttons/alignment/select_alignment_buttons.dart';
import 'buttons/arrow_indicated_list_button.dart'; import 'buttons/arrow_indicated_list_button.dart';
@ -9,13 +11,37 @@ import 'simple_toolbar_provider.dart';
class QuillSimpleToolbar extends StatelessWidget class QuillSimpleToolbar extends StatelessWidget
implements PreferredSizeWidget { implements PreferredSizeWidget {
const QuillSimpleToolbar({ factory QuillSimpleToolbar({
required this.configurations, required QuillSimpleToolbarConfigurations? configurations,
QuillController? controller,
Key? key,
}) {
// ignore: deprecated_member_use_from_same_package
controller ??= configurations?.controller;
assert(controller != null,
'controller required. Provide controller directly (preferred) or indirectly through configurations (not recommended - will be removed in future versions).');
controller ??= QuillController(
document: Document(),
selection: const TextSelection.collapsed(offset: 0));
//
controller.toolbarConfigurations = configurations;
//
return QuillSimpleToolbar._(
controller: controller,
key: key,
);
}
const QuillSimpleToolbar._({
required this.controller,
super.key, super.key,
}); });
final QuillController controller;
/// The configurations for the toolbar widget of flutter quill /// The configurations for the toolbar widget of flutter quill
final QuillSimpleToolbarConfigurations configurations; QuillSimpleToolbarConfigurations get configurations =>
controller.toolbarConfigurations;
double get _toolbarSize => configurations.toolbarSize * 1.4; double get _toolbarSize => configurations.toolbarSize * 1.4;
@ -23,34 +49,6 @@ class QuillSimpleToolbar extends StatelessWidget
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theEmbedButtons = configurations.embedButtons; final theEmbedButtons = configurations.embedButtons;
final isButtonGroupShown = [
configurations.showFontFamily ||
configurations.showFontSize ||
configurations.showBoldButton ||
configurations.showItalicButton ||
configurations.showSmallButton ||
configurations.showUnderLineButton ||
configurations.showLineHeightButton ||
configurations.showStrikeThrough ||
configurations.showInlineCode ||
configurations.showColorButton ||
configurations.showBackgroundColorButton ||
configurations.showClearFormat ||
theEmbedButtons?.isNotEmpty == true,
configurations.showLeftAlignment ||
configurations.showCenterAlignment ||
configurations.showRightAlignment ||
configurations.showJustifyAlignment ||
configurations.showDirection,
configurations.showHeaderStyle,
configurations.showListNumbers ||
configurations.showListBullets ||
configurations.showListCheck ||
configurations.showCodeBlock,
configurations.showQuote || configurations.showIndent,
configurations.showLink || configurations.showSearchButton
];
List<Widget> childrenBuilder(BuildContext context) { List<Widget> childrenBuilder(BuildContext context) {
final toolbarConfigurations = final toolbarConfigurations =
context.requireQuillSimpleToolbarConfigurations; context.requireQuillSimpleToolbarConfigurations;
@ -58,7 +56,6 @@ class QuillSimpleToolbar extends StatelessWidget
final globalIconSize = toolbarConfigurations.buttonOptions.base.iconSize; final globalIconSize = toolbarConfigurations.buttonOptions.base.iconSize;
final axis = toolbarConfigurations.axis; final axis = toolbarConfigurations.axis;
final globalController = configurations.controller;
final divider = SizedBox( final divider = SizedBox(
height: _toolbarSize, height: _toolbarSize,
@ -68,258 +65,254 @@ class QuillSimpleToolbar extends StatelessWidget
space: configurations.sectionDividerSpace, space: configurations.sectionDividerSpace,
)); ));
return [ final groups = [
if (configurations.showUndo) [
QuillToolbarHistoryButton( if (configurations.showUndo)
isUndo: true, QuillToolbarHistoryButton(
options: toolbarConfigurations.buttonOptions.undoHistory, isUndo: true,
controller: globalController, options: toolbarConfigurations.buttonOptions.undoHistory,
), controller: controller,
if (configurations.showRedo) ),
QuillToolbarHistoryButton( if (configurations.showRedo)
isUndo: false, QuillToolbarHistoryButton(
options: toolbarConfigurations.buttonOptions.redoHistory, isUndo: false,
controller: globalController, options: toolbarConfigurations.buttonOptions.redoHistory,
), controller: controller,
if (configurations.showFontFamily) ),
QuillToolbarFontFamilyButton( if (configurations.showFontFamily)
options: toolbarConfigurations.buttonOptions.fontFamily, QuillToolbarFontFamilyButton(
controller: globalController, options: toolbarConfigurations.buttonOptions.fontFamily,
), controller: controller,
if (configurations.showFontSize) ),
QuillToolbarFontSizeButton( if (configurations.showFontSize)
options: toolbarConfigurations.buttonOptions.fontSize, QuillToolbarFontSizeButton(
controller: globalController, options: toolbarConfigurations.buttonOptions.fontSize,
), controller: controller,
if (configurations.showBoldButton) ),
QuillToolbarToggleStyleButton( if (configurations.showBoldButton)
attribute: Attribute.bold, QuillToolbarToggleStyleButton(
options: toolbarConfigurations.buttonOptions.bold, attribute: Attribute.bold,
controller: globalController, options: toolbarConfigurations.buttonOptions.bold,
), controller: controller,
if (configurations.showItalicButton) ),
QuillToolbarToggleStyleButton( if (configurations.showItalicButton)
attribute: Attribute.italic, QuillToolbarToggleStyleButton(
options: toolbarConfigurations.buttonOptions.italic, attribute: Attribute.italic,
controller: globalController, options: toolbarConfigurations.buttonOptions.italic,
), controller: controller,
if (configurations.showUnderLineButton) ),
QuillToolbarToggleStyleButton( if (configurations.showUnderLineButton)
attribute: Attribute.underline, QuillToolbarToggleStyleButton(
options: toolbarConfigurations.buttonOptions.underLine, attribute: Attribute.underline,
controller: globalController, options: toolbarConfigurations.buttonOptions.underLine,
), controller: controller,
if (configurations.showStrikeThrough) ),
QuillToolbarToggleStyleButton( if (configurations.showStrikeThrough)
attribute: Attribute.strikeThrough, QuillToolbarToggleStyleButton(
options: toolbarConfigurations.buttonOptions.strikeThrough, attribute: Attribute.strikeThrough,
controller: globalController, options: toolbarConfigurations.buttonOptions.strikeThrough,
), controller: controller,
if (configurations.showInlineCode)
QuillToolbarToggleStyleButton(
attribute: Attribute.inlineCode,
options: toolbarConfigurations.buttonOptions.inlineCode,
controller: globalController,
),
if (configurations.showSubscript)
QuillToolbarToggleStyleButton(
attribute: Attribute.subscript,
options: toolbarConfigurations.buttonOptions.subscript,
controller: globalController,
),
if (configurations.showSuperscript)
QuillToolbarToggleStyleButton(
attribute: Attribute.superscript,
options: toolbarConfigurations.buttonOptions.superscript,
controller: globalController,
),
if (configurations.showSmallButton)
QuillToolbarToggleStyleButton(
attribute: Attribute.small,
options: toolbarConfigurations.buttonOptions.small,
controller: globalController,
),
if (configurations.showColorButton)
QuillToolbarColorButton(
controller: globalController,
isBackground: false,
options: toolbarConfigurations.buttonOptions.color,
),
if (configurations.showBackgroundColorButton)
QuillToolbarColorButton(
options: toolbarConfigurations.buttonOptions.backgroundColor,
controller: globalController,
isBackground: true,
),
if (configurations.showClearFormat)
QuillToolbarClearFormatButton(
controller: globalController,
options: toolbarConfigurations.buttonOptions.clearFormat,
),
if (theEmbedButtons != null)
for (final builder in theEmbedButtons)
builder(
globalController,
globalIconSize ?? kDefaultIconSize,
context.quillToolbarBaseButtonOptions?.iconTheme,
configurations.dialogTheme),
if (configurations.showDividers &&
isButtonGroupShown[0] &&
(isButtonGroupShown[1] ||
isButtonGroupShown[2] ||
isButtonGroupShown[3] ||
isButtonGroupShown[4] ||
isButtonGroupShown[5]))
divider,
if (configurations.showAlignmentButtons)
QuillToolbarSelectAlignmentButtons(
controller: globalController,
options: toolbarConfigurations.buttonOptions.selectAlignmentButtons
.copyWith(
showLeftAlignment: configurations.showLeftAlignment,
showCenterAlignment: configurations.showCenterAlignment,
showRightAlignment: configurations.showRightAlignment,
showJustifyAlignment: configurations.showJustifyAlignment,
), ),
), if (configurations.showInlineCode)
if (configurations.showDirection) QuillToolbarToggleStyleButton(
QuillToolbarToggleStyleButton( attribute: Attribute.inlineCode,
attribute: Attribute.rtl, options: toolbarConfigurations.buttonOptions.inlineCode,
options: toolbarConfigurations.buttonOptions.direction, controller: controller,
controller: globalController, ),
), if (configurations.showSubscript)
if (configurations.showDividers && QuillToolbarToggleStyleButton(
isButtonGroupShown[1] && attribute: Attribute.subscript,
(isButtonGroupShown[2] || options: toolbarConfigurations.buttonOptions.subscript,
isButtonGroupShown[3] || controller: controller,
isButtonGroupShown[4] || ),
isButtonGroupShown[5])) if (configurations.showSuperscript)
divider, QuillToolbarToggleStyleButton(
if (configurations.showLineHeightButton) attribute: Attribute.superscript,
QuillToolbarSelectLineHeightStyleDropdownButton( options: toolbarConfigurations.buttonOptions.superscript,
controller: globalController, controller: controller,
options: toolbarConfigurations ),
.buttonOptions.selectLineHeightStyleDropdownButton, if (configurations.showSmallButton)
), QuillToolbarToggleStyleButton(
if (configurations.showHeaderStyle) ...[ attribute: Attribute.small,
if (configurations.headerStyleType.isOriginal) options: toolbarConfigurations.buttonOptions.small,
QuillToolbarSelectHeaderStyleDropdownButton( controller: controller,
controller: globalController, ),
if (configurations.showColorButton)
QuillToolbarColorButton(
controller: controller,
isBackground: false,
options: toolbarConfigurations.buttonOptions.color,
),
if (configurations.showBackgroundColorButton)
QuillToolbarColorButton(
options: toolbarConfigurations.buttonOptions.backgroundColor,
controller: controller,
isBackground: true,
),
if (configurations.showClearFormat)
QuillToolbarClearFormatButton(
controller: controller,
options: toolbarConfigurations.buttonOptions.clearFormat,
),
if (theEmbedButtons != null)
for (final builder in theEmbedButtons)
builder(
controller,
globalIconSize ?? kDefaultIconSize,
context.quillToolbarBaseButtonOptions?.iconTheme,
configurations.dialogTheme),
],
[
if (configurations.showAlignmentButtons)
QuillToolbarSelectAlignmentButtons(
controller: controller,
options: toolbarConfigurations options: toolbarConfigurations
.buttonOptions.selectHeaderStyleDropdownButton, .buttonOptions.selectAlignmentButtons
) .copyWith(
else showLeftAlignment: configurations.showLeftAlignment,
QuillToolbarSelectHeaderStyleButtons( showCenterAlignment: configurations.showCenterAlignment,
controller: globalController, showRightAlignment: configurations.showRightAlignment,
options: showJustifyAlignment: configurations.showJustifyAlignment,
toolbarConfigurations.buttonOptions.selectHeaderStyleButtons, ),
),
if (configurations.showDirection)
QuillToolbarToggleStyleButton(
attribute: Attribute.rtl,
options: toolbarConfigurations.buttonOptions.direction,
controller: controller,
), ),
], ],
if (configurations.showDividers && [
configurations.showHeaderStyle && if (configurations.showLineHeightButton)
isButtonGroupShown[2] && QuillToolbarSelectLineHeightStyleDropdownButton(
(isButtonGroupShown[3] || controller: controller,
isButtonGroupShown[4] || options: toolbarConfigurations
isButtonGroupShown[5])) .buttonOptions.selectLineHeightStyleDropdownButton,
divider, ),
if (configurations.showListNumbers) if (configurations.showHeaderStyle) ...[
QuillToolbarToggleStyleButton( if (configurations.headerStyleType.isOriginal)
attribute: Attribute.ol, QuillToolbarSelectHeaderStyleDropdownButton(
options: toolbarConfigurations.buttonOptions.listNumbers, controller: controller,
controller: globalController, options: toolbarConfigurations
), .buttonOptions.selectHeaderStyleDropdownButton,
if (configurations.showListBullets) )
QuillToolbarToggleStyleButton( else
attribute: Attribute.ul, QuillToolbarSelectHeaderStyleButtons(
options: toolbarConfigurations.buttonOptions.listBullets, controller: controller,
controller: globalController, options: toolbarConfigurations
), .buttonOptions.selectHeaderStyleButtons,
if (configurations.showListCheck) ),
QuillToolbarToggleCheckListButton( ],
options: toolbarConfigurations.buttonOptions.toggleCheckList, ],
controller: globalController, [
), if (configurations.showListNumbers)
if (configurations.showCodeBlock) QuillToolbarToggleStyleButton(
QuillToolbarToggleStyleButton( attribute: Attribute.ol,
attribute: Attribute.codeBlock, options: toolbarConfigurations.buttonOptions.listNumbers,
options: toolbarConfigurations.buttonOptions.codeBlock, controller: controller,
controller: globalController, ),
), if (configurations.showListBullets)
if (configurations.showDividers && QuillToolbarToggleStyleButton(
isButtonGroupShown[3] && attribute: Attribute.ul,
(isButtonGroupShown[4] || isButtonGroupShown[5])) ...[ options: toolbarConfigurations.buttonOptions.listBullets,
divider, controller: controller,
),
if (configurations.showListCheck)
QuillToolbarToggleCheckListButton(
options: toolbarConfigurations.buttonOptions.toggleCheckList,
controller: controller,
),
if (configurations.showCodeBlock)
QuillToolbarToggleStyleButton(
attribute: Attribute.codeBlock,
options: toolbarConfigurations.buttonOptions.codeBlock,
controller: controller,
),
], ],
if (configurations.showQuote) [
QuillToolbarToggleStyleButton( if (configurations.showQuote)
options: toolbarConfigurations.buttonOptions.quote, QuillToolbarToggleStyleButton(
controller: globalController, options: toolbarConfigurations.buttonOptions.quote,
attribute: Attribute.blockQuote, controller: controller,
), attribute: Attribute.blockQuote,
if (configurations.showIndent) ),
QuillToolbarIndentButton( if (configurations.showIndent)
controller: globalController, QuillToolbarIndentButton(
isIncrease: true, controller: controller,
options: toolbarConfigurations.buttonOptions.indentIncrease, isIncrease: true,
), options: toolbarConfigurations.buttonOptions.indentIncrease,
if (configurations.showIndent) ),
QuillToolbarIndentButton( if (configurations.showIndent)
controller: globalController, QuillToolbarIndentButton(
isIncrease: false, controller: controller,
options: toolbarConfigurations.buttonOptions.indentDecrease, isIncrease: false,
), options: toolbarConfigurations.buttonOptions.indentDecrease,
if (configurations.showDividers && ),
isButtonGroupShown[4] && ],
isButtonGroupShown[5]) [
divider, if (configurations.showLink)
if (configurations.showLink) toolbarConfigurations.linkStyleType.isOriginal
toolbarConfigurations.linkStyleType.isOriginal ? QuillToolbarLinkStyleButton(
? QuillToolbarLinkStyleButton( controller: controller,
controller: globalController, options: toolbarConfigurations.buttonOptions.linkStyle,
options: toolbarConfigurations.buttonOptions.linkStyle, )
) : QuillToolbarLinkStyleButton2(
: QuillToolbarLinkStyleButton2( controller: controller,
controller: globalController, options: toolbarConfigurations.buttonOptions.linkStyle2,
options: toolbarConfigurations.buttonOptions.linkStyle2, ),
if (configurations.showSearchButton)
switch (configurations.searchButtonType) {
SearchButtonType.legacy => QuillToolbarLegacySearchButton(
controller: controller,
options: toolbarConfigurations.buttonOptions.search,
), ),
if (configurations.showSearchButton) SearchButtonType.modern => QuillToolbarSearchButton(
switch (configurations.searchButtonType) { controller: controller,
SearchButtonType.legacy => QuillToolbarLegacySearchButton( options: toolbarConfigurations.buttonOptions.search,
controller: globalController, ),
options: toolbarConfigurations.buttonOptions.search, },
), if (configurations.showClipboardCut)
SearchButtonType.modern => QuillToolbarSearchButton( QuillToolbarClipboardButton(
controller: globalController, options: toolbarConfigurations.buttonOptions.clipboardCut,
options: toolbarConfigurations.buttonOptions.search, controller: controller,
), clipboardAction: ClipboardAction.cut,
}, ),
if (configurations.showClipboardCut) if (configurations.showClipboardCopy)
QuillToolbarClipboardButton( QuillToolbarClipboardButton(
options: toolbarConfigurations.buttonOptions.clipboardCut, options: toolbarConfigurations.buttonOptions.clipboardCopy,
controller: globalController, controller: controller,
clipboardAction: ClipboardAction.cut, clipboardAction: ClipboardAction.copy,
), ),
if (configurations.showClipboardCopy) if (configurations.showClipboardPaste)
QuillToolbarClipboardButton( QuillToolbarClipboardButton(
options: toolbarConfigurations.buttonOptions.clipboardCopy, options: toolbarConfigurations.buttonOptions.clipboardPaste,
controller: globalController, controller: controller,
clipboardAction: ClipboardAction.copy, clipboardAction: ClipboardAction.paste,
), ),
if (configurations.showClipboardPaste) ],
QuillToolbarClipboardButton( [
options: toolbarConfigurations.buttonOptions.clipboardPaste,
controller: globalController,
clipboardAction: ClipboardAction.paste,
),
if (configurations.customButtons.isNotEmpty) ...[
if (configurations.showDividers) divider,
for (final customButton in configurations.customButtons) for (final customButton in configurations.customButtons)
QuillToolbarCustomButton( QuillToolbarCustomButton(
options: customButton, options: customButton,
controller: globalController, controller: controller,
), ),
], ],
]; ];
final buttonsAll = <Widget>[];
for (var i = 0; i < groups.length; i++) {
final buttons = groups[i];
if (buttons.isNotEmpty) {
if (buttonsAll.isNotEmpty) {
buttonsAll.add(divider);
}
buttonsAll.addAll(buttons);
}
}
return buttonsAll;
} }
return QuillSimpleToolbarProvider( return QuillSimpleToolbarProvider(

@ -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: 10.1.0 version: 10.1.6
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/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -19,10 +19,10 @@ void main() {
MaterialApp( MaterialApp(
home: Scaffold( home: Scaffold(
body: QuillSimpleToolbar( body: QuillSimpleToolbar(
configurations: QuillSimpleToolbarConfigurations( controller: controller,
controller: controller, configurations: const QuillSimpleToolbarConfigurations(
showRedo: false, showRedo: false,
customButtons: const [ customButtons: [
QuillToolbarCustomButtonOptions( QuillToolbarCustomButtonOptions(
tooltip: tooltip, tooltip: tooltip,
) )
@ -56,9 +56,7 @@ void main() {
setUp(() { setUp(() {
controller = QuillController.basic(); controller = QuillController.basic();
editor = QuillEditor.basic( editor = QuillEditor.basic(
configurations: QuillEditorConfigurations( controller: controller,
controller: controller,
),
); );
}); });
@ -142,8 +140,8 @@ void main() {
home: QuillEditor( home: QuillEditor(
focusNode: FocusNode(), focusNode: FocusNode(),
scrollController: ScrollController(), scrollController: ScrollController(),
configurations: QuillEditorConfigurations( controller: controller,
controller: controller, configurations: const QuillEditorConfigurations(
autoFocus: true, autoFocus: true,
expands: true, expands: true,
), ),

@ -0,0 +1,190 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/quill_delta.dart';
import 'package:test/test.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
group('copy', () {
const testDocumentContents = 'data';
late QuillController controller;
setUp(() {
controller = QuillController.basic()
..compose(Delta()..insert(testDocumentContents),
const TextSelection.collapsed(offset: 0), ChangeSource.local);
});
test('clipboardSelection empty', () {
expect(controller.clipboardSelection(true), false,
reason: 'No effect when no selection');
expect(controller.clipboardSelection(false), false);
});
test('clipboardSelection', () {
controller
..replaceText(0, 4, 'bold plain italic', null)
..formatText(0, 4, Attribute.bold)
..formatText(11, 17, Attribute.italic)
..updateSelection(const TextSelection(baseOffset: 2, extentOffset: 14),
ChangeSource.local);
//
expect(controller.clipboardSelection(true), true);
expect(controller.document.length, 18,
reason: 'Copy does not change the document');
expect(controller.clipboardSelection(false), true);
expect(controller.document.length, 6, reason: 'Cut changes the document');
//
controller
..readOnly = true
..updateSelection(const TextSelection(baseOffset: 2, extentOffset: 4),
ChangeSource.local);
expect(controller.selection.isCollapsed, false);
expect(controller.clipboardSelection(true), true);
expect(controller.document.length, 6);
expect(controller.clipboardSelection(false), false);
expect(controller.document.length, 6,
reason: 'Cut not permitted on readOnly document');
});
});
group('paste', () {
test('Plain', () async {
final controller = QuillController.basic()
..compose(Delta()..insert('[]'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..updateSelection(
const TextSelection.collapsed(offset: 1), ChangeSource.local);
//
expect(controller.document.toPlainText(), '[]\n');
expect(controller.pasteUsingPlainOrDelta('insert'), true);
expect(controller.document.toPlainText(), '[insert]\n');
});
test('Plain lines', () async {
final controller = QuillController.basic()
..compose(Delta()..insert('[]'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..updateSelection(
const TextSelection.collapsed(offset: 1), ChangeSource.local);
//
expect(controller.document.toPlainText(), '[]\n');
expect(controller.pasteUsingPlainOrDelta('1\n2\n3\n'), true);
expect(controller.document.toPlainText(), '[1\n2\n3\n]\n');
});
test('Paste from external', () async {
final source = QuillController.basic()
..compose(Delta()..insert('Plain text'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..updateSelection(const TextSelection(baseOffset: 4, extentOffset: 8),
ChangeSource.local);
assert(source.clipboardSelection(true));
expect(source.pastePlainText, 'n te');
//
final controller = QuillController.basic()
..compose(Delta()..insert('[]'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..updateSelection(
const TextSelection.collapsed(offset: 1), ChangeSource.local);
//
expect(controller.pasteUsingPlainOrDelta('insert'), true,
reason: 'External paste');
expect(controller.document.toPlainText(), '[insert]\n');
});
test('Delta simple', () async {
final source = QuillController.basic()
..compose(Delta()..insert('Plain text'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..formatText(6, 8, Attribute.bold)
..updateSelection(const TextSelection(baseOffset: 4, extentOffset: 8),
ChangeSource.local);
assert(source.clipboardSelection(true));
expect(source.pastePlainText, 'n te');
expect(
source.pasteDelta,
Delta()
..insert('n ')
..insert('te', {'bold': true}));
//
final controller = QuillController.basic()
..compose(Delta()..insert('[]'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..updateSelection(
const TextSelection.collapsed(offset: 1), ChangeSource.local);
//
expect(controller.pasteUsingPlainOrDelta('n te'), true,
reason: 'Internal paste');
expect(controller.document.toPlainText(), '[n te]\n');
expect(
controller.document.toDelta(),
Delta()
..insert('[n ')
..insert('te', {'bold': true})
..insert(']\n'));
expect(controller.selection, const TextSelection.collapsed(offset: 5));
});
test('Delta multi line', () async {
const blockAttribute = Attribute.ol;
const plainSelection = 'BC\nDEF\nGHI\nJK';
final source = QuillController.basic()
..compose(Delta()..insert('ABC\nDEF\nGHI\nJKL'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..formatText(1, 1, Attribute.underline) // ABC with B underlined
..formatText(4, 0, blockAttribute) // 1. DEF with E in italic
..formatText(5, 1, Attribute.italic)
..formatText(8, 0, blockAttribute) // 2. GHI with H as inline code
..formatText(9, 1, Attribute.inlineCode)
..formatText(13, 1, Attribute.strikeThrough) // JKL with K strikethrough
..updateSelection(const TextSelection(baseOffset: 1, extentOffset: 14),
ChangeSource.local);
//
assert(source.clipboardSelection(true));
expect(source.pastePlainText, plainSelection);
expect(
source.pasteDelta,
Delta()
..insert('B', {'underline': true})
..insert('C\nD')
..insert('E', {'italic': true})
..insert('F')
..insert('\n', {'list': 'ordered'})
..insert('G')
..insert('H', {'code': true})
..insert('I')
..insert('\n', {'list': 'ordered'})
..insert('J')
..insert('K', {'strike': true}));
//
final controller = QuillController.basic()
..compose(Delta()..insert('[]'),
const TextSelection.collapsed(offset: 0), ChangeSource.local)
..updateSelection(
const TextSelection.collapsed(offset: 1), ChangeSource.local);
//
expect(controller.pasteUsingPlainOrDelta(plainSelection), true,
reason: 'Internal paste');
expect(controller.document.toPlainText(), '[$plainSelection]\n');
expect(
controller.document.toDelta(),
Delta()
..insert('[')
..insert('B', {'underline': true})
..insert('C\nD')
..insert('E', {'italic': true})
..insert('F')
..insert('\n', {'list': 'ordered'})
..insert('G')
..insert('H', {'code': true})
..insert('I')
..insert('\n', {'list': 'ordered'})
..insert('J')
..insert('K', {'strike': true})
..insert(']\n'));
expect(controller.selection, const TextSelection.collapsed(offset: 14));
});
});
}

@ -324,38 +324,6 @@ void main() {
Delta()..insert('test $originalContents')); Delta()..insert('test $originalContents'));
}); });
test('clipboardSelection empty', () {
expect(controller.clipboardSelection(true), false,
reason: 'No effect when no selection');
expect(controller.clipboardSelection(false), false);
});
test('clipboardSelection', () {
controller
..replaceText(0, 4, 'bold plain italic', null)
..formatText(0, 4, Attribute.bold)
..formatText(11, 17, Attribute.italic)
..updateSelection(const TextSelection(baseOffset: 2, extentOffset: 14),
ChangeSource.local);
//
expect(controller.clipboardSelection(true), true);
expect(controller.document.length, 18,
reason: 'Copy does not change the document');
expect(controller.clipboardSelection(false), true);
expect(controller.document.length, 6, reason: 'Cut changes the document');
//
controller
..readOnly = true
..updateSelection(const TextSelection(baseOffset: 2, extentOffset: 4),
ChangeSource.local);
expect(controller.selection.isCollapsed, false);
expect(controller.clipboardSelection(true), true);
expect(controller.document.length, 6);
expect(controller.clipboardSelection(false), false);
expect(controller.document.length, 6,
reason: 'Cut not permitted on readOnly document');
});
test('blockSelectionStyles', () { test('blockSelectionStyles', () {
Style select(int start, int end) { Style select(int start, int end) {
controller.updateSelection( controller.updateSelection(

@ -4,6 +4,60 @@ import 'package:test/test.dart';
void main() { void main() {
group('collectStyle', () { group('collectStyle', () {
/// Lists and alignments have the same block attribute key but can have different values.
/// Changing the format value updates the document but must also update the toolbar button state
/// by ensuring the collectStyles method returns the attribute selected for the newly entered line.
test('Change block value type', () {
void doTest(Map<String, dynamic> start, Attribute attr,
Map<String, dynamic> change) {
/// Create a document with 2 lines of block attribute using [start]
/// Change the format of the last line using [attr] and verify [change]
final delta = Delta()
..insert('A')
..insert('\n', start)
..insert('B')
..insert('\n', start);
final document = Document.fromDelta(delta)
/// insert a newline
..insert(3, '\n');
/// Verify inserted blank line and block type has not changed
expect(
document.toDelta(),
Delta()
..insert('A')
..insert('\n', start)
..insert('B')
..insert('\n\n', start));
/// Change format of last (empty) line
document.format(4, 0, attr);
expect(
document.toDelta(),
Delta()
..insert('A')
..insert('\n', start)
..insert('B')
..insert('\n', start)
..insert('\n', change),
reason: 'document updated');
/// Verify that the reported style reflects the newly formatted state
expect(document.collectStyle(4, 0), Style.attr({attr.key: attr}),
reason: 'collectStyle reporting correct attribute');
}
doTest({'list': 'ordered'}, const ListAttribute('bullet'),
{'list': 'bullet'});
doTest({'list': 'checked'}, const ListAttribute('bullet'),
{'list': 'bullet'});
doTest({'align': 'center'}, const AlignAttribute('right'),
{'align': 'right'});
doTest({'align': 'left'}, const AlignAttribute('center'),
{'align': 'center'});
});
/// Enter key inserts newline as plain text without inline styles. /// Enter key inserts newline as plain text without inline styles.
/// collectStyle needs to retrieve style of preceding line /// collectStyle needs to retrieve style of preceding line
test('Simulate double enter key at end', () { test('Simulate double enter key at end', () {

@ -24,11 +24,11 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: QuillEditor.basic( home: QuillEditor.basic(
controller: controller,
// ignore: avoid_redundant_argument_values // ignore: avoid_redundant_argument_values
configurations: QuillEditorConfigurations( configurations: const QuillEditorConfigurations(
controller: controller, // ignore: avoid_redundant_argument_values
// ignore: avoid_redundant_argument_values ),
),
), ),
), ),
); );
@ -44,8 +44,8 @@ void main() {
home: QuillEditor( home: QuillEditor(
focusNode: FocusNode(), focusNode: FocusNode(),
scrollController: ScrollController(), scrollController: ScrollController(),
controller: controller,
configurations: QuillEditorConfigurations( configurations: QuillEditorConfigurations(
controller: controller,
// ignore: avoid_redundant_argument_values // ignore: avoid_redundant_argument_values
autoFocus: true, autoFocus: true,
expands: true, expands: true,
@ -116,9 +116,9 @@ void main() {
home: QuillEditor( home: QuillEditor(
focusNode: FocusNode(), focusNode: FocusNode(),
scrollController: ScrollController(), scrollController: ScrollController(),
controller: controller,
// ignore: avoid_redundant_argument_values // ignore: avoid_redundant_argument_values
configurations: QuillEditorConfigurations( configurations: QuillEditorConfigurations(
controller: controller,
// ignore: avoid_redundant_argument_values // ignore: avoid_redundant_argument_values
autoFocus: true, autoFocus: true,
expands: true, expands: true,
@ -146,7 +146,8 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: QuillEditor.basic( home: QuillEditor.basic(
configurations: QuillEditorConfigurations(controller: controller), controller: controller,
configurations: const QuillEditorConfigurations(),
focusNode: editorFocusNode, focusNode: editorFocusNode,
), ),
), ),

Loading…
Cancel
Save