Sound null safety migration (#87)

* Upgrade upgradable packages

* Apply default null-safety migrations

* Remove hashnode as a dependency and localize its functionality to the package.

Maintenance was done long time ago hence no need to wait for the update

* Localize ui package to reduce maintenance burdens

* Replace universal html with dart:html

* Remove unnecessary checks

* Fix formatting

* Migrate app to null safety

* Enable methods to be nullable

* Fix non-nullable issue with node methods

* Cast as Node

* Use universal html

* Use universal html package to bring in the ImageElement class

* Remove unused imports

* Fix imports on the editor file

* Add key to quill editor

* Remove final from the GlobalKey

* Remove final on GlobalKey

* Remove custom util implementation in favor of quiver

* Fix issue with null on token attrivute

* Remove final hashcode that is replaced by quiver functionality

* Fix merge request

* Fix hit test position in text_line.dart

* Fix null safety errors on text_selection.dart

* Fix sound null safe errors in toolbar.dart

* Import null safe file picker
pull/95/head^2
Miller Adulu 4 years ago committed by GitHub
parent dda5805935
commit c593026fd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      analysis_options.yaml
  2. 15
      app/lib/pages/home_page.dart
  3. 4
      app/lib/pages/read_only_page.dart
  4. 16
      app/lib/widgets/demo_scaffold.dart
  5. 50
      app/lib/widgets/field.dart
  6. 75
      app/pubspec.lock
  7. 2
      app/pubspec.yaml
  8. 82
      lib/models/documents/attribute.dart
  9. 20
      lib/models/documents/document.dart
  10. 4
      lib/models/documents/history.dart
  11. 16
      lib/models/documents/nodes/block.dart
  12. 44
      lib/models/documents/nodes/container.dart
  13. 4
      lib/models/documents/nodes/embed.dart
  14. 51
      lib/models/documents/nodes/leaf.dart
  15. 70
      lib/models/documents/nodes/line.dart
  16. 23
      lib/models/documents/nodes/node.dart
  17. 6
      lib/models/documents/style.dart
  18. 126
      lib/models/quill_delta.dart
  19. 56
      lib/models/rules/delete.dart
  20. 52
      lib/models/rules/format.dart
  21. 123
      lib/models/rules/insert.dart
  22. 14
      lib/models/rules/rule.dart
  23. 4
      lib/utils/color.dart
  24. 14
      lib/utils/diff_delta.dart
  25. 3
      lib/utils/universal_ui/fake_ui.dart
  26. 9
      lib/utils/universal_ui/real_ui.dart
  27. 22
      lib/utils/universal_ui/universal_ui.dart
  28. 8
      lib/widgets/box.dart
  29. 44
      lib/widgets/controller.dart
  30. 47
      lib/widgets/cursor.dart
  31. 60
      lib/widgets/default_styles.dart
  32. 47
      lib/widgets/delegate.dart
  33. 266
      lib/widgets/editor.dart
  34. 2
      lib/widgets/image.dart
  35. 7
      lib/widgets/keyboard_listener.dart
  36. 75
      lib/widgets/proxy.dart
  37. 294
      lib/widgets/raw_editor.dart
  38. 8
      lib/widgets/responsive_widget.dart
  39. 180
      lib/widgets/text_block.dart
  40. 246
      lib/widgets/text_line.dart
  41. 238
      lib/widgets/text_selection.dart
  42. 262
      lib/widgets/toolbar.dart
  43. 43
      pubspec.lock
  44. 88
      pubspec.yaml

@ -0,0 +1,3 @@
analyzer:
errors:
undefined_prefixed_name: ignore

@ -23,7 +23,7 @@ class HomePage extends StatefulWidget {
} }
class _HomePageState extends State<HomePage> { class _HomePageState extends State<HomePage> {
QuillController _controller; QuillController? _controller;
final FocusNode _focusNode = FocusNode(); final FocusNode _focusNode = FocusNode();
@override @override
@ -75,15 +75,15 @@ class _HomePageState extends State<HomePage> {
focusNode: FocusNode(), focusNode: FocusNode(),
onKey: (RawKeyEvent event) { onKey: (RawKeyEvent event) {
if (event.data.isControlPressed && event.character == 'b') { if (event.data.isControlPressed && event.character == 'b') {
if (_controller if (_controller!
.getSelectionStyle() .getSelectionStyle()
.attributes .attributes
.keys .keys
.contains("bold")) { .contains("bold")) {
_controller _controller!
.formatSelection(Attribute.clone(Attribute.bold, null)); .formatSelection(Attribute.clone(Attribute.bold, null));
} else { } else {
_controller.formatSelection(Attribute.bold); _controller!.formatSelection(Attribute.bold);
print("not bold"); print("not bold");
} }
} }
@ -104,7 +104,7 @@ class _HomePageState extends State<HomePage> {
color: Colors.white, color: Colors.white,
padding: const EdgeInsets.only(left: 16.0, right: 16.0), padding: const EdgeInsets.only(left: 16.0, right: 16.0),
child: QuillEditor( child: QuillEditor(
controller: _controller, controller: _controller!,
scrollController: ScrollController(), scrollController: ScrollController(),
scrollable: true, scrollable: true,
focusNode: _focusNode, focusNode: _focusNode,
@ -135,12 +135,12 @@ class _HomePageState extends State<HomePage> {
child: Container( child: Container(
padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8),
child: QuillToolbar.basic( child: QuillToolbar.basic(
controller: _controller, controller: _controller!,
onImagePickCallback: _onImagePickCallback), onImagePickCallback: _onImagePickCallback),
)) ))
: Container( : Container(
child: QuillToolbar.basic( child: QuillToolbar.basic(
controller: _controller, controller: _controller!,
onImagePickCallback: _onImagePickCallback), onImagePickCallback: _onImagePickCallback),
), ),
], ],
@ -151,7 +151,6 @@ class _HomePageState extends State<HomePage> {
// Renders the image picked by imagePicker from local file storage // Renders the image picked by imagePicker from local file storage
// You can also upload the picked image to any server (eg : AWS s3 or Firebase) and then return the uploaded image URL // You can also upload the picked image to any server (eg : AWS s3 or Firebase) and then return the uploaded image URL
Future<String> _onImagePickCallback(File file) async { Future<String> _onImagePickCallback(File file) async {
if (file == null) return null;
// Copies the picked file from temporary cache to applications directory // Copies the picked file from temporary cache to applications directory
Directory appDocDir = await getApplicationDocumentsDirectory(); Directory appDocDir = await getApplicationDocumentsDirectory();
File copiedFile = File copiedFile =

@ -27,7 +27,7 @@ class _ReadOnlyPageState extends State<ReadOnlyPage> {
); );
} }
Widget _buildContent(BuildContext context, QuillController controller) { Widget _buildContent(BuildContext context, QuillController? controller) {
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Container( child: Container(
@ -36,7 +36,7 @@ class _ReadOnlyPageState extends State<ReadOnlyPage> {
border: Border.all(color: Colors.grey.shade200), border: Border.all(color: Colors.grey.shade200),
), ),
child: QuillEditor( child: QuillEditor(
controller: controller, controller: controller!,
scrollController: ScrollController(), scrollController: ScrollController(),
scrollable: true, scrollable: true,
focusNode: _focusNode, focusNode: _focusNode,

@ -7,21 +7,21 @@ import 'package:flutter_quill/widgets/controller.dart';
import 'package:flutter_quill/widgets/toolbar.dart'; import 'package:flutter_quill/widgets/toolbar.dart';
typedef DemoContentBuilder = Widget Function( typedef DemoContentBuilder = Widget Function(
BuildContext context, QuillController controller); BuildContext context, QuillController? controller);
// Common scaffold for all examples. // Common scaffold for all examples.
class DemoScaffold extends StatefulWidget { class DemoScaffold extends StatefulWidget {
/// Filename of the document to load into the editor. /// Filename of the document to load into the editor.
final String documentFilename; final String documentFilename;
final DemoContentBuilder builder; final DemoContentBuilder builder;
final List<Widget> actions; final List<Widget>? actions;
final Widget floatingActionButton; final Widget? floatingActionButton;
final bool showToolbar; final bool showToolbar;
const DemoScaffold({ const DemoScaffold({
Key key, Key? key,
@required this.documentFilename, required this.documentFilename,
@required this.builder, required this.builder,
this.actions, this.actions,
this.showToolbar = true, this.showToolbar = true,
this.floatingActionButton, this.floatingActionButton,
@ -33,7 +33,7 @@ class DemoScaffold extends StatefulWidget {
class _DemoScaffoldState extends State<DemoScaffold> { class _DemoScaffoldState extends State<DemoScaffold> {
final _scaffoldKey = GlobalKey<ScaffoldState>(); final _scaffoldKey = GlobalKey<ScaffoldState>();
QuillController _controller; QuillController? _controller;
bool _loading = false; bool _loading = false;
@ -92,7 +92,7 @@ class _DemoScaffoldState extends State<DemoScaffold> {
), ),
title: _loading || widget.showToolbar == false title: _loading || widget.showToolbar == false
? null ? null
: QuillToolbar.basic(controller: _controller), : QuillToolbar.basic(controller: _controller!),
actions: actions, actions: actions,
), ),
floatingActionButton: widget.floatingActionButton, floatingActionButton: widget.floatingActionButton,

@ -6,28 +6,28 @@ import 'package:flutter_quill/widgets/editor.dart';
class QuillField extends StatefulWidget { class QuillField extends StatefulWidget {
final QuillController controller; final QuillController controller;
final FocusNode focusNode; final FocusNode? focusNode;
final ScrollController scrollController; final ScrollController? scrollController;
final bool scrollable; final bool scrollable;
final EdgeInsetsGeometry padding; final EdgeInsetsGeometry padding;
final bool autofocus; final bool autofocus;
final bool showCursor; final bool showCursor;
final bool readOnly; final bool readOnly;
final bool enableInteractiveSelection; final bool enableInteractiveSelection;
final double minHeight; final double? minHeight;
final double maxHeight; final double? maxHeight;
final bool expands; final bool expands;
final TextCapitalization textCapitalization; final TextCapitalization textCapitalization;
final Brightness keyboardAppearance; final Brightness keyboardAppearance;
final ScrollPhysics scrollPhysics; final ScrollPhysics? scrollPhysics;
final ValueChanged<String> onLaunchUrl; final ValueChanged<String>? onLaunchUrl;
final InputDecoration decoration; final InputDecoration? decoration;
final Widget toolbar; final Widget? toolbar;
final EmbedBuilder embedBuilder; final EmbedBuilder? embedBuilder;
QuillField({ QuillField({
Key key, Key? key,
@required this.controller, required this.controller,
this.focusNode, this.focusNode,
this.scrollController, this.scrollController,
this.scrollable = true, this.scrollable = true,
@ -53,28 +53,28 @@ class QuillField extends StatefulWidget {
} }
class _QuillFieldState extends State<QuillField> { class _QuillFieldState extends State<QuillField> {
bool _focused; late bool _focused;
void _editorFocusChanged() { void _editorFocusChanged() {
setState(() { setState(() {
_focused = widget.focusNode.hasFocus; _focused = widget.focusNode!.hasFocus;
}); });
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_focused = widget.focusNode.hasFocus; _focused = widget.focusNode!.hasFocus;
widget.focusNode.addListener(_editorFocusChanged); widget.focusNode!.addListener(_editorFocusChanged);
} }
@override @override
void didUpdateWidget(covariant QuillField oldWidget) { void didUpdateWidget(covariant QuillField oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.focusNode != oldWidget.focusNode) { if (widget.focusNode != oldWidget.focusNode) {
oldWidget.focusNode.removeListener(_editorFocusChanged); oldWidget.focusNode!.removeListener(_editorFocusChanged);
widget.focusNode.addListener(_editorFocusChanged); widget.focusNode!.addListener(_editorFocusChanged);
_focused = widget.focusNode.hasFocus; _focused = widget.focusNode!.hasFocus;
} }
} }
@ -82,8 +82,8 @@ class _QuillFieldState extends State<QuillField> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget child = QuillEditor( Widget child = QuillEditor(
controller: widget.controller, controller: widget.controller,
focusNode: widget.focusNode, focusNode: widget.focusNode!,
scrollController: widget.scrollController, scrollController: widget.scrollController!,
scrollable: widget.scrollable, scrollable: widget.scrollable,
padding: widget.padding, padding: widget.padding,
autoFocus: widget.autofocus, autoFocus: widget.autofocus,
@ -97,7 +97,7 @@ class _QuillFieldState extends State<QuillField> {
keyboardAppearance: widget.keyboardAppearance, keyboardAppearance: widget.keyboardAppearance,
scrollPhysics: widget.scrollPhysics, scrollPhysics: widget.scrollPhysics,
onLaunchUrl: widget.onLaunchUrl, onLaunchUrl: widget.onLaunchUrl,
embedBuilder: widget.embedBuilder, embedBuilder: widget.embedBuilder!,
); );
if (widget.toolbar != null) { if (widget.toolbar != null) {
@ -105,7 +105,7 @@ class _QuillFieldState extends State<QuillField> {
children: [ children: [
child, child,
Visibility( Visibility(
child: widget.toolbar, child: widget.toolbar!,
visible: _focused, visible: _focused,
maintainSize: true, maintainSize: true,
maintainAnimation: true, maintainAnimation: true,
@ -117,11 +117,11 @@ class _QuillFieldState extends State<QuillField> {
return AnimatedBuilder( return AnimatedBuilder(
animation: animation:
Listenable.merge(<Listenable>[widget.focusNode, widget.controller]), Listenable.merge(<Listenable?>[widget.focusNode, widget.controller]),
builder: (BuildContext context, Widget child) { builder: (BuildContext context, Widget? child) {
return InputDecorator( return InputDecorator(
decoration: _getEffectiveDecoration(), decoration: _getEffectiveDecoration(),
isFocused: widget.focusNode.hasFocus, isFocused: widget.focusNode!.hasFocus,
// TODO: Document should be considered empty of it has single empty line with no styles applied // TODO: Document should be considered empty of it has single empty line with no styles applied
isEmpty: widget.controller.document.length == 1, isEmpty: widget.controller.document.length == 1,
child: child, child: child,

@ -43,27 +43,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.15.0" version: "1.15.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.2"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -117,7 +96,7 @@ packages:
name: flutter_colorpicker name: flutter_colorpicker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.5" version: "0.4.0-nullsafety.0"
flutter_keyboard_visibility: flutter_keyboard_visibility:
dependency: transitive dependency: transitive
description: description:
@ -163,13 +142,6 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.0+4"
http: http:
dependency: transitive dependency: transitive
description: description:
@ -190,7 +162,7 @@ packages:
name: image_picker name: image_picker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.2" version: "0.7.2+1"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -302,14 +274,7 @@ packages:
name: quiver name: quiver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.5" version: "3.0.0"
quiver_hashcode:
dependency: transitive
description:
name: quiver_hashcode
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -349,7 +314,7 @@ packages:
name: string_validator name: string_validator
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.4" version: "0.2.0-nullsafety.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@ -370,7 +335,7 @@ packages:
name: tuple name: tuple
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "2.0.0"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -378,27 +343,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
universal_html:
dependency: transitive
description:
name: universal_html
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.4"
universal_io:
dependency: transitive
description:
name: universal_io
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
universal_ui:
dependency: transitive
description:
name: universal_ui
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.8"
url_launcher: url_launcher:
dependency: transitive dependency: transitive
description: description:
@ -462,13 +406,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.0" version: "0.2.0"
zone_local:
dependency: transitive
description:
name: zone_local
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
sdks: sdks:
dart: ">=2.12.0 <3.0.0" dart: ">=2.12.0 <3.0.0"
flutter: ">=1.22.0" flutter: ">=1.24.0-10.2.pre"

@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
flutter: flutter:

@ -75,7 +75,7 @@ class Attribute<T> {
static final StyleAttribute style = StyleAttribute(null); static final StyleAttribute style = StyleAttribute(null);
static final TokenAttribute token = TokenAttribute(null); static final TokenAttribute token = TokenAttribute('');
static final Set<String> inlineKeys = { static final Set<String> inlineKeys = {
Attribute.bold.key, Attribute.bold.key,
@ -105,46 +105,46 @@ class Attribute<T> {
Attribute.blockQuote.key, Attribute.blockQuote.key,
}; };
static Attribute<int> get h1 => HeaderAttribute(level: 1); static Attribute<int?> get h1 => HeaderAttribute(level: 1);
static Attribute<int> get h2 => HeaderAttribute(level: 2); static Attribute<int?> get h2 => HeaderAttribute(level: 2);
static Attribute<int> get h3 => HeaderAttribute(level: 3); static Attribute<int?> get h3 => HeaderAttribute(level: 3);
// "attributes":{"align":"left"} // "attributes":{"align":"left"}
static Attribute<String> get leftAlignment => AlignAttribute('left'); static Attribute<String?> get leftAlignment => AlignAttribute('left');
// "attributes":{"align":"center"} // "attributes":{"align":"center"}
static Attribute<String> get centerAlignment => AlignAttribute('center'); static Attribute<String?> get centerAlignment => AlignAttribute('center');
// "attributes":{"align":"right"} // "attributes":{"align":"right"}
static Attribute<String> get rightAlignment => AlignAttribute('right'); static Attribute<String?> get rightAlignment => AlignAttribute('right');
// "attributes":{"align":"justify"} // "attributes":{"align":"justify"}
static Attribute<String> get justifyAlignment => AlignAttribute('justify'); static Attribute<String?> get justifyAlignment => AlignAttribute('justify');
// "attributes":{"list":"bullet"} // "attributes":{"list":"bullet"}
static Attribute<String> get ul => ListAttribute('bullet'); static Attribute<String?> get ul => ListAttribute('bullet');
// "attributes":{"list":"ordered"} // "attributes":{"list":"ordered"}
static Attribute<String> get ol => ListAttribute('ordered'); static Attribute<String?> get ol => ListAttribute('ordered');
// "attributes":{"list":"checked"} // "attributes":{"list":"checked"}
static Attribute<String> get checked => ListAttribute('checked'); static Attribute<String?> get checked => ListAttribute('checked');
// "attributes":{"list":"unchecked"} // "attributes":{"list":"unchecked"}
static Attribute<String> get unchecked => ListAttribute('unchecked'); static Attribute<String?> get unchecked => ListAttribute('unchecked');
// "attributes":{"indent":1"} // "attributes":{"indent":1"}
static Attribute<int> get indentL1 => IndentAttribute(level: 1); static Attribute<int?> get indentL1 => IndentAttribute(level: 1);
// "attributes":{"indent":2"} // "attributes":{"indent":2"}
static Attribute<int> get indentL2 => IndentAttribute(level: 2); static Attribute<int?> get indentL2 => IndentAttribute(level: 2);
// "attributes":{"indent":3"} // "attributes":{"indent":3"}
static Attribute<int> get indentL3 => IndentAttribute(level: 3); static Attribute<int?> get indentL3 => IndentAttribute(level: 3);
static Attribute<int> getIndentLevel(int level) { static Attribute<int?> getIndentLevel(int? level) {
if (level == 1) { if (level == 1) {
return indentL1; return indentL1;
} }
@ -164,7 +164,7 @@ class Attribute<T> {
if (!_registry.containsKey(key)) { if (!_registry.containsKey(key)) {
throw ArgumentError.value(key, 'key "$key" not found.'); throw ArgumentError.value(key, 'key "$key" not found.');
} }
Attribute origin = _registry[key]; Attribute origin = _registry[key]!;
Attribute attribute = clone(origin, value); Attribute attribute = clone(origin, value);
return attribute; return attribute;
} }
@ -208,24 +208,24 @@ class StrikeThroughAttribute extends Attribute<bool> {
StrikeThroughAttribute() : super('strike', AttributeScope.INLINE, true); StrikeThroughAttribute() : super('strike', AttributeScope.INLINE, true);
} }
class FontAttribute extends Attribute<String> { class FontAttribute extends Attribute<String?> {
FontAttribute(String val) : super('font', AttributeScope.INLINE, val); FontAttribute(String? val) : super('font', AttributeScope.INLINE, val);
} }
class SizeAttribute extends Attribute<String> { class SizeAttribute extends Attribute<String?> {
SizeAttribute(String val) : super('size', AttributeScope.INLINE, val); SizeAttribute(String? val) : super('size', AttributeScope.INLINE, val);
} }
class LinkAttribute extends Attribute<String> { class LinkAttribute extends Attribute<String?> {
LinkAttribute(String val) : super('link', AttributeScope.INLINE, val); LinkAttribute(String? val) : super('link', AttributeScope.INLINE, val);
} }
class ColorAttribute extends Attribute<String> { class ColorAttribute extends Attribute<String?> {
ColorAttribute(String val) : super('color', AttributeScope.INLINE, val); ColorAttribute(String? val) : super('color', AttributeScope.INLINE, val);
} }
class BackgroundAttribute extends Attribute<String> { class BackgroundAttribute extends Attribute<String?> {
BackgroundAttribute(String val) BackgroundAttribute(String? val)
: super('background', AttributeScope.INLINE, val); : super('background', AttributeScope.INLINE, val);
} }
@ -234,20 +234,20 @@ class PlaceholderAttribute extends Attribute<bool> {
PlaceholderAttribute() : super('placeholder', AttributeScope.INLINE, true); PlaceholderAttribute() : super('placeholder', AttributeScope.INLINE, true);
} }
class HeaderAttribute extends Attribute<int> { class HeaderAttribute extends Attribute<int?> {
HeaderAttribute({int level}) : super('header', AttributeScope.BLOCK, level); HeaderAttribute({int? level}) : super('header', AttributeScope.BLOCK, level);
} }
class IndentAttribute extends Attribute<int> { class IndentAttribute extends Attribute<int?> {
IndentAttribute({int level}) : super('indent', AttributeScope.BLOCK, level); IndentAttribute({int? level}) : super('indent', AttributeScope.BLOCK, level);
} }
class AlignAttribute extends Attribute<String> { class AlignAttribute extends Attribute<String?> {
AlignAttribute(String val) : super('align', AttributeScope.BLOCK, val); AlignAttribute(String? val) : super('align', AttributeScope.BLOCK, val);
} }
class ListAttribute extends Attribute<String> { class ListAttribute extends Attribute<String?> {
ListAttribute(String val) : super('list', AttributeScope.BLOCK, val); ListAttribute(String? val) : super('list', AttributeScope.BLOCK, val);
} }
class CodeBlockAttribute extends Attribute<bool> { class CodeBlockAttribute extends Attribute<bool> {
@ -258,16 +258,16 @@ class BlockQuoteAttribute extends Attribute<bool> {
BlockQuoteAttribute() : super('blockquote', AttributeScope.BLOCK, true); BlockQuoteAttribute() : super('blockquote', AttributeScope.BLOCK, true);
} }
class WidthAttribute extends Attribute<String> { class WidthAttribute extends Attribute<String?> {
WidthAttribute(String val) : super('width', AttributeScope.IGNORE, val); WidthAttribute(String? val) : super('width', AttributeScope.IGNORE, val);
} }
class HeightAttribute extends Attribute<String> { class HeightAttribute extends Attribute<String?> {
HeightAttribute(String val) : super('height', AttributeScope.IGNORE, val); HeightAttribute(String? val) : super('height', AttributeScope.IGNORE, val);
} }
class StyleAttribute extends Attribute<String> { class StyleAttribute extends Attribute<String?> {
StyleAttribute(String val) : super('style', AttributeScope.IGNORE, val); StyleAttribute(String? val) : super('style', AttributeScope.IGNORE, val);
} }
class TokenAttribute extends Attribute<String> { class TokenAttribute extends Attribute<String> {

@ -43,11 +43,11 @@ class Document {
_loadDocument(_delta); _loadDocument(_delta);
} }
Delta insert(int index, Object data) { Delta insert(int index, Object? data) {
assert(index >= 0); assert(index >= 0);
assert(data is String || data is Embeddable); assert(data is String || data is Embeddable);
if (data is Embeddable) { if (data is Embeddable) {
data = (data as Embeddable).toJson(); data = data.toJson();
} else if ((data as String).isEmpty) { } else if ((data as String).isEmpty) {
return Delta(); return Delta();
} }
@ -66,7 +66,7 @@ class Document {
return delta; return delta;
} }
Delta replace(int index, int len, Object data) { Delta replace(int index, int len, Object? data) {
assert(index >= 0); assert(index >= 0);
assert(data is String || data is Embeddable); assert(data is String || data is Embeddable);
@ -88,7 +88,7 @@ class Document {
return delta; return delta;
} }
Delta format(int index, int len, Attribute attribute) { Delta format(int index, int len, Attribute? attribute) {
assert(index >= 0 && len >= 0 && attribute != null); assert(index >= 0 && len >= 0 && attribute != null);
Delta delta = Delta(); Delta delta = Delta();
@ -113,7 +113,7 @@ class Document {
if (res.node is Line) { if (res.node is Line) {
return res; return res;
} }
Block block = res.node; Block block = res.node as Block;
return block.queryChild(res.offset, true); return block.queryChild(res.offset, true);
} }
@ -126,7 +126,7 @@ class Document {
delta = _transform(delta); delta = _transform(delta);
Delta originalDelta = toDelta(); Delta originalDelta = toDelta();
for (Operation op in delta.toList()) { for (Operation op in delta.toList()) {
Style style = Style? style =
op.attributes != null ? Style.fromJson(op.attributes) : null; op.attributes != null ? Style.fromJson(op.attributes) : null;
if (op.isInsert) { if (op.isInsert) {
@ -138,7 +138,7 @@ class Document {
} }
if (!op.isDelete) { if (!op.isDelete) {
offset += op.length; offset += op.length!;
} }
} }
try { try {
@ -197,7 +197,7 @@ class Document {
} }
} }
Object _normalize(Object data) { Object _normalize(Object? data) {
if (data is String) { if (data is String) {
return data; return data;
} }
@ -205,7 +205,7 @@ class Document {
if (data is Embeddable) { if (data is Embeddable) {
return data; return data;
} }
return Embeddable.fromJson(data); return Embeddable.fromJson(data as Map<String, dynamic>);
} }
close() { close() {
@ -227,7 +227,7 @@ class Document {
op.attributes != null ? Style.fromJson(op.attributes) : null; op.attributes != null ? Style.fromJson(op.attributes) : null;
final data = _normalize(op.data); final data = _normalize(op.data);
_root.insert(offset, data, style); _root.insert(offset, data, style);
offset += op.length; offset += op.length!;
} }
final node = _root.last; final node = _root.last;
if (node is Line && if (node is Line &&

@ -90,13 +90,13 @@ class History {
} }
Delta delta = source.removeLast(); Delta delta = source.removeLast();
// look for insert or delete // look for insert or delete
int len = 0; int? len = 0;
List<Operation> ops = delta.toList(); List<Operation> ops = delta.toList();
for (var i = 0; i < ops.length; i++) { for (var i = 0; i < ops.length; i++) {
if (ops[i].key == Operation.insertKey) { if (ops[i].key == Operation.insertKey) {
len = ops[i].length; len = ops[i].length;
} else if (ops[i].key == Operation.deleteKey) { } else if (ops[i].key == Operation.deleteKey) {
len = ops[i].length * -1; len = ops[i].length! * -1;
} }
} }
Delta base = Delta.from(doc.toDelta()); Delta base = Delta.from(doc.toDelta());

@ -4,7 +4,7 @@ import 'container.dart';
import 'line.dart'; import 'line.dart';
import 'node.dart'; import 'node.dart';
class Block extends Container<Line> { class Block extends Container<Line?> {
@override @override
Line get defaultChild => Line(); Line get defaultChild => Line();
@ -18,7 +18,7 @@ class Block extends Container<Line> {
@override @override
adjust() { adjust() {
if (isEmpty) { if (isEmpty) {
Node sibling = previous; Node? sibling = previous;
unlink(); unlink();
if (sibling != null) { if (sibling != null) {
sibling.adjust(); sibling.adjust();
@ -27,18 +27,18 @@ class Block extends Container<Line> {
} }
Block block = this; Block block = this;
Node prev = block.previous; Node? prev = block.previous;
// merging it with previous block if style is the same // merging it with previous block if style is the same
if (!block.isFirst && if (!block.isFirst &&
block.previous is Block && block.previous is Block &&
prev.style == block.style) { prev!.style == block.style) {
block.moveChildToNewParent(prev); block.moveChildToNewParent(prev as Container<Node?>?);
block.unlink(); block.unlink();
block = prev; block = prev as Block;
} }
Node next = block.next; Node? next = block.next;
// merging it with next block if style is the same // merging it with next block if style is the same
if (!block.isLast && block.next is Block && next.style == block.style) { if (!block.isLast && block.next is Block && next!.style == block.style) {
(next as Block).moveChildToNewParent(block); (next as Block).moveChildToNewParent(block);
next.unlink(); next.unlink();
} }

@ -4,7 +4,7 @@ import '../style.dart';
import 'node.dart'; import 'node.dart';
/* Container of multiple nodes */ /* Container of multiple nodes */
abstract class Container<T extends Node> extends Node { abstract class Container<T extends Node?> extends Node {
final LinkedList<Node> _children = LinkedList<Node>(); final LinkedList<Node> _children = LinkedList<Node>();
LinkedList<Node> get children => _children; LinkedList<Node> get children => _children;
@ -26,32 +26,32 @@ abstract class Container<T extends Node> extends Node {
/// abstract methods end /// abstract methods end
add(T node) { add(T node) {
assert(node.parent == null); assert(node?.parent == null);
node.parent = this; node?.parent = this;
_children.add(node); _children.add(node as Node);
} }
addFirst(T node) { addFirst(T node) {
assert(node.parent == null); assert(node?.parent == null);
node.parent = this; node?.parent = this;
_children.addFirst(node); _children.addFirst(node as Node);
} }
void remove(T node) { void remove(T node) {
assert(node.parent == this); assert(node?.parent == this);
node.parent = null; node?.parent = null;
_children.remove(node); _children.remove(node as Node);
} }
void moveChildToNewParent(Container newParent) { void moveChildToNewParent(Container? newParent) {
if (isEmpty) { if (isEmpty) {
return; return;
} }
T last = newParent.isEmpty ? null : newParent.last; T? last = newParent!.isEmpty ? null : newParent.last as T?;
while (isNotEmpty) { while (isNotEmpty) {
T child = first; T child = first as T;
child.unlink(); child?.unlink();
newParent.add(child); newParent.add(child);
} }
@ -80,12 +80,12 @@ abstract class Container<T extends Node> extends Node {
int get length => _children.fold(0, (cur, node) => cur + node.length); int get length => _children.fold(0, (cur, node) => cur + node.length);
@override @override
insert(int index, Object data, Style style) { insert(int index, Object data, Style? style) {
assert(index == 0 || (index > 0 && index < length)); assert(index == 0 || (index > 0 && index < length));
if (isNotEmpty) { if (isNotEmpty) {
ChildQuery child = queryChild(index, false); ChildQuery child = queryChild(index, false);
child.node.insert(child.offset, data, style); child.node!.insert(child.offset, data, style);
return; return;
} }
@ -93,21 +93,21 @@ abstract class Container<T extends Node> extends Node {
assert(index == 0); assert(index == 0);
T node = defaultChild; T node = defaultChild;
add(node); add(node);
node.insert(index, data, style); node?.insert(index, data, style);
} }
@override @override
retain(int index, int length, Style attributes) { retain(int index, int? length, Style? attributes) {
assert(isNotEmpty); assert(isNotEmpty);
ChildQuery child = queryChild(index, false); ChildQuery child = queryChild(index, false);
child.node.retain(child.offset, length, attributes); child.node!.retain(child.offset, length, attributes);
} }
@override @override
delete(int index, int length) { delete(int index, int? length) {
assert(isNotEmpty); assert(isNotEmpty);
ChildQuery child = queryChild(index, false); ChildQuery child = queryChild(index, false);
child.node.delete(child.offset, length); child.node!.delete(child.offset, length);
} }
@override @override
@ -116,7 +116,7 @@ abstract class Container<T extends Node> extends Node {
/// Query of a child in a Container /// Query of a child in a Container
class ChildQuery { class ChildQuery {
final Node node; // null if not found final Node? node; // null if not found
final int offset; final int offset;

@ -2,9 +2,7 @@ class Embeddable {
final String type; final String type;
final dynamic data; final dynamic data;
Embeddable(this.type, this.data) Embeddable(this.type, this.data);
: assert(type != null),
assert(data != null);
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
Map<String, String> m = {type: data}; Map<String, String> m = {type: data};

@ -13,13 +13,9 @@ abstract class Leaf extends Node {
Object get value => _value; Object get value => _value;
Leaf.val(Object val) Leaf.val(Object val) : _value = val;
: assert(val != null),
_value = val;
factory Leaf([Object data]) {
assert(data != null);
factory Leaf(Object data) {
if (data is Embeddable) { if (data is Embeddable) {
return Embed(data); return Embed(data);
} }
@ -30,14 +26,13 @@ abstract class Leaf extends Node {
@override @override
void applyStyle(Style value) { void applyStyle(Style value) {
assert( assert((value.isInline || value.isIgnored || value.isEmpty),
value != null && (value.isInline || value.isIgnored || value.isEmpty),
'Unable to apply Style to leaf: $value'); 'Unable to apply Style to leaf: $value');
super.applyStyle(value); super.applyStyle(value);
} }
@override @override
Line get parent => super.parent as Line; Line? get parent => super.parent as Line?;
@override @override
int get length { int get length {
@ -55,11 +50,11 @@ abstract class Leaf extends Node {
} }
@override @override
insert(int index, Object data, Style style) { insert(int index, Object data, Style? style) {
assert(data != null && index >= 0 && index <= length); assert(index >= 0 && index <= length);
Leaf node = Leaf(data); Leaf node = Leaf(data);
if (index < length) { if (index < length) {
splitAt(index).insertBefore(node); splitAt(index)!.insertBefore(node);
} else { } else {
insertAfter(node); insertAfter(node);
} }
@ -67,36 +62,36 @@ abstract class Leaf extends Node {
} }
@override @override
retain(int index, int len, Style style) { retain(int index, int? len, Style? style) {
if (style == null) { if (style == null) {
return; return;
} }
int local = math.min(this.length - index, len); int local = math.min(this.length - index, len!);
int remain = len - local; int remain = len - local;
Leaf node = _isolate(index, local); Leaf node = _isolate(index, local);
if (remain > 0) { if (remain > 0) {
assert(node.next != null); assert(node.next != null);
node.next.retain(0, remain, style); node.next!.retain(0, remain, style);
} }
node.format(style); node.format(style);
} }
@override @override
delete(int index, int len) { delete(int index, int? len) {
assert(index < this.length); assert(index < this.length);
int local = math.min(this.length - index, len); int local = math.min(this.length - index, len!);
Leaf target = _isolate(index, local); Leaf target = _isolate(index, local);
Leaf prev = target.previous; Leaf? prev = target.previous as Leaf?;
Leaf next = target.next; Leaf? next = target.next as Leaf?;
target.unlink(); target.unlink();
int remain = len - local; int remain = len - local;
if (remain > 0) { if (remain > 0) {
assert(next != null); assert(next != null);
next.delete(0, remain); next!.delete(0, remain);
} }
if (prev != null) { if (prev != null) {
@ -112,7 +107,7 @@ abstract class Leaf extends Node {
Text node = this as Text; Text node = this as Text;
// merging it with previous node if style is the same // merging it with previous node if style is the same
Node prev = node.previous; Node? prev = node.previous;
if (!node.isFirst && prev is Text && prev.style == node.style) { if (!node.isFirst && prev is Text && prev.style == node.style) {
prev._value = prev.value + node.value; prev._value = prev.value + node.value;
node.unlink(); node.unlink();
@ -120,27 +115,27 @@ abstract class Leaf extends Node {
} }
// merging it with next node if style is the same // merging it with next node if style is the same
Node next = node.next; Node? next = node.next;
if (!node.isLast && next is Text && next.style == node.style) { if (!node.isLast && next is Text && next.style == node.style) {
node._value = node.value + next.value; node._value = node.value + next.value;
next.unlink(); next.unlink();
} }
} }
Leaf cutAt(int index) { Leaf? cutAt(int index) {
assert(index >= 0 && index <= length); assert(index >= 0 && index <= length);
Leaf cut = splitAt(index); Leaf? cut = splitAt(index);
cut?.unlink(); cut?.unlink();
return cut; return cut;
} }
Leaf splitAt(int index) { Leaf? splitAt(int index) {
assert(index >= 0 && index <= length); assert(index >= 0 && index <= length);
if (index == 0) { if (index == 0) {
return this; return this;
} }
if (index == length) { if (index == length) {
return isLast ? null : next as Leaf; return isLast ? null : next as Leaf?;
} }
assert(this is Text); assert(this is Text);
@ -152,7 +147,7 @@ abstract class Leaf extends Node {
return split; return split;
} }
format(Style style) { format(Style? style) {
if (style != null && style.isNotEmpty) { if (style != null && style.isNotEmpty) {
applyStyle(style); applyStyle(style);
} }
@ -163,7 +158,7 @@ abstract class Leaf extends Node {
Leaf _isolate(int index, int length) { Leaf _isolate(int index, int length) {
assert( assert(
index >= 0 && index < this.length && (index + length <= this.length)); index >= 0 && index < this.length && (index + length <= this.length));
Leaf target = splitAt(index); Leaf target = splitAt(index)!;
target.splitAt(length); target.splitAt(length);
return target; return target;
} }

@ -10,7 +10,7 @@ import 'container.dart';
import 'embed.dart'; import 'embed.dart';
import 'leaf.dart'; import 'leaf.dart';
class Line extends Container<Leaf> { class Line extends Container<Leaf?> {
@override @override
Leaf get defaultChild => Text(); Leaf get defaultChild => Text();
@ -25,28 +25,30 @@ class Line extends Container<Leaf> {
return children.single is Embed; return children.single is Embed;
} }
Line get nextLine { Line? get nextLine {
if (!isLast) { if (!isLast) {
return next is Block ? (next as Block).first : next; return next is Block ? (next as Block).first as Line? : next as Line?;
} }
if (parent is! Block) { if (parent is! Block) {
return null; return null;
} }
if (parent.isLast) { if (parent!.isLast) {
return null; return null;
} }
return parent.next is Block ? (parent.next as Block).first : parent.next; return parent!.next is Block
? (parent!.next as Block).first as Line?
: parent!.next as Line?;
} }
@override @override
Delta toDelta() { Delta toDelta() {
final delta = children final delta = children
.map((child) => child.toDelta()) .map((child) => child.toDelta())
.fold(Delta(), (a, b) => a.concat(b)); .fold(Delta(), (dynamic a, b) => a.concat(b));
var attributes = style; var attributes = style;
if (parent is Block) { if (parent is Block) {
Block block = parent; Block block = parent as Block;
attributes = attributes.mergeAll(block.style); attributes = attributes.mergeAll(block.style);
} }
delta.insert('\n', attributes.toJson()); delta.insert('\n', attributes.toJson());
@ -64,7 +66,7 @@ class Line extends Container<Leaf> {
} }
@override @override
insert(int index, Object data, Style style) { insert(int index, Object data, Style? style) {
if (data is Embeddable) { if (data is Embeddable) {
_insert(index, data, style); _insert(index, data, style);
return; return;
@ -99,13 +101,13 @@ class Line extends Container<Leaf> {
} }
@override @override
retain(int index, int len, Style style) { retain(int index, int? len, Style? style) {
if (style == null) { if (style == null) {
return; return;
} }
int thisLen = this.length; int thisLen = this.length;
int local = math.min(thisLen - index, len); int local = math.min(thisLen - index, len!);
if (index + local == thisLen && local == 1) { if (index + local == thisLen && local == 1) {
assert(style.values.every((attr) => attr.scope == AttributeScope.BLOCK)); assert(style.values.every((attr) => attr.scope == AttributeScope.BLOCK));
@ -119,13 +121,13 @@ class Line extends Container<Leaf> {
int remain = len - local; int remain = len - local;
if (remain > 0) { if (remain > 0) {
assert(nextLine != null); assert(nextLine != null);
nextLine.retain(0, remain, style); nextLine!.retain(0, remain, style);
} }
} }
@override @override
delete(int index, int len) { delete(int index, int? len) {
int local = math.min(this.length - index, len); int local = math.min(this.length - index, len!);
bool deleted = index + local == this.length; bool deleted = index + local == this.length;
if (deleted) { if (deleted) {
clearStyle(); clearStyle();
@ -139,35 +141,35 @@ class Line extends Container<Leaf> {
int remain = len - local; int remain = len - local;
if (remain > 0) { if (remain > 0) {
assert(nextLine != null); assert(nextLine != null);
nextLine.delete(0, remain); nextLine!.delete(0, remain);
} }
if (deleted && isNotEmpty) { if (deleted && isNotEmpty) {
assert(nextLine != null); assert(nextLine != null);
nextLine.moveChildToNewParent(this); nextLine!.moveChildToNewParent(this);
moveChildToNewParent(nextLine); moveChildToNewParent(nextLine);
} }
if (deleted) { if (deleted) {
Node p = parent; Node p = parent!;
unlink(); unlink();
p.adjust(); p.adjust();
} }
} }
void _format(Style newStyle) { void _format(Style? newStyle) {
if (newStyle == null || newStyle.isEmpty) { if (newStyle == null || newStyle.isEmpty) {
return; return;
} }
applyStyle(newStyle); applyStyle(newStyle);
Attribute blockStyle = newStyle.getBlockExceptHeader(); Attribute? blockStyle = newStyle.getBlockExceptHeader();
if (blockStyle == null) { if (blockStyle == null) {
return; return;
} }
if (parent is Block) { if (parent is Block) {
Attribute parentStyle = (parent as Block).style.getBlockExceptHeader(); Attribute? parentStyle = (parent as Block).style.getBlockExceptHeader();
if (blockStyle.value == null) { if (blockStyle.value == null) {
_unwrap(); _unwrap();
} else if (blockStyle != parentStyle) { } else if (blockStyle != parentStyle) {
@ -196,7 +198,7 @@ class Line extends Container<Leaf> {
if (parent is! Block) { if (parent is! Block) {
throw ArgumentError('Invalid parent'); throw ArgumentError('Invalid parent');
} }
Block block = parent; Block block = parent as Block;
assert(block.children.contains(this)); assert(block.children.contains(this));
@ -207,10 +209,10 @@ class Line extends Container<Leaf> {
unlink(); unlink();
block.insertAfter(this); block.insertAfter(this);
} else { } else {
Block before = block.clone(); Block before = block.clone() as Block;
block.insertBefore(before); block.insertBefore(before);
Line child = block.first; Line child = block.first as Line;
while (child != this) { while (child != this) {
child.unlink(); child.unlink();
before.add(child); before.add(child);
@ -232,19 +234,19 @@ class Line extends Container<Leaf> {
} }
ChildQuery query = queryChild(index, false); ChildQuery query = queryChild(index, false);
while (!query.node.isLast) { while (!query.node!.isLast) {
Leaf next = last; Leaf next = last as Leaf;
next.unlink(); next.unlink();
line.addFirst(next); line.addFirst(next);
} }
Leaf child = query.node; Leaf child = query.node as Leaf;
Leaf cut = child.splitAt(query.offset); Leaf? cut = child.splitAt(query.offset);
cut?.unlink(); cut?.unlink();
line.addFirst(cut); line.addFirst(cut);
return line; return line;
} }
_insert(int index, Object data, Style style) { _insert(int index, Object data, Style? style) {
assert(index == 0 || (index > 0 && index < length)); assert(index == 0 || (index > 0 && index < length));
if (data is String) { if (data is String) {
@ -256,7 +258,7 @@ class Line extends Container<Leaf> {
if (isNotEmpty) { if (isNotEmpty) {
ChildQuery result = queryChild(index, true); ChildQuery result = queryChild(index, true);
result.node.insert(result.offset, data, style); result.node!.insert(result.offset, data, style);
return; return;
} }
@ -291,26 +293,26 @@ class Line extends Container<Leaf> {
} }
ChildQuery data = queryChild(offset, true); ChildQuery data = queryChild(offset, true);
Leaf node = data.node; Leaf? node = data.node as Leaf?;
if (node != null) { if (node != null) {
res = res.mergeAll(node.style); res = res.mergeAll(node.style);
int pos = node.length - data.offset; int pos = node.length - data.offset;
while (!node.isLast && pos < local) { while (!node!.isLast && pos < local) {
node = node.next as Leaf; node = node.next as Leaf?;
_handle(node.style); _handle(node!.style);
pos += node.length; pos += node.length;
} }
} }
res = res.mergeAll(style); res = res.mergeAll(style);
if (parent is Block) { if (parent is Block) {
Block block = parent; Block block = parent as Block;
res = res.mergeAll(block.style); res = res.mergeAll(block.style);
} }
int remain = len - local; int remain = len - local;
if (remain > 0) { if (remain > 0) {
_handle(nextLine.collectStyle(0, remain)); _handle(nextLine!.collectStyle(0, remain));
} }
return res; return res;

@ -9,7 +9,7 @@ import 'line.dart';
/* node in a document tree */ /* node in a document tree */
abstract class Node extends LinkedListEntry<Node> { abstract class Node extends LinkedListEntry<Node> {
Container parent; Container? parent;
Style _style = Style(); Style _style = Style();
Style get style => _style; Style get style => _style;
@ -19,9 +19,6 @@ abstract class Node extends LinkedListEntry<Node> {
} }
void applyStyle(Style value) { void applyStyle(Style value) {
if (value == null) {
throw ArgumentError('null value');
}
_style = _style.mergeAll(value); _style = _style.mergeAll(value);
} }
@ -29,9 +26,9 @@ abstract class Node extends LinkedListEntry<Node> {
_style = Style(); _style = Style();
} }
bool get isFirst => list.first == this; bool get isFirst => list!.first == this;
bool get isLast => list.last == this; bool get isLast => list!.last == this;
int get length; int get length;
@ -50,14 +47,14 @@ abstract class Node extends LinkedListEntry<Node> {
Node cur = this; Node cur = this;
do { do {
cur = cur.previous; cur = cur.previous!;
offset += cur.length; offset += cur.length;
} while (!cur.isFirst); } while (!cur.isFirst);
return offset; return offset;
} }
int getDocumentOffset() { int getDocumentOffset() {
final parentOffset = (parent is! Root) ? parent.getDocumentOffset() : 0; final parentOffset = (parent is! Root) ? parent!.getDocumentOffset() : 0;
return parentOffset + getOffset(); return parentOffset + getOffset();
} }
@ -99,20 +96,20 @@ abstract class Node extends LinkedListEntry<Node> {
Delta toDelta(); Delta toDelta();
insert(int index, Object data, Style style); insert(int index, Object data, Style? style);
retain(int index, int len, Style style); retain(int index, int? len, Style? style);
delete(int index, int len); delete(int index, int? len);
/// abstract methods end /// abstract methods end
} }
/* Root node of document tree */ /* Root node of document tree */
class Root extends Container<Container<Node>> { class Root extends Container<Container<Node?>> {
@override @override
Container<Node> get defaultChild => Line(); Container<Node?> get defaultChild => Line();
@override @override
Delta toDelta() => children Delta toDelta() => children

@ -10,7 +10,7 @@ class Style {
Style() : _attributes = <String, Attribute>{}; Style() : _attributes = <String, Attribute>{};
static Style fromJson(Map<String, dynamic> attributes) { static Style fromJson(Map<String, dynamic>? attributes) {
if (attributes == null) { if (attributes == null) {
return Style(); return Style();
} }
@ -22,7 +22,7 @@ class Style {
return Style.attr(result); return Style.attr(result);
} }
Map<String, dynamic> toJson() => _attributes.isEmpty Map<String, dynamic>? toJson() => _attributes.isEmpty
? null ? null
: _attributes.map<String, dynamic>((String _, Attribute attribute) => : _attributes.map<String, dynamic>((String _, Attribute attribute) =>
MapEntry<String, dynamic>(attribute.key, attribute.value)); MapEntry<String, dynamic>(attribute.key, attribute.value));
@ -46,7 +46,7 @@ class Style {
bool containsKey(String key) => _attributes.containsKey(key); bool containsKey(String key) => _attributes.containsKey(key);
Attribute getBlockExceptHeader() { Attribute? getBlockExceptHeader() {
for (Attribute val in values) { for (Attribute val in values) {
if (val.isBlockExceptHeader) { if (val.isBlockExceptHeader) {
return val; return val;

@ -15,10 +15,10 @@ const _valueEquality = DeepCollectionEquality();
/// Decoder function to convert raw `data` object into a user-defined data type. /// Decoder function to convert raw `data` object into a user-defined data type.
/// ///
/// Useful with embedded content. /// Useful with embedded content.
typedef DataDecoder = Object Function(Object data); typedef DataDecoder = Object? Function(Object data);
/// Default data decoder which simply passes through the original value. /// Default data decoder which simply passes through the original value.
Object _passThroughDataDecoder(Object data) => data; Object? _passThroughDataDecoder(Object? data) => data;
/// Operation performed on a rich-text document. /// Operation performed on a rich-text document.
class Operation { class Operation {
@ -40,19 +40,18 @@ class Operation {
final String key; final String key;
/// Length of this operation. /// Length of this operation.
final int length; final int? length;
/// Payload of "insert" operation, for other types is set to empty string. /// Payload of "insert" operation, for other types is set to empty string.
final Object data; final Object? data;
/// Rich-text attributes set by this operation, can be `null`. /// Rich-text attributes set by this operation, can be `null`.
Map<String, dynamic> get attributes => Map<String, dynamic>? get attributes =>
_attributes == null ? null : Map<String, dynamic>.from(_attributes); _attributes == null ? null : Map<String, dynamic>.from(_attributes!);
final Map<String, dynamic> _attributes; final Map<String, dynamic>? _attributes;
Operation._(this.key, this.length, this.data, Map attributes) Operation._(this.key, this.length, this.data, Map? attributes)
: assert(key != null && length != null && data != null), : assert(_validKeys.contains(key), 'Invalid operation key "$key".'),
assert(_validKeys.contains(key), 'Invalid operation key "$key".'),
assert(() { assert(() {
if (key != Operation.insertKey) return true; if (key != Operation.insertKey) return true;
return data is String ? data.length == length : length == 1; return data is String ? data.length == length : length == 1;
@ -64,7 +63,7 @@ class Operation {
/// ///
/// If `dataDecoder` parameter is not null then it is used to additionally /// If `dataDecoder` parameter is not null then it is used to additionally
/// decode the operation's data object. Only applied to insert operations. /// decode the operation's data object. Only applied to insert operations.
static Operation fromJson(Map data, {DataDecoder dataDecoder}) { static Operation fromJson(Map data, {DataDecoder? dataDecoder}) {
dataDecoder ??= _passThroughDataDecoder; dataDecoder ??= _passThroughDataDecoder;
final map = Map<String, dynamic>.from(data); final map = Map<String, dynamic>.from(data);
if (map.containsKey(Operation.insertKey)) { if (map.containsKey(Operation.insertKey)) {
@ -73,10 +72,10 @@ class Operation {
return Operation._( return Operation._(
Operation.insertKey, dataLength, data, map[Operation.attributesKey]); Operation.insertKey, dataLength, data, map[Operation.attributesKey]);
} else if (map.containsKey(Operation.deleteKey)) { } else if (map.containsKey(Operation.deleteKey)) {
final int length = map[Operation.deleteKey]; final int? length = map[Operation.deleteKey];
return Operation._(Operation.deleteKey, length, '', null); return Operation._(Operation.deleteKey, length, '', null);
} else if (map.containsKey(Operation.retainKey)) { } else if (map.containsKey(Operation.retainKey)) {
final int length = map[Operation.retainKey]; final int? length = map[Operation.retainKey];
return Operation._( return Operation._(
Operation.retainKey, length, '', map[Operation.attributesKey]); Operation.retainKey, length, '', map[Operation.attributesKey]);
} }
@ -95,13 +94,13 @@ class Operation {
Operation._(Operation.deleteKey, length, '', null); Operation._(Operation.deleteKey, length, '', null);
/// Creates operation which inserts [text] with optional [attributes]. /// Creates operation which inserts [text] with optional [attributes].
factory Operation.insert(dynamic data, [Map<String, dynamic> attributes]) => factory Operation.insert(dynamic data, [Map<String, dynamic>? attributes]) =>
Operation._(Operation.insertKey, data is String ? data.length : 1, data, Operation._(Operation.insertKey, data is String ? data.length : 1, data,
attributes); attributes);
/// Creates operation which retains [length] of characters and optionally /// Creates operation which retains [length] of characters and optionally
/// applies attributes. /// applies attributes.
factory Operation.retain(int length, [Map<String, dynamic> attributes]) => factory Operation.retain(int? length, [Map<String, dynamic>? attributes]) =>
Operation._(Operation.retainKey, length, '', attributes); Operation._(Operation.retainKey, length, '', attributes);
/// Returns value of this operation. /// Returns value of this operation.
@ -119,7 +118,7 @@ class Operation {
bool get isRetain => key == Operation.retainKey; bool get isRetain => key == Operation.retainKey;
/// Returns `true` if this operation has no attributes, e.g. is plain text. /// Returns `true` if this operation has no attributes, e.g. is plain text.
bool get isPlain => (_attributes == null || _attributes.isEmpty); bool get isPlain => (_attributes == null || _attributes!.isEmpty);
/// Returns `true` if this operation sets at least one attribute. /// Returns `true` if this operation sets at least one attribute.
bool get isNotPlain => !isPlain; bool get isNotPlain => !isPlain;
@ -130,7 +129,7 @@ class Operation {
bool get isEmpty => length == 0; bool get isEmpty => length == 0;
/// Returns `true` is this operation is not empty. /// Returns `true` is this operation is not empty.
bool get isNotEmpty => length > 0; bool get isNotEmpty => length! > 0;
@override @override
bool operator ==(other) { bool operator ==(other) {
@ -144,7 +143,8 @@ class Operation {
} }
/// Returns `true` if this operation has attribute specified by [name]. /// Returns `true` if this operation has attribute specified by [name].
bool hasAttribute(String name) => isNotPlain && _attributes.containsKey(name); bool hasAttribute(String name) =>
isNotPlain && _attributes!.containsKey(name);
/// Returns `true` if [other] operation has the same attributes as this one. /// Returns `true` if [other] operation has the same attributes as this one.
bool hasSameAttributes(Operation other) { bool hasSameAttributes(Operation other) {
@ -153,9 +153,9 @@ class Operation {
@override @override
int get hashCode { int get hashCode {
if (_attributes != null && _attributes.isNotEmpty) { if (_attributes != null && _attributes!.isNotEmpty) {
final attrsHash = final attrsHash =
hashObjects(_attributes.entries.map((e) => hash2(e.key, e.value))); hashObjects(_attributes!.entries.map((e) => hash2(e.key, e.value)));
return hash3(key, value, attrsHash); return hash3(key, value, attrsHash);
} }
return hash2(key, value); return hash2(key, value);
@ -181,8 +181,8 @@ class Operation {
/// it is a "change delta". /// it is a "change delta".
class Delta { class Delta {
/// Transforms two attribute sets. /// Transforms two attribute sets.
static Map<String, dynamic> transformAttributes( static Map<String, dynamic>? transformAttributes(
Map<String, dynamic> a, Map<String, dynamic> b, bool priority) { Map<String, dynamic>? a, Map<String, dynamic>? b, bool priority) {
if (a == null) return b; if (a == null) return b;
if (b == null) return null; if (b == null) return null;
@ -197,8 +197,8 @@ class Delta {
} }
/// Composes two attribute sets. /// Composes two attribute sets.
static Map<String, dynamic> composeAttributes( static Map<String, dynamic>? composeAttributes(
Map<String, dynamic> a, Map<String, dynamic> b, Map<String, dynamic>? a, Map<String, dynamic>? b,
{bool keepNull = false}) { {bool keepNull = false}) {
a ??= const {}; a ??= const {};
b ??= const {}; b ??= const {};
@ -217,12 +217,12 @@ class Delta {
///get anti-attr result base on base ///get anti-attr result base on base
static Map<String, dynamic> invertAttributes( static Map<String, dynamic> invertAttributes(
Map<String, dynamic> attr, Map<String, dynamic> base) { Map<String, dynamic>? attr, Map<String, dynamic>? base) {
attr ??= const {}; attr ??= const {};
base ??= const {}; base ??= const {};
var baseInverted = base.keys.fold({}, (memo, key) { var baseInverted = base.keys.fold({}, (dynamic memo, key) {
if (base[key] != attr[key] && attr.containsKey(key)) { if (base![key] != attr![key] && attr.containsKey(key)) {
memo[key] = base[key]; memo[key] = base[key];
} }
return memo; return memo;
@ -230,7 +230,7 @@ class Delta {
var inverted = var inverted =
Map<String, dynamic>.from(attr.keys.fold(baseInverted, (memo, key) { Map<String, dynamic>.from(attr.keys.fold(baseInverted, (memo, key) {
if (base[key] != attr[key] && !base.containsKey(key)) { if (base![key] != attr![key] && !base.containsKey(key)) {
memo[key] = null; memo[key] = null;
} }
return memo; return memo;
@ -242,9 +242,7 @@ class Delta {
int _modificationCount = 0; int _modificationCount = 0;
Delta._(List<Operation> operations) Delta._(List<Operation> operations) : _operations = operations;
: assert(operations != null),
_operations = operations;
/// Creates new empty [Delta]. /// Creates new empty [Delta].
factory Delta() => Delta._(<Operation>[]); factory Delta() => Delta._(<Operation>[]);
@ -257,7 +255,7 @@ class Delta {
/// ///
/// If `dataDecoder` parameter is not null then it is used to additionally /// If `dataDecoder` parameter is not null then it is used to additionally
/// decode the operation's data object. Only applied to insert operations. /// decode the operation's data object. Only applied to insert operations.
static Delta fromJson(List data, {DataDecoder dataDecoder}) { static Delta fromJson(List data, {DataDecoder? dataDecoder}) {
return Delta._(data return Delta._(data
.map((op) => Operation.fromJson(op, dataDecoder: dataDecoder)) .map((op) => Operation.fromJson(op, dataDecoder: dataDecoder))
.toList()); .toList());
@ -304,15 +302,14 @@ class Delta {
int get hashCode => hashObjects(_operations); int get hashCode => hashObjects(_operations);
/// Retain [count] of characters from current position. /// Retain [count] of characters from current position.
void retain(int count, [Map<String, dynamic> attributes]) { void retain(int count, [Map<String, dynamic>? attributes]) {
assert(count >= 0); assert(count >= 0);
if (count == 0) return; // no-op if (count == 0) return; // no-op
push(Operation.retain(count, attributes)); push(Operation.retain(count, attributes));
} }
/// Insert [data] at current position. /// Insert [data] at current position.
void insert(dynamic data, [Map<String, dynamic> attributes]) { void insert(dynamic data, [Map<String, dynamic>? attributes]) {
assert(data != null);
if (data is String && data.isEmpty) return; // no-op if (data is String && data.isEmpty) return; // no-op
push(Operation.insert(data, attributes)); push(Operation.insert(data, attributes));
} }
@ -326,10 +323,10 @@ class Delta {
void _mergeWithTail(Operation operation) { void _mergeWithTail(Operation operation) {
assert(isNotEmpty); assert(isNotEmpty);
assert(operation != null && last.key == operation.key); assert(last.key == operation.key);
assert(operation.data is String && last.data is String); assert(operation.data is String && last.data is String);
final length = operation.length + last.length; final length = operation.length! + last.length!;
final lastText = last.data as String; final lastText = last.data as String;
final opText = operation.data as String; final opText = operation.data as String;
final resultText = lastText + opText; final resultText = lastText + opText;
@ -396,12 +393,13 @@ class Delta {
/// Returns new operation or `null` if operations from [thisIter] and /// Returns new operation or `null` if operations from [thisIter] and
/// [otherIter] nullify each other. For instance, for the pair `insert('abc')` /// [otherIter] nullify each other. For instance, for the pair `insert('abc')`
/// and `delete(3)` composition result would be empty string. /// and `delete(3)` composition result would be empty string.
Operation _composeOperation(DeltaIterator thisIter, DeltaIterator otherIter) { Operation? _composeOperation(
DeltaIterator thisIter, DeltaIterator otherIter) {
if (otherIter.isNextInsert) return otherIter.next(); if (otherIter.isNextInsert) return otherIter.next();
if (thisIter.isNextDelete) return thisIter.next(); if (thisIter.isNextDelete) return thisIter.next();
final length = math.min(thisIter.peekLength(), otherIter.peekLength()); final length = math.min(thisIter.peekLength(), otherIter.peekLength());
final thisOp = thisIter.next(length); final thisOp = thisIter.next(length as int);
final otherOp = otherIter.next(length); final otherOp = otherIter.next(length);
assert(thisOp.length == otherOp.length); assert(thisOp.length == otherOp.length);
@ -448,7 +446,7 @@ class Delta {
/// [thisIter]. /// [thisIter].
/// ///
/// Returns `null` if both operations nullify each other. /// Returns `null` if both operations nullify each other.
Operation _transformOperation( Operation? _transformOperation(
DeltaIterator thisIter, DeltaIterator otherIter, bool priority) { DeltaIterator thisIter, DeltaIterator otherIter, bool priority) {
if (thisIter.isNextInsert && (priority || !otherIter.isNextInsert)) { if (thisIter.isNextInsert && (priority || !otherIter.isNextInsert)) {
return Operation.retain(thisIter.next().length); return Operation.retain(thisIter.next().length);
@ -457,7 +455,7 @@ class Delta {
} }
final length = math.min(thisIter.peekLength(), otherIter.peekLength()); final length = math.min(thisIter.peekLength(), otherIter.peekLength());
final thisOp = thisIter.next(length); final thisOp = thisIter.next(length as int);
final otherOp = otherIter.next(length); final otherOp = otherIter.next(length);
assert(thisOp.length == otherOp.length); assert(thisOp.length == otherOp.length);
@ -520,12 +518,12 @@ class Delta {
var baseIndex = 0; var baseIndex = 0;
for (final op in _operations) { for (final op in _operations) {
if (op.isInsert) { if (op.isInsert) {
inverted.delete(op.length); inverted.delete(op.length!);
} else if (op.isRetain && op.isPlain) { } else if (op.isRetain && op.isPlain) {
inverted.retain(op.length, null); inverted.retain(op.length!, null);
baseIndex += op.length; baseIndex += op.length!;
} else if (op.isDelete || (op.isRetain && op.isNotPlain)) { } else if (op.isDelete || (op.isRetain && op.isNotPlain)) {
final length = op.length; final length = op.length!;
final sliceDelta = base.slice(baseIndex, baseIndex + length); final sliceDelta = base.slice(baseIndex, baseIndex + length);
sliceDelta.toList().forEach((baseOp) { sliceDelta.toList().forEach((baseOp) {
if (op.isDelete) { if (op.isDelete) {
@ -533,7 +531,7 @@ class Delta {
} else if (op.isRetain && op.isNotPlain) { } else if (op.isRetain && op.isNotPlain) {
var invertAttr = invertAttributes(op.attributes, baseOp.attributes); var invertAttr = invertAttributes(op.attributes, baseOp.attributes);
inverted.retain( inverted.retain(
baseOp.length, invertAttr.isEmpty ? null : invertAttr); baseOp.length!, invertAttr.isEmpty ? null : invertAttr);
} }
}); });
baseIndex += length; baseIndex += length;
@ -547,7 +545,7 @@ class Delta {
/// Returns slice of this delta from [start] index (inclusive) to [end] /// Returns slice of this delta from [start] index (inclusive) to [end]
/// (exclusive). /// (exclusive).
Delta slice(int start, [int end]) { Delta slice(int start, [int? end]) {
final delta = Delta(); final delta = Delta();
var index = 0; var index = 0;
var opIterator = DeltaIterator(this); var opIterator = DeltaIterator(this);
@ -559,10 +557,10 @@ class Delta {
if (index < start) { if (index < start) {
op = opIterator.next(start - index); op = opIterator.next(start - index);
} else { } else {
op = opIterator.next(actualEnd - index); op = opIterator.next(actualEnd - index as int);
delta.push(op); delta.push(op);
} }
index += op.length; index += op.length!;
} }
return delta; return delta;
} }
@ -585,12 +583,12 @@ class Delta {
while (iter.hasNext && offset <= index) { while (iter.hasNext && offset <= index) {
final op = iter.next(); final op = iter.next();
if (op.isDelete) { if (op.isDelete) {
index -= math.min(op.length, index - offset); index -= math.min(op.length!, index - offset);
continue; continue;
} else if (op.isInsert && (offset < index || force)) { } else if (op.isInsert && (offset < index || force)) {
index += op.length; index += op.length!;
} }
offset += op.length; offset += op.length!;
} }
return index; return index;
} }
@ -614,7 +612,7 @@ class DeltaIterator {
bool get isNextRetain => nextOperationKey == Operation.retainKey; bool get isNextRetain => nextOperationKey == Operation.retainKey;
String get nextOperationKey { String? get nextOperationKey {
if (_index < delta.length) { if (_index < delta.length) {
return delta.elementAt(_index).key; return delta.elementAt(_index).key;
} else { } else {
@ -630,7 +628,7 @@ class DeltaIterator {
num peekLength() { num peekLength() {
if (_index < delta.length) { if (_index < delta.length) {
final operation = delta._operations[_index]; final operation = delta._operations[_index];
return operation.length - _offset; return operation.length! - _offset;
} }
return double.infinity; return double.infinity;
} }
@ -640,8 +638,6 @@ class DeltaIterator {
/// Optional [length] specifies maximum length of operation to return. Note /// Optional [length] specifies maximum length of operation to return. Note
/// that actual length of returned operation may be less than specified value. /// that actual length of returned operation may be less than specified value.
Operation next([int length = 4294967296]) { Operation next([int length = 4294967296]) {
assert(length != null);
if (_modificationCount != delta._modificationCount) { if (_modificationCount != delta._modificationCount) {
throw ConcurrentModificationError(delta); throw ConcurrentModificationError(delta);
} }
@ -651,21 +647,21 @@ class DeltaIterator {
final opKey = op.key; final opKey = op.key;
final opAttributes = op.attributes; final opAttributes = op.attributes;
final _currentOffset = _offset; final _currentOffset = _offset;
final actualLength = math.min(op.length - _currentOffset, length); final actualLength = math.min(op.length! - _currentOffset, length);
if (actualLength == op.length - _currentOffset) { if (actualLength == op.length! - _currentOffset) {
_index++; _index++;
_offset = 0; _offset = 0;
} else { } else {
_offset += actualLength; _offset += actualLength;
} }
final opData = op.isInsert && op.data is String final opData = op.isInsert && op.data is String
? (op.data as String) ? (op.data as String).substring(
.substring(_currentOffset, _currentOffset + actualLength) _currentOffset as int, _currentOffset + (actualLength as int))
: op.data; : op.data;
final opIsNotEmpty = final opIsNotEmpty =
opData is String ? opData.isNotEmpty : true; // embeds are never empty opData is String ? opData.isNotEmpty : true; // embeds are never empty
final opLength = opData is String ? opData.length : 1; final opLength = opData is String ? opData.length : 1;
final int opActualLength = opIsNotEmpty ? opLength : actualLength; final int opActualLength = opIsNotEmpty ? opLength : actualLength as int;
return Operation._(opKey, opActualLength, opData, opAttributes); return Operation._(opKey, opActualLength, opData, opAttributes);
} }
return Operation.retain(length); return Operation.retain(length);
@ -674,14 +670,14 @@ class DeltaIterator {
/// Skips [length] characters in source delta. /// Skips [length] characters in source delta.
/// ///
/// Returns last skipped operation, or `null` if there was nothing to skip. /// Returns last skipped operation, or `null` if there was nothing to skip.
Operation skip(int length) { Operation? skip(int length) {
var skipped = 0; var skipped = 0;
Operation op; Operation? op;
while (skipped < length && hasNext) { while (skipped < length && hasNext) {
final opLength = peekLength(); final opLength = peekLength();
final skip = math.min(length - skipped, opLength); final skip = math.min(length - skipped, opLength);
op = next(skip); op = next(skip as int);
skipped += op.length; skipped += op.length!;
} }
return op; return op;
} }

@ -9,7 +9,7 @@ abstract class DeleteRule extends Rule {
RuleType get type => RuleType.DELETE; RuleType get type => RuleType.DELETE;
@override @override
validateArgs(int len, Object data, Attribute attribute) { validateArgs(int? len, Object? data, Attribute? attribute) {
assert(len != null); assert(len != null);
assert(data == null); assert(data == null);
assert(attribute == null); assert(attribute == null);
@ -21,10 +21,10 @@ class CatchAllDeleteRule extends DeleteRule {
@override @override
Delta applyRule(Delta document, int index, Delta applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
return Delta() return Delta()
..retain(index) ..retain(index)
..delete(len); ..delete(len!);
} }
} }
@ -32,8 +32,8 @@ class PreserveLineStyleOnMergeRule extends DeleteRule {
const PreserveLineStyleOnMergeRule(); const PreserveLineStyleOnMergeRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
itr.skip(index); itr.skip(index);
Operation op = itr.next(1); Operation op = itr.next(1);
@ -42,30 +42,30 @@ class PreserveLineStyleOnMergeRule extends DeleteRule {
} }
bool isNotPlain = op.isNotPlain; bool isNotPlain = op.isNotPlain;
Map<String, dynamic> attrs = op.attributes; Map<String, dynamic>? attrs = op.attributes;
itr.skip(len - 1); itr.skip(len! - 1);
Delta delta = Delta() Delta delta = Delta()
..retain(index) ..retain(index)
..delete(len); ..delete(len);
while (itr.hasNext) { while (itr.hasNext) {
op = itr.next(); op = itr.next();
String text = op.data is String ? op.data as String : ''; String text = op.data is String ? (op.data as String?)! : '';
int lineBreak = text.indexOf('\n'); int lineBreak = text.indexOf('\n');
if (lineBreak == -1) { if (lineBreak == -1) {
delta..retain(op.length); delta..retain(op.length!);
continue; continue;
} }
Map<String, dynamic> attributes = op.attributes == null Map<String, dynamic>? attributes = op.attributes == null
? null ? null
: op.attributes.map<String, dynamic>((String key, dynamic value) => : op.attributes!.map<String, dynamic>((String key, dynamic value) =>
MapEntry<String, dynamic>(key, null)); MapEntry<String, dynamic>(key, null));
if (isNotPlain) { if (isNotPlain) {
attributes ??= <String, dynamic>{}; attributes ??= <String, dynamic>{};
attributes.addAll(attrs); attributes.addAll(attrs!);
} }
delta..retain(lineBreak)..retain(1, attributes); delta..retain(lineBreak)..retain(1, attributes);
break; break;
@ -78,33 +78,35 @@ class EnsureEmbedLineRule extends DeleteRule {
const EnsureEmbedLineRule(); const EnsureEmbedLineRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
Operation op = itr.skip(index); Operation? op = itr.skip(index);
int indexDelta = 0, lengthDelta = 0, remain = len; int? indexDelta = 0, lengthDelta = 0, remain = len;
bool embedFound = op != null && op.data is! String; bool embedFound = op != null && op.data is! String;
bool hasLineBreakBefore = bool hasLineBreakBefore =
!embedFound && (op == null || (op?.data as String).endsWith('\n')); !embedFound && (op == null || (op.data as String).endsWith('\n'));
if (embedFound) { if (embedFound) {
Operation candidate = itr.next(1); Operation candidate = itr.next(1);
remain--; if (remain != null) {
if (candidate.data == '\n') {
indexDelta++;
lengthDelta--;
candidate = itr.next(1);
remain--; remain--;
if (candidate.data == '\n') { if (candidate.data == '\n') {
lengthDelta++; indexDelta++;
lengthDelta--;
candidate = itr.next(1);
remain--;
if (candidate.data == '\n') {
lengthDelta++;
}
} }
} }
} }
op = itr.skip(remain); op = itr.skip(remain!);
if (op != null && if (op != null &&
(op?.data is String ? op.data as String : '').endsWith('\n')) { (op.data is String ? op.data as String? : '')!.endsWith('\n')) {
Operation candidate = itr.next(1); Operation candidate = itr.next(1);
if (candidate.data is! String && !hasLineBreakBefore) { if (candidate.data is! String && !hasLineBreakBefore) {
embedFound = true; embedFound = true;
@ -118,6 +120,6 @@ class EnsureEmbedLineRule extends DeleteRule {
return Delta() return Delta()
..retain(index + indexDelta) ..retain(index + indexDelta)
..delete(len + lengthDelta); ..delete(len! + lengthDelta);
} }
} }

@ -9,7 +9,7 @@ abstract class FormatRule extends Rule {
RuleType get type => RuleType.FORMAT; RuleType get type => RuleType.FORMAT;
@override @override
validateArgs(int len, Object data, Attribute attribute) { validateArgs(int? len, Object? data, Attribute? attribute) {
assert(len != null); assert(len != null);
assert(data == null); assert(data == null);
assert(attribute != null); assert(attribute != null);
@ -20,9 +20,9 @@ class ResolveLineFormatRule extends FormatRule {
const ResolveLineFormatRule(); const ResolveLineFormatRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (attribute.scope != AttributeScope.BLOCK) { if (attribute!.scope != AttributeScope.BLOCK) {
return null; return null;
} }
@ -30,13 +30,13 @@ class ResolveLineFormatRule extends FormatRule {
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
itr.skip(index); itr.skip(index);
Operation op; Operation op;
for (int cur = 0; cur < len && itr.hasNext; cur += op.length) { for (int cur = 0; cur < len! && itr.hasNext; cur += op.length!) {
op = itr.next(len - cur); op = itr.next(len - cur);
if (op.data is! String || !(op.data as String).contains('\n')) { if (op.data is! String || !(op.data as String).contains('\n')) {
delta.retain(op.length); delta.retain(op.length!);
continue; continue;
} }
String text = op.data; String text = op.data as String;
Delta tmp = Delta(); Delta tmp = Delta();
int offset = 0; int offset = 0;
@ -52,10 +52,10 @@ class ResolveLineFormatRule extends FormatRule {
while (itr.hasNext) { while (itr.hasNext) {
op = itr.next(); op = itr.next();
String text = op.data is String ? op.data as String : ''; String text = op.data is String ? (op.data as String?)! : '';
int lineBreak = text.indexOf('\n'); int lineBreak = text.indexOf('\n');
if (lineBreak < 0) { if (lineBreak < 0) {
delta..retain(op.length); delta..retain(op.length!);
continue; continue;
} }
delta..retain(lineBreak)..retain(1, attribute.toJson()); delta..retain(lineBreak)..retain(1, attribute.toJson());
@ -69,28 +69,28 @@ class FormatLinkAtCaretPositionRule extends FormatRule {
const FormatLinkAtCaretPositionRule(); const FormatLinkAtCaretPositionRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (attribute.key != Attribute.link.key || len > 0) { if (attribute!.key != Attribute.link.key || len! > 0) {
return null; return null;
} }
Delta delta = Delta(); Delta delta = Delta();
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
Operation before = itr.skip(index), after = itr.next(); Operation? before = itr.skip(index), after = itr.next();
int beg = index, retain = 0; int? beg = index, retain = 0;
if (before != null && before.hasAttribute(attribute.key)) { if (before != null && before.hasAttribute(attribute.key)) {
beg -= before.length; beg -= before.length!;
retain = before.length; retain = before.length;
} }
if (after != null && after.hasAttribute(attribute.key)) { if (after.hasAttribute(attribute.key)) {
retain += after.length; if (retain != null) retain += after.length!;
} }
if (retain == 0) { if (retain == 0) {
return null; return null;
} }
delta..retain(beg)..retain(retain, attribute.toJson()); delta..retain(beg)..retain(retain!, attribute.toJson());
return delta; return delta;
} }
} }
@ -99,9 +99,9 @@ class ResolveInlineFormatRule extends FormatRule {
const ResolveInlineFormatRule(); const ResolveInlineFormatRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (attribute.scope != AttributeScope.INLINE) { if (attribute!.scope != AttributeScope.INLINE) {
return null; return null;
} }
@ -110,12 +110,12 @@ class ResolveInlineFormatRule extends FormatRule {
itr.skip(index); itr.skip(index);
Operation op; Operation op;
for (int cur = 0; cur < len && itr.hasNext; cur += op.length) { for (int cur = 0; cur < len! && itr.hasNext; cur += op.length!) {
op = itr.next(len - cur); op = itr.next(len - cur);
String text = op.data is String ? op.data as String : ''; String text = op.data is String ? (op.data as String?)! : '';
int lineBreak = text.indexOf('\n'); int lineBreak = text.indexOf('\n');
if (lineBreak < 0) { if (lineBreak < 0) {
delta.retain(op.length, attribute.toJson()); delta.retain(op.length!, attribute.toJson());
continue; continue;
} }
int pos = 0; int pos = 0;
@ -124,8 +124,8 @@ class ResolveInlineFormatRule extends FormatRule {
pos = lineBreak + 1; pos = lineBreak + 1;
lineBreak = text.indexOf('\n', pos); lineBreak = text.indexOf('\n', pos);
} }
if (pos < op.length) { if (pos < op.length!) {
delta.retain(op.length - pos, attribute.toJson()); delta.retain(op.length! - pos, attribute.toJson());
} }
} }

@ -11,7 +11,7 @@ abstract class InsertRule extends Rule {
RuleType get type => RuleType.INSERT; RuleType get type => RuleType.INSERT;
@override @override
validateArgs(int len, Object data, Attribute attribute) { validateArgs(int? len, Object? data, Attribute? attribute) {
assert(len == null); assert(len == null);
assert(data != null); assert(data != null);
assert(attribute == null); assert(attribute == null);
@ -22,23 +22,21 @@ class PreserveLineStyleOnSplitRule extends InsertRule {
const PreserveLineStyleOnSplitRule(); const PreserveLineStyleOnSplitRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (data is! String || (data as String) != '\n') { if (data is! String || data != '\n') {
return null; return null;
} }
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
Operation before = itr.skip(index); Operation? before = itr.skip(index);
if (before == null || if (before == null ||
before.data is! String || before.data is! String ||
(before.data as String).endsWith('\n')) { (before.data as String).endsWith('\n')) {
return null; return null;
} }
Operation after = itr.next(); Operation after = itr.next();
if (after == null || if (after.data is! String || (after.data as String).startsWith('\n')) {
after.data is! String ||
(after.data as String).startsWith('\n')) {
return null; return null;
} }
@ -50,8 +48,8 @@ class PreserveLineStyleOnSplitRule extends InsertRule {
delta..insert('\n'); delta..insert('\n');
return delta; return delta;
} }
Tuple2<Operation, int> nextNewLine = _getNextNewLine(itr); Tuple2<Operation?, int?> nextNewLine = _getNextNewLine(itr);
Map<String, dynamic> attributes = nextNewLine?.item1?.attributes; Map<String, dynamic>? attributes = nextNewLine.item1?.attributes;
return delta..insert('\n', attributes); return delta..insert('\n', attributes);
} }
@ -61,33 +59,33 @@ class PreserveBlockStyleOnInsertRule extends InsertRule {
const PreserveBlockStyleOnInsertRule(); const PreserveBlockStyleOnInsertRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (data is! String || !(data as String).contains('\n')) { if (data is! String || !data.contains('\n')) {
return null; return null;
} }
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
itr.skip(index); itr.skip(index);
Tuple2<Operation, int> nextNewLine = _getNextNewLine(itr); Tuple2<Operation?, int?> nextNewLine = _getNextNewLine(itr);
Style lineStyle = Style lineStyle =
Style.fromJson(nextNewLine.item1?.attributes ?? <String, dynamic>{}); Style.fromJson(nextNewLine.item1?.attributes ?? <String, dynamic>{});
Attribute attribute = lineStyle.getBlockExceptHeader(); Attribute? attribute = lineStyle.getBlockExceptHeader();
if (attribute == null) { if (attribute == null) {
return null; return null;
} }
var blockStyle = <String, dynamic>{attribute.key: attribute.value}; var blockStyle = <String, dynamic>{attribute.key: attribute.value};
Map<String, dynamic> resetStyle; Map<String, dynamic>? resetStyle;
if (lineStyle.containsKey(Attribute.header.key)) { if (lineStyle.containsKey(Attribute.header.key)) {
resetStyle = Attribute.header.toJson(); resetStyle = Attribute.header.toJson();
} }
List<String> lines = (data as String).split('\n'); List<String> lines = data.split('\n');
Delta delta = Delta()..retain(index); Delta delta = Delta()..retain(index);
for (int i = 0; i < lines.length; i++) { for (int i = 0; i < lines.length; i++) {
String line = lines[i]; String line = lines[i];
@ -102,9 +100,9 @@ class PreserveBlockStyleOnInsertRule extends InsertRule {
} }
if (resetStyle != null) { if (resetStyle != null) {
delta.retain(nextNewLine.item2); delta.retain(nextNewLine.item2!);
delta delta
..retain((nextNewLine.item1.data as String).indexOf('\n')) ..retain((nextNewLine.item1!.data as String).indexOf('\n'))
..retain(1, resetStyle); ..retain(1, resetStyle);
} }
@ -115,26 +113,26 @@ class PreserveBlockStyleOnInsertRule extends InsertRule {
class AutoExitBlockRule extends InsertRule { class AutoExitBlockRule extends InsertRule {
const AutoExitBlockRule(); const AutoExitBlockRule();
bool _isEmptyLine(Operation before, Operation after) { bool _isEmptyLine(Operation? before, Operation? after) {
if (before == null) { if (before == null) {
return true; return true;
} }
return before.data is String && return before.data is String &&
(before.data as String).endsWith('\n') && (before.data as String).endsWith('\n') &&
after.data is String && after!.data is String &&
(after.data as String).startsWith('\n'); (after.data as String).startsWith('\n');
} }
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (data is! String || (data as String) != '\n') { if (data is! String || data != '\n') {
return null; return null;
} }
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
Operation prev = itr.skip(index), cur = itr.next(); Operation? prev = itr.skip(index), cur = itr.next();
Attribute blockStyle = Attribute? blockStyle =
Style.fromJson(cur.attributes).getBlockExceptHeader(); Style.fromJson(cur.attributes).getBlockExceptHeader();
if (cur.isPlain || blockStyle == null) { if (cur.isPlain || blockStyle == null) {
return null; return null;
@ -147,10 +145,10 @@ class AutoExitBlockRule extends InsertRule {
return null; return null;
} }
Tuple2<Operation, int> nextNewLine = _getNextNewLine(itr); Tuple2<Operation?, int?> nextNewLine = _getNextNewLine(itr);
if (nextNewLine.item1 != null && if (nextNewLine.item1 != null &&
nextNewLine.item1.attributes != null && nextNewLine.item1!.attributes != null &&
Style.fromJson(nextNewLine.item1.attributes).getBlockExceptHeader() == Style.fromJson(nextNewLine.item1!.attributes).getBlockExceptHeader() ==
blockStyle) { blockStyle) {
return null; return null;
} }
@ -168,9 +166,9 @@ class ResetLineFormatOnNewLineRule extends InsertRule {
const ResetLineFormatOnNewLineRule(); const ResetLineFormatOnNewLineRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (data is! String || (data as String) != '\n') { if (data is! String || data != '\n') {
return null; return null;
} }
@ -181,9 +179,9 @@ class ResetLineFormatOnNewLineRule extends InsertRule {
return null; return null;
} }
Map<String, dynamic> resetStyle; Map<String, dynamic>? resetStyle;
if (cur.attributes != null && if (cur.attributes != null &&
cur.attributes.containsKey(Attribute.header.key)) { cur.attributes!.containsKey(Attribute.header.key)) {
resetStyle = Attribute.header.toJson(); resetStyle = Attribute.header.toJson();
} }
return Delta() return Delta()
@ -198,33 +196,33 @@ class InsertEmbedsRule extends InsertRule {
const InsertEmbedsRule(); const InsertEmbedsRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (data is String) { if (data is String) {
return null; return null;
} }
Delta delta = Delta()..retain(index); Delta delta = Delta()..retain(index);
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
Operation prev = itr.skip(index), cur = itr.next(); Operation? prev = itr.skip(index), cur = itr.next();
String textBefore = prev?.data is String ? prev.data as String : ''; String? textBefore = prev?.data is String ? prev!.data as String? : '';
String textAfter = cur.data is String ? cur.data as String : ''; String textAfter = cur.data is String ? (cur.data as String?)! : '';
final isNewlineBefore = prev == null || textBefore.endsWith('\n'); final isNewlineBefore = prev == null || textBefore!.endsWith('\n');
final isNewlineAfter = textAfter.startsWith('\n'); final isNewlineAfter = textAfter.startsWith('\n');
if (isNewlineBefore && isNewlineAfter) { if (isNewlineBefore && isNewlineAfter) {
return delta..insert(data); return delta..insert(data);
} }
Map<String, dynamic> lineStyle; Map<String, dynamic>? lineStyle;
if (textAfter.contains('\n')) { if (textAfter.contains('\n')) {
lineStyle = cur.attributes; lineStyle = cur.attributes;
} else { } else {
while (itr.hasNext) { while (itr.hasNext) {
Operation op = itr.next(); Operation op = itr.next();
if ((op.data is String ? op.data as String : '').indexOf('\n') >= 0) { if ((op.data is String ? op.data as String? : '')!.indexOf('\n') >= 0) {
lineStyle = op.attributes; lineStyle = op.attributes;
break; break;
} }
@ -246,13 +244,13 @@ class ForceNewlineForInsertsAroundEmbedRule extends InsertRule {
const ForceNewlineForInsertsAroundEmbedRule(); const ForceNewlineForInsertsAroundEmbedRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (data is! String) { if (data is! String) {
return null; return null;
} }
String text = data as String; String text = data;
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
final prev = itr.skip(index); final prev = itr.skip(index);
final cur = itr.next(); final cur = itr.next();
@ -277,14 +275,14 @@ class AutoFormatLinksRule extends InsertRule {
const AutoFormatLinksRule(); const AutoFormatLinksRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (data is! String || (data as String) != ' ') { if (data is! String || data != ' ') {
return null; return null;
} }
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
Operation prev = itr.skip(index); Operation? prev = itr.skip(index);
if (prev == null || prev.data is! String) { if (prev == null || prev.data is! String) {
return null; return null;
} }
@ -305,7 +303,7 @@ class AutoFormatLinksRule extends InsertRule {
return Delta() return Delta()
..retain(index - cand.length) ..retain(index - cand.length)
..retain(cand.length, attributes) ..retain(cand.length, attributes)
..insert(data as String, prev.attributes); ..insert(data, prev.attributes);
} on FormatException { } on FormatException {
return null; return null;
} }
@ -316,22 +314,22 @@ class PreserveInlineStylesRule extends InsertRule {
const PreserveInlineStylesRule(); const PreserveInlineStylesRule();
@override @override
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
if (data is! String || (data as String).contains('\n')) { if (data is! String || data.contains('\n')) {
return null; return null;
} }
DeltaIterator itr = DeltaIterator(document); DeltaIterator itr = DeltaIterator(document);
Operation prev = itr.skip(index); Operation? prev = itr.skip(index);
if (prev == null || if (prev == null ||
prev.data is! String || prev.data is! String ||
(prev.data as String).contains('\n')) { (prev.data as String).contains('\n')) {
return null; return null;
} }
Map<String, dynamic> attributes = prev.attributes; Map<String, dynamic>? attributes = prev.attributes;
String text = data as String; String text = data;
if (attributes == null || !attributes.containsKey(Attribute.link.key)) { if (attributes == null || !attributes.containsKey(Attribute.link.key)) {
return Delta() return Delta()
..retain(index) ..retain(index)
@ -343,9 +341,7 @@ class PreserveInlineStylesRule extends InsertRule {
..retain(index) ..retain(index)
..insert(text, attributes.isEmpty ? null : attributes); ..insert(text, attributes.isEmpty ? null : attributes);
Operation next = itr.next(); Operation next = itr.next();
if (next == null) {
return delta;
}
Map<String, dynamic> nextAttributes = Map<String, dynamic> nextAttributes =
next.attributes ?? const <String, dynamic>{}; next.attributes ?? const <String, dynamic>{};
if (!nextAttributes.containsKey(Attribute.link.key)) { if (!nextAttributes.containsKey(Attribute.link.key)) {
@ -365,18 +361,19 @@ class CatchAllInsertRule extends InsertRule {
@override @override
Delta applyRule(Delta document, int index, Delta applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
return Delta() return Delta()
..retain(index) ..retain(index)
..insert(data); ..insert(data);
} }
} }
Tuple2<Operation, int> _getNextNewLine(DeltaIterator iterator) { Tuple2<Operation?, int?> _getNextNewLine(DeltaIterator iterator) {
Operation op; Operation op;
for (int skipped = 0; iterator.hasNext; skipped += op.length) { for (int skipped = 0; iterator.hasNext; skipped += op.length!) {
op = iterator.next(); op = iterator.next();
int lineBreak = (op.data is String ? op.data as String : '').indexOf('\n'); int lineBreak =
(op.data is String ? op.data as String? : '')!.indexOf('\n');
if (lineBreak >= 0) { if (lineBreak >= 0) {
return Tuple2(op, skipped); return Tuple2(op, skipped);
} }

@ -11,19 +11,17 @@ enum RuleType { INSERT, DELETE, FORMAT }
abstract class Rule { abstract class Rule {
const Rule(); const Rule();
Delta apply(Delta document, int index, Delta? apply(Delta document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
assert(document != null);
assert(index != null);
validateArgs(len, data, attribute); validateArgs(len, data, attribute);
return applyRule(document, index, return applyRule(document, index,
len: len, data: data, attribute: attribute); len: len, data: data, attribute: attribute);
} }
validateArgs(int len, Object data, Attribute attribute); validateArgs(int? len, Object? data, Attribute? attribute);
Delta applyRule(Delta document, int index, Delta? applyRule(Delta document, int index,
{int len, Object data, Attribute attribute}); {int? len, Object? data, Attribute? attribute});
RuleType get type; RuleType get type;
} }
@ -53,7 +51,7 @@ class Rules {
static Rules getInstance() => _instance; static Rules getInstance() => _instance;
Delta apply(RuleType ruleType, Document document, int index, Delta apply(RuleType ruleType, Document document, int index,
{int len, Object data, Attribute attribute}) { {int? len, Object? data, Attribute? attribute}) {
final delta = document.toDelta(); final delta = document.toDelta();
for (var rule in _rules) { for (var rule in _rules) {
if (rule.type != ruleType) { if (rule.type != ruleType) {

@ -2,7 +2,7 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Color stringToColor(String s) { Color stringToColor(String? s) {
switch (s) { switch (s) {
case 'transparent': case 'transparent':
return Colors.transparent; return Colors.transparent;
@ -106,7 +106,7 @@ Color stringToColor(String s) {
return Colors.brown; return Colors.brown;
} }
if (s.startsWith('rgba')) { if (s!.startsWith('rgba')) {
s = s.substring(5); // trim left 'rgba(' s = s.substring(5); // trim left 'rgba('
s = s.substring(0, s.length - 1); // trim right ')' s = s.substring(0, s.length - 1); // trim right ')'
final arr = s.split(',').map((e) => e.trim()).toList(); final arr = s.split(',').map((e) => e.trim()).toList();

@ -76,7 +76,7 @@ int getPositionDelta(Delta user, Delta actual) {
int diff = 0; int diff = 0;
while (userItr.hasNext || actualItr.hasNext) { while (userItr.hasNext || actualItr.hasNext) {
final length = math.min(userItr.peekLength(), actualItr.peekLength()); final length = math.min(userItr.peekLength(), actualItr.peekLength());
Operation userOperation = userItr.next(length); Operation userOperation = userItr.next(length as int);
Operation actualOperation = actualItr.next(length); Operation actualOperation = actualItr.next(length);
if (userOperation.length != actualOperation.length) { if (userOperation.length != actualOperation.length) {
throw ('userOp ' + throw ('userOp ' +
@ -88,18 +88,18 @@ int getPositionDelta(Delta user, Delta actual) {
if (userOperation.key == actualOperation.key) { if (userOperation.key == actualOperation.key) {
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 = ''; String? operationTxt = '';
if (actualOperation.data is String) { if (actualOperation.data is String) {
operationTxt = actualOperation.data as String; operationTxt = actualOperation.data as String?;
} }
if (operationTxt.startsWith('\n')) { if (operationTxt!.startsWith('\n')) {
continue; continue;
} }
diff += actualOperation.length; diff += actualOperation.length!;
} }
} }
return diff; return diff;

@ -0,0 +1,3 @@
class platformViewRegistry {
static registerViewFactory(String viewId, dynamic cb) {}
}

@ -0,0 +1,9 @@
import 'dart:ui' as ui;
// ignore: camel_case_types
class platformViewRegistry {
static registerViewFactory(String viewId, dynamic cb) {
// ignore:undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(viewId, cb);
}
}

@ -0,0 +1,22 @@
library universal_ui;
import 'package:flutter/foundation.dart';
import 'fake_ui.dart' if (dart.library.html) 'real_ui.dart' as ui_instance;
class PlatformViewRegistryFix {
registerViewFactory(dynamic x, dynamic y) {
if (kIsWeb) {
// ignore: undefined_prefixed_name
ui_instance.platformViewRegistry.registerViewFactory(
x,
y,
);
} else {}
}
}
class UniversalUI {
PlatformViewRegistryFix platformViewRegistry = PlatformViewRegistryFix();
}
var ui = UniversalUI();

@ -4,11 +4,11 @@ import 'package:flutter_quill/models/documents/nodes/container.dart';
abstract class RenderContentProxyBox implements RenderBox { abstract class RenderContentProxyBox implements RenderBox {
double getPreferredLineHeight(); double getPreferredLineHeight();
Offset getOffsetForCaret(TextPosition position, Rect caretPrototype); Offset getOffsetForCaret(TextPosition position, Rect? caretPrototype);
TextPosition getPositionForOffset(Offset offset); TextPosition getPositionForOffset(Offset offset);
double getFullHeightForCaret(TextPosition position); double? getFullHeightForCaret(TextPosition position);
TextRange getWordBoundary(TextPosition position); TextRange getWordBoundary(TextPosition position);
@ -24,9 +24,9 @@ abstract class RenderEditableBox extends RenderBox {
TextPosition getPositionForOffset(Offset offset); TextPosition getPositionForOffset(Offset offset);
TextPosition getPositionAbove(TextPosition position); TextPosition? getPositionAbove(TextPosition position);
TextPosition getPositionBelow(TextPosition position); TextPosition? getPositionBelow(TextPosition position);
TextRange getWordBoundary(TextPosition position); TextRange getWordBoundary(TextPosition position);

@ -14,9 +14,7 @@ class QuillController extends ChangeNotifier {
TextSelection selection; TextSelection selection;
Style toggledStyle = Style(); Style toggledStyle = Style();
QuillController({@required this.document, @required this.selection}) QuillController({required this.document, required this.selection});
: assert(document != null),
assert(selection != null);
factory QuillController.basic() { factory QuillController.basic() {
return QuillController( return QuillController(
@ -49,14 +47,14 @@ class QuillController extends ChangeNotifier {
} }
} }
void _handleHistoryChange(int len) { void _handleHistoryChange(int? len) {
if (len != 0) { if (len != 0) {
// if (this.selection.extentOffset >= document.length) { // if (this.selection.extentOffset >= document.length) {
// // cursor exceeds the length of document, position it in the end // // cursor exceeds the length of document, position it in the end
// updateSelection( // updateSelection(
// TextSelection.collapsed(offset: document.length), ChangeSource.LOCAL); // TextSelection.collapsed(offset: document.length), ChangeSource.LOCAL);
updateSelection( updateSelection(
TextSelection.collapsed(offset: this.selection.baseOffset + len), TextSelection.collapsed(offset: this.selection.baseOffset + len!),
ChangeSource.LOCAL); ChangeSource.LOCAL);
} else { } else {
// no need to move cursor // no need to move cursor
@ -75,19 +73,18 @@ class QuillController extends ChangeNotifier {
get hasRedo => document.hasRedo; get hasRedo => document.hasRedo;
replaceText(int index, int len, Object data, TextSelection textSelection) { replaceText(int index, int len, Object? data, TextSelection? textSelection) {
assert(data is String || data is Embeddable); assert(data is String || data is Embeddable);
Delta delta; Delta? delta;
if (len > 0 || data is! String || (data as String).isNotEmpty) { if (len > 0 || data is! String || data.isNotEmpty) {
try { try {
delta = document.replace(index, len, data); delta = document.replace(index, len, data);
} catch (e) { } catch (e) {
print('document.replace failed: $e'); print('document.replace failed: $e');
throw e; throw e;
} }
bool shouldRetainDelta = delta != null && bool shouldRetainDelta = toggledStyle.isNotEmpty &&
toggledStyle.isNotEmpty &&
delta.isNotEmpty && delta.isNotEmpty &&
delta.length <= 2 && delta.length <= 2 &&
delta.last.isInsert; delta.last.isInsert;
@ -137,8 +134,10 @@ class QuillController extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
formatText(int index, int len, Attribute attribute) { formatText(int index, int len, Attribute? attribute) {
if (len == 0 && attribute.isInline && attribute.key != Attribute.link.key) { if (len == 0 &&
attribute!.isInline &&
attribute.key != Attribute.link.key) {
toggledStyle = toggledStyle.put(attribute); toggledStyle = toggledStyle.put(attribute);
} }
@ -152,7 +151,7 @@ class QuillController extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
formatSelection(Attribute attribute) { formatSelection(Attribute? attribute) {
formatText(selection.start, selection.end - selection.start, attribute); formatText(selection.start, selection.end - selection.start, attribute);
} }
@ -165,18 +164,15 @@ class QuillController extends ChangeNotifier {
if (delta.isNotEmpty) { if (delta.isNotEmpty) {
document.compose(delta, source); document.compose(delta, source);
} }
if (textSelection != null) {
textSelection = selection.copyWith(
baseOffset: delta.transformPosition(selection.baseOffset, force: false),
extentOffset:
delta.transformPosition(selection.extentOffset, force: false));
if (selection != textSelection) {
_updateSelection(textSelection, source); _updateSelection(textSelection, source);
} else {
textSelection = selection.copyWith(
baseOffset:
delta.transformPosition(selection.baseOffset, force: false),
extentOffset:
delta.transformPosition(selection.extentOffset, force: false));
if (selection != textSelection) {
_updateSelection(textSelection, source);
}
} }
notifyListeners(); notifyListeners();
} }
@ -187,8 +183,6 @@ class QuillController extends ChangeNotifier {
} }
_updateSelection(TextSelection textSelection, ChangeSource source) { _updateSelection(TextSelection textSelection, ChangeSource source) {
assert(textSelection != null);
assert(source != null);
selection = textSelection; selection = textSelection;
int end = document.length - 1; int end = document.length - 1;
selection = selection.copyWith( selection = selection.copyWith(

@ -11,25 +11,22 @@ class CursorStyle {
final Color color; final Color color;
final Color backgroundColor; final Color backgroundColor;
final double width; final double width;
final double height; final double? height;
final Radius radius; final Radius? radius;
final Offset offset; final Offset? offset;
final bool opacityAnimates; final bool opacityAnimates;
final bool paintAboveText; final bool paintAboveText;
const CursorStyle({ const CursorStyle({
@required this.color, required this.color,
@required this.backgroundColor, required this.backgroundColor,
this.width = 1.0, this.width = 1.0,
this.height, this.height,
this.radius, this.radius,
this.offset, this.offset,
this.opacityAnimates = false, this.opacityAnimates = false,
this.paintAboveText = false, this.paintAboveText = false,
}) : assert(color != null), });
assert(backgroundColor != null),
assert(opacityAnimates != null),
assert(paintAboveText != null);
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@ -61,19 +58,16 @@ class CursorCont extends ChangeNotifier {
final ValueNotifier<bool> show; final ValueNotifier<bool> show;
final ValueNotifier<bool> _blink; final ValueNotifier<bool> _blink;
final ValueNotifier<Color> color; final ValueNotifier<Color> color;
AnimationController _blinkOpacityCont; late AnimationController _blinkOpacityCont;
Timer _cursorTimer; Timer? _cursorTimer;
bool _targetCursorVisibility = false; bool _targetCursorVisibility = false;
CursorStyle _style; CursorStyle _style;
CursorCont({ CursorCont({
@required ValueNotifier<bool> show, required ValueNotifier<bool> show,
@required CursorStyle style, required CursorStyle style,
@required TickerProvider tickerProvider, required TickerProvider tickerProvider,
}) : assert(show != null), }) : show = show,
assert(style != null),
assert(tickerProvider != null),
show = show ?? ValueNotifier<bool>(false),
_style = style, _style = style,
_blink = ValueNotifier(false), _blink = ValueNotifier(false),
color = ValueNotifier(style.color) { color = ValueNotifier(style.color) {
@ -89,7 +83,6 @@ class CursorCont extends ChangeNotifier {
CursorStyle get style => _style; CursorStyle get style => _style;
set style(CursorStyle value) { set style(CursorStyle value) {
assert(value != null);
if (_style == value) return; if (_style == value) return;
_style = value; _style = value;
notifyListeners(); notifyListeners();
@ -161,9 +154,9 @@ class CursorCont extends ChangeNotifier {
} }
class CursorPainter { class CursorPainter {
final RenderContentProxyBox editable; final RenderContentProxyBox? editable;
final CursorStyle style; final CursorStyle style;
final Rect prototype; final Rect? prototype;
final Color color; final Color color;
final double devicePixelRatio; final double devicePixelRatio;
@ -174,17 +167,17 @@ class CursorPainter {
assert(prototype != null); assert(prototype != null);
Offset caretOffset = Offset caretOffset =
editable.getOffsetForCaret(position, prototype) + offset; editable!.getOffsetForCaret(position, prototype) + offset;
Rect caretRect = prototype.shift(caretOffset); Rect caretRect = prototype!.shift(caretOffset);
if (style.offset != null) { if (style.offset != null) {
caretRect = caretRect.shift(style.offset); caretRect = caretRect.shift(style.offset!);
} }
if (caretRect.left < 0.0) { if (caretRect.left < 0.0) {
caretRect = caretRect.shift(Offset(-caretRect.left, 0.0)); caretRect = caretRect.shift(Offset(-caretRect.left, 0.0));
} }
double caretHeight = editable.getFullHeightForCaret(position); double? caretHeight = editable!.getFullHeightForCaret(position);
if (caretHeight != null) { if (caretHeight != null) {
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.android: case TargetPlatform.android:
@ -212,7 +205,7 @@ class CursorPainter {
} }
} }
Offset caretPosition = editable.localToGlobal(caretRect.topLeft); Offset caretPosition = editable!.localToGlobal(caretRect.topLeft);
double pixelMultiple = 1.0 / devicePixelRatio; double pixelMultiple = 1.0 / devicePixelRatio;
caretRect = caretRect.shift(Offset( caretRect = caretRect.shift(Offset(
caretPosition.dx.isFinite caretPosition.dx.isFinite
@ -230,7 +223,7 @@ class CursorPainter {
return; return;
} }
RRect caretRRect = RRect.fromRectAndRadius(caretRect, style.radius); RRect caretRRect = RRect.fromRectAndRadius(caretRect, style.radius!);
canvas.drawRRect(caretRRect, paint); canvas.drawRRect(caretRRect, paint);
} }
} }

@ -6,25 +6,23 @@ class QuillStyles extends InheritedWidget {
final DefaultStyles data; final DefaultStyles data;
QuillStyles({ QuillStyles({
Key key, Key? key,
@required this.data, required this.data,
@required Widget child, required Widget child,
}) : assert(data != null), }) : super(key: key, child: child);
assert(child != null),
super(key: key, child: child);
@override @override
bool updateShouldNotify(QuillStyles oldWidget) { bool updateShouldNotify(QuillStyles oldWidget) {
return data != oldWidget.data; return data != oldWidget.data;
} }
static DefaultStyles getStyles(BuildContext context, bool nullOk) { static DefaultStyles? getStyles(BuildContext context, bool nullOk) {
var widget = context.dependOnInheritedWidgetOfExactType<QuillStyles>(); var widget = context.dependOnInheritedWidgetOfExactType<QuillStyles>();
if (widget == null && nullOk) { if (widget == null && nullOk) {
return null; return null;
} }
assert(widget != null); assert(widget != null);
return widget.data; return widget!.data;
} }
} }
@ -35,31 +33,31 @@ class DefaultTextBlockStyle {
final Tuple2<double, double> lineSpacing; final Tuple2<double, double> lineSpacing;
final BoxDecoration decoration; final BoxDecoration? decoration;
DefaultTextBlockStyle( DefaultTextBlockStyle(
this.style, this.verticalSpacing, this.lineSpacing, this.decoration); this.style, this.verticalSpacing, this.lineSpacing, this.decoration);
} }
class DefaultStyles { class DefaultStyles {
final DefaultTextBlockStyle h1; final DefaultTextBlockStyle? h1;
final DefaultTextBlockStyle h2; final DefaultTextBlockStyle? h2;
final DefaultTextBlockStyle h3; final DefaultTextBlockStyle? h3;
final DefaultTextBlockStyle paragraph; final DefaultTextBlockStyle? paragraph;
final TextStyle bold; final TextStyle? bold;
final TextStyle italic; final TextStyle? italic;
final TextStyle underline; final TextStyle? underline;
final TextStyle strikeThrough; final TextStyle? strikeThrough;
final TextStyle sizeSmall; // 'small' final TextStyle? sizeSmall; // 'small'
final TextStyle sizeLarge; // 'large' final TextStyle? sizeLarge; // 'large'
final TextStyle sizeHuge; // 'huge' final TextStyle? sizeHuge; // 'huge'
final TextStyle link; final TextStyle? link;
final DefaultTextBlockStyle placeHolder; final DefaultTextBlockStyle? placeHolder;
final DefaultTextBlockStyle lists; final DefaultTextBlockStyle? lists;
final DefaultTextBlockStyle quote; final DefaultTextBlockStyle? quote;
final DefaultTextBlockStyle code; final DefaultTextBlockStyle? code;
final DefaultTextBlockStyle indent; final DefaultTextBlockStyle? indent;
final DefaultTextBlockStyle align; final DefaultTextBlockStyle? align;
DefaultStyles( DefaultStyles(
{this.h1, {this.h1,
@ -109,7 +107,7 @@ class DefaultStyles {
h1: DefaultTextBlockStyle( h1: DefaultTextBlockStyle(
defaultTextStyle.style.copyWith( defaultTextStyle.style.copyWith(
fontSize: 34.0, fontSize: 34.0,
color: defaultTextStyle.style.color.withOpacity(0.70), color: defaultTextStyle.style.color!.withOpacity(0.70),
height: 1.15, height: 1.15,
fontWeight: FontWeight.w300, fontWeight: FontWeight.w300,
), ),
@ -119,7 +117,7 @@ class DefaultStyles {
h2: DefaultTextBlockStyle( h2: DefaultTextBlockStyle(
defaultTextStyle.style.copyWith( defaultTextStyle.style.copyWith(
fontSize: 24.0, fontSize: 24.0,
color: defaultTextStyle.style.color.withOpacity(0.70), color: defaultTextStyle.style.color!.withOpacity(0.70),
height: 1.15, height: 1.15,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
@ -129,7 +127,7 @@ class DefaultStyles {
h3: DefaultTextBlockStyle( h3: DefaultTextBlockStyle(
defaultTextStyle.style.copyWith( defaultTextStyle.style.copyWith(
fontSize: 20.0, fontSize: 20.0,
color: defaultTextStyle.style.color.withOpacity(0.70), color: defaultTextStyle.style.color!.withOpacity(0.70),
height: 1.25, height: 1.25,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@ -158,7 +156,7 @@ class DefaultStyles {
lists: DefaultTextBlockStyle( lists: DefaultTextBlockStyle(
baseStyle, baseSpacing, Tuple2(0.0, 6.0), null), baseStyle, baseSpacing, Tuple2(0.0, 6.0), null),
quote: DefaultTextBlockStyle( quote: DefaultTextBlockStyle(
TextStyle(color: baseStyle.color.withOpacity(0.6)), TextStyle(color: baseStyle.color!.withOpacity(0.6)),
baseSpacing, baseSpacing,
Tuple2(6.0, 2.0), Tuple2(6.0, 2.0),
BoxDecoration( BoxDecoration(

@ -14,28 +14,27 @@ abstract class EditorTextSelectionGestureDetectorBuilderDelegate {
bool getForcePressEnabled(); bool getForcePressEnabled();
bool getSelectionEnabled(); bool? getSelectionEnabled();
} }
class EditorTextSelectionGestureDetectorBuilder { class EditorTextSelectionGestureDetectorBuilder {
final EditorTextSelectionGestureDetectorBuilderDelegate delegate; final EditorTextSelectionGestureDetectorBuilderDelegate delegate;
bool shouldShowSelectionToolbar = true; bool shouldShowSelectionToolbar = true;
EditorTextSelectionGestureDetectorBuilder(this.delegate) EditorTextSelectionGestureDetectorBuilder(this.delegate);
: assert(delegate != null);
EditorState getEditor() { EditorState? getEditor() {
return delegate.getEditableTextKey().currentState; return delegate.getEditableTextKey().currentState;
} }
RenderEditor getRenderEditor() { RenderEditor? getRenderEditor() {
return this.getEditor().getRenderEditor(); return this.getEditor()!.getRenderEditor();
} }
onTapDown(TapDownDetails details) { onTapDown(TapDownDetails details) {
getRenderEditor().handleTapDown(details); getRenderEditor()!.handleTapDown(details);
PointerDeviceKind kind = details.kind; PointerDeviceKind? kind = details.kind;
shouldShowSelectionToolbar = kind == null || shouldShowSelectionToolbar = kind == null ||
kind == PointerDeviceKind.touch || kind == PointerDeviceKind.touch ||
kind == PointerDeviceKind.stylus; kind == PointerDeviceKind.stylus;
@ -44,8 +43,8 @@ class EditorTextSelectionGestureDetectorBuilder {
onForcePressStart(ForcePressDetails details) { onForcePressStart(ForcePressDetails details) {
assert(delegate.getForcePressEnabled()); assert(delegate.getForcePressEnabled());
shouldShowSelectionToolbar = true; shouldShowSelectionToolbar = true;
if (delegate.getSelectionEnabled()) { if (delegate.getSelectionEnabled()!) {
getRenderEditor().selectWordsInRange( getRenderEditor()!.selectWordsInRange(
details.globalPosition, details.globalPosition,
null, null,
SelectionChangedCause.forcePress, SelectionChangedCause.forcePress,
@ -55,27 +54,27 @@ class EditorTextSelectionGestureDetectorBuilder {
onForcePressEnd(ForcePressDetails details) { onForcePressEnd(ForcePressDetails details) {
assert(delegate.getForcePressEnabled()); assert(delegate.getForcePressEnabled());
getRenderEditor().selectWordsInRange( getRenderEditor()!.selectWordsInRange(
details.globalPosition, details.globalPosition,
null, null,
SelectionChangedCause.forcePress, SelectionChangedCause.forcePress,
); );
if (shouldShowSelectionToolbar) { if (shouldShowSelectionToolbar) {
getEditor().showToolbar(); getEditor()!.showToolbar();
} }
} }
onSingleTapUp(TapUpDetails details) { onSingleTapUp(TapUpDetails details) {
if (delegate.getSelectionEnabled()) { if (delegate.getSelectionEnabled()!) {
getRenderEditor().selectWordEdge(SelectionChangedCause.tap); getRenderEditor()!.selectWordEdge(SelectionChangedCause.tap);
} }
} }
onSingleTapCancel() {} onSingleTapCancel() {}
onSingleLongTapStart(LongPressStartDetails details) { onSingleLongTapStart(LongPressStartDetails details) {
if (delegate.getSelectionEnabled()) { if (delegate.getSelectionEnabled()!) {
getRenderEditor().selectPositionAt( getRenderEditor()!.selectPositionAt(
details.globalPosition, details.globalPosition,
null, null,
SelectionChangedCause.longPress, SelectionChangedCause.longPress,
@ -84,8 +83,8 @@ class EditorTextSelectionGestureDetectorBuilder {
} }
onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) { onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
if (delegate.getSelectionEnabled()) { if (delegate.getSelectionEnabled()!) {
getRenderEditor().selectPositionAt( getRenderEditor()!.selectPositionAt(
details.globalPosition, details.globalPosition,
null, null,
SelectionChangedCause.longPress, SelectionChangedCause.longPress,
@ -95,21 +94,21 @@ class EditorTextSelectionGestureDetectorBuilder {
onSingleLongTapEnd(LongPressEndDetails details) { onSingleLongTapEnd(LongPressEndDetails details) {
if (shouldShowSelectionToolbar) { if (shouldShowSelectionToolbar) {
getEditor().showToolbar(); getEditor()!.showToolbar();
} }
} }
onDoubleTapDown(TapDownDetails details) { onDoubleTapDown(TapDownDetails details) {
if (delegate.getSelectionEnabled()) { if (delegate.getSelectionEnabled()!) {
getRenderEditor().selectWord(SelectionChangedCause.tap); getRenderEditor()!.selectWord(SelectionChangedCause.tap);
if (shouldShowSelectionToolbar) { if (shouldShowSelectionToolbar) {
getEditor().showToolbar(); getEditor()!.showToolbar();
} }
} }
} }
onDragSelectionStart(DragStartDetails details) { onDragSelectionStart(DragStartDetails details) {
getRenderEditor().selectPositionAt( getRenderEditor()!.selectPositionAt(
details.globalPosition, details.globalPosition,
null, null,
SelectionChangedCause.drag, SelectionChangedCause.drag,
@ -118,7 +117,7 @@ class EditorTextSelectionGestureDetectorBuilder {
onDragSelectionUpdate( onDragSelectionUpdate(
DragStartDetails startDetails, DragUpdateDetails updateDetails) { DragStartDetails startDetails, DragUpdateDetails updateDetails) {
getRenderEditor().selectPositionAt( getRenderEditor()!.selectPositionAt(
startDetails.globalPosition, startDetails.globalPosition,
updateDetails.globalPosition, updateDetails.globalPosition,
SelectionChangedCause.drag, SelectionChangedCause.drag,

@ -16,13 +16,13 @@ import 'package:flutter_quill/models/documents/nodes/embed.dart';
import 'package:flutter_quill/models/documents/nodes/leaf.dart' as leaf; import 'package:flutter_quill/models/documents/nodes/leaf.dart' as leaf;
import 'package:flutter_quill/models/documents/nodes/line.dart'; import 'package:flutter_quill/models/documents/nodes/line.dart';
import 'package:flutter_quill/models/documents/nodes/node.dart'; import 'package:flutter_quill/models/documents/nodes/node.dart';
import 'package:flutter_quill/utils/universal_ui/universal_ui.dart';
import 'package:flutter_quill/widgets/image.dart'; import 'package:flutter_quill/widgets/image.dart';
import 'package:flutter_quill/widgets/raw_editor.dart'; import 'package:flutter_quill/widgets/raw_editor.dart';
import 'package:flutter_quill/widgets/responsive_widget.dart'; import 'package:flutter_quill/widgets/responsive_widget.dart';
import 'package:flutter_quill/widgets/text_selection.dart'; import 'package:flutter_quill/widgets/text_selection.dart';
import 'package:string_validator/string_validator.dart'; import 'package:string_validator/string_validator.dart';
import 'package:universal_html/prefer_universal/html.dart' as html; import 'package:universal_html/html.dart' as html;
import 'package:universal_ui/universal_ui.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'box.dart'; import 'box.dart';
@ -53,9 +53,9 @@ abstract class EditorState extends State<RawEditor> {
void setTextEditingValue(TextEditingValue value); void setTextEditingValue(TextEditingValue value);
RenderEditor getRenderEditor(); RenderEditor? getRenderEditor();
EditorTextSelectionOverlay getSelectionOverlay(); EditorTextSelectionOverlay? getSelectionOverlay();
bool showToolbar(); bool showToolbar();
@ -146,51 +146,45 @@ class QuillEditor extends StatefulWidget {
final bool scrollable; final bool scrollable;
final EdgeInsetsGeometry padding; final EdgeInsetsGeometry padding;
final bool autoFocus; final bool autoFocus;
final bool showCursor; final bool? showCursor;
final bool readOnly; final bool readOnly;
final String placeholder; final String? placeholder;
final bool enableInteractiveSelection; final bool? enableInteractiveSelection;
final double minHeight; final double? minHeight;
final double maxHeight; final double? maxHeight;
final DefaultStyles customStyles; final DefaultStyles? customStyles;
final bool expands; final bool expands;
final TextCapitalization textCapitalization; final TextCapitalization textCapitalization;
final Brightness keyboardAppearance; final Brightness keyboardAppearance;
final ScrollPhysics scrollPhysics; final ScrollPhysics? scrollPhysics;
final ValueChanged<String> onLaunchUrl; final ValueChanged<String>? onLaunchUrl;
final EmbedBuilder embedBuilder; final EmbedBuilder embedBuilder;
QuillEditor( QuillEditor(
{@required this.controller, {Key? key,
@required this.focusNode, required this.controller,
@required this.scrollController, required this.focusNode,
@required this.scrollable, required this.scrollController,
@required this.padding, required this.scrollable,
@required this.autoFocus, required this.padding,
required this.autoFocus,
this.showCursor, this.showCursor,
@required this.readOnly, required this.readOnly,
this.placeholder, this.placeholder,
this.enableInteractiveSelection, this.enableInteractiveSelection,
this.minHeight, this.minHeight,
this.maxHeight, this.maxHeight,
this.customStyles, this.customStyles,
@required this.expands, required this.expands,
this.textCapitalization = TextCapitalization.sentences, this.textCapitalization = TextCapitalization.sentences,
this.keyboardAppearance = Brightness.light, this.keyboardAppearance = Brightness.light,
this.scrollPhysics, this.scrollPhysics,
this.onLaunchUrl, this.onLaunchUrl,
this.embedBuilder = this.embedBuilder =
kIsWeb ? _defaultEmbedBuilderWeb : _defaultEmbedBuilder}) kIsWeb ? _defaultEmbedBuilderWeb : _defaultEmbedBuilder});
: assert(controller != null),
assert(scrollController != null),
assert(scrollable != null),
assert(focusNode != null),
assert(autoFocus != null),
assert(readOnly != null),
assert(embedBuilder != null);
factory QuillEditor.basic( factory QuillEditor.basic(
{@required QuillController controller, bool readOnly}) { {Key? key, required QuillController controller, required bool readOnly}) {
return QuillEditor( return QuillEditor(
controller: controller, controller: controller,
scrollController: ScrollController(), scrollController: ScrollController(),
@ -209,8 +203,9 @@ class QuillEditor extends StatefulWidget {
class _QuillEditorState extends State<QuillEditor> class _QuillEditorState extends State<QuillEditor>
implements EditorTextSelectionGestureDetectorBuilderDelegate { implements EditorTextSelectionGestureDetectorBuilderDelegate {
final GlobalKey<EditorState> _editorKey = GlobalKey<EditorState>(); GlobalKey<EditorState> _editorKey = GlobalKey<EditorState>();
EditorTextSelectionGestureDetectorBuilder _selectionGestureDetectorBuilder; late EditorTextSelectionGestureDetectorBuilder
_selectionGestureDetectorBuilder;
@override @override
void initState() { void initState() {
@ -227,10 +222,10 @@ class _QuillEditorState extends State<QuillEditor>
TextSelectionControls textSelectionControls; TextSelectionControls textSelectionControls;
bool paintCursorAboveText; bool paintCursorAboveText;
bool cursorOpacityAnimates; bool cursorOpacityAnimates;
Offset cursorOffset; Offset? cursorOffset;
Color cursorColor; Color? cursorColor;
Color selectionColor; Color selectionColor;
Radius cursorRadius; Radius? cursorRadius;
switch (theme.platform) { switch (theme.platform) {
case TargetPlatform.android: case TargetPlatform.android:
@ -301,7 +296,7 @@ class _QuillEditorState extends State<QuillEditor>
selectionColor, selectionColor,
textSelectionControls, textSelectionControls,
widget.keyboardAppearance, widget.keyboardAppearance,
widget.enableInteractiveSelection, widget.enableInteractiveSelection!,
widget.scrollPhysics, widget.scrollPhysics,
widget.embedBuilder), widget.embedBuilder),
); );
@ -318,12 +313,12 @@ class _QuillEditorState extends State<QuillEditor>
} }
@override @override
bool getSelectionEnabled() { bool? getSelectionEnabled() {
return widget.enableInteractiveSelection; return widget.enableInteractiveSelection;
} }
_requestKeyboard() { _requestKeyboard() {
_editorKey.currentState.requestKeyboard(); _editorKey.currentState!.requestKeyboard();
} }
} }
@ -336,8 +331,8 @@ class _QuillEditorSelectionGestureDetectorBuilder
@override @override
onForcePressStart(ForcePressDetails details) { onForcePressStart(ForcePressDetails details) {
super.onForcePressStart(details); super.onForcePressStart(details);
if (delegate.getSelectionEnabled() && shouldShowSelectionToolbar) { if (delegate.getSelectionEnabled()! && shouldShowSelectionToolbar) {
getEditor().showToolbar(); getEditor()!.showToolbar();
} }
} }
@ -346,13 +341,13 @@ class _QuillEditorSelectionGestureDetectorBuilder
@override @override
void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) { void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
if (!delegate.getSelectionEnabled()) { if (!delegate.getSelectionEnabled()!) {
return; return;
} }
switch (Theme.of(_state.context).platform) { switch (Theme.of(_state.context).platform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS: case TargetPlatform.macOS:
getRenderEditor().selectPositionAt( getRenderEditor()!.selectPositionAt(
details.globalPosition, details.globalPosition,
null, null,
SelectionChangedCause.longPress, SelectionChangedCause.longPress,
@ -362,7 +357,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.linux: case TargetPlatform.linux:
case TargetPlatform.windows: case TargetPlatform.windows:
getRenderEditor().selectWordsInRange( getRenderEditor()!.selectWordsInRange(
details.globalPosition - details.offsetFromOrigin, details.globalPosition - details.offsetFromOrigin,
details.globalPosition, details.globalPosition,
SelectionChangedCause.longPress, SelectionChangedCause.longPress,
@ -378,9 +373,9 @@ class _QuillEditorSelectionGestureDetectorBuilder
return false; return false;
} }
TextPosition pos = TextPosition pos =
getRenderEditor().getPositionForOffset(details.globalPosition); getRenderEditor()!.getPositionForOffset(details.globalPosition);
containerNode.ChildQuery result = containerNode.ChildQuery result =
getEditor().widget.controller.document.queryChild(pos.offset); getEditor()!.widget.controller.document.queryChild(pos.offset);
if (result.node == null) { if (result.node == null) {
return false; return false;
} }
@ -391,7 +386,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
if (line.length == 1) { if (line.length == 1) {
// tapping when no text yet on this line // tapping when no text yet on this line
_flipListCheckbox(pos, line, segmentResult); _flipListCheckbox(pos, line, segmentResult);
getEditor().widget.controller.updateSelection( getEditor()!.widget.controller.updateSelection(
TextSelection.collapsed(offset: pos.offset), ChangeSource.LOCAL); TextSelection.collapsed(offset: pos.offset), ChangeSource.LOCAL);
return true; return true;
} }
@ -399,33 +394,34 @@ class _QuillEditorSelectionGestureDetectorBuilder
} }
leaf.Leaf segment = segmentResult.node as leaf.Leaf; leaf.Leaf segment = segmentResult.node as leaf.Leaf;
if (segment.style.containsKey(Attribute.link.key)) { if (segment.style.containsKey(Attribute.link.key)) {
var launchUrl = getEditor().widget.onLaunchUrl; var launchUrl = getEditor()!.widget.onLaunchUrl;
if (launchUrl == null) { if (launchUrl == null) {
launchUrl = _launchUrl; launchUrl = _launchUrl;
} }
String link = segment.style.attributes[Attribute.link.key].value; String? link = segment.style.attributes[Attribute.link.key]!.value;
if (getEditor().widget.readOnly && link != null) { if (getEditor()!.widget.readOnly && link != null) {
link = link.trim(); link = link.trim();
if (!linkPrefixes if (!linkPrefixes
.any((linkPrefix) => link.toLowerCase().startsWith(linkPrefix))) { .any((linkPrefix) => link!.toLowerCase().startsWith(linkPrefix))) {
link = 'https://$link'; link = 'https://$link';
} }
launchUrl(link); launchUrl(link);
} }
return false; return false;
} }
if (getEditor().widget.readOnly && segment.value is BlockEmbed) { if (getEditor()!.widget.readOnly && segment.value is BlockEmbed) {
BlockEmbed blockEmbed = segment.value as BlockEmbed; BlockEmbed blockEmbed = segment.value as BlockEmbed;
if (blockEmbed.type == 'image') { if (blockEmbed.type == 'image') {
final String imageUrl = blockEmbed.data; final String imageUrl = blockEmbed.data;
Navigator.push( Navigator.push(
getEditor().context, getEditor()!.context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => ImageTapWrapper( builder: (context) => ImageTapWrapper(
imageProvider: imageUrl.startsWith('http') imageProvider: imageUrl.startsWith('http')
? NetworkImage(imageUrl) ? NetworkImage(imageUrl)
: isBase64(imageUrl) : isBase64(imageUrl)
? Image.memory(base64.decode(imageUrl)) ? Image.memory(base64.decode(imageUrl))
as ImageProvider<Object>?
: FileImage(io.File(imageUrl)), : FileImage(io.File(imageUrl)),
), ),
), ),
@ -441,25 +437,25 @@ class _QuillEditorSelectionGestureDetectorBuilder
bool _flipListCheckbox( bool _flipListCheckbox(
TextPosition pos, Line line, containerNode.ChildQuery segmentResult) { TextPosition pos, Line line, containerNode.ChildQuery segmentResult) {
if (getEditor().widget.readOnly || if (getEditor()!.widget.readOnly ||
!line.style.containsKey(Attribute.list.key) || !line.style.containsKey(Attribute.list.key) ||
segmentResult.offset != 0) { segmentResult.offset != 0) {
return false; return false;
} }
// segmentResult.offset == 0 means tap at the beginning of the TextLine // segmentResult.offset == 0 means tap at the beginning of the TextLine
String listVal = line.style.attributes[Attribute.list.key].value; String? listVal = line.style.attributes[Attribute.list.key]!.value;
if (listVal == Attribute.unchecked.value) { if (listVal == Attribute.unchecked.value) {
getEditor() getEditor()!
.widget .widget
.controller .controller
.formatText(pos.offset, 0, Attribute.checked); .formatText(pos.offset, 0, Attribute.checked);
} else if (listVal == Attribute.checked.value) { } else if (listVal == Attribute.checked.value) {
getEditor() getEditor()!
.widget .widget
.controller .controller
.formatText(pos.offset, 0, Attribute.unchecked); .formatText(pos.offset, 0, Attribute.unchecked);
} }
getEditor().widget.controller.updateSelection( getEditor()!.widget.controller.updateSelection(
TextSelection.collapsed(offset: pos.offset), ChangeSource.LOCAL); TextSelection.collapsed(offset: pos.offset), ChangeSource.LOCAL);
return true; return true;
} }
@ -470,11 +466,11 @@ class _QuillEditorSelectionGestureDetectorBuilder
@override @override
onSingleTapUp(TapUpDetails details) { onSingleTapUp(TapUpDetails details) {
getEditor().hideToolbar(); getEditor()!.hideToolbar();
bool positionSelected = _onTapping(details); bool positionSelected = _onTapping(details);
if (delegate.getSelectionEnabled() && !positionSelected) { if (delegate.getSelectionEnabled()! && !positionSelected) {
switch (Theme.of(_state.context).platform) { switch (Theme.of(_state.context).platform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS: case TargetPlatform.macOS:
@ -482,11 +478,11 @@ class _QuillEditorSelectionGestureDetectorBuilder
case PointerDeviceKind.mouse: case PointerDeviceKind.mouse:
case PointerDeviceKind.stylus: case PointerDeviceKind.stylus:
case PointerDeviceKind.invertedStylus: case PointerDeviceKind.invertedStylus:
getRenderEditor().selectPosition(SelectionChangedCause.tap); getRenderEditor()!.selectPosition(SelectionChangedCause.tap);
break; break;
case PointerDeviceKind.touch: case PointerDeviceKind.touch:
case PointerDeviceKind.unknown: case PointerDeviceKind.unknown:
getRenderEditor().selectWordEdge(SelectionChangedCause.tap); getRenderEditor()!.selectWordEdge(SelectionChangedCause.tap);
break; break;
} }
break; break;
@ -494,7 +490,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.linux: case TargetPlatform.linux:
case TargetPlatform.windows: case TargetPlatform.windows:
getRenderEditor().selectPosition(SelectionChangedCause.tap); getRenderEditor()!.selectPosition(SelectionChangedCause.tap);
break; break;
} }
} }
@ -503,11 +499,11 @@ class _QuillEditorSelectionGestureDetectorBuilder
@override @override
void onSingleLongTapStart(LongPressStartDetails details) { void onSingleLongTapStart(LongPressStartDetails details) {
if (delegate.getSelectionEnabled()) { if (delegate.getSelectionEnabled()!) {
switch (Theme.of(_state.context).platform) { switch (Theme.of(_state.context).platform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS: case TargetPlatform.macOS:
getRenderEditor().selectPositionAt( getRenderEditor()!.selectPositionAt(
details.globalPosition, details.globalPosition,
null, null,
SelectionChangedCause.longPress, SelectionChangedCause.longPress,
@ -517,7 +513,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.linux: case TargetPlatform.linux:
case TargetPlatform.windows: case TargetPlatform.windows:
getRenderEditor().selectWord(SelectionChangedCause.longPress); getRenderEditor()!.selectWord(SelectionChangedCause.longPress);
Feedback.forLongPress(_state.context); Feedback.forLongPress(_state.context);
break; break;
default: default:
@ -548,7 +544,7 @@ class RenderEditor extends RenderEditableContainerBox
final ValueNotifier<bool> _selectionEndInViewport = ValueNotifier<bool>(true); final ValueNotifier<bool> _selectionEndInViewport = ValueNotifier<bool>(true);
RenderEditor( RenderEditor(
List<RenderEditableBox> children, List<RenderEditableBox>? children,
TextDirection textDirection, TextDirection textDirection,
EdgeInsetsGeometry padding, EdgeInsetsGeometry padding,
this.document, this.document,
@ -558,11 +554,7 @@ class RenderEditor extends RenderEditableContainerBox
this._startHandleLayerLink, this._startHandleLayerLink,
this._endHandleLayerLink, this._endHandleLayerLink,
EdgeInsets floatingCursorAddedMargin) EdgeInsets floatingCursorAddedMargin)
: assert(document != null), : super(
assert(textDirection != null),
assert(_hasFocus != null),
assert(floatingCursorAddedMargin != null),
super(
children, children,
document.root, document.root,
textDirection, textDirection,
@ -570,7 +562,6 @@ class RenderEditor extends RenderEditableContainerBox
); );
setDocument(Document doc) { setDocument(Document doc) {
assert(doc != null);
if (document == doc) { if (document == doc) {
return; return;
} }
@ -579,7 +570,6 @@ class RenderEditor extends RenderEditableContainerBox
} }
setHasFocus(bool h) { setHasFocus(bool h) {
assert(h != null);
if (_hasFocus == h) { if (_hasFocus == h) {
return; return;
} }
@ -614,15 +604,13 @@ class RenderEditor extends RenderEditableContainerBox
@override @override
List<TextSelectionPoint> getEndpointsForSelection( List<TextSelectionPoint> getEndpointsForSelection(
TextSelection textSelection) { TextSelection textSelection) {
assert(constraints != null);
if (textSelection.isCollapsed) { if (textSelection.isCollapsed) {
RenderEditableBox child = childAtPosition(textSelection.extent); RenderEditableBox child = childAtPosition(textSelection.extent);
TextPosition localPosition = TextPosition( TextPosition localPosition = TextPosition(
offset: offset:
textSelection.extentOffset - child.getContainer().getOffset()); textSelection.extentOffset - child.getContainer().getOffset());
Offset localOffset = child.getOffsetForCaret(localPosition); Offset localOffset = child.getOffsetForCaret(localPosition);
BoxParentData parentData = child.parentData; BoxParentData parentData = child.parentData as BoxParentData;
return <TextSelectionPoint>[ return <TextSelectionPoint>[
TextSelectionPoint( TextSelectionPoint(
Offset(0.0, child.preferredLineHeight(localPosition)) + Offset(0.0, child.preferredLineHeight(localPosition)) +
@ -632,7 +620,7 @@ class RenderEditor extends RenderEditableContainerBox
]; ];
} }
Node baseNode = _container.queryChild(textSelection.start, false).node; Node? baseNode = _container.queryChild(textSelection.start, false).node;
var baseChild = firstChild; var baseChild = firstChild;
while (baseChild != null) { while (baseChild != null) {
@ -643,7 +631,7 @@ class RenderEditor extends RenderEditableContainerBox
} }
assert(baseChild != null); assert(baseChild != null);
BoxParentData baseParentData = baseChild.parentData; BoxParentData baseParentData = baseChild!.parentData as BoxParentData;
TextSelection baseSelection = TextSelection baseSelection =
localSelection(baseChild.getContainer(), textSelection, true); localSelection(baseChild.getContainer(), textSelection, true);
TextSelectionPoint basePoint = TextSelectionPoint basePoint =
@ -651,8 +639,8 @@ class RenderEditor extends RenderEditableContainerBox
basePoint = TextSelectionPoint( basePoint = TextSelectionPoint(
basePoint.point + baseParentData.offset, basePoint.direction); basePoint.point + baseParentData.offset, basePoint.direction);
Node extentNode = _container.queryChild(textSelection.end, false).node; Node? extentNode = _container.queryChild(textSelection.end, false).node;
var extentChild = baseChild; RenderEditableBox? extentChild = baseChild;
while (extentChild != null) { while (extentChild != null) {
if (extentChild.getContainer() == extentNode) { if (extentChild.getContainer() == extentNode) {
break; break;
@ -661,7 +649,7 @@ class RenderEditor extends RenderEditableContainerBox
} }
assert(extentChild != null); assert(extentChild != null);
BoxParentData extentParentData = extentChild.parentData; BoxParentData extentParentData = extentChild!.parentData as BoxParentData;
TextSelection extentSelection = TextSelection extentSelection =
localSelection(extentChild.getContainer(), textSelection, true); localSelection(extentChild.getContainer(), textSelection, true);
TextSelectionPoint extentPoint = TextSelectionPoint extentPoint =
@ -672,7 +660,7 @@ class RenderEditor extends RenderEditableContainerBox
return <TextSelectionPoint>[basePoint, extentPoint]; return <TextSelectionPoint>[basePoint, extentPoint];
} }
Offset _lastTapDownPosition; Offset? _lastTapDownPosition;
@override @override
handleTapDown(TapDownDetails details) { handleTapDown(TapDownDetails details) {
@ -682,14 +670,9 @@ class RenderEditor extends RenderEditableContainerBox
@override @override
selectWordsInRange( selectWordsInRange(
Offset from, Offset from,
Offset to, Offset? to,
SelectionChangedCause cause, SelectionChangedCause cause,
) { ) {
assert(cause != null);
assert(from != null);
if (onSelectionChanged == null) {
return;
}
TextPosition firstPosition = getPositionForOffset(from); TextPosition firstPosition = getPositionForOffset(from);
TextSelection firstWord = selectWordAtPosition(firstPosition); TextSelection firstWord = selectWordAtPosition(firstPosition);
TextSelection lastWord = TextSelection lastWord =
@ -717,19 +700,13 @@ class RenderEditor extends RenderEditableContainerBox
!focusingEmpty) { !focusingEmpty) {
return; return;
} }
if (onSelectionChanged != null) { onSelectionChanged(nextSelection, cause);
onSelectionChanged(nextSelection, cause);
}
} }
@override @override
selectWordEdge(SelectionChangedCause cause) { selectWordEdge(SelectionChangedCause cause) {
assert(cause != null);
assert(_lastTapDownPosition != null); assert(_lastTapDownPosition != null);
if (onSelectionChanged == null) { TextPosition position = getPositionForOffset(_lastTapDownPosition!);
return;
}
TextPosition position = getPositionForOffset(_lastTapDownPosition);
RenderEditableBox child = childAtPosition(position); RenderEditableBox child = childAtPosition(position);
int nodeOffset = child.getContainer().getOffset(); int nodeOffset = child.getContainer().getOffset();
TextPosition localPosition = TextPosition( TextPosition localPosition = TextPosition(
@ -759,16 +736,11 @@ class RenderEditor extends RenderEditableContainerBox
@override @override
selectPositionAt( selectPositionAt(
Offset from, Offset from,
Offset to, Offset? to,
SelectionChangedCause cause, SelectionChangedCause cause,
) { ) {
assert(cause != null);
assert(from != null);
if (onSelectionChanged == null) {
return;
}
TextPosition fromPosition = getPositionForOffset(from); TextPosition fromPosition = getPositionForOffset(from);
TextPosition toPosition = to == null ? null : getPositionForOffset(to); TextPosition? toPosition = to == null ? null : getPositionForOffset(to);
int baseOffset = fromPosition.offset; int baseOffset = fromPosition.offset;
int extentOffset = fromPosition.offset; int extentOffset = fromPosition.offset;
@ -787,12 +759,12 @@ class RenderEditor extends RenderEditableContainerBox
@override @override
selectWord(SelectionChangedCause cause) { selectWord(SelectionChangedCause cause) {
selectWordsInRange(_lastTapDownPosition, null, cause); selectWordsInRange(_lastTapDownPosition!, null, cause);
} }
@override @override
selectPosition(SelectionChangedCause cause) { selectPosition(SelectionChangedCause cause) {
selectPositionAt(_lastTapDownPosition, null, cause); selectPositionAt(_lastTapDownPosition!, null, cause);
} }
@override @override
@ -837,7 +809,7 @@ class RenderEditor extends RenderEditableContainerBox
} }
@override @override
bool hitTestChildren(BoxHitTestResult result, {Offset position}) { bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
return defaultHitTestChildren(result, position: position); return defaultHitTestChildren(result, position: position);
} }
@ -877,9 +849,9 @@ class RenderEditor extends RenderEditableContainerBox
@override @override
TextPosition getPositionForOffset(Offset offset) { TextPosition getPositionForOffset(Offset offset) {
Offset local = globalToLocal(offset); Offset local = globalToLocal(offset);
RenderEditableBox child = childAtOffset(local); RenderEditableBox child = childAtOffset(local)!;
BoxParentData parentData = child.parentData; BoxParentData parentData = child.parentData as BoxParentData;
Offset localOffset = local - parentData.offset; Offset localOffset = local - parentData.offset;
TextPosition localPosition = child.getPositionForOffset(localOffset); TextPosition localPosition = child.getPositionForOffset(localOffset);
return TextPosition( return TextPosition(
@ -888,7 +860,7 @@ class RenderEditor extends RenderEditableContainerBox
); );
} }
double getOffsetToRevealCursor( double? getOffsetToRevealCursor(
double viewportHeight, double scrollOffset, double offsetInViewport) { double viewportHeight, double scrollOffset, double offsetInViewport) {
List<TextSelectionPoint> endpoints = getEndpointsForSelection(selection); List<TextSelectionPoint> endpoints = getEndpointsForSelection(selection);
TextSelectionPoint endpoint = endpoints.first; TextSelectionPoint endpoint = endpoints.first;
@ -902,7 +874,7 @@ class RenderEditor extends RenderEditableContainerBox
kMargin + kMargin +
offsetInViewport; offsetInViewport;
final caretBottom = endpoint.point.dy + kMargin + offsetInViewport; final caretBottom = endpoint.point.dy + kMargin + offsetInViewport;
double dy; double? dy;
if (caretTop < scrollOffset) { if (caretTop < scrollOffset) {
dy = caretTop; dy = caretTop;
} else if (caretBottom > scrollOffset + viewportHeight) { } else if (caretBottom > scrollOffset + viewportHeight) {
@ -927,14 +899,11 @@ class RenderEditableContainerBox extends RenderBox
containerNode.Container _container; containerNode.Container _container;
TextDirection textDirection; TextDirection textDirection;
EdgeInsetsGeometry _padding; EdgeInsetsGeometry _padding;
EdgeInsets _resolvedPadding; EdgeInsets? _resolvedPadding;
RenderEditableContainerBox(List<RenderEditableBox> children, this._container, RenderEditableContainerBox(List<RenderEditableBox>? children, this._container,
this.textDirection, this._padding) this.textDirection, this._padding)
: assert(_container != null), : assert(_padding.isNonNegative) {
assert(textDirection != null),
assert(_padding != null),
assert(_padding.isNonNegative) {
addAll(children); addAll(children);
} }
@ -943,7 +912,6 @@ class RenderEditableContainerBox extends RenderBox
} }
setContainer(containerNode.Container c) { setContainer(containerNode.Container c) {
assert(c != null);
if (_container == c) { if (_container == c) {
return; return;
} }
@ -954,7 +922,6 @@ class RenderEditableContainerBox extends RenderBox
EdgeInsetsGeometry getPadding() => _padding; EdgeInsetsGeometry getPadding() => _padding;
setPadding(EdgeInsetsGeometry value) { setPadding(EdgeInsetsGeometry value) {
assert(value != null);
assert(value.isNonNegative); assert(value.isNonNegative);
if (_padding == value) { if (_padding == value) {
return; return;
@ -963,22 +930,22 @@ class RenderEditableContainerBox extends RenderBox
_markNeedsPaddingResolution(); _markNeedsPaddingResolution();
} }
EdgeInsets get resolvedPadding => _resolvedPadding; EdgeInsets? get resolvedPadding => _resolvedPadding;
_resolvePadding() { _resolvePadding() {
if (_resolvedPadding != null) { if (_resolvedPadding != null) {
return; return;
} }
_resolvedPadding = _padding.resolve(textDirection); _resolvedPadding = _padding.resolve(textDirection);
_resolvedPadding = _resolvedPadding.copyWith(left: _resolvedPadding.left); _resolvedPadding = _resolvedPadding!.copyWith(left: _resolvedPadding!.left);
assert(_resolvedPadding.isNonNegative); assert(_resolvedPadding!.isNonNegative);
} }
RenderEditableBox childAtPosition(TextPosition position) { RenderEditableBox childAtPosition(TextPosition position) {
assert(firstChild != null); assert(firstChild != null);
Node targetNode = _container.queryChild(position.offset, false).node; Node? targetNode = _container.queryChild(position.offset, false).node;
var targetChild = firstChild; var targetChild = firstChild;
while (targetChild != null) { while (targetChild != null) {
@ -998,19 +965,19 @@ class RenderEditableContainerBox extends RenderBox
markNeedsLayout(); markNeedsLayout();
} }
RenderEditableBox childAtOffset(Offset offset) { RenderEditableBox? childAtOffset(Offset offset) {
assert(firstChild != null); assert(firstChild != null);
_resolvePadding(); _resolvePadding();
if (offset.dy <= _resolvedPadding.top) { if (offset.dy <= _resolvedPadding!.top) {
return firstChild; return firstChild;
} }
if (offset.dy >= size.height - _resolvedPadding.bottom) { if (offset.dy >= size.height - _resolvedPadding!.bottom) {
return lastChild; return lastChild;
} }
var child = firstChild; var child = firstChild;
double dx = -offset.dx, dy = _resolvedPadding.top; double dx = -offset.dx, dy = _resolvedPadding!.top;
while (child != null) { while (child != null) {
if (child.size.contains(offset.translate(dx, -dy))) { if (child.size.contains(offset.translate(dx, -dy))) {
return child; return child;
@ -1037,20 +1004,21 @@ class RenderEditableContainerBox extends RenderBox
_resolvePadding(); _resolvePadding();
assert(_resolvedPadding != null); assert(_resolvedPadding != null);
double mainAxisExtent = _resolvedPadding.top; double mainAxisExtent = _resolvedPadding!.top;
var child = firstChild; var child = firstChild;
BoxConstraints innerConstraints = BoxConstraints innerConstraints =
BoxConstraints.tightFor(width: constraints.maxWidth) BoxConstraints.tightFor(width: constraints.maxWidth)
.deflate(_resolvedPadding); .deflate(_resolvedPadding!);
while (child != null) { while (child != null) {
child.layout(innerConstraints, parentUsesSize: true); child.layout(innerConstraints, parentUsesSize: true);
final EditableContainerParentData childParentData = child.parentData; final EditableContainerParentData childParentData =
childParentData.offset = Offset(_resolvedPadding.left, mainAxisExtent); child.parentData as EditableContainerParentData;
childParentData.offset = Offset(_resolvedPadding!.left, mainAxisExtent);
mainAxisExtent += child.size.height; mainAxisExtent += child.size.height;
assert(child.parentData == childParentData); assert(child.parentData == childParentData);
child = childParentData.nextSibling; child = childParentData.nextSibling;
} }
mainAxisExtent += _resolvedPadding.bottom; mainAxisExtent += _resolvedPadding!.bottom;
size = constraints.constrain(Size(constraints.maxWidth, mainAxisExtent)); size = constraints.constrain(Size(constraints.maxWidth, mainAxisExtent));
assert(size.isFinite); assert(size.isFinite);
@ -1061,7 +1029,8 @@ class RenderEditableContainerBox extends RenderBox
var child = firstChild; var child = firstChild;
while (child != null) { while (child != null) {
extent = math.max(extent, childSize(child)); extent = math.max(extent, childSize(child));
EditableContainerParentData childParentData = child.parentData; EditableContainerParentData childParentData =
child.parentData as EditableContainerParentData;
child = childParentData.nextSibling; child = childParentData.nextSibling;
} }
return extent; return extent;
@ -1072,7 +1041,8 @@ class RenderEditableContainerBox extends RenderBox
var child = firstChild; var child = firstChild;
while (child != null) { while (child != null) {
extent += childSize(child); extent += childSize(child);
EditableContainerParentData childParentData = child.parentData; EditableContainerParentData childParentData =
child.parentData as EditableContainerParentData;
child = childParentData.nextSibling; child = childParentData.nextSibling;
} }
return extent; return extent;
@ -1083,10 +1053,10 @@ class RenderEditableContainerBox extends RenderBox
_resolvePadding(); _resolvePadding();
return _getIntrinsicCrossAxis((RenderBox child) { return _getIntrinsicCrossAxis((RenderBox child) {
double childHeight = math.max( double childHeight = math.max(
0.0, height - _resolvedPadding.top + _resolvedPadding.bottom); 0.0, height - _resolvedPadding!.top + _resolvedPadding!.bottom);
return child.getMinIntrinsicWidth(childHeight) + return child.getMinIntrinsicWidth(childHeight) +
_resolvedPadding.left + _resolvedPadding!.left +
_resolvedPadding.right; _resolvedPadding!.right;
}); });
} }
@ -1095,10 +1065,10 @@ class RenderEditableContainerBox extends RenderBox
_resolvePadding(); _resolvePadding();
return _getIntrinsicCrossAxis((RenderBox child) { return _getIntrinsicCrossAxis((RenderBox child) {
double childHeight = math.max( double childHeight = math.max(
0.0, height - _resolvedPadding.top + _resolvedPadding.bottom); 0.0, height - _resolvedPadding!.top + _resolvedPadding!.bottom);
return child.getMaxIntrinsicWidth(childHeight) + return child.getMaxIntrinsicWidth(childHeight) +
_resolvedPadding.left + _resolvedPadding!.left +
_resolvedPadding.right; _resolvedPadding!.right;
}); });
} }
@ -1106,11 +1076,11 @@ class RenderEditableContainerBox extends RenderBox
double computeMinIntrinsicHeight(double width) { double computeMinIntrinsicHeight(double width) {
_resolvePadding(); _resolvePadding();
return _getIntrinsicMainAxis((RenderBox child) { return _getIntrinsicMainAxis((RenderBox child) {
double childWidth = double childWidth = math.max(
math.max(0.0, width - _resolvedPadding.left + _resolvedPadding.right); 0.0, width - _resolvedPadding!.left + _resolvedPadding!.right);
return child.getMinIntrinsicHeight(childWidth) + return child.getMinIntrinsicHeight(childWidth) +
_resolvedPadding.top + _resolvedPadding!.top +
_resolvedPadding.bottom; _resolvedPadding!.bottom;
}); });
} }
@ -1118,18 +1088,18 @@ class RenderEditableContainerBox extends RenderBox
double computeMaxIntrinsicHeight(double width) { double computeMaxIntrinsicHeight(double width) {
_resolvePadding(); _resolvePadding();
return _getIntrinsicMainAxis((RenderBox child) { return _getIntrinsicMainAxis((RenderBox child) {
final childWidth = final childWidth = math.max(
math.max(0.0, width - _resolvedPadding.left + _resolvedPadding.right); 0.0, width - _resolvedPadding!.left + _resolvedPadding!.right);
return child.getMaxIntrinsicHeight(childWidth) + return child.getMaxIntrinsicHeight(childWidth) +
_resolvedPadding.top + _resolvedPadding!.top +
_resolvedPadding.bottom; _resolvedPadding!.bottom;
}); });
} }
@override @override
double computeDistanceToActualBaseline(TextBaseline baseline) { double computeDistanceToActualBaseline(TextBaseline baseline) {
_resolvePadding(); _resolvePadding();
return defaultComputeDistanceToFirstActualBaseline(baseline) + return defaultComputeDistanceToFirstActualBaseline(baseline)! +
_resolvedPadding.top; _resolvedPadding!.top;
} }
} }

@ -8,7 +8,7 @@ class ImageTapWrapper extends StatelessWidget {
this.imageProvider, this.imageProvider,
}); });
final ImageProvider imageProvider; final ImageProvider? imageProvider;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

@ -5,7 +5,7 @@ enum InputShortcut { CUT, COPY, PASTE, SELECT_ALL }
typedef CursorMoveCallback = void Function( typedef CursorMoveCallback = void Function(
LogicalKeyboardKey key, bool wordModifier, bool lineModifier, bool shift); LogicalKeyboardKey key, bool wordModifier, bool lineModifier, bool shift);
typedef InputShortcutCallback = void Function(InputShortcut shortcut); typedef InputShortcutCallback = void Function(InputShortcut? shortcut);
typedef OnDeleteCallback = void Function(bool forward); typedef OnDeleteCallback = void Function(bool forward);
class KeyboardListener { class KeyboardListener {
@ -59,10 +59,7 @@ class KeyboardListener {
LogicalKeyboardKey.keyA: InputShortcut.SELECT_ALL, LogicalKeyboardKey.keyA: InputShortcut.SELECT_ALL,
}; };
KeyboardListener(this.onCursorMove, this.onShortcut, this.onDelete) KeyboardListener(this.onCursorMove, this.onShortcut, this.onDelete);
: assert(onCursorMove != null),
assert(onShortcut != null),
assert(onDelete != null);
bool handleRawKeyEvent(RawKeyEvent event) { bool handleRawKeyEvent(RawKeyEvent event) {
if (kIsWeb) { if (kIsWeb) {

@ -4,17 +4,17 @@ import 'package:flutter/widgets.dart';
import 'box.dart'; import 'box.dart';
class BaselineProxy extends SingleChildRenderObjectWidget { class BaselineProxy extends SingleChildRenderObjectWidget {
final TextStyle textStyle; final TextStyle? textStyle;
final EdgeInsets padding; final EdgeInsets? padding;
BaselineProxy({Key key, Widget child, this.textStyle, this.padding}) BaselineProxy({Key? key, Widget? child, this.textStyle, this.padding})
: super(key: key, child: child); : super(key: key, child: child);
@override @override
RenderBaselineProxy createRenderObject(BuildContext context) { RenderBaselineProxy createRenderObject(BuildContext context) {
return RenderBaselineProxy( return RenderBaselineProxy(
null, null,
textStyle, textStyle!,
padding, padding,
); );
} }
@ -23,16 +23,16 @@ class BaselineProxy extends SingleChildRenderObjectWidget {
void updateRenderObject( void updateRenderObject(
BuildContext context, covariant RenderBaselineProxy renderObject) { BuildContext context, covariant RenderBaselineProxy renderObject) {
renderObject renderObject
..textStyle = textStyle ..textStyle = textStyle!
..padding = padding; ..padding = padding!;
} }
} }
class RenderBaselineProxy extends RenderProxyBox { class RenderBaselineProxy extends RenderProxyBox {
RenderBaselineProxy( RenderBaselineProxy(
RenderParagraph child, RenderParagraph? child,
TextStyle textStyle, TextStyle textStyle,
EdgeInsets padding, EdgeInsets? padding,
) : _prototypePainter = TextPainter( ) : _prototypePainter = TextPainter(
text: TextSpan(text: ' ', style: textStyle), text: TextSpan(text: ' ', style: textStyle),
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
@ -43,18 +43,16 @@ class RenderBaselineProxy extends RenderProxyBox {
final TextPainter _prototypePainter; final TextPainter _prototypePainter;
set textStyle(TextStyle value) { set textStyle(TextStyle value) {
assert(value != null); if (_prototypePainter.text!.style == value) {
if (_prototypePainter.text.style == value) {
return; return;
} }
_prototypePainter.text = TextSpan(text: ' ', style: value); _prototypePainter.text = TextSpan(text: ' ', style: value);
markNeedsLayout(); markNeedsLayout();
} }
EdgeInsets _padding; EdgeInsets? _padding;
set padding(EdgeInsets value) { set padding(EdgeInsets value) {
assert(value != null);
if (_padding == value) { if (_padding == value) {
return; return;
} }
@ -64,9 +62,8 @@ class RenderBaselineProxy extends RenderProxyBox {
@override @override
double computeDistanceToActualBaseline(TextBaseline baseline) => double computeDistanceToActualBaseline(TextBaseline baseline) =>
_prototypePainter.computeDistanceToActualBaseline(baseline) + _prototypePainter.computeDistanceToActualBaseline(baseline);
_padding?.top ?? // SEE What happens + _padding?.top;
0.0;
@override @override
performLayout() { performLayout() {
@ -84,7 +81,7 @@ class EmbedProxy extends SingleChildRenderObjectWidget {
} }
class RenderEmbedProxy extends RenderProxyBox implements RenderContentProxyBox { class RenderEmbedProxy extends RenderProxyBox implements RenderContentProxyBox {
RenderEmbedProxy(RenderBox child) : super(child); RenderEmbedProxy(RenderBox? child) : super(child);
@override @override
List<TextBox> getBoxesForSelection(TextSelection selection) { List<TextBox> getBoxesForSelection(TextSelection selection) {
@ -105,10 +102,8 @@ class RenderEmbedProxy extends RenderProxyBox implements RenderContentProxyBox {
double getFullHeightForCaret(TextPosition position) => size.height; double getFullHeightForCaret(TextPosition position) => size.height;
@override @override
Offset getOffsetForCaret(TextPosition position, Rect caretPrototype) { Offset getOffsetForCaret(TextPosition position, Rect? caretPrototype) {
assert(position.offset != null && assert(position.offset <= 1 && position.offset >= 0);
position.offset <= 1 &&
position.offset >= 0);
return position.offset == 0 ? Offset.zero : Offset(size.width, 0.0); return position.offset == 0 ? Offset.zero : Offset(size.width, 0.0);
} }
@ -134,7 +129,7 @@ class RichTextProxy extends SingleChildRenderObjectWidget {
final Locale locale; final Locale locale;
final StrutStyle strutStyle; final StrutStyle strutStyle;
final TextWidthBasis textWidthBasis; final TextWidthBasis textWidthBasis;
final TextHeightBehavior textHeightBehavior; final TextHeightBehavior? textHeightBehavior;
@override @override
RenderParagraphProxy createRenderObject(BuildContext context) { RenderParagraphProxy createRenderObject(BuildContext context) {
@ -160,13 +155,7 @@ class RichTextProxy extends SingleChildRenderObjectWidget {
this.strutStyle, this.strutStyle,
this.textWidthBasis, this.textWidthBasis,
this.textHeightBehavior) this.textHeightBehavior)
: assert(child != null), : super(child: child);
assert(textStyle != null),
assert(textAlign != null),
assert(textDirection != null),
assert(locale != null),
assert(strutStyle != null),
super(child: child);
@override @override
void updateRenderObject( void updateRenderObject(
@ -185,7 +174,7 @@ class RichTextProxy extends SingleChildRenderObjectWidget {
class RenderParagraphProxy extends RenderProxyBox class RenderParagraphProxy extends RenderProxyBox
implements RenderContentProxyBox { implements RenderContentProxyBox {
RenderParagraphProxy( RenderParagraphProxy(
RenderParagraph child, RenderParagraph? child,
TextStyle textStyle, TextStyle textStyle,
TextAlign textAlign, TextAlign textAlign,
TextDirection textDirection, TextDirection textDirection,
@ -193,7 +182,7 @@ class RenderParagraphProxy extends RenderProxyBox
StrutStyle strutStyle, StrutStyle strutStyle,
Locale locale, Locale locale,
TextWidthBasis textWidthBasis, TextWidthBasis textWidthBasis,
TextHeightBehavior textHeightBehavior, TextHeightBehavior? textHeightBehavior,
) : _prototypePainter = TextPainter( ) : _prototypePainter = TextPainter(
text: TextSpan(text: ' ', style: textStyle), text: TextSpan(text: ' ', style: textStyle),
textAlign: textAlign, textAlign: textAlign,
@ -208,8 +197,7 @@ class RenderParagraphProxy extends RenderProxyBox
final TextPainter _prototypePainter; final TextPainter _prototypePainter;
set textStyle(TextStyle value) { set textStyle(TextStyle value) {
assert(value != null); if (_prototypePainter.text!.style == value) {
if (_prototypePainter.text.style == value) {
return; return;
} }
_prototypePainter.text = TextSpan(text: ' ', style: value); _prototypePainter.text = TextSpan(text: ' ', style: value);
@ -217,7 +205,6 @@ class RenderParagraphProxy extends RenderProxyBox
} }
set textAlign(TextAlign value) { set textAlign(TextAlign value) {
assert(value != null);
if (_prototypePainter.textAlign == value) { if (_prototypePainter.textAlign == value) {
return; return;
} }
@ -226,7 +213,6 @@ class RenderParagraphProxy extends RenderProxyBox
} }
set textDirection(TextDirection value) { set textDirection(TextDirection value) {
assert(value != null);
if (_prototypePainter.textDirection == value) { if (_prototypePainter.textDirection == value) {
return; return;
} }
@ -235,7 +221,6 @@ class RenderParagraphProxy extends RenderProxyBox
} }
set textScaleFactor(double value) { set textScaleFactor(double value) {
assert(value != null);
if (_prototypePainter.textScaleFactor == value) { if (_prototypePainter.textScaleFactor == value) {
return; return;
} }
@ -244,7 +229,6 @@ class RenderParagraphProxy extends RenderProxyBox
} }
set strutStyle(StrutStyle value) { set strutStyle(StrutStyle value) {
assert(value != null);
if (_prototypePainter.strutStyle == value) { if (_prototypePainter.strutStyle == value) {
return; return;
} }
@ -261,7 +245,6 @@ class RenderParagraphProxy extends RenderProxyBox
} }
set textWidthBasis(TextWidthBasis value) { set textWidthBasis(TextWidthBasis value) {
assert(value != null);
if (_prototypePainter.textWidthBasis == value) { if (_prototypePainter.textWidthBasis == value) {
return; return;
} }
@ -269,7 +252,7 @@ class RenderParagraphProxy extends RenderProxyBox
markNeedsLayout(); markNeedsLayout();
} }
set textHeightBehavior(TextHeightBehavior value) { set textHeightBehavior(TextHeightBehavior? value) {
if (_prototypePainter.textHeightBehavior == value) { if (_prototypePainter.textHeightBehavior == value) {
return; return;
} }
@ -278,7 +261,7 @@ class RenderParagraphProxy extends RenderProxyBox
} }
@override @override
RenderParagraph get child => super.child; RenderParagraph? get child => super.child as RenderParagraph?;
@override @override
double getPreferredLineHeight() { double getPreferredLineHeight() {
@ -286,24 +269,24 @@ class RenderParagraphProxy extends RenderProxyBox
} }
@override @override
Offset getOffsetForCaret(TextPosition position, Rect caretPrototype) => Offset getOffsetForCaret(TextPosition position, Rect? caretPrototype) =>
child.getOffsetForCaret(position, caretPrototype); child!.getOffsetForCaret(position, caretPrototype!);
@override @override
TextPosition getPositionForOffset(Offset offset) => TextPosition getPositionForOffset(Offset offset) =>
child.getPositionForOffset(offset); child!.getPositionForOffset(offset);
@override @override
double getFullHeightForCaret(TextPosition position) => double? getFullHeightForCaret(TextPosition position) =>
child.getFullHeightForCaret(position); child!.getFullHeightForCaret(position);
@override @override
TextRange getWordBoundary(TextPosition position) => TextRange getWordBoundary(TextPosition position) =>
child.getWordBoundary(position); child!.getWordBoundary(position);
@override @override
List<TextBox> getBoxesForSelection(TextSelection selection) => List<TextBox> getBoxesForSelection(TextSelection selection) =>
child.getBoxesForSelection(selection); child!.getBoxesForSelection(selection);
@override @override
performLayout() { performLayout() {

@ -36,23 +36,23 @@ class RawEditor extends StatefulWidget {
final bool scrollable; final bool scrollable;
final EdgeInsetsGeometry padding; final EdgeInsetsGeometry padding;
final bool readOnly; final bool readOnly;
final String placeholder; final String? placeholder;
final ValueChanged<String> onLaunchUrl; final ValueChanged<String>? onLaunchUrl;
final ToolbarOptions toolbarOptions; final ToolbarOptions toolbarOptions;
final bool showSelectionHandles; final bool showSelectionHandles;
final bool showCursor; final bool showCursor;
final CursorStyle cursorStyle; final CursorStyle cursorStyle;
final TextCapitalization textCapitalization; final TextCapitalization textCapitalization;
final double maxHeight; final double? maxHeight;
final double minHeight; final double? minHeight;
final DefaultStyles customStyles; final DefaultStyles? customStyles;
final bool expands; final bool expands;
final bool autoFocus; final bool autoFocus;
final Color selectionColor; final Color selectionColor;
final TextSelectionControls selectionCtrls; final TextSelectionControls selectionCtrls;
final Brightness keyboardAppearance; final Brightness keyboardAppearance;
final bool enableInteractiveSelection; final bool enableInteractiveSelection;
final ScrollPhysics scrollPhysics; final ScrollPhysics? scrollPhysics;
final EmbedBuilder embedBuilder; final EmbedBuilder embedBuilder;
RawEditor( RawEditor(
@ -67,7 +67,7 @@ class RawEditor extends StatefulWidget {
this.onLaunchUrl, this.onLaunchUrl,
this.toolbarOptions, this.toolbarOptions,
this.showSelectionHandles, this.showSelectionHandles,
bool showCursor, bool? showCursor,
this.cursorStyle, this.cursorStyle,
this.textCapitalization, this.textCapitalization,
this.maxHeight, this.maxHeight,
@ -81,26 +81,11 @@ class RawEditor extends StatefulWidget {
this.enableInteractiveSelection, this.enableInteractiveSelection,
this.scrollPhysics, this.scrollPhysics,
this.embedBuilder) this.embedBuilder)
: assert(controller != null, 'controller cannot be null'), : assert(maxHeight == null || maxHeight > 0, 'maxHeight cannot be null'),
assert(focusNode != null, 'focusNode cannot be null'),
assert(scrollable || scrollController != null,
'scrollController cannot be null'),
assert(selectionColor != null, 'selectionColor cannot be null'),
assert(enableInteractiveSelection != null,
'enableInteractiveSelection cannot be null'),
assert(showSelectionHandles != null,
'showSelectionHandles cannot be null'),
assert(readOnly != null, 'readOnly cannot be null'),
assert(maxHeight == null || maxHeight > 0, 'maxHeight cannot be null'),
assert(minHeight == null || minHeight >= 0, 'minHeight cannot be null'), assert(minHeight == null || minHeight >= 0, 'minHeight cannot be null'),
assert(maxHeight == null || minHeight == null || maxHeight >= minHeight, assert(maxHeight == null || minHeight == null || maxHeight >= minHeight,
'maxHeight cannot be null'), 'maxHeight cannot be null'),
assert(autoFocus != null, 'autoFocus cannot be null'),
assert(toolbarOptions != null, 'toolbarOptions cannot be null'),
showCursor = showCursor ?? !readOnly, showCursor = showCursor ?? !readOnly,
assert(embedBuilder != null, 'embedBuilder cannot be null'),
assert(expands != null, 'expands cannot be null'),
assert(padding != null),
super(key: key); super(key: key);
@override @override
@ -115,23 +100,23 @@ class RawEditorState extends EditorState
WidgetsBindingObserver, WidgetsBindingObserver,
TickerProviderStateMixin<RawEditor> TickerProviderStateMixin<RawEditor>
implements TextSelectionDelegate, TextInputClient { implements TextSelectionDelegate, TextInputClient {
final GlobalKey _editorKey = GlobalKey(); GlobalKey _editorKey = GlobalKey();
final List<TextEditingValue> _sentRemoteValues = []; final List<TextEditingValue> _sentRemoteValues = [];
TextInputConnection _textInputConnection; TextInputConnection? _textInputConnection;
TextEditingValue _lastKnownRemoteTextEditingValue; TextEditingValue? _lastKnownRemoteTextEditingValue;
int _cursorResetLocation = -1; int _cursorResetLocation = -1;
bool _wasSelectingVerticallyWithKeyboard = false; bool _wasSelectingVerticallyWithKeyboard = false;
EditorTextSelectionOverlay _selectionOverlay; EditorTextSelectionOverlay? _selectionOverlay;
FocusAttachment _focusAttachment; FocusAttachment? _focusAttachment;
CursorCont _cursorCont; late CursorCont _cursorCont;
ScrollController _scrollController; ScrollController? _scrollController;
KeyboardVisibilityController _keyboardVisibilityController; late KeyboardVisibilityController _keyboardVisibilityController;
StreamSubscription<bool> _keyboardVisibilitySubscription; late StreamSubscription<bool> _keyboardVisibilitySubscription;
KeyboardListener _keyboardListener; late KeyboardListener _keyboardListener;
bool _didAutoFocus = false; bool _didAutoFocus = false;
bool _keyboardVisible = false; bool _keyboardVisible = false;
DefaultStyles _styles; DefaultStyles? _styles;
final ClipboardStatusNotifier _clipboardStatus = final ClipboardStatusNotifier? _clipboardStatus =
kIsWeb ? null : ClipboardStatusNotifier(); kIsWeb ? null : ClipboardStatusNotifier();
final LayerLink _toolbarLayerLink = LayerLink(); final LayerLink _toolbarLayerLink = LayerLink();
final LayerLink _startHandleLayerLink = LayerLink(); final LayerLink _startHandleLayerLink = LayerLink();
@ -156,7 +141,6 @@ class RawEditorState extends EditorState
TextDirection get _textDirection { TextDirection get _textDirection {
TextDirection result = Directionality.of(context); TextDirection result = Directionality.of(context);
assert(result != null);
return result; return result;
} }
@ -170,7 +154,6 @@ class RawEditorState extends EditorState
return; return;
} }
TextSelection selection = widget.controller.selection; TextSelection selection = widget.controller.selection;
assert(selection != null);
TextSelection newSelection = widget.controller.selection; TextSelection newSelection = widget.controller.selection;
@ -226,19 +209,20 @@ class RawEditorState extends EditorState
TextPosition originPosition = TextPosition( TextPosition originPosition = TextPosition(
offset: upKey ? selection.baseOffset : selection.extentOffset); offset: upKey ? selection.baseOffset : selection.extentOffset);
RenderEditableBox child = getRenderEditor().childAtPosition(originPosition); RenderEditableBox child =
getRenderEditor()!.childAtPosition(originPosition);
TextPosition localPosition = TextPosition( TextPosition localPosition = TextPosition(
offset: offset:
originPosition.offset - child.getContainer().getDocumentOffset()); originPosition.offset - child.getContainer().getDocumentOffset());
TextPosition position = upKey TextPosition? position = upKey
? child.getPositionAbove(localPosition) ? child.getPositionAbove(localPosition)
: child.getPositionBelow(localPosition); : child.getPositionBelow(localPosition);
if (position == null) { if (position == null) {
var sibling = upKey var sibling = upKey
? getRenderEditor().childBefore(child) ? getRenderEditor()!.childBefore(child)
: getRenderEditor().childAfter(child); : getRenderEditor()!.childAfter(child);
if (sibling == null) { if (sibling == null) {
position = TextPosition(offset: upKey ? 0 : plainText.length - 1); position = TextPosition(offset: upKey ? 0 : plainText.length - 1);
} else { } else {
@ -289,20 +273,20 @@ class RawEditorState extends EditorState
bool shift) { bool shift) {
if (wordModifier) { if (wordModifier) {
if (leftKey) { if (leftKey) {
TextSelection textSelection = getRenderEditor().selectWordAtPosition( TextSelection textSelection = getRenderEditor()!.selectWordAtPosition(
TextPosition( TextPosition(
offset: _previousCharacter( offset: _previousCharacter(
newSelection.extentOffset, plainText, false))); newSelection.extentOffset, plainText, false)));
return newSelection.copyWith(extentOffset: textSelection.baseOffset); return newSelection.copyWith(extentOffset: textSelection.baseOffset);
} }
TextSelection textSelection = getRenderEditor().selectWordAtPosition( TextSelection textSelection = getRenderEditor()!.selectWordAtPosition(
TextPosition( TextPosition(
offset: offset:
_nextCharacter(newSelection.extentOffset, plainText, false))); _nextCharacter(newSelection.extentOffset, plainText, false)));
return newSelection.copyWith(extentOffset: textSelection.extentOffset); return newSelection.copyWith(extentOffset: textSelection.extentOffset);
} else if (lineModifier) { } else if (lineModifier) {
if (leftKey) { if (leftKey) {
TextSelection textSelection = getRenderEditor().selectLineAtPosition( TextSelection textSelection = getRenderEditor()!.selectLineAtPosition(
TextPosition( TextPosition(
offset: _previousCharacter( offset: _previousCharacter(
newSelection.extentOffset, plainText, false))); newSelection.extentOffset, plainText, false)));
@ -310,7 +294,7 @@ class RawEditorState extends EditorState
} }
int startPoint = newSelection.extentOffset; int startPoint = newSelection.extentOffset;
if (startPoint < plainText.length) { if (startPoint < plainText.length) {
TextSelection textSelection = getRenderEditor() TextSelection textSelection = getRenderEditor()!
.selectLineAtPosition(TextPosition(offset: startPoint)); .selectLineAtPosition(TextPosition(offset: startPoint));
return newSelection.copyWith(extentOffset: textSelection.extentOffset); return newSelection.copyWith(extentOffset: textSelection.extentOffset);
} }
@ -368,7 +352,7 @@ class RawEditorState extends EditorState
} }
int count = 0; int count = 0;
int lastNonWhitespace; int? lastNonWhitespace;
for (String currentString in string.characters) { for (String currentString in string.characters) {
if (!includeWhitespace && if (!includeWhitespace &&
!WHITE_SPACE.contains( !WHITE_SPACE.contains(
@ -384,7 +368,7 @@ class RawEditorState extends EditorState
} }
bool get hasConnection => bool get hasConnection =>
_textInputConnection != null && _textInputConnection.attached; _textInputConnection != null && _textInputConnection!.attached;
openConnectionIfNeeded() { openConnectionIfNeeded() {
if (!shouldCreateInputConnection) { if (!shouldCreateInputConnection) {
@ -406,17 +390,17 @@ class RawEditorState extends EditorState
), ),
); );
_textInputConnection.setEditingState(_lastKnownRemoteTextEditingValue); _textInputConnection!.setEditingState(_lastKnownRemoteTextEditingValue!);
// _sentRemoteValues.add(_lastKnownRemoteTextEditingValue); // _sentRemoteValues.add(_lastKnownRemoteTextEditingValue);
} }
_textInputConnection.show(); _textInputConnection!.show();
} }
closeConnectionIfNeeded() { closeConnectionIfNeeded() {
if (!hasConnection) { if (!hasConnection) {
return; return;
} }
_textInputConnection.close(); _textInputConnection!.close();
_textInputConnection = null; _textInputConnection = null;
_lastKnownRemoteTextEditingValue = null; _lastKnownRemoteTextEditingValue = null;
_sentRemoteValues.clear(); _sentRemoteValues.clear();
@ -428,7 +412,7 @@ class RawEditorState extends EditorState
} }
TextEditingValue actualValue = textEditingValue.copyWith( TextEditingValue actualValue = textEditingValue.copyWith(
composing: _lastKnownRemoteTextEditingValue.composing, composing: _lastKnownRemoteTextEditingValue!.composing,
); );
if (actualValue == _lastKnownRemoteTextEditingValue) { if (actualValue == _lastKnownRemoteTextEditingValue) {
@ -436,20 +420,20 @@ class RawEditorState extends EditorState
} }
bool shouldRemember = bool shouldRemember =
textEditingValue.text != _lastKnownRemoteTextEditingValue.text; textEditingValue.text != _lastKnownRemoteTextEditingValue!.text;
_lastKnownRemoteTextEditingValue = actualValue; _lastKnownRemoteTextEditingValue = actualValue;
_textInputConnection.setEditingState(actualValue); _textInputConnection!.setEditingState(actualValue);
if (shouldRemember) { if (shouldRemember) {
_sentRemoteValues.add(actualValue); _sentRemoteValues.add(actualValue);
} }
} }
@override @override
TextEditingValue get currentTextEditingValue => TextEditingValue? get currentTextEditingValue =>
_lastKnownRemoteTextEditingValue; _lastKnownRemoteTextEditingValue;
@override @override
AutofillScope get currentAutofillScope => null; AutofillScope? get currentAutofillScope => null;
@override @override
void updateEditingValue(TextEditingValue value) { void updateEditingValue(TextEditingValue value) {
@ -466,13 +450,14 @@ class RawEditorState extends EditorState
return; return;
} }
if (_lastKnownRemoteTextEditingValue.text == value.text && if (_lastKnownRemoteTextEditingValue!.text == value.text &&
_lastKnownRemoteTextEditingValue.selection == value.selection) { _lastKnownRemoteTextEditingValue!.selection == value.selection) {
_lastKnownRemoteTextEditingValue = value; _lastKnownRemoteTextEditingValue = value;
return; return;
} }
TextEditingValue effectiveLastKnownValue = _lastKnownRemoteTextEditingValue; TextEditingValue effectiveLastKnownValue =
_lastKnownRemoteTextEditingValue!;
_lastKnownRemoteTextEditingValue = value; _lastKnownRemoteTextEditingValue = value;
String oldText = effectiveLastKnownValue.text; String oldText = effectiveLastKnownValue.text;
String text = value.text; String text = value.text;
@ -516,7 +501,7 @@ class RawEditorState extends EditorState
if (!hasConnection) { if (!hasConnection) {
return; return;
} }
_textInputConnection.connectionClosedReceived(); _textInputConnection!.connectionClosedReceived();
_textInputConnection = null; _textInputConnection = null;
_lastKnownRemoteTextEditingValue = null; _lastKnownRemoteTextEditingValue = null;
_sentRemoteValues.clear(); _sentRemoteValues.clear();
@ -525,7 +510,7 @@ class RawEditorState extends EditorState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMediaQuery(context));
_focusAttachment.reparent(); _focusAttachment!.reparent();
super.build(context); super.build(context);
Document _doc = widget.controller.document; Document _doc = widget.controller.document;
@ -556,9 +541,9 @@ class RawEditorState extends EditorState
if (widget.scrollable) { if (widget.scrollable) {
EdgeInsets baselinePadding = EdgeInsets baselinePadding =
EdgeInsets.only(top: _styles.paragraph.verticalSpacing.item1); EdgeInsets.only(top: _styles!.paragraph!.verticalSpacing.item1);
child = BaselineProxy( child = BaselineProxy(
textStyle: _styles.paragraph.style, textStyle: _styles!.paragraph!.style,
padding: baselinePadding, padding: baselinePadding,
child: SingleChildScrollView( child: SingleChildScrollView(
controller: _scrollController, controller: _scrollController,
@ -575,7 +560,7 @@ class RawEditorState extends EditorState
maxHeight: widget.maxHeight ?? double.infinity); maxHeight: widget.maxHeight ?? double.infinity);
return QuillStyles( return QuillStyles(
data: _styles, data: _styles!,
child: MouseRegion( child: MouseRegion(
cursor: SystemMouseCursors.text, cursor: SystemMouseCursors.text,
child: Container( child: Container(
@ -636,7 +621,7 @@ class RawEditorState extends EditorState
line: node, line: node,
textDirection: _textDirection, textDirection: _textDirection,
embedBuilder: widget.embedBuilder, embedBuilder: widget.embedBuilder,
styles: _styles, styles: _styles!,
); );
EditableTextLine editableTextLine = EditableTextLine( EditableTextLine editableTextLine = EditableTextLine(
node, node,
@ -655,36 +640,36 @@ class RawEditorState extends EditorState
} }
Tuple2<double, double> _getVerticalSpacingForLine( Tuple2<double, double> _getVerticalSpacingForLine(
Line line, DefaultStyles defaultStyles) { Line line, DefaultStyles? defaultStyles) {
Map<String, Attribute> attrs = line.style.attributes; Map<String, Attribute> attrs = line.style.attributes;
if (attrs.containsKey(Attribute.header.key)) { if (attrs.containsKey(Attribute.header.key)) {
int level = attrs[Attribute.header.key].value; int? level = attrs[Attribute.header.key]!.value;
switch (level) { switch (level) {
case 1: case 1:
return defaultStyles.h1.verticalSpacing; return defaultStyles!.h1!.verticalSpacing;
case 2: case 2:
return defaultStyles.h2.verticalSpacing; return defaultStyles!.h2!.verticalSpacing;
case 3: case 3:
return defaultStyles.h3.verticalSpacing; return defaultStyles!.h3!.verticalSpacing;
default: default:
throw ('Invalid level $level'); throw ('Invalid level $level');
} }
} }
return defaultStyles.paragraph.verticalSpacing; return defaultStyles!.paragraph!.verticalSpacing;
} }
Tuple2<double, double> _getVerticalSpacingForBlock( Tuple2<double, double> _getVerticalSpacingForBlock(
Block node, DefaultStyles defaultStyles) { Block node, DefaultStyles? defaultStyles) {
Map<String, Attribute> attrs = node.style.attributes; Map<String, Attribute> attrs = node.style.attributes;
if (attrs.containsKey(Attribute.blockQuote.key)) { if (attrs.containsKey(Attribute.blockQuote.key)) {
return defaultStyles.quote.verticalSpacing; return defaultStyles!.quote!.verticalSpacing;
} else if (attrs.containsKey(Attribute.codeBlock.key)) { } else if (attrs.containsKey(Attribute.codeBlock.key)) {
return defaultStyles.code.verticalSpacing; return defaultStyles!.code!.verticalSpacing;
} else if (attrs.containsKey(Attribute.indent.key)) { } else if (attrs.containsKey(Attribute.indent.key)) {
return defaultStyles.indent.verticalSpacing; return defaultStyles!.indent!.verticalSpacing;
} }
return defaultStyles.lists.verticalSpacing; return defaultStyles!.lists!.verticalSpacing;
} }
@override @override
@ -695,17 +680,12 @@ class RawEditorState extends EditorState
widget.controller.addListener(_didChangeTextEditingValue); widget.controller.addListener(_didChangeTextEditingValue);
_scrollController = widget.scrollController ?? ScrollController(); _scrollController = widget.scrollController;
_scrollController.addListener(_updateSelectionOverlayForScroll); _scrollController!.addListener(_updateSelectionOverlayForScroll);
_cursorCont = CursorCont( _cursorCont = CursorCont(
show: ValueNotifier<bool>(widget.showCursor ?? false), show: ValueNotifier<bool>(widget.showCursor),
style: widget.cursorStyle ?? style: widget.cursorStyle,
CursorStyle(
color: Colors.blueAccent,
backgroundColor: Colors.grey,
width: 2.0,
),
tickerProvider: this, tickerProvider: this,
); );
@ -739,14 +719,14 @@ class RawEditorState extends EditorState
@override @override
didChangeDependencies() { didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
DefaultStyles parentStyles = QuillStyles.getStyles(context, true); DefaultStyles? parentStyles = QuillStyles.getStyles(context, true);
DefaultStyles defaultStyles = DefaultStyles.getInstance(context); DefaultStyles defaultStyles = DefaultStyles.getInstance(context);
_styles = (parentStyles != null) _styles = (parentStyles != null)
? defaultStyles.merge(parentStyles) ? defaultStyles.merge(parentStyles)
: defaultStyles; : defaultStyles;
if (widget.customStyles != null) { if (widget.customStyles != null) {
_styles = _styles.merge(widget.customStyles); _styles = _styles!.merge(widget.customStyles!);
} }
if (!_didAutoFocus && widget.autoFocus) { if (!_didAutoFocus && widget.autoFocus) {
@ -768,11 +748,10 @@ class RawEditorState extends EditorState
updateRemoteValueIfNeeded(); updateRemoteValueIfNeeded();
} }
if (widget.scrollController != null && if (widget.scrollController != _scrollController) {
widget.scrollController != _scrollController) { _scrollController!.removeListener(_updateSelectionOverlayForScroll);
_scrollController.removeListener(_updateSelectionOverlayForScroll);
_scrollController = widget.scrollController; _scrollController = widget.scrollController;
_scrollController.addListener(_updateSelectionOverlayForScroll); _scrollController!.addListener(_updateSelectionOverlayForScroll);
} }
if (widget.focusNode != oldWidget.focusNode) { if (widget.focusNode != oldWidget.focusNode) {
@ -806,7 +785,6 @@ class RawEditorState extends EditorState
handleDelete(bool forward) { handleDelete(bool forward) {
TextSelection selection = widget.controller.selection; TextSelection selection = widget.controller.selection;
String plainText = textEditingValue.text; String plainText = textEditingValue.text;
assert(selection != null);
int cursorPosition = selection.start; int cursorPosition = selection.start;
String textBefore = selection.textBefore(plainText); String textBefore = selection.textBefore(plainText);
String textAfter = selection.textAfter(plainText); String textAfter = selection.textAfter(plainText);
@ -834,9 +812,8 @@ class RawEditorState extends EditorState
); );
} }
Future<void> handleShortcut(InputShortcut shortcut) async { void handleShortcut(InputShortcut? shortcut) async {
TextSelection selection = widget.controller.selection; TextSelection selection = widget.controller.selection;
assert(selection != null);
String plainText = textEditingValue.text; String plainText = textEditingValue.text;
if (shortcut == InputShortcut.COPY) { if (shortcut == InputShortcut.COPY) {
if (!selection.isCollapsed) { if (!selection.isCollapsed) {
@ -865,13 +842,13 @@ class RawEditorState extends EditorState
return; return;
} }
if (shortcut == InputShortcut.PASTE && !widget.readOnly) { if (shortcut == InputShortcut.PASTE && !widget.readOnly) {
ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain); ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null) { if (data != null) {
widget.controller.replaceText( widget.controller.replaceText(
selection.start, selection.start,
selection.end - selection.start, selection.end - selection.start,
data.text, data.text,
TextSelection.collapsed(offset: selection.start + data.text.length), TextSelection.collapsed(offset: selection.start + data.text!.length),
); );
} }
return; return;
@ -891,13 +868,13 @@ class RawEditorState extends EditorState
@override @override
void dispose() { void dispose() {
closeConnectionIfNeeded(); closeConnectionIfNeeded();
_keyboardVisibilitySubscription?.cancel(); _keyboardVisibilitySubscription.cancel();
assert(!hasConnection); assert(!hasConnection);
_selectionOverlay?.dispose(); _selectionOverlay?.dispose();
_selectionOverlay = null; _selectionOverlay = null;
widget.controller.removeListener(_didChangeTextEditingValue); widget.controller.removeListener(_didChangeTextEditingValue);
widget.focusNode.removeListener(_handleFocusChanged); widget.focusNode.removeListener(_handleFocusChanged);
_focusAttachment.detach(); _focusAttachment!.detach();
_cursorCont.dispose(); _cursorCont.dispose();
_clipboardStatus?.removeListener(_onChangedClipboardStatus); _clipboardStatus?.removeListener(_onChangedClipboardStatus);
_clipboardStatus?.dispose(); _clipboardStatus?.dispose();
@ -932,7 +909,7 @@ class RawEditorState extends EditorState
_cursorCont.startCursorTimer(); _cursorCont.startCursorTimer();
} }
SchedulerBinding.instance.addPostFrameCallback( SchedulerBinding.instance!.addPostFrameCallback(
(Duration _) => _updateOrDisposeSelectionOverlayIfNeeded()); (Duration _) => _updateOrDisposeSelectionOverlayIfNeeded());
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
@ -944,33 +921,32 @@ class RawEditorState extends EditorState
_updateOrDisposeSelectionOverlayIfNeeded() { _updateOrDisposeSelectionOverlayIfNeeded() {
if (_selectionOverlay != null) { if (_selectionOverlay != null) {
if (_hasFocus) { if (_hasFocus) {
_selectionOverlay.update(textEditingValue); _selectionOverlay!.update(textEditingValue);
} else { } else {
_selectionOverlay.dispose(); _selectionOverlay!.dispose();
_selectionOverlay = null; _selectionOverlay = null;
} }
} else if (_hasFocus) { } else if (_hasFocus) {
_selectionOverlay?.hide(); _selectionOverlay?.hide();
_selectionOverlay = null; _selectionOverlay = null;
if (widget.selectionCtrls != null) { _selectionOverlay = EditorTextSelectionOverlay(
_selectionOverlay = EditorTextSelectionOverlay( textEditingValue,
textEditingValue, false,
false, context,
context, widget,
widget, _toolbarLayerLink,
_toolbarLayerLink, _startHandleLayerLink,
_startHandleLayerLink, _endHandleLayerLink,
_endHandleLayerLink, getRenderEditor(),
getRenderEditor(), widget.selectionCtrls,
widget.selectionCtrls, this,
this, DragStartBehavior.start,
DragStartBehavior.start, null,
null, _clipboardStatus!,
_clipboardStatus); );
_selectionOverlay.handlesVisible = _shouldShowSelectionHandles(); _selectionOverlay!.handlesVisible = _shouldShowSelectionHandles();
_selectionOverlay.showHandles(); _selectionOverlay!.showHandles();
}
} }
} }
@ -980,10 +956,10 @@ class RawEditorState extends EditorState
_hasFocus, widget.controller.selection); _hasFocus, widget.controller.selection);
_updateOrDisposeSelectionOverlayIfNeeded(); _updateOrDisposeSelectionOverlayIfNeeded();
if (_hasFocus) { if (_hasFocus) {
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance!.addObserver(this);
_showCaretOnScreen(); _showCaretOnScreen();
} else { } else {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance!.removeObserver(this);
} }
updateKeepAlive(); updateKeepAlive();
} }
@ -1004,23 +980,22 @@ class RawEditorState extends EditorState
} }
_showCaretOnScreenScheduled = true; _showCaretOnScreenScheduled = true;
SchedulerBinding.instance.addPostFrameCallback((Duration _) { SchedulerBinding.instance!.addPostFrameCallback((Duration _) {
_showCaretOnScreenScheduled = false; _showCaretOnScreenScheduled = false;
final viewport = RenderAbstractViewport.of(getRenderEditor()); final viewport = RenderAbstractViewport.of(getRenderEditor())!;
assert(viewport != null); final editorOffset = getRenderEditor()!
final editorOffset = .localToGlobal(Offset(0.0, 0.0), ancestor: viewport);
getRenderEditor().localToGlobal(Offset(0.0, 0.0), ancestor: viewport); final offsetInViewport = _scrollController!.offset + editorOffset.dy;
final offsetInViewport = _scrollController.offset + editorOffset.dy;
final offset = getRenderEditor().getOffsetToRevealCursor( final offset = getRenderEditor()!.getOffsetToRevealCursor(
_scrollController.position.viewportDimension, _scrollController!.position.viewportDimension,
_scrollController.offset, _scrollController!.offset,
offsetInViewport, offsetInViewport,
); );
if (offset != null) { if (offset != null) {
_scrollController.animateTo( _scrollController!.animateTo(
offset, offset,
duration: Duration(milliseconds: 100), duration: Duration(milliseconds: 100),
curve: Curves.fastOutSlowIn, curve: Curves.fastOutSlowIn,
@ -1030,12 +1005,12 @@ class RawEditorState extends EditorState
} }
@override @override
RenderEditor getRenderEditor() { RenderEditor? getRenderEditor() {
return _editorKey.currentContext.findRenderObject(); return _editorKey.currentContext!.findRenderObject() as RenderEditor?;
} }
@override @override
EditorTextSelectionOverlay getSelectionOverlay() { EditorTextSelectionOverlay? getSelectionOverlay() {
return _selectionOverlay; return _selectionOverlay;
} }
@ -1091,7 +1066,7 @@ class RawEditorState extends EditorState
); );
} else { } else {
final TextEditingValue value = textEditingValue; final TextEditingValue value = textEditingValue;
final ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain); final ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null) { if (data != null) {
final length = final length =
textEditingValue.selection.end - textEditingValue.selection.start; textEditingValue.selection.end - textEditingValue.selection.start;
@ -1104,32 +1079,37 @@ class RawEditorState extends EditorState
// move cursor to the end of pasted text selection // move cursor to the end of pasted text selection
widget.controller.updateSelection( widget.controller.updateSelection(
TextSelection.collapsed( TextSelection.collapsed(
offset: value.selection.start + data.text.length), offset: value.selection.start + data.text!.length),
ChangeSource.LOCAL); ChangeSource.LOCAL);
} }
} }
} }
Future<bool> __isItCut(TextEditingValue value) async { Future<bool> __isItCut(TextEditingValue value) async {
final ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain); final ClipboardData data = await (Clipboard.getData(Clipboard.kTextPlain)
return textEditingValue.text.length - value.text.length == data.text.length; as FutureOr<ClipboardData>);
return textEditingValue.text.length - value.text.length ==
data.text!.length;
} }
@override @override
bool showToolbar() { bool showToolbar() {
// Web is using native dom elements to enable clipboard functionality of the if (_selectionOverlay == null || _selectionOverlay!.toolbar != null) {
// toolbar: copy, paste, select, cut. It might also provide additional // Web is using native dom elements to enable clipboard functionality of the
// functionality depending on the browser (such as translate). Due to this // toolbar: copy, paste, select, cut. It might also provide additional
// we should not show a Flutter toolbar for the editable text elements. // functionality depending on the browser (such as translate). Due to this
if (kIsWeb) { // we should not show a Flutter toolbar for the editable text elements.
return false; if (kIsWeb) {
} return false;
}
if (_selectionOverlay == null || _selectionOverlay.toolbar != null) { if (_selectionOverlay == null || _selectionOverlay!.toolbar != null) {
return false; return false;
} }
_selectionOverlay.showToolbar(); _selectionOverlay!.showToolbar();
return true;
}
return true; return true;
} }
@ -1147,15 +1127,15 @@ class RawEditorState extends EditorState
class _Editor extends MultiChildRenderObjectWidget { class _Editor extends MultiChildRenderObjectWidget {
_Editor({ _Editor({
@required Key key, required Key key,
@required List<Widget> children, required List<Widget> children,
@required this.document, required this.document,
@required this.textDirection, required this.textDirection,
@required this.hasFocus, required this.hasFocus,
@required this.selection, required this.selection,
@required this.startHandleLayerLink, required this.startHandleLayerLink,
@required this.endHandleLayerLink, required this.endHandleLayerLink,
@required this.onSelectionChanged, required this.onSelectionChanged,
this.padding = EdgeInsets.zero, this.padding = EdgeInsets.zero,
}) : super(key: key, children: children); }) : super(key: key, children: children);

@ -2,12 +2,12 @@ import 'package:flutter/material.dart';
class ResponsiveWidget extends StatelessWidget { class ResponsiveWidget extends StatelessWidget {
final Widget largeScreen; final Widget largeScreen;
final Widget mediumScreen; final Widget? mediumScreen;
final Widget smallScreen; final Widget? smallScreen;
const ResponsiveWidget( const ResponsiveWidget(
{Key key, {Key? key,
@required this.largeScreen, required this.largeScreen,
this.mediumScreen, this.mediumScreen,
this.smallScreen}) this.smallScreen})
: super(key: key); : super(key: key);

@ -53,10 +53,10 @@ class EditableTextBlock extends StatelessWidget {
final Tuple2 verticalSpacing; final Tuple2 verticalSpacing;
final TextSelection textSelection; final TextSelection textSelection;
final Color color; final Color color;
final DefaultStyles styles; final DefaultStyles? styles;
final bool enableInteractiveSelection; final bool enableInteractiveSelection;
final bool hasFocus; final bool hasFocus;
final EdgeInsets contentPadding; final EdgeInsets? contentPadding;
final EmbedBuilder embedBuilder; final EmbedBuilder embedBuilder;
final CursorCont cursorCont; final CursorCont cursorCont;
final Map<int, int> indentLevelCounts; final Map<int, int> indentLevelCounts;
@ -73,44 +73,41 @@ class EditableTextBlock extends StatelessWidget {
this.contentPadding, this.contentPadding,
this.embedBuilder, this.embedBuilder,
this.cursorCont, this.cursorCont,
this.indentLevelCounts) this.indentLevelCounts);
: assert(hasFocus != null),
assert(embedBuilder != null),
assert(cursorCont != null);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMediaQuery(context));
DefaultStyles defaultStyles = QuillStyles.getStyles(context, false); DefaultStyles? defaultStyles = QuillStyles.getStyles(context, false);
return _EditableBlock( return _EditableBlock(
block, block,
textDirection, textDirection,
verticalSpacing, verticalSpacing as Tuple2<double, double>,
_getDecorationForBlock(block, defaultStyles) ?? BoxDecoration(), _getDecorationForBlock(block, defaultStyles) ?? BoxDecoration(),
contentPadding, contentPadding,
_buildChildren(context, this.indentLevelCounts)); _buildChildren(context, this.indentLevelCounts));
} }
BoxDecoration _getDecorationForBlock( BoxDecoration? _getDecorationForBlock(
Block node, DefaultStyles defaultStyles) { Block node, DefaultStyles? defaultStyles) {
Map<String, Attribute> attrs = block.style.attributes; Map<String, Attribute> attrs = block.style.attributes;
if (attrs.containsKey(Attribute.blockQuote.key)) { if (attrs.containsKey(Attribute.blockQuote.key)) {
return defaultStyles.quote.decoration; return defaultStyles!.quote!.decoration;
} }
if (attrs.containsKey(Attribute.codeBlock.key)) { if (attrs.containsKey(Attribute.codeBlock.key)) {
return defaultStyles.code.decoration; return defaultStyles!.code!.decoration;
} }
return null; return null;
} }
List<Widget> _buildChildren( List<Widget> _buildChildren(
BuildContext context, Map<int, int> indentLevelCounts) { BuildContext context, Map<int, int> indentLevelCounts) {
DefaultStyles defaultStyles = QuillStyles.getStyles(context, false); DefaultStyles? defaultStyles = QuillStyles.getStyles(context, false);
int count = block.children.length; int count = block.children.length;
var children = <Widget>[]; var children = <Widget>[];
int index = 0; int index = 0;
for (Line line in block.children) { for (Line line in block.children as Iterable<Line>) {
index++; index++;
EditableTextLine editableTextLine = EditableTextLine( EditableTextLine editableTextLine = EditableTextLine(
line, line,
@ -119,7 +116,7 @@ class EditableTextBlock extends StatelessWidget {
line: line, line: line,
textDirection: textDirection, textDirection: textDirection,
embedBuilder: embedBuilder, embedBuilder: embedBuilder,
styles: styles, styles: styles!,
), ),
_getIndentWidth(), _getIndentWidth(),
_getSpacingForLine(line, index, count, defaultStyles), _getSpacingForLine(line, index, count, defaultStyles),
@ -135,16 +132,16 @@ class EditableTextBlock extends StatelessWidget {
return children.toList(growable: false); return children.toList(growable: false);
} }
Widget _buildLeading(BuildContext context, Line line, int index, Widget? _buildLeading(BuildContext context, Line line, int index,
Map<int, int> indentLevelCounts, int count) { Map<int, int> indentLevelCounts, int count) {
DefaultStyles defaultStyles = QuillStyles.getStyles(context, false); DefaultStyles? defaultStyles = QuillStyles.getStyles(context, false);
Map<String, Attribute> attrs = line.style.attributes; Map<String, Attribute> attrs = line.style.attributes;
if (attrs[Attribute.list.key] == Attribute.ol) { if (attrs[Attribute.list.key] == Attribute.ol) {
return _NumberPoint( return _NumberPoint(
index: index, index: index,
indentLevelCounts: indentLevelCounts, indentLevelCounts: indentLevelCounts,
count: count, count: count,
style: defaultStyles.paragraph.style, style: defaultStyles!.paragraph!.style,
attrs: attrs, attrs: attrs,
width: 32.0, width: 32.0,
padding: 8.0, padding: 8.0,
@ -153,20 +150,20 @@ class EditableTextBlock extends StatelessWidget {
if (attrs[Attribute.list.key] == Attribute.ul) { if (attrs[Attribute.list.key] == Attribute.ul) {
return _BulletPoint( return _BulletPoint(
style: style: defaultStyles!.paragraph!.style
defaultStyles.paragraph.style.copyWith(fontWeight: FontWeight.bold), .copyWith(fontWeight: FontWeight.bold),
width: 32, width: 32,
); );
} }
if (attrs[Attribute.list.key] == Attribute.checked) { if (attrs[Attribute.list.key] == Attribute.checked) {
return _Checkbox( return _Checkbox(
style: defaultStyles.paragraph.style, width: 32, isChecked: true); style: defaultStyles!.paragraph!.style, width: 32, isChecked: true);
} }
if (attrs[Attribute.list.key] == Attribute.unchecked) { if (attrs[Attribute.list.key] == Attribute.unchecked) {
return _Checkbox( return _Checkbox(
style: defaultStyles.paragraph.style, width: 32, isChecked: false); style: defaultStyles!.paragraph!.style, width: 32, isChecked: false);
} }
if (attrs.containsKey(Attribute.codeBlock.key)) { if (attrs.containsKey(Attribute.codeBlock.key)) {
@ -174,8 +171,8 @@ class EditableTextBlock extends StatelessWidget {
index: index, index: index,
indentLevelCounts: indentLevelCounts, indentLevelCounts: indentLevelCounts,
count: count, count: count,
style: defaultStyles.code.style style: defaultStyles!.code!.style
.copyWith(color: defaultStyles.code.style.color.withOpacity(0.4)), .copyWith(color: defaultStyles.code!.style.color!.withOpacity(0.4)),
width: 32.0, width: 32.0,
attrs: attrs, attrs: attrs,
padding: 16.0, padding: 16.0,
@ -188,7 +185,7 @@ class EditableTextBlock extends StatelessWidget {
double _getIndentWidth() { double _getIndentWidth() {
Map<String, Attribute> attrs = block.style.attributes; Map<String, Attribute> attrs = block.style.attributes;
Attribute indent = attrs[Attribute.indent.key]; Attribute? indent = attrs[Attribute.indent.key];
double extraIndent = 0.0; double extraIndent = 0.0;
if (indent != null && indent.value != null) { if (indent != null && indent.value != null) {
extraIndent = 16.0 * indent.value; extraIndent = 16.0 * indent.value;
@ -202,40 +199,40 @@ class EditableTextBlock extends StatelessWidget {
} }
Tuple2 _getSpacingForLine( Tuple2 _getSpacingForLine(
Line node, int index, int count, DefaultStyles defaultStyles) { Line node, int index, int count, DefaultStyles? defaultStyles) {
double top = 0.0, bottom = 0.0; double top = 0.0, bottom = 0.0;
Map<String, Attribute> attrs = block.style.attributes; Map<String, Attribute> attrs = block.style.attributes;
if (attrs.containsKey(Attribute.header.key)) { if (attrs.containsKey(Attribute.header.key)) {
int level = attrs[Attribute.header.key].value; int? level = attrs[Attribute.header.key]!.value;
switch (level) { switch (level) {
case 1: case 1:
top = defaultStyles.h1.verticalSpacing.item1; top = defaultStyles!.h1!.verticalSpacing.item1;
bottom = defaultStyles.h1.verticalSpacing.item2; bottom = defaultStyles.h1!.verticalSpacing.item2;
break; break;
case 2: case 2:
top = defaultStyles.h2.verticalSpacing.item1; top = defaultStyles!.h2!.verticalSpacing.item1;
bottom = defaultStyles.h2.verticalSpacing.item2; bottom = defaultStyles.h2!.verticalSpacing.item2;
break; break;
case 3: case 3:
top = defaultStyles.h3.verticalSpacing.item1; top = defaultStyles!.h3!.verticalSpacing.item1;
bottom = defaultStyles.h3.verticalSpacing.item2; bottom = defaultStyles.h3!.verticalSpacing.item2;
break; break;
default: default:
throw ('Invalid level $level'); throw ('Invalid level $level');
} }
} else { } else {
Tuple2 lineSpacing; late Tuple2 lineSpacing;
if (attrs.containsKey(Attribute.blockQuote.key)) { if (attrs.containsKey(Attribute.blockQuote.key)) {
lineSpacing = defaultStyles.quote.lineSpacing; lineSpacing = defaultStyles!.quote!.lineSpacing;
} else if (attrs.containsKey(Attribute.indent.key)) { } else if (attrs.containsKey(Attribute.indent.key)) {
lineSpacing = defaultStyles.indent.lineSpacing; lineSpacing = defaultStyles!.indent!.lineSpacing;
} else if (attrs.containsKey(Attribute.list.key)) { } else if (attrs.containsKey(Attribute.list.key)) {
lineSpacing = defaultStyles.lists.lineSpacing; lineSpacing = defaultStyles!.lists!.lineSpacing;
} else if (attrs.containsKey(Attribute.codeBlock.key)) { } else if (attrs.containsKey(Attribute.codeBlock.key)) {
lineSpacing = defaultStyles.code.lineSpacing; lineSpacing = defaultStyles!.code!.lineSpacing;
} else if (attrs.containsKey(Attribute.align.key)) { } else if (attrs.containsKey(Attribute.align.key)) {
lineSpacing = defaultStyles.align.lineSpacing; lineSpacing = defaultStyles!.align!.lineSpacing;
} }
top = lineSpacing.item1; top = lineSpacing.item1;
bottom = lineSpacing.item2; bottom = lineSpacing.item2;
@ -256,19 +253,14 @@ class EditableTextBlock extends StatelessWidget {
class RenderEditableTextBlock extends RenderEditableContainerBox class RenderEditableTextBlock extends RenderEditableContainerBox
implements RenderEditableBox { implements RenderEditableBox {
RenderEditableTextBlock({ RenderEditableTextBlock({
List<RenderEditableBox> children, List<RenderEditableBox>? children,
@required Block block, required Block block,
@required TextDirection textDirection, required TextDirection textDirection,
@required EdgeInsetsGeometry padding, required EdgeInsetsGeometry padding,
@required Decoration decoration, required Decoration decoration,
ImageConfiguration configuration = ImageConfiguration.empty, ImageConfiguration configuration = ImageConfiguration.empty,
EdgeInsets contentPadding = EdgeInsets.zero, EdgeInsets contentPadding = EdgeInsets.zero,
}) : assert(block != null), }) : _decoration = decoration,
assert(textDirection != null),
assert(decoration != null),
assert(padding != null),
assert(contentPadding != null),
_decoration = decoration,
_configuration = configuration, _configuration = configuration,
_savedPadding = padding, _savedPadding = padding,
_contentPadding = contentPadding, _contentPadding = contentPadding,
@ -283,7 +275,6 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
EdgeInsets _contentPadding; EdgeInsets _contentPadding;
set contentPadding(EdgeInsets value) { set contentPadding(EdgeInsets value) {
assert(value != null);
if (_contentPadding == value) return; if (_contentPadding == value) return;
_contentPadding = value; _contentPadding = value;
super.setPadding(_savedPadding.add(_contentPadding)); super.setPadding(_savedPadding.add(_contentPadding));
@ -295,13 +286,12 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
_savedPadding = value; _savedPadding = value;
} }
BoxPainter _painter; BoxPainter? _painter;
Decoration get decoration => _decoration; Decoration get decoration => _decoration;
Decoration _decoration; Decoration _decoration;
set decoration(Decoration value) { set decoration(Decoration value) {
assert(value != null);
if (value == _decoration) return; if (value == _decoration) return;
_painter?.dispose(); _painter?.dispose();
_painter = null; _painter = null;
@ -313,7 +303,6 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
ImageConfiguration _configuration; ImageConfiguration _configuration;
set configuration(ImageConfiguration value) { set configuration(ImageConfiguration value) {
assert(value != null);
if (value == _configuration) return; if (value == _configuration) return;
_configuration = value; _configuration = value;
markNeedsPaint(); markNeedsPaint();
@ -344,8 +333,8 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
@override @override
TextPosition getPositionForOffset(Offset offset) { TextPosition getPositionForOffset(Offset offset) {
RenderEditableBox child = childAtOffset(offset); RenderEditableBox child = childAtOffset(offset)!;
BoxParentData parentData = child.parentData; BoxParentData parentData = child.parentData as BoxParentData;
TextPosition localPosition = TextPosition localPosition =
child.getPositionForOffset(offset - parentData.offset); child.getPositionForOffset(offset - parentData.offset);
return TextPosition( return TextPosition(
@ -367,19 +356,19 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
} }
@override @override
TextPosition getPositionAbove(TextPosition position) { TextPosition? getPositionAbove(TextPosition position) {
assert(position.offset < getContainer().length); assert(position.offset < getContainer().length);
RenderEditableBox child = childAtPosition(position); RenderEditableBox child = childAtPosition(position);
TextPosition childLocalPosition = TextPosition( TextPosition childLocalPosition = TextPosition(
offset: position.offset - child.getContainer().getOffset()); offset: position.offset - child.getContainer().getOffset());
TextPosition result = child.getPositionAbove(childLocalPosition); TextPosition? result = child.getPositionAbove(childLocalPosition);
if (result != null) { if (result != null) {
return TextPosition( return TextPosition(
offset: result.offset + child.getContainer().getOffset()); offset: result.offset + child.getContainer().getOffset());
} }
RenderEditableBox sibling = childBefore(child); RenderEditableBox? sibling = childBefore(child);
if (sibling == null) { if (sibling == null) {
return null; return null;
} }
@ -395,19 +384,19 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
} }
@override @override
TextPosition getPositionBelow(TextPosition position) { TextPosition? getPositionBelow(TextPosition position) {
assert(position.offset < getContainer().length); assert(position.offset < getContainer().length);
RenderEditableBox child = childAtPosition(position); RenderEditableBox child = childAtPosition(position);
TextPosition childLocalPosition = TextPosition( TextPosition childLocalPosition = TextPosition(
offset: position.offset - child.getContainer().getOffset()); offset: position.offset - child.getContainer().getOffset());
TextPosition result = child.getPositionBelow(childLocalPosition); TextPosition? result = child.getPositionBelow(childLocalPosition);
if (result != null) { if (result != null) {
return TextPosition( return TextPosition(
offset: result.offset + child.getContainer().getOffset()); offset: result.offset + child.getContainer().getOffset());
} }
RenderEditableBox sibling = childAfter(child); RenderEditableBox? sibling = childAfter(child);
if (sibling == null) { if (sibling == null) {
return null; return null;
} }
@ -436,7 +425,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
null); null);
} }
Node baseNode = getContainer().queryChild(selection.start, false).node; Node? baseNode = getContainer().queryChild(selection.start, false).node;
var baseChild = firstChild; var baseChild = firstChild;
while (baseChild != null) { while (baseChild != null) {
if (baseChild.getContainer() == baseNode) { if (baseChild.getContainer() == baseNode) {
@ -446,7 +435,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
} }
assert(baseChild != null); assert(baseChild != null);
TextSelectionPoint basePoint = baseChild.getBaseEndpointForSelection( TextSelectionPoint basePoint = baseChild!.getBaseEndpointForSelection(
localSelection(baseChild.getContainer(), selection, true)); localSelection(baseChild.getContainer(), selection, true));
return TextSelectionPoint( return TextSelectionPoint(
basePoint.point + (baseChild.parentData as BoxParentData).offset, basePoint.point + (baseChild.parentData as BoxParentData).offset,
@ -462,7 +451,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
null); null);
} }
Node extentNode = getContainer().queryChild(selection.end, false).node; Node? extentNode = getContainer().queryChild(selection.end, false).node;
var extentChild = firstChild; var extentChild = firstChild;
while (extentChild != null) { while (extentChild != null) {
@ -473,7 +462,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
} }
assert(extentChild != null); assert(extentChild != null);
TextSelectionPoint extentPoint = extentChild.getExtentEndpointForSelection( TextSelectionPoint extentPoint = extentChild!.getExtentEndpointForSelection(
localSelection(extentChild.getContainer(), selection, true)); localSelection(extentChild.getContainer(), selection, true));
return TextSelectionPoint( return TextSelectionPoint(
extentPoint.point + (extentChild.parentData as BoxParentData).offset, extentPoint.point + (extentChild.parentData as BoxParentData).offset,
@ -495,11 +484,9 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
} }
_paintDecoration(PaintingContext context, Offset offset) { _paintDecoration(PaintingContext context, Offset offset) {
assert(size.width != null);
assert(size.height != null);
_painter ??= _decoration.createBoxPainter(markNeedsPaint); _painter ??= _decoration.createBoxPainter(markNeedsPaint);
EdgeInsets decorationPadding = resolvedPadding - _contentPadding; EdgeInsets decorationPadding = resolvedPadding! - _contentPadding;
ImageConfiguration filledConfiguration = ImageConfiguration filledConfiguration =
configuration.copyWith(size: decorationPadding.deflateSize(size)); configuration.copyWith(size: decorationPadding.deflateSize(size));
@ -507,7 +494,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
final decorationOffset = final decorationOffset =
offset.translate(decorationPadding.left, decorationPadding.top); offset.translate(decorationPadding.left, decorationPadding.top);
_painter.paint(context.canvas, decorationOffset, filledConfiguration); _painter!.paint(context.canvas, decorationOffset, filledConfiguration);
if (debugSaveCount != context.canvas.getSaveCount()) { if (debugSaveCount != context.canvas.getSaveCount()) {
throw ('${_decoration.runtimeType} painter had mismatching save and restore calls.'); throw ('${_decoration.runtimeType} painter had mismatching save and restore calls.');
} }
@ -517,7 +504,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
} }
@override @override
bool hitTestChildren(BoxHitTestResult result, {Offset position}) { bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
return defaultHitTestChildren(result, position: position); return defaultHitTestChildren(result, position: position);
} }
} }
@ -527,16 +514,11 @@ class _EditableBlock extends MultiChildRenderObjectWidget {
final TextDirection textDirection; final TextDirection textDirection;
final Tuple2<double, double> padding; final Tuple2<double, double> padding;
final Decoration decoration; final Decoration decoration;
final EdgeInsets contentPadding; final EdgeInsets? contentPadding;
_EditableBlock(this.block, this.textDirection, this.padding, this.decoration, _EditableBlock(this.block, this.textDirection, this.padding, this.decoration,
this.contentPadding, List<Widget> children) this.contentPadding, List<Widget> children)
: assert(block != null), : super(children: children);
assert(textDirection != null),
assert(padding != null),
assert(decoration != null),
assert(children != null),
super(children: children);
EdgeInsets get _padding => EdgeInsets get _padding =>
EdgeInsets.only(top: padding.item1, bottom: padding.item2); EdgeInsets.only(top: padding.item1, bottom: padding.item2);
@ -567,7 +549,7 @@ class _EditableBlock extends MultiChildRenderObjectWidget {
class _NumberPoint extends StatelessWidget { class _NumberPoint extends StatelessWidget {
final int index; final int index;
final Map<int, int> indentLevelCounts; final Map<int?, int> indentLevelCounts;
final int count; final int count;
final TextStyle style; final TextStyle style;
final double width; final double width;
@ -576,13 +558,13 @@ class _NumberPoint extends StatelessWidget {
final double padding; final double padding;
const _NumberPoint({ const _NumberPoint({
Key key, Key? key,
@required this.index, required this.index,
@required this.indentLevelCounts, required this.indentLevelCounts,
@required this.count, required this.count,
@required this.style, required this.style,
@required this.width, required this.width,
@required this.attrs, required this.attrs,
this.withDot = true, this.withDot = true,
this.padding = 0.0, this.padding = 0.0,
}) : super(key: key); }) : super(key: key);
@ -590,7 +572,7 @@ class _NumberPoint extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String s = this.index.toString(); String s = this.index.toString();
int level = 0; int? level = 0;
if (!this.attrs.containsKey(Attribute.indent.key) && if (!this.attrs.containsKey(Attribute.indent.key) &&
!this.indentLevelCounts.containsKey(1)) { !this.indentLevelCounts.containsKey(1)) {
this.indentLevelCounts.clear(); this.indentLevelCounts.clear();
@ -602,13 +584,13 @@ class _NumberPoint extends StatelessWidget {
); );
} }
if (this.attrs.containsKey(Attribute.indent.key)) { if (this.attrs.containsKey(Attribute.indent.key)) {
level = this.attrs[Attribute.indent.key].value; level = this.attrs[Attribute.indent.key]!.value;
} else { } else {
// first level but is back from previous indent level // first level but is back from previous indent level
// supposed to be "2." // supposed to be "2."
this.indentLevelCounts[0] = 1; this.indentLevelCounts[0] = 1;
} }
if (this.indentLevelCounts.containsKey(level + 1)) { if (this.indentLevelCounts.containsKey(level! + 1)) {
// last visited level is done, going up // last visited level is done, going up
this.indentLevelCounts.remove(level + 1); this.indentLevelCounts.remove(level + 1);
} }
@ -674,9 +656,9 @@ class _BulletPoint extends StatelessWidget {
final double width; final double width;
const _BulletPoint({ const _BulletPoint({
Key key, Key? key,
@required this.style, required this.style,
@required this.width, required this.width,
}) : super(key: key); }) : super(key: key);
@override @override
@ -691,23 +673,23 @@ class _BulletPoint extends StatelessWidget {
} }
class _Checkbox extends StatefulWidget { class _Checkbox extends StatefulWidget {
final TextStyle style; final TextStyle? style;
final double width; final double? width;
final bool isChecked; final bool? isChecked;
const _Checkbox({Key key, this.style, this.width, this.isChecked}) const _Checkbox({Key? key, this.style, this.width, this.isChecked})
: super(key: key); : super(key: key);
@override @override
__CheckboxState createState() => __CheckboxState(); __CheckboxState createState() => __CheckboxState();
} }
class __CheckboxState extends State<_Checkbox> { class __CheckboxState extends State<_Checkbox> {
bool isChecked; bool? isChecked;
void _onCheckboxClicked(bool newValue) => setState(() { void _onCheckboxClicked(bool? newValue) => setState(() {
isChecked = newValue; isChecked = newValue;
if (isChecked) { if (isChecked!) {
// check list // check list
} else { } else {
// uncheck list // uncheck list

@ -23,16 +23,17 @@ import 'delegate.dart';
class TextLine extends StatelessWidget { class TextLine extends StatelessWidget {
final Line line; final Line line;
final TextDirection textDirection; final TextDirection? textDirection;
final EmbedBuilder embedBuilder; final EmbedBuilder embedBuilder;
final DefaultStyles styles; final DefaultStyles styles;
const TextLine( const TextLine(
{Key key, this.line, this.textDirection, this.embedBuilder, this.styles}) {Key? key,
: assert(line != null), required this.line,
assert(embedBuilder != null), this.textDirection,
assert(styles != null), required this.embedBuilder,
super(key: key); required this.styles})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -45,7 +46,7 @@ class TextLine extends StatelessWidget {
TextSpan textSpan = _buildTextSpan(context); TextSpan textSpan = _buildTextSpan(context);
StrutStyle strutStyle = StrutStyle strutStyle =
StrutStyle.fromTextStyle(textSpan.style, forceStrutHeight: true); StrutStyle.fromTextStyle(textSpan.style!, forceStrutHeight: true);
final textAlign = _getTextAlign(); final textAlign = _getTextAlign();
RichText child = RichText( RichText child = RichText(
text: TextSpan(children: [textSpan]), text: TextSpan(children: [textSpan]),
@ -56,9 +57,9 @@ class TextLine extends StatelessWidget {
); );
return RichTextProxy( return RichTextProxy(
child, child,
textSpan.style, textSpan.style!,
textAlign, textAlign,
textDirection, textDirection!,
1.0, 1.0,
Localizations.localeOf(context), Localizations.localeOf(context),
strutStyle, strutStyle,
@ -89,27 +90,27 @@ class TextLine extends StatelessWidget {
TextStyle textStyle = TextStyle(); TextStyle textStyle = TextStyle();
if (line.style.containsKey(Attribute.placeholder.key)) { if (line.style.containsKey(Attribute.placeholder.key)) {
textStyle = defaultStyles.placeHolder.style; textStyle = defaultStyles.placeHolder!.style;
return TextSpan(children: children, style: textStyle); return TextSpan(children: children, style: textStyle);
} }
Attribute header = line.style.attributes[Attribute.header.key]; Attribute? header = line.style.attributes[Attribute.header.key];
Map<Attribute, TextStyle> m = { Map<Attribute, TextStyle> m = {
Attribute.h1: defaultStyles.h1.style, Attribute.h1: defaultStyles.h1!.style,
Attribute.h2: defaultStyles.h2.style, Attribute.h2: defaultStyles.h2!.style,
Attribute.h3: defaultStyles.h3.style, Attribute.h3: defaultStyles.h3!.style,
}; };
textStyle = textStyle.merge(m[header] ?? defaultStyles.paragraph.style); textStyle = textStyle.merge(m[header!] ?? defaultStyles.paragraph!.style);
Attribute block = line.style.getBlockExceptHeader(); Attribute? block = line.style.getBlockExceptHeader();
TextStyle toMerge; TextStyle? toMerge;
if (block == Attribute.blockQuote) { if (block == Attribute.blockQuote) {
toMerge = defaultStyles.quote.style; toMerge = defaultStyles.quote!.style;
} else if (block == Attribute.codeBlock) { } else if (block == Attribute.codeBlock) {
toMerge = defaultStyles.code.style; toMerge = defaultStyles.code!.style;
} else if (block != null) { } else if (block != null) {
toMerge = defaultStyles.lists.style; toMerge = defaultStyles.lists!.style;
} }
textStyle = textStyle.merge(toMerge); textStyle = textStyle.merge(toMerge);
@ -122,7 +123,7 @@ class TextLine extends StatelessWidget {
Style style = textNode.style; Style style = textNode.style;
TextStyle res = TextStyle(); TextStyle res = TextStyle();
Map<String, TextStyle> m = { Map<String, TextStyle?> m = {
Attribute.bold.key: defaultStyles.bold, Attribute.bold.key: defaultStyles.bold,
Attribute.italic.key: defaultStyles.italic, Attribute.italic.key: defaultStyles.italic,
Attribute.link.key: defaultStyles.link, Attribute.link.key: defaultStyles.link,
@ -131,16 +132,16 @@ class TextLine extends StatelessWidget {
}; };
m.forEach((k, s) { m.forEach((k, s) {
if (style.values.any((v) => v.key == k)) { if (style.values.any((v) => v.key == k)) {
res = _merge(res, s); res = _merge(res, s!);
} }
}); });
Attribute font = textNode.style.attributes[Attribute.font.key]; Attribute? font = textNode.style.attributes[Attribute.font.key];
if (font != null && font.value != null) { if (font != null && font.value != null) {
res = res.merge(TextStyle(fontFamily: font.value)); res = res.merge(TextStyle(fontFamily: font.value));
} }
Attribute size = textNode.style.attributes[Attribute.size.key]; Attribute? size = textNode.style.attributes[Attribute.size.key];
if (size != null && size.value != null) { if (size != null && size.value != null) {
switch (size.value) { switch (size.value) {
case 'small': case 'small':
@ -153,7 +154,7 @@ class TextLine extends StatelessWidget {
res = res.merge(defaultStyles.sizeHuge); res = res.merge(defaultStyles.sizeHuge);
break; break;
default: default:
double fontSize = double.tryParse(size.value); double? fontSize = double.tryParse(size.value);
if (fontSize != null) { if (fontSize != null) {
res = res.merge(TextStyle(fontSize: fontSize)); res = res.merge(TextStyle(fontSize: fontSize));
} else { } else {
@ -162,13 +163,13 @@ class TextLine extends StatelessWidget {
} }
} }
Attribute color = textNode.style.attributes[Attribute.color.key]; Attribute? color = textNode.style.attributes[Attribute.color.key];
if (color != null && color.value != null) { if (color != null && color.value != null) {
final textColor = stringToColor(color.value); final textColor = stringToColor(color.value);
res = res.merge(new TextStyle(color: textColor)); res = res.merge(new TextStyle(color: textColor));
} }
Attribute background = textNode.style.attributes[Attribute.background.key]; Attribute? background = textNode.style.attributes[Attribute.background.key];
if (background != null && background.value != null) { if (background != null && background.value != null) {
final backgroundColor = stringToColor(background.value); final backgroundColor = stringToColor(background.value);
res = res.merge(new TextStyle(backgroundColor: backgroundColor)); res = res.merge(new TextStyle(backgroundColor: backgroundColor));
@ -178,20 +179,22 @@ class TextLine extends StatelessWidget {
} }
TextStyle _merge(TextStyle a, TextStyle b) { TextStyle _merge(TextStyle a, TextStyle b) {
final decorations = <TextDecoration>[]; final decorations = <TextDecoration?>[];
if (a.decoration != null) { if (a.decoration != null) {
decorations.add(a.decoration); decorations.add(a.decoration);
} }
if (b.decoration != null) { if (b.decoration != null) {
decorations.add(b.decoration); decorations.add(b.decoration);
} }
return a.merge(b).apply(decoration: TextDecoration.combine(decorations)); return a.merge(b).apply(
decoration:
TextDecoration.combine(decorations as List<TextDecoration>));
} }
} }
class EditableTextLine extends RenderObjectWidget { class EditableTextLine extends RenderObjectWidget {
final Line line; final Line line;
final Widget leading; final Widget? leading;
final Widget body; final Widget body;
final double indentWidth; final double indentWidth;
final Tuple2 verticalSpacing; final Tuple2 verticalSpacing;
@ -215,14 +218,7 @@ class EditableTextLine extends RenderObjectWidget {
this.enableInteractiveSelection, this.enableInteractiveSelection,
this.hasFocus, this.hasFocus,
this.devicePixelRatio, this.devicePixelRatio,
this.cursorCont) this.cursorCont);
: assert(line != null),
assert(indentWidth != null),
assert(textSelection != null),
assert(color != null),
assert(enableInteractiveSelection != null),
assert(hasFocus != null),
assert(cursorCont != null);
@override @override
RenderObjectElement createElement() { RenderObjectElement createElement() {
@ -268,8 +264,8 @@ class EditableTextLine extends RenderObjectWidget {
enum TextLineSlot { LEADING, BODY } enum TextLineSlot { LEADING, BODY }
class RenderEditableTextLine extends RenderEditableBox { class RenderEditableTextLine extends RenderEditableBox {
RenderBox _leading; RenderBox? _leading;
RenderContentProxyBox _body; RenderContentProxyBox? _body;
Line line; Line line;
TextDirection textDirection; TextDirection textDirection;
TextSelection textSelection; TextSelection textSelection;
@ -279,10 +275,10 @@ class RenderEditableTextLine extends RenderEditableBox {
double devicePixelRatio; double devicePixelRatio;
EdgeInsetsGeometry padding; EdgeInsetsGeometry padding;
CursorCont cursorCont; CursorCont cursorCont;
EdgeInsets _resolvedPadding; EdgeInsets? _resolvedPadding;
bool _containsCursor; bool? _containsCursor;
List<TextBox> _selectedRects; List<TextBox>? _selectedRects;
Rect _caretPrototype; Rect? _caretPrototype;
final Map<TextLineSlot, RenderBox> children = <TextLineSlot, RenderBox>{}; final Map<TextLineSlot, RenderBox> children = <TextLineSlot, RenderBox>{};
RenderEditableTextLine( RenderEditableTextLine(
@ -294,26 +290,18 @@ class RenderEditableTextLine extends RenderEditableBox {
this.devicePixelRatio, this.devicePixelRatio,
this.padding, this.padding,
this.color, this.color,
this.cursorCont) this.cursorCont);
: assert(line != null),
assert(padding != null),
assert(padding.isNonNegative),
assert(devicePixelRatio != null),
assert(hasFocus != null),
assert(color != null),
assert(cursorCont != null);
Iterable<RenderBox> get _children sync* { Iterable<RenderBox> get _children sync* {
if (_leading != null) { if (_leading != null) {
yield _leading; yield _leading!;
} }
if (_body != null) { if (_body != null) {
yield _body; yield _body!;
} }
} }
setCursorCont(CursorCont c) { setCursorCont(CursorCont c) {
assert(c != null);
if (cursorCont == c) { if (cursorCont == c) {
return; return;
} }
@ -383,7 +371,6 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
setLine(Line l) { setLine(Line l) {
assert(l != null);
if (line == l) { if (line == l) {
return; return;
} }
@ -393,7 +380,6 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
setPadding(EdgeInsetsGeometry p) { setPadding(EdgeInsetsGeometry p) {
assert(p != null);
assert(p.isNonNegative); assert(p.isNonNegative);
if (padding == p) { if (padding == p) {
return; return;
@ -403,12 +389,12 @@ class RenderEditableTextLine extends RenderEditableBox {
markNeedsLayout(); markNeedsLayout();
} }
setLeading(RenderBox l) { setLeading(RenderBox? l) {
_leading = _updateChild(_leading, l, TextLineSlot.LEADING); _leading = _updateChild(_leading, l, TextLineSlot.LEADING);
} }
setBody(RenderContentProxyBox b) { setBody(RenderContentProxyBox? b) {
_body = _updateChild(_body, b, TextLineSlot.BODY); _body = _updateChild(_body, b, TextLineSlot.BODY) as RenderContentProxyBox?;
} }
bool containsTextSelection() { bool containsTextSelection() {
@ -421,7 +407,8 @@ class RenderEditableTextLine extends RenderEditableBox {
line.containsOffset(textSelection.baseOffset); line.containsOffset(textSelection.baseOffset);
} }
RenderBox _updateChild(RenderBox old, RenderBox newChild, TextLineSlot slot) { RenderBox? _updateChild(
RenderBox? old, RenderBox? newChild, TextLineSlot slot) {
if (old != null) { if (old != null) {
dropChild(old); dropChild(old);
children.remove(slot); children.remove(slot);
@ -434,10 +421,10 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
List<TextBox> _getBoxes(TextSelection textSelection) { List<TextBox> _getBoxes(TextSelection textSelection) {
BoxParentData parentData = _body.parentData as BoxParentData; BoxParentData? parentData = _body!.parentData as BoxParentData?;
return _body.getBoxesForSelection(textSelection).map((box) { return _body!.getBoxesForSelection(textSelection).map((box) {
return TextBox.fromLTRBD( return TextBox.fromLTRBD(
box.left + parentData.offset.dx, box.left + parentData!.offset.dx,
box.top + parentData.offset.dy, box.top + parentData.offset.dy,
box.right + parentData.offset.dx, box.right + parentData.offset.dx,
box.bottom + parentData.offset.dy, box.bottom + parentData.offset.dy,
@ -451,7 +438,7 @@ class RenderEditableTextLine extends RenderEditableBox {
return; return;
} }
_resolvedPadding = padding.resolve(textDirection); _resolvedPadding = padding.resolve(textDirection);
assert(_resolvedPadding.isNonNegative); assert(_resolvedPadding!.isNonNegative);
} }
@override @override
@ -498,26 +485,26 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
Offset getOffsetForCaret(TextPosition position) { Offset getOffsetForCaret(TextPosition position) {
return _body.getOffsetForCaret(position, _caretPrototype) + return _body!.getOffsetForCaret(position, _caretPrototype) +
(_body.parentData as BoxParentData).offset; (_body!.parentData as BoxParentData).offset;
} }
@override @override
TextPosition getPositionAbove(TextPosition position) { TextPosition? getPositionAbove(TextPosition position) {
return _getPosition(position, -0.5); return _getPosition(position, -0.5);
} }
@override @override
TextPosition getPositionBelow(TextPosition position) { TextPosition? getPositionBelow(TextPosition position) {
return _getPosition(position, 1.5); return _getPosition(position, 1.5);
} }
TextPosition _getPosition(TextPosition textPosition, double dyScale) { TextPosition? _getPosition(TextPosition textPosition, double dyScale) {
assert(textPosition.offset < line.length); assert(textPosition.offset < line.length);
Offset offset = getOffsetForCaret(textPosition) Offset offset = getOffsetForCaret(textPosition)
.translate(0, dyScale * preferredLineHeight(textPosition)); .translate(0, dyScale * preferredLineHeight(textPosition));
if (_body.size if (_body!.size
.contains(offset - (_body.parentData as BoxParentData).offset)) { .contains(offset - (_body!.parentData as BoxParentData).offset)) {
return getPositionForOffset(offset); return getPositionForOffset(offset);
} }
return null; return null;
@ -525,18 +512,18 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
TextPosition getPositionForOffset(Offset offset) { TextPosition getPositionForOffset(Offset offset) {
return _body.getPositionForOffset( return _body!.getPositionForOffset(
offset - (_body.parentData as BoxParentData).offset); offset - (_body!.parentData as BoxParentData).offset);
} }
@override @override
TextRange getWordBoundary(TextPosition position) { TextRange getWordBoundary(TextPosition position) {
return _body.getWordBoundary(position); return _body!.getWordBoundary(position);
} }
@override @override
double preferredLineHeight(TextPosition position) { double preferredLineHeight(TextPosition position) {
return _body.getPreferredLineHeight(); return _body!.getPreferredLineHeight();
} }
@override @override
@ -550,7 +537,6 @@ class RenderEditableTextLine extends RenderEditableBox {
cursorCont.style.height ?? preferredLineHeight(TextPosition(offset: 0)); cursorCont.style.height ?? preferredLineHeight(TextPosition(offset: 0));
_computeCaretPrototype() { _computeCaretPrototype() {
assert(defaultTargetPlatform != null);
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS: case TargetPlatform.macOS:
@ -606,7 +592,7 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
List<DiagnosticsNode> debugDescribeChildren() { List<DiagnosticsNode> debugDescribeChildren() {
var value = <DiagnosticsNode>[]; var value = <DiagnosticsNode>[];
void add(RenderBox child, String name) { void add(RenderBox? child, String name) {
if (child != null) { if (child != null) {
value.add(child.toDiagnosticsNode(name: name)); value.add(child.toDiagnosticsNode(name: name));
} }
@ -623,38 +609,40 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
double computeMinIntrinsicWidth(double height) { double computeMinIntrinsicWidth(double height) {
_resolvePadding(); _resolvePadding();
double horizontalPadding = _resolvedPadding.left + _resolvedPadding.right; double horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
double verticalPadding = _resolvedPadding.top + _resolvedPadding.bottom; double verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
int leadingWidth = _leading == null int leadingWidth = _leading == null
? 0 ? 0
: _leading.getMinIntrinsicWidth(height - verticalPadding); : _leading!.getMinIntrinsicWidth(height - verticalPadding) as int;
int bodyWidth = _body == null int bodyWidth = _body == null
? 0 ? 0
: _body.getMinIntrinsicWidth(math.max(0.0, height - verticalPadding)); : _body!.getMinIntrinsicWidth(math.max(0.0, height - verticalPadding))
as int;
return horizontalPadding + leadingWidth + bodyWidth; return horizontalPadding + leadingWidth + bodyWidth;
} }
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) {
_resolvePadding(); _resolvePadding();
double horizontalPadding = _resolvedPadding.left + _resolvedPadding.right; double horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
double verticalPadding = _resolvedPadding.top + _resolvedPadding.bottom; double verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
int leadingWidth = _leading == null int leadingWidth = _leading == null
? 0 ? 0
: _leading.getMaxIntrinsicWidth(height - verticalPadding); : _leading!.getMaxIntrinsicWidth(height - verticalPadding) as int;
int bodyWidth = _body == null int bodyWidth = _body == null
? 0 ? 0
: _body.getMaxIntrinsicWidth(math.max(0.0, height - verticalPadding)); : _body!.getMaxIntrinsicWidth(math.max(0.0, height - verticalPadding))
as int;
return horizontalPadding + leadingWidth + bodyWidth; return horizontalPadding + leadingWidth + bodyWidth;
} }
@override @override
double computeMinIntrinsicHeight(double width) { double computeMinIntrinsicHeight(double width) {
_resolvePadding(); _resolvePadding();
double horizontalPadding = _resolvedPadding.left + _resolvedPadding.right; double horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
double verticalPadding = _resolvedPadding.top + _resolvedPadding.bottom; double verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
if (_body != null) { if (_body != null) {
return _body return _body!
.getMinIntrinsicHeight(math.max(0.0, width - horizontalPadding)) + .getMinIntrinsicHeight(math.max(0.0, width - horizontalPadding)) +
verticalPadding; verticalPadding;
} }
@ -664,10 +652,10 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
double computeMaxIntrinsicHeight(double width) { double computeMaxIntrinsicHeight(double width) {
_resolvePadding(); _resolvePadding();
double horizontalPadding = _resolvedPadding.left + _resolvedPadding.right; double horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
double verticalPadding = _resolvedPadding.top + _resolvedPadding.bottom; double verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
if (_body != null) { if (_body != null) {
return _body return _body!
.getMaxIntrinsicHeight(math.max(0.0, width - horizontalPadding)) + .getMaxIntrinsicHeight(math.max(0.0, width - horizontalPadding)) +
verticalPadding; verticalPadding;
} }
@ -677,7 +665,8 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
double computeDistanceToActualBaseline(TextBaseline baseline) { double computeDistanceToActualBaseline(TextBaseline baseline) {
_resolvePadding(); _resolvePadding();
return _body.getDistanceToActualBaseline(baseline) + _resolvedPadding.top; return _body!.getDistanceToActualBaseline(baseline)! +
_resolvedPadding!.top;
} }
@override @override
@ -690,34 +679,35 @@ class RenderEditableTextLine extends RenderEditableBox {
if (_body == null && _leading == null) { if (_body == null && _leading == null) {
size = constraints.constrain(Size( size = constraints.constrain(Size(
_resolvedPadding.left + _resolvedPadding.right, _resolvedPadding!.left + _resolvedPadding!.right,
_resolvedPadding.top + _resolvedPadding.bottom, _resolvedPadding!.top + _resolvedPadding!.bottom,
)); ));
return; return;
} }
final innerConstraints = constraints.deflate(_resolvedPadding); final innerConstraints = constraints.deflate(_resolvedPadding!);
final indentWidth = textDirection == TextDirection.ltr final indentWidth = textDirection == TextDirection.ltr
? _resolvedPadding.left ? _resolvedPadding!.left
: _resolvedPadding.right; : _resolvedPadding!.right;
_body.layout(innerConstraints, parentUsesSize: true); _body!.layout(innerConstraints, parentUsesSize: true);
final bodyParentData = _body.parentData as BoxParentData; final bodyParentData = _body!.parentData as BoxParentData;
bodyParentData.offset = Offset(_resolvedPadding.left, _resolvedPadding.top); bodyParentData.offset =
Offset(_resolvedPadding!.left, _resolvedPadding!.top);
if (_leading != null) { if (_leading != null) {
final leadingConstraints = innerConstraints.copyWith( final leadingConstraints = innerConstraints.copyWith(
minWidth: indentWidth, minWidth: indentWidth,
maxWidth: indentWidth, maxWidth: indentWidth,
maxHeight: _body.size.height); maxHeight: _body!.size.height);
_leading.layout(leadingConstraints, parentUsesSize: true); _leading!.layout(leadingConstraints, parentUsesSize: true);
final parentData = _leading.parentData as BoxParentData; final parentData = _leading!.parentData as BoxParentData;
parentData.offset = Offset(0.0, _resolvedPadding.top); parentData.offset = Offset(0.0, _resolvedPadding!.top);
} }
size = constraints.constrain(Size( size = constraints.constrain(Size(
_resolvedPadding.left + _body.size.width + _resolvedPadding.right, _resolvedPadding!.left + _body!.size.width + _resolvedPadding!.right,
_resolvedPadding.top + _body.size.height + _resolvedPadding.bottom, _resolvedPadding!.top + _body!.size.height + _resolvedPadding!.bottom,
)); ));
_computeCaretPrototype(); _computeCaretPrototype();
@ -734,19 +724,19 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
paint(PaintingContext context, Offset offset) { paint(PaintingContext context, Offset offset) {
if (_leading != null) { if (_leading != null) {
final parentData = _leading.parentData as BoxParentData; final parentData = _leading!.parentData as BoxParentData;
final effectiveOffset = offset + parentData.offset; final effectiveOffset = offset + parentData.offset;
context.paintChild(_leading, effectiveOffset); context.paintChild(_leading!, effectiveOffset);
} }
if (_body != null) { if (_body != null) {
final parentData = _body.parentData as BoxParentData; final parentData = _body!.parentData as BoxParentData;
final effectiveOffset = offset + parentData.offset; final effectiveOffset = offset + parentData.offset;
if ((enableInteractiveSelection ?? true) && if ((enableInteractiveSelection) &&
line.getDocumentOffset() <= textSelection.end && line.getDocumentOffset() <= textSelection.end &&
textSelection.start <= line.getDocumentOffset() + line.length - 1) { textSelection.start <= line.getDocumentOffset() + line.length - 1) {
final local = localSelection(line, textSelection, false); final local = localSelection(line, textSelection, false);
_selectedRects ??= _body.getBoxesForSelection( _selectedRects ??= _body!.getBoxesForSelection(
local, local,
); );
_paintSelection(context, effectiveOffset); _paintSelection(context, effectiveOffset);
@ -759,7 +749,7 @@ class RenderEditableTextLine extends RenderEditableBox {
_paintCursor(context, effectiveOffset); _paintCursor(context, effectiveOffset);
} }
context.paintChild(_body, effectiveOffset); context.paintChild(_body!, effectiveOffset);
if (hasFocus && if (hasFocus &&
cursorCont.show.value && cursorCont.show.value &&
@ -773,7 +763,7 @@ class RenderEditableTextLine extends RenderEditableBox {
_paintSelection(PaintingContext context, Offset effectiveOffset) { _paintSelection(PaintingContext context, Offset effectiveOffset) {
assert(_selectedRects != null); assert(_selectedRects != null);
final paint = Paint()..color = color; final paint = Paint()..color = color;
for (final box in _selectedRects) { for (final box in _selectedRects!) {
context.canvas.drawRect(box.toRect().shift(effectiveOffset), paint); context.canvas.drawRect(box.toRect().shift(effectiveOffset), paint);
} }
} }
@ -787,7 +777,7 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
@override @override
bool hitTestChildren(BoxHitTestResult result, {Offset position}) { bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
return this._children.first.hitTest(result, position: position); return this._children.first.hitTest(result, position: position);
} }
} }
@ -819,7 +809,7 @@ class _TextLineElement extends RenderObjectElement {
} }
@override @override
mount(Element parent, dynamic newSlot) { mount(Element? parent, dynamic newSlot) {
super.mount(parent, newSlot); super.mount(parent, newSlot);
_mountChild(widget.leading, TextLineSlot.LEADING); _mountChild(widget.leading, TextLineSlot.LEADING);
_mountChild(widget.body, TextLineSlot.BODY); _mountChild(widget.body, TextLineSlot.BODY);
@ -834,16 +824,16 @@ class _TextLineElement extends RenderObjectElement {
} }
@override @override
insertRenderObjectChild(RenderObject child, TextLineSlot slot) { insertRenderObjectChild(RenderObject child, TextLineSlot? slot) {
assert(child is RenderBox); assert(child is RenderBox);
_updateRenderObject(child, slot); _updateRenderObject(child, slot);
assert(renderObject.children.keys.contains(slot)); assert(renderObject.children.keys.contains(slot));
} }
@override @override
removeRenderObjectChild(RenderObject child, TextLineSlot slot) { removeRenderObjectChild(RenderObject child, TextLineSlot? slot) {
assert(child is RenderBox); assert(child is RenderBox);
assert(renderObject.children[slot] == child); assert(renderObject.children[slot!] == child);
_updateRenderObject(null, slot); _updateRenderObject(null, slot);
assert(!renderObject.children.keys.contains(slot)); assert(!renderObject.children.keys.contains(slot));
} }
@ -853,9 +843,9 @@ class _TextLineElement extends RenderObjectElement {
throw UnimplementedError(); throw UnimplementedError();
} }
_mountChild(Widget widget, TextLineSlot slot) { _mountChild(Widget? widget, TextLineSlot slot) {
Element oldChild = _slotToChildren[slot]; Element? oldChild = _slotToChildren[slot];
Element newChild = updateChild(oldChild, widget, slot); Element? newChild = updateChild(oldChild, widget, slot);
if (oldChild != null) { if (oldChild != null) {
_slotToChildren.remove(slot); _slotToChildren.remove(slot);
} }
@ -864,22 +854,22 @@ class _TextLineElement extends RenderObjectElement {
} }
} }
_updateRenderObject(RenderObject child, TextLineSlot slot) { _updateRenderObject(RenderObject? child, TextLineSlot? slot) {
switch (slot) { switch (slot) {
case TextLineSlot.LEADING: case TextLineSlot.LEADING:
renderObject.setLeading(child as RenderBox); renderObject.setLeading(child as RenderBox?);
break; break;
case TextLineSlot.BODY: case TextLineSlot.BODY:
renderObject.setBody(child as RenderBox); renderObject.setBody((child as RenderBox?) as RenderContentProxyBox?);
break; break;
default: default:
throw UnimplementedError(); throw UnimplementedError();
} }
} }
_updateChild(Widget widget, TextLineSlot slot) { _updateChild(Widget? widget, TextLineSlot slot) {
Element oldChild = _slotToChildren[slot]; Element? oldChild = _slotToChildren[slot];
Element newChild = updateChild(oldChild, widget, slot); Element? newChild = updateChild(oldChild, widget, slot);
if (oldChild != null) { if (oldChild != null) {
_slotToChildren.remove(slot); _slotToChildren.remove(slot);
} }

@ -31,15 +31,15 @@ class EditorTextSelectionOverlay {
final LayerLink toolbarLayerLink; final LayerLink toolbarLayerLink;
final LayerLink startHandleLayerLink; final LayerLink startHandleLayerLink;
final LayerLink endHandleLayerLink; final LayerLink endHandleLayerLink;
final RenderEditor renderObject; final RenderEditor? renderObject;
final TextSelectionControls selectionCtrls; final TextSelectionControls selectionCtrls;
final TextSelectionDelegate selectionDelegate; final TextSelectionDelegate selectionDelegate;
final DragStartBehavior dragStartBehavior; final DragStartBehavior dragStartBehavior;
final VoidCallback onSelectionHandleTapped; final VoidCallback? onSelectionHandleTapped;
final ClipboardStatusNotifier clipboardStatus; final ClipboardStatusNotifier clipboardStatus;
AnimationController _toolbarController; late AnimationController _toolbarController;
List<OverlayEntry> _handles; List<OverlayEntry>? _handles;
OverlayEntry toolbar; OverlayEntry? toolbar;
EditorTextSelectionOverlay( EditorTextSelectionOverlay(
this.value, this.value,
@ -54,14 +54,9 @@ class EditorTextSelectionOverlay {
this.selectionDelegate, this.selectionDelegate,
this.dragStartBehavior, this.dragStartBehavior,
this.onSelectionHandleTapped, this.onSelectionHandleTapped,
this.clipboardStatus) this.clipboardStatus) {
: assert(value != null), OverlayState overlay = Overlay.of(context, rootOverlay: true)!;
assert(context != null),
assert(handlesVisible != null) {
OverlayState overlay = Overlay.of(context, rootOverlay: true);
assert(
overlay != null,
);
_toolbarController = AnimationController( _toolbarController = AnimationController(
duration: Duration(milliseconds: 150), vsync: overlay); duration: Duration(milliseconds: 150), vsync: overlay);
} }
@ -71,14 +66,13 @@ class EditorTextSelectionOverlay {
Animation<double> get _toolbarOpacity => _toolbarController.view; Animation<double> get _toolbarOpacity => _toolbarController.view;
setHandlesVisible(bool visible) { setHandlesVisible(bool visible) {
assert(visible != null);
if (handlesVisible == visible) { if (handlesVisible == visible) {
return; return;
} }
handlesVisible = visible; handlesVisible = visible;
if (SchedulerBinding.instance.schedulerPhase == if (SchedulerBinding.instance!.schedulerPhase ==
SchedulerPhase.persistentCallbacks) { SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance.addPostFrameCallback(markNeedsBuild); SchedulerBinding.instance!.addPostFrameCallback(markNeedsBuild);
} else { } else {
markNeedsBuild(); markNeedsBuild();
} }
@ -88,37 +82,36 @@ class EditorTextSelectionOverlay {
if (_handles == null) { if (_handles == null) {
return; return;
} }
_handles[0].remove(); _handles![0].remove();
_handles[1].remove(); _handles![1].remove();
_handles = null; _handles = null;
} }
hideToolbar() { hideToolbar() {
assert(toolbar != null); assert(toolbar != null);
_toolbarController.stop(); _toolbarController.stop();
toolbar.remove(); toolbar!.remove();
toolbar = null; toolbar = null;
} }
showToolbar() { showToolbar() {
assert(toolbar == null); assert(toolbar == null);
toolbar = OverlayEntry(builder: _buildToolbar); toolbar = OverlayEntry(builder: _buildToolbar);
Overlay.of(context, rootOverlay: true, debugRequiredFor: debugRequiredFor) Overlay.of(context, rootOverlay: true, debugRequiredFor: debugRequiredFor)!
.insert(toolbar); .insert(toolbar!);
_toolbarController.forward(from: 0.0); _toolbarController.forward(from: 0.0);
} }
Widget _buildHandle( Widget _buildHandle(
BuildContext context, _TextSelectionHandlePosition position) { BuildContext context, _TextSelectionHandlePosition position) {
if ((_selection.isCollapsed && if ((_selection.isCollapsed &&
position == _TextSelectionHandlePosition.END) || position == _TextSelectionHandlePosition.END)) {
selectionCtrls == null) {
return Container(); return Container();
} }
return Visibility( return Visibility(
visible: handlesVisible, visible: handlesVisible,
child: _TextSelectionHandleOverlay( child: _TextSelectionHandleOverlay(
onSelectionHandleChanged: (TextSelection newSelection) { onSelectionHandleChanged: (TextSelection? newSelection) {
_handleSelectionHandleChanged(newSelection, position); _handleSelectionHandleChanged(newSelection, position);
}, },
onSelectionHandleTapped: onSelectionHandleTapped, onSelectionHandleTapped: onSelectionHandleTapped,
@ -137,23 +130,26 @@ class EditorTextSelectionOverlay {
return; return;
} }
value = newValue; value = newValue;
if (SchedulerBinding.instance.schedulerPhase == if (SchedulerBinding.instance!.schedulerPhase ==
SchedulerPhase.persistentCallbacks) { SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance.addPostFrameCallback(markNeedsBuild); SchedulerBinding.instance!.addPostFrameCallback(markNeedsBuild);
} else { } else {
markNeedsBuild(); markNeedsBuild();
} }
} }
_handleSelectionHandleChanged( _handleSelectionHandleChanged(
TextSelection newSelection, _TextSelectionHandlePosition position) { TextSelection? newSelection, _TextSelectionHandlePosition position) {
TextPosition textPosition; TextPosition textPosition;
switch (position) { switch (position) {
case _TextSelectionHandlePosition.START: case _TextSelectionHandlePosition.START:
textPosition = newSelection.base; textPosition =
newSelection != null ? newSelection.base : TextPosition(offset: 0);
break; break;
case _TextSelectionHandlePosition.END: case _TextSelectionHandlePosition.END:
textPosition = newSelection.extent; textPosition = newSelection != null
? newSelection.extent
: TextPosition(offset: 0);
break; break;
default: default:
throw ('Invalid position'); throw ('Invalid position');
@ -164,21 +160,17 @@ class EditorTextSelectionOverlay {
} }
Widget _buildToolbar(BuildContext context) { Widget _buildToolbar(BuildContext context) {
if (selectionCtrls == null) {
return Container();
}
List<TextSelectionPoint> endpoints = List<TextSelectionPoint> endpoints =
renderObject.getEndpointsForSelection(_selection); renderObject!.getEndpointsForSelection(_selection);
Rect editingRegion = Rect.fromPoints( Rect editingRegion = Rect.fromPoints(
renderObject.localToGlobal(Offset.zero), renderObject!.localToGlobal(Offset.zero),
renderObject.localToGlobal(renderObject.size.bottomRight(Offset.zero)), renderObject!.localToGlobal(renderObject!.size.bottomRight(Offset.zero)),
); );
double baseLineHeight = renderObject.preferredLineHeight(_selection.base); double baseLineHeight = renderObject!.preferredLineHeight(_selection.base);
double extentLineHeight = double extentLineHeight =
renderObject.preferredLineHeight(_selection.extent); renderObject!.preferredLineHeight(_selection.extent);
double smallestLineHeight = math.min(baseLineHeight, extentLineHeight); double smallestLineHeight = math.min(baseLineHeight, extentLineHeight);
bool isMultiline = endpoints.last.point.dy - endpoints.first.point.dy > bool isMultiline = endpoints.last.point.dy - endpoints.first.point.dy >
smallestLineHeight / 2; smallestLineHeight / 2;
@ -211,18 +203,18 @@ class EditorTextSelectionOverlay {
); );
} }
markNeedsBuild([Duration duration]) { markNeedsBuild([Duration? duration]) {
if (_handles != null) { if (_handles != null) {
_handles[0].markNeedsBuild(); _handles![0].markNeedsBuild();
_handles[1].markNeedsBuild(); _handles![1].markNeedsBuild();
} }
toolbar?.markNeedsBuild(); toolbar?.markNeedsBuild();
} }
hide() { hide() {
if (_handles != null) { if (_handles != null) {
_handles[0].remove(); _handles![0].remove();
_handles[1].remove(); _handles![1].remove();
_handles = null; _handles = null;
} }
if (toolbar != null) { if (toolbar != null) {
@ -246,22 +238,22 @@ class EditorTextSelectionOverlay {
_buildHandle(context, _TextSelectionHandlePosition.END)), _buildHandle(context, _TextSelectionHandlePosition.END)),
]; ];
Overlay.of(context, rootOverlay: true, debugRequiredFor: debugRequiredFor) Overlay.of(context, rootOverlay: true, debugRequiredFor: debugRequiredFor)!
.insertAll(_handles); .insertAll(_handles!);
} }
} }
class _TextSelectionHandleOverlay extends StatefulWidget { class _TextSelectionHandleOverlay extends StatefulWidget {
const _TextSelectionHandleOverlay({ const _TextSelectionHandleOverlay({
Key key, Key? key,
@required this.selection, required this.selection,
@required this.position, required this.position,
@required this.startHandleLayerLink, required this.startHandleLayerLink,
@required this.endHandleLayerLink, required this.endHandleLayerLink,
@required this.renderObject, required this.renderObject,
@required this.onSelectionHandleChanged, required this.onSelectionHandleChanged,
@required this.onSelectionHandleTapped, required this.onSelectionHandleTapped,
@required this.selectionControls, required this.selectionControls,
this.dragStartBehavior = DragStartBehavior.start, this.dragStartBehavior = DragStartBehavior.start,
}) : super(key: key); }) : super(key: key);
@ -269,9 +261,9 @@ class _TextSelectionHandleOverlay extends StatefulWidget {
final _TextSelectionHandlePosition position; final _TextSelectionHandlePosition position;
final LayerLink startHandleLayerLink; final LayerLink startHandleLayerLink;
final LayerLink endHandleLayerLink; final LayerLink endHandleLayerLink;
final RenderEditor renderObject; final RenderEditor? renderObject;
final ValueChanged<TextSelection> onSelectionHandleChanged; final ValueChanged<TextSelection?> onSelectionHandleChanged;
final VoidCallback onSelectionHandleTapped; final VoidCallback? onSelectionHandleTapped;
final TextSelectionControls selectionControls; final TextSelectionControls selectionControls;
final DragStartBehavior dragStartBehavior; final DragStartBehavior dragStartBehavior;
@ -279,21 +271,20 @@ class _TextSelectionHandleOverlay extends StatefulWidget {
_TextSelectionHandleOverlayState createState() => _TextSelectionHandleOverlayState createState() =>
_TextSelectionHandleOverlayState(); _TextSelectionHandleOverlayState();
ValueListenable<bool> get _visibility { ValueListenable<bool>? get _visibility {
switch (position) { switch (position) {
case _TextSelectionHandlePosition.START: case _TextSelectionHandlePosition.START:
return renderObject.selectionStartInViewport; return renderObject!.selectionStartInViewport;
case _TextSelectionHandlePosition.END: case _TextSelectionHandlePosition.END:
return renderObject.selectionEndInViewport; return renderObject!.selectionEndInViewport;
} }
return null;
} }
} }
class _TextSelectionHandleOverlayState class _TextSelectionHandleOverlayState
extends State<_TextSelectionHandleOverlay> extends State<_TextSelectionHandleOverlay>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
AnimationController _controller; late AnimationController _controller;
Animation<double> get _opacity => _controller.view; Animation<double> get _opacity => _controller.view;
@ -305,11 +296,11 @@ class _TextSelectionHandleOverlayState
AnimationController(duration: Duration(milliseconds: 150), vsync: this); AnimationController(duration: Duration(milliseconds: 150), vsync: this);
_handleVisibilityChanged(); _handleVisibilityChanged();
widget._visibility.addListener(_handleVisibilityChanged); widget._visibility!.addListener(_handleVisibilityChanged);
} }
_handleVisibilityChanged() { _handleVisibilityChanged() {
if (widget._visibility.value) { if (widget._visibility!.value) {
_controller.forward(); _controller.forward();
} else { } else {
_controller.reverse(); _controller.reverse();
@ -319,14 +310,14 @@ class _TextSelectionHandleOverlayState
@override @override
didUpdateWidget(_TextSelectionHandleOverlay oldWidget) { didUpdateWidget(_TextSelectionHandleOverlay oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
oldWidget._visibility.removeListener(_handleVisibilityChanged); oldWidget._visibility!.removeListener(_handleVisibilityChanged);
_handleVisibilityChanged(); _handleVisibilityChanged();
widget._visibility.addListener(_handleVisibilityChanged); widget._visibility!.addListener(_handleVisibilityChanged);
} }
@override @override
void dispose() { void dispose() {
widget._visibility.removeListener(_handleVisibilityChanged); widget._visibility!.removeListener(_handleVisibilityChanged);
_controller.dispose(); _controller.dispose();
super.dispose(); super.dispose();
} }
@ -335,7 +326,7 @@ class _TextSelectionHandleOverlayState
_handleDragUpdate(DragUpdateDetails details) { _handleDragUpdate(DragUpdateDetails details) {
TextPosition position = TextPosition position =
widget.renderObject.getPositionForOffset(details.globalPosition); widget.renderObject!.getPositionForOffset(details.globalPosition);
if (widget.selection.isCollapsed) { if (widget.selection.isCollapsed) {
widget.onSelectionHandleChanged(TextSelection.fromPosition(position)); widget.onSelectionHandleChanged(TextSelection.fromPosition(position));
return; return;
@ -343,7 +334,7 @@ class _TextSelectionHandleOverlayState
bool isNormalized = bool isNormalized =
widget.selection.extentOffset >= widget.selection.baseOffset; widget.selection.extentOffset >= widget.selection.baseOffset;
TextSelection newSelection; TextSelection? newSelection;
switch (widget.position) { switch (widget.position) {
case _TextSelectionHandlePosition.START: case _TextSelectionHandlePosition.START:
newSelection = TextSelection( newSelection = TextSelection(
@ -368,19 +359,19 @@ class _TextSelectionHandleOverlayState
_handleTap() { _handleTap() {
if (widget.onSelectionHandleTapped != null) if (widget.onSelectionHandleTapped != null)
widget.onSelectionHandleTapped(); widget.onSelectionHandleTapped!();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
LayerLink layerLink; late LayerLink layerLink;
TextSelectionHandleType type; TextSelectionHandleType? type;
switch (widget.position) { switch (widget.position) {
case _TextSelectionHandlePosition.START: case _TextSelectionHandlePosition.START:
layerLink = widget.startHandleLayerLink; layerLink = widget.startHandleLayerLink;
type = _chooseType( type = _chooseType(
widget.renderObject.textDirection, widget.renderObject!.textDirection,
TextSelectionHandleType.left, TextSelectionHandleType.left,
TextSelectionHandleType.right, TextSelectionHandleType.right,
); );
@ -389,7 +380,7 @@ class _TextSelectionHandleOverlayState
assert(!widget.selection.isCollapsed); assert(!widget.selection.isCollapsed);
layerLink = widget.endHandleLayerLink; layerLink = widget.endHandleLayerLink;
type = _chooseType( type = _chooseType(
widget.renderObject.textDirection, widget.renderObject!.textDirection,
TextSelectionHandleType.right, TextSelectionHandleType.right,
TextSelectionHandleType.left, TextSelectionHandleType.left,
); );
@ -400,9 +391,9 @@ class _TextSelectionHandleOverlayState
widget.position == _TextSelectionHandlePosition.START widget.position == _TextSelectionHandlePosition.START
? widget.selection.base ? widget.selection.base
: widget.selection.extent; : widget.selection.extent;
double lineHeight = widget.renderObject.preferredLineHeight(textPosition); double lineHeight = widget.renderObject!.preferredLineHeight(textPosition);
Offset handleAnchor = Offset handleAnchor =
widget.selectionControls.getHandleAnchor(type, lineHeight); widget.selectionControls.getHandleAnchor(type!, lineHeight);
Size handleSize = widget.selectionControls.getHandleSize(lineHeight); Size handleSize = widget.selectionControls.getHandleSize(lineHeight);
Rect handleRect = Rect.fromLTWH( Rect handleRect = Rect.fromLTWH(
@ -458,27 +449,25 @@ class _TextSelectionHandleOverlayState
); );
} }
TextSelectionHandleType _chooseType( TextSelectionHandleType? _chooseType(
TextDirection textDirection, TextDirection textDirection,
TextSelectionHandleType ltrType, TextSelectionHandleType ltrType,
TextSelectionHandleType rtlType, TextSelectionHandleType rtlType,
) { ) {
if (widget.selection.isCollapsed) return TextSelectionHandleType.collapsed; if (widget.selection.isCollapsed) return TextSelectionHandleType.collapsed;
assert(textDirection != null);
switch (textDirection) { switch (textDirection) {
case TextDirection.ltr: case TextDirection.ltr:
return ltrType; return ltrType;
case TextDirection.rtl: case TextDirection.rtl:
return rtlType; return rtlType;
} }
return null;
} }
} }
class EditorTextSelectionGestureDetector extends StatefulWidget { class EditorTextSelectionGestureDetector extends StatefulWidget {
const EditorTextSelectionGestureDetector({ const EditorTextSelectionGestureDetector({
Key key, Key? key,
this.onTapDown, this.onTapDown,
this.onForcePressStart, this.onForcePressStart,
this.onForcePressEnd, this.onForcePressEnd,
@ -492,35 +481,34 @@ class EditorTextSelectionGestureDetector extends StatefulWidget {
this.onDragSelectionUpdate, this.onDragSelectionUpdate,
this.onDragSelectionEnd, this.onDragSelectionEnd,
this.behavior, this.behavior,
@required this.child, required this.child,
}) : assert(child != null), }) : super(key: key);
super(key: key);
final GestureTapDownCallback onTapDown; final GestureTapDownCallback? onTapDown;
final GestureForcePressStartCallback onForcePressStart; final GestureForcePressStartCallback? onForcePressStart;
final GestureForcePressEndCallback onForcePressEnd; final GestureForcePressEndCallback? onForcePressEnd;
final GestureTapUpCallback onSingleTapUp; final GestureTapUpCallback? onSingleTapUp;
final GestureTapCancelCallback onSingleTapCancel; final GestureTapCancelCallback? onSingleTapCancel;
final GestureLongPressStartCallback onSingleLongTapStart; final GestureLongPressStartCallback? onSingleLongTapStart;
final GestureLongPressMoveUpdateCallback onSingleLongTapMoveUpdate; final GestureLongPressMoveUpdateCallback? onSingleLongTapMoveUpdate;
final GestureLongPressEndCallback onSingleLongTapEnd; final GestureLongPressEndCallback? onSingleLongTapEnd;
final GestureTapDownCallback onDoubleTapDown; final GestureTapDownCallback? onDoubleTapDown;
final GestureDragStartCallback onDragSelectionStart; final GestureDragStartCallback? onDragSelectionStart;
final DragSelectionUpdateCallback onDragSelectionUpdate; final DragSelectionUpdateCallback? onDragSelectionUpdate;
final GestureDragEndCallback onDragSelectionEnd; final GestureDragEndCallback? onDragSelectionEnd;
final HitTestBehavior behavior; final HitTestBehavior? behavior;
final Widget child; final Widget child;
@ -531,8 +519,8 @@ class EditorTextSelectionGestureDetector extends StatefulWidget {
class _EditorTextSelectionGestureDetectorState class _EditorTextSelectionGestureDetectorState
extends State<EditorTextSelectionGestureDetector> { extends State<EditorTextSelectionGestureDetector> {
Timer _doubleTapTimer; Timer? _doubleTapTimer;
Offset _lastTapOffset; Offset? _lastTapOffset;
bool _isDoubleTap = false; bool _isDoubleTap = false;
@override @override
@ -544,15 +532,15 @@ class _EditorTextSelectionGestureDetectorState
_handleTapDown(TapDownDetails details) { _handleTapDown(TapDownDetails details) {
if (widget.onTapDown != null) { if (widget.onTapDown != null) {
widget.onTapDown(details); widget.onTapDown!(details);
} }
if (_doubleTapTimer != null && if (_doubleTapTimer != null &&
_isWithinDoubleTapTolerance(details.globalPosition)) { _isWithinDoubleTapTolerance(details.globalPosition)) {
if (widget.onDoubleTapDown != null) { if (widget.onDoubleTapDown != null) {
widget.onDoubleTapDown(details); widget.onDoubleTapDown!(details);
} }
_doubleTapTimer.cancel(); _doubleTapTimer!.cancel();
_doubleTapTimeout(); _doubleTapTimeout();
_isDoubleTap = true; _isDoubleTap = true;
} }
@ -561,7 +549,7 @@ class _EditorTextSelectionGestureDetectorState
_handleTapUp(TapUpDetails details) { _handleTapUp(TapUpDetails details) {
if (!_isDoubleTap) { if (!_isDoubleTap) {
if (widget.onSingleTapUp != null) { if (widget.onSingleTapUp != null) {
widget.onSingleTapUp(details); widget.onSingleTapUp!(details);
} }
_lastTapOffset = details.globalPosition; _lastTapOffset = details.globalPosition;
_doubleTapTimer = Timer(kDoubleTapTimeout, _doubleTapTimeout); _doubleTapTimer = Timer(kDoubleTapTimeout, _doubleTapTimeout);
@ -571,19 +559,19 @@ class _EditorTextSelectionGestureDetectorState
_handleTapCancel() { _handleTapCancel() {
if (widget.onSingleTapCancel != null) { if (widget.onSingleTapCancel != null) {
widget.onSingleTapCancel(); widget.onSingleTapCancel!();
} }
} }
DragStartDetails _lastDragStartDetails; DragStartDetails? _lastDragStartDetails;
DragUpdateDetails _lastDragUpdateDetails; DragUpdateDetails? _lastDragUpdateDetails;
Timer _dragUpdateThrottleTimer; Timer? _dragUpdateThrottleTimer;
_handleDragStart(DragStartDetails details) { _handleDragStart(DragStartDetails details) {
assert(_lastDragStartDetails == null); assert(_lastDragStartDetails == null);
_lastDragStartDetails = details; _lastDragStartDetails = details;
if (widget.onDragSelectionStart != null) { if (widget.onDragSelectionStart != null) {
widget.onDragSelectionStart(details); widget.onDragSelectionStart!(details);
} }
} }
@ -597,8 +585,8 @@ class _EditorTextSelectionGestureDetectorState
assert(_lastDragStartDetails != null); assert(_lastDragStartDetails != null);
assert(_lastDragUpdateDetails != null); assert(_lastDragUpdateDetails != null);
if (widget.onDragSelectionUpdate != null) { if (widget.onDragSelectionUpdate != null) {
widget.onDragSelectionUpdate( widget.onDragSelectionUpdate!(
_lastDragStartDetails, _lastDragUpdateDetails); _lastDragStartDetails!, _lastDragUpdateDetails!);
} }
_dragUpdateThrottleTimer = null; _dragUpdateThrottleTimer = null;
_lastDragUpdateDetails = null; _lastDragUpdateDetails = null;
@ -607,11 +595,11 @@ class _EditorTextSelectionGestureDetectorState
_handleDragEnd(DragEndDetails details) { _handleDragEnd(DragEndDetails details) {
assert(_lastDragStartDetails != null); assert(_lastDragStartDetails != null);
if (_dragUpdateThrottleTimer != null) { if (_dragUpdateThrottleTimer != null) {
_dragUpdateThrottleTimer.cancel(); _dragUpdateThrottleTimer!.cancel();
_handleDragUpdateThrottled(); _handleDragUpdateThrottled();
} }
if (widget.onDragSelectionEnd != null) { if (widget.onDragSelectionEnd != null) {
widget.onDragSelectionEnd(details); widget.onDragSelectionEnd!(details);
} }
_dragUpdateThrottleTimer = null; _dragUpdateThrottleTimer = null;
_lastDragStartDetails = null; _lastDragStartDetails = null;
@ -622,31 +610,31 @@ class _EditorTextSelectionGestureDetectorState
_doubleTapTimer?.cancel(); _doubleTapTimer?.cancel();
_doubleTapTimer = null; _doubleTapTimer = null;
if (widget.onForcePressStart != null) { if (widget.onForcePressStart != null) {
widget.onForcePressStart(details); widget.onForcePressStart!(details);
} }
} }
_forcePressEnded(ForcePressDetails details) { _forcePressEnded(ForcePressDetails details) {
if (widget.onForcePressEnd != null) { if (widget.onForcePressEnd != null) {
widget.onForcePressEnd(details); widget.onForcePressEnd!(details);
} }
} }
_handleLongPressStart(LongPressStartDetails details) { _handleLongPressStart(LongPressStartDetails details) {
if (!_isDoubleTap && widget.onSingleLongTapStart != null) { if (!_isDoubleTap && widget.onSingleLongTapStart != null) {
widget.onSingleLongTapStart(details); widget.onSingleLongTapStart!(details);
} }
} }
_handleLongPressMoveUpdate(LongPressMoveUpdateDetails details) { _handleLongPressMoveUpdate(LongPressMoveUpdateDetails details) {
if (!_isDoubleTap && widget.onSingleLongTapMoveUpdate != null) { if (!_isDoubleTap && widget.onSingleLongTapMoveUpdate != null) {
widget.onSingleLongTapMoveUpdate(details); widget.onSingleLongTapMoveUpdate!(details);
} }
} }
_handleLongPressEnd(LongPressEndDetails details) { _handleLongPressEnd(LongPressEndDetails details) {
if (!_isDoubleTap && widget.onSingleLongTapEnd != null) { if (!_isDoubleTap && widget.onSingleLongTapEnd != null) {
widget.onSingleLongTapEnd(details); widget.onSingleLongTapEnd!(details);
} }
_isDoubleTap = false; _isDoubleTap = false;
} }
@ -657,12 +645,11 @@ class _EditorTextSelectionGestureDetectorState
} }
bool _isWithinDoubleTapTolerance(Offset secondTapOffset) { bool _isWithinDoubleTapTolerance(Offset secondTapOffset) {
assert(secondTapOffset != null);
if (_lastTapOffset == null) { if (_lastTapOffset == null) {
return false; return false;
} }
return (secondTapOffset - _lastTapOffset).distance <= kDoubleTapSlop; return (secondTapOffset - _lastTapOffset!).distance <= kDoubleTapSlop;
} }
@override @override
@ -735,3 +722,18 @@ class _EditorTextSelectionGestureDetectorState
); );
} }
} }
class _TransparentTapGestureRecognizer extends TapGestureRecognizer {
_TransparentTapGestureRecognizer({
Object? debugOwner,
}) : super(debugOwner: debugOwner);
@override
void rejectGesture(int pointer) {
if (state == GestureRecognizerState.ready) {
acceptGesture(pointer);
} else {
super.rejectGesture(pointer);
}
}
}

@ -26,9 +26,9 @@ class InsertEmbedButton extends StatelessWidget {
final IconData icon; final IconData icon;
const InsertEmbedButton({ const InsertEmbedButton({
Key key, Key? key,
@required this.controller, required this.controller,
@required this.icon, required this.icon,
}) : super(key: key); }) : super(key: key);
@override @override
@ -54,11 +54,11 @@ class InsertEmbedButton extends StatelessWidget {
class LinkStyleButton extends StatefulWidget { class LinkStyleButton extends StatefulWidget {
final QuillController controller; final QuillController controller;
final IconData icon; final IconData? icon;
const LinkStyleButton({ const LinkStyleButton({
Key key, Key? key,
@required this.controller, required this.controller,
this.icon, this.icon,
}) : super(key: key); }) : super(key: key);
@ -120,7 +120,7 @@ class _LinkStyleButtonState extends State<LinkStyleButton> {
).then(_linkSubmitted); ).then(_linkSubmitted);
} }
void _linkSubmitted(String value) { void _linkSubmitted(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return; return;
} }
@ -129,7 +129,7 @@ class _LinkStyleButtonState extends State<LinkStyleButton> {
} }
class _LinkDialog extends StatefulWidget { class _LinkDialog extends StatefulWidget {
const _LinkDialog({Key key}) : super(key: key); const _LinkDialog({Key? key}) : super(key: key);
@override @override
_LinkDialogState createState() => _LinkDialogState(); _LinkDialogState createState() => _LinkDialogState();
@ -170,8 +170,8 @@ typedef ToggleStyleButtonBuilder = Widget Function(
BuildContext context, BuildContext context,
Attribute attribute, Attribute attribute,
IconData icon, IconData icon,
bool isToggled, bool? isToggled,
VoidCallback onPressed, VoidCallback? onPressed,
); );
class ToggleStyleButton extends StatefulWidget { class ToggleStyleButton extends StatefulWidget {
@ -184,23 +184,19 @@ class ToggleStyleButton extends StatefulWidget {
final ToggleStyleButtonBuilder childBuilder; final ToggleStyleButtonBuilder childBuilder;
ToggleStyleButton({ ToggleStyleButton({
Key key, Key? key,
@required this.attribute, required this.attribute,
@required this.icon, required this.icon,
@required this.controller, required this.controller,
this.childBuilder = defaultToggleStyleButtonBuilder, this.childBuilder = defaultToggleStyleButtonBuilder,
}) : assert(attribute.value != null), }) : super(key: key);
assert(icon != null),
assert(controller != null),
assert(childBuilder != null),
super(key: key);
@override @override
_ToggleStyleButtonState createState() => _ToggleStyleButtonState(); _ToggleStyleButtonState createState() => _ToggleStyleButtonState();
} }
class _ToggleStyleButtonState extends State<ToggleStyleButton> { class _ToggleStyleButtonState extends State<ToggleStyleButton> {
bool _isToggled; bool? _isToggled;
Style get _selectionStyle => widget.controller.getSelectionStyle(); Style get _selectionStyle => widget.controller.getSelectionStyle();
@ -220,7 +216,7 @@ class _ToggleStyleButtonState extends State<ToggleStyleButton> {
bool _getIsToggled(Map<String, Attribute> attrs) { bool _getIsToggled(Map<String, Attribute> attrs) {
if (widget.attribute.key == Attribute.list.key) { if (widget.attribute.key == Attribute.list.key) {
Attribute attribute = attrs[widget.attribute.key]; Attribute? attribute = attrs[widget.attribute.key];
if (attribute == null) { if (attribute == null) {
return false; return false;
} }
@ -256,7 +252,7 @@ class _ToggleStyleButtonState extends State<ToggleStyleButton> {
} }
_toggleAttribute() { _toggleAttribute() {
widget.controller.formatSelection(_isToggled widget.controller.formatSelection(_isToggled!
? Attribute.clone(widget.attribute, null) ? Attribute.clone(widget.attribute, null)
: widget.attribute); : widget.attribute);
} }
@ -272,22 +268,19 @@ class ToggleCheckListButton extends StatefulWidget {
final Attribute attribute; final Attribute attribute;
ToggleCheckListButton({ ToggleCheckListButton({
Key key, Key? key,
@required this.icon, required this.icon,
@required this.controller, required this.controller,
this.childBuilder = defaultToggleStyleButtonBuilder, this.childBuilder = defaultToggleStyleButtonBuilder,
@required this.attribute, required this.attribute,
}) : assert(icon != null), }) : super(key: key);
assert(controller != null),
assert(childBuilder != null),
super(key: key);
@override @override
_ToggleCheckListButtonState createState() => _ToggleCheckListButtonState(); _ToggleCheckListButtonState createState() => _ToggleCheckListButtonState();
} }
class _ToggleCheckListButtonState extends State<ToggleCheckListButton> { class _ToggleCheckListButtonState extends State<ToggleCheckListButton> {
bool _isToggled; bool? _isToggled;
Style get _selectionStyle => widget.controller.getSelectionStyle(); Style get _selectionStyle => widget.controller.getSelectionStyle();
@ -307,7 +300,7 @@ class _ToggleCheckListButtonState extends State<ToggleCheckListButton> {
bool _getIsToggled(Map<String, Attribute> attrs) { bool _getIsToggled(Map<String, Attribute> attrs) {
if (widget.attribute.key == Attribute.list.key) { if (widget.attribute.key == Attribute.list.key) {
Attribute attribute = attrs[widget.attribute.key]; Attribute? attribute = attrs[widget.attribute.key];
if (attribute == null) { if (attribute == null) {
return false; return false;
} }
@ -344,7 +337,7 @@ class _ToggleCheckListButtonState extends State<ToggleCheckListButton> {
} }
_toggleAttribute() { _toggleAttribute() {
widget.controller.formatSelection(_isToggled widget.controller.formatSelection(_isToggled!
? Attribute.clone(Attribute.unchecked, null) ? Attribute.clone(Attribute.unchecked, null)
: Attribute.unchecked); : Attribute.unchecked);
} }
@ -354,17 +347,18 @@ Widget defaultToggleStyleButtonBuilder(
BuildContext context, BuildContext context,
Attribute attribute, Attribute attribute,
IconData icon, IconData icon,
bool isToggled, bool? isToggled,
VoidCallback onPressed, VoidCallback? onPressed,
) { ) {
final theme = Theme.of(context); final theme = Theme.of(context);
final isEnabled = onPressed != null; final isEnabled = onPressed != null;
final iconColor = isEnabled final iconColor = isEnabled
? isToggled ? isToggled != null
? theme.primaryIconTheme.color ? theme.primaryIconTheme.color
: theme.iconTheme.color : theme.iconTheme.color
: theme.disabledColor; : theme.disabledColor;
final fillColor = isToggled ? theme.toggleableActiveColor : theme.canvasColor; final fillColor =
isToggled != null ? theme.toggleableActiveColor : theme.canvasColor;
return QuillIconButton( return QuillIconButton(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
@ -378,7 +372,7 @@ Widget defaultToggleStyleButtonBuilder(
class SelectHeaderStyleButton extends StatefulWidget { class SelectHeaderStyleButton extends StatefulWidget {
final QuillController controller; final QuillController controller;
const SelectHeaderStyleButton({Key key, @required this.controller}) const SelectHeaderStyleButton({Key? key, required this.controller})
: super(key: key); : super(key: key);
@override @override
@ -387,7 +381,7 @@ class SelectHeaderStyleButton extends StatefulWidget {
} }
class _SelectHeaderStyleButtonState extends State<SelectHeaderStyleButton> { class _SelectHeaderStyleButtonState extends State<SelectHeaderStyleButton> {
Attribute _value; Attribute? _value;
Style get _selectionStyle => widget.controller.getSelectionStyle(); Style get _selectionStyle => widget.controller.getSelectionStyle();
@ -435,8 +429,8 @@ class _SelectHeaderStyleButtonState extends State<SelectHeaderStyleButton> {
} }
} }
Widget _selectHeadingStyleButtonBuilder( Widget _selectHeadingStyleButtonBuilder(BuildContext context, Attribute? value,
BuildContext context, Attribute value, ValueChanged<Attribute> onSelected) { ValueChanged<Attribute?> onSelected) {
final style = TextStyle(fontSize: 13); final style = TextStyle(fontSize: 13);
final Map<Attribute, String> _valueToText = { final Map<Attribute, String> _valueToText = {
@ -446,42 +440,42 @@ Widget _selectHeadingStyleButtonBuilder(
Attribute.h3: 'Heading 3', Attribute.h3: 'Heading 3',
}; };
return QuillDropdownButton<Attribute>( return QuillDropdownButton<Attribute?>(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
height: iconSize * 1.77, height: iconSize * 1.77,
fillColor: Theme.of(context).canvasColor, fillColor: Theme.of(context).canvasColor,
child: Text( child: Text(
!kIsWeb !kIsWeb
? _valueToText[value] ? _valueToText[value!]!
: _valueToText[value.key == "header" : _valueToText[value!.key == "header"
? Attribute.header ? Attribute.header
: (value.key == "h1") : (value.key == "h1")
? Attribute.h1 ? Attribute.h1
: (value.key == "h2") : (value.key == "h2")
? Attribute.h2 ? Attribute.h2
: Attribute.h3], : Attribute.h3]!,
style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600), style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
), ),
initialValue: value, initialValue: value,
items: [ items: [
PopupMenuItem( PopupMenuItem(
child: Text(_valueToText[Attribute.header], style: style), child: Text(_valueToText[Attribute.header]!, style: style),
value: Attribute.header, value: Attribute.header,
height: iconSize * 1.77, height: iconSize * 1.77,
), ),
PopupMenuItem( PopupMenuItem(
child: Text(_valueToText[Attribute.h1], style: style), child: Text(_valueToText[Attribute.h1]!, style: style),
value: Attribute.h1, value: Attribute.h1,
height: iconSize * 1.77, height: iconSize * 1.77,
), ),
PopupMenuItem( PopupMenuItem(
child: Text(_valueToText[Attribute.h2], style: style), child: Text(_valueToText[Attribute.h2]!, style: style),
value: Attribute.h2, value: Attribute.h2,
height: iconSize * 1.77, height: iconSize * 1.77,
), ),
PopupMenuItem( PopupMenuItem(
child: Text(_valueToText[Attribute.h3], style: style), child: Text(_valueToText[Attribute.h3]!, style: style),
value: Attribute.h3, value: Attribute.h3,
height: iconSize * 1.77, height: iconSize * 1.77,
), ),
@ -495,43 +489,40 @@ class ImageButton extends StatefulWidget {
final QuillController controller; final QuillController controller;
final OnImagePickCallback onImagePickCallback; final OnImagePickCallback? onImagePickCallback;
final ImagePickImpl imagePickImpl; final ImagePickImpl? imagePickImpl;
final ImageSource imageSource; final ImageSource imageSource;
ImageButton( ImageButton(
{Key key, {Key? key,
@required this.icon, required this.icon,
@required this.controller, required this.controller,
@required this.imageSource, required this.imageSource,
this.onImagePickCallback, this.onImagePickCallback,
this.imagePickImpl}) this.imagePickImpl})
: assert(icon != null), : super(key: key);
assert(controller != null),
super(key: key);
@override @override
_ImageButtonState createState() => _ImageButtonState(); _ImageButtonState createState() => _ImageButtonState();
} }
class _ImageButtonState extends State<ImageButton> { class _ImageButtonState extends State<ImageButton> {
List<PlatformFile> _paths; List<PlatformFile>? _paths;
String _extension; String? _extension;
final _picker = ImagePicker(); final _picker = ImagePicker();
FileType _pickingType = FileType.any; FileType _pickingType = FileType.any;
Future<String> _pickImage(ImageSource source) async { Future<String?> _pickImage(ImageSource source) async {
final PickedFile pickedFile = await _picker.getImage(source: source); final PickedFile? pickedFile = await _picker.getImage(source: source);
if (pickedFile == null) return null; if (pickedFile == null) return null;
final File file = File(pickedFile.path); final File file = File(pickedFile.path);
if (file == null || widget.onImagePickCallback == null) return null;
// We simply return the absolute path to selected file. // We simply return the absolute path to selected file.
try { try {
String url = await widget.onImagePickCallback(file); String url = await widget.onImagePickCallback!(file);
print('Image uploaded and its url is $url'); print('Image uploaded and its url is $url');
return url; return url;
} catch (error) { } catch (error) {
@ -540,13 +531,13 @@ class _ImageButtonState extends State<ImageButton> {
return null; return null;
} }
Future<String> _pickImageWeb() async { Future<String?> _pickImageWeb() async {
try { try {
_paths = (await FilePicker.platform.pickFiles( _paths = (await FilePicker.platform.pickFiles(
type: _pickingType, type: _pickingType,
allowMultiple: false, allowMultiple: false,
allowedExtensions: (_extension?.isNotEmpty ?? false) allowedExtensions: (_extension?.isNotEmpty ?? false)
? _extension?.replaceAll(' ', '')?.split(',') ? _extension?.replaceAll(' ', '').split(',')
: null, : null,
)) ))
?.files; ?.files;
@ -556,14 +547,13 @@ class _ImageButtonState extends State<ImageButton> {
print(ex); print(ex);
} }
var _fileName = var _fileName =
_paths != null ? _paths.map((e) => e.name).toString() : '...'; _paths != null ? _paths!.map((e) => e.name).toString() : '...';
if (_paths != null) { if (_paths != null) {
File file = File(_fileName); File file = File(_fileName);
if (file == null || widget.onImagePickCallback == null) return null;
// We simply return the absolute path to selected file. // We simply return the absolute path to selected file.
try { try {
String url = await widget.onImagePickCallback(file); String url = await widget.onImagePickCallback!(file);
print('Image uploaded and its url is $url'); print('Image uploaded and its url is $url');
return url; return url;
} catch (error) { } catch (error) {
@ -584,16 +574,16 @@ class _ImageButtonState extends State<ImageButton> {
fsType: FilesystemType.file, fsType: FilesystemType.file,
fileTileSelectMode: FileTileSelectMode.wholeTile, fileTileSelectMode: FileTileSelectMode.wholeTile,
); );
if (filePath == null || filePath.isEmpty) return null; if (filePath != null && filePath.isEmpty) return '';
final File file = File(filePath); final File file = File(filePath!);
String url = await widget.onImagePickCallback(file); String url = await widget.onImagePickCallback!(file);
print('Image uploaded and its url is $url'); print('Image uploaded and its url is $url');
return url; return url;
} catch (error) { } catch (error) {
print('Upload image error $error'); print('Upload image error $error');
} }
return null; return '';
} }
@override @override
@ -610,9 +600,9 @@ class _ImageButtonState extends State<ImageButton> {
onPressed: () { onPressed: () {
final index = widget.controller.selection.baseOffset; final index = widget.controller.selection.baseOffset;
final length = widget.controller.selection.extentOffset - index; final length = widget.controller.selection.extentOffset - index;
Future<String> image; Future<String?> image;
if (widget.imagePickImpl != null) { if (widget.imagePickImpl != null) {
image = widget.imagePickImpl(widget.imageSource); image = widget.imagePickImpl!(widget.imageSource);
} else { } else {
if (kIsWeb) { if (kIsWeb) {
image = _pickImageWeb(); image = _pickImageWeb();
@ -623,11 +613,8 @@ class _ImageButtonState extends State<ImageButton> {
} }
} }
image.then((imageUploadUrl) => { image.then((imageUploadUrl) => {
if (imageUploadUrl != null) widget.controller.replaceText(
{ index, length, BlockEmbed.image(imageUploadUrl!), null)
widget.controller.replaceText(
index, length, BlockEmbed.image(imageUploadUrl), null)
}
}); });
}, },
); );
@ -644,24 +631,21 @@ class ColorButton extends StatefulWidget {
final QuillController controller; final QuillController controller;
ColorButton( ColorButton(
{Key key, {Key? key,
@required this.icon, required this.icon,
@required this.controller, required this.controller,
@required this.background}) required this.background})
: assert(icon != null), : super(key: key);
assert(controller != null),
assert(background != null),
super(key: key);
@override @override
_ColorButtonState createState() => _ColorButtonState(); _ColorButtonState createState() => _ColorButtonState();
} }
class _ColorButtonState extends State<ColorButton> { class _ColorButtonState extends State<ColorButton> {
bool _isToggledColor; late bool _isToggledColor;
bool _isToggledBackground; late bool _isToggledBackground;
bool _isWhite; late bool _isWhite;
bool _isWhitebackground; late bool _isWhitebackground;
Style get _selectionStyle => widget.controller.getSelectionStyle(); Style get _selectionStyle => widget.controller.getSelectionStyle();
@ -672,9 +656,9 @@ class _ColorButtonState extends State<ColorButton> {
_isToggledBackground = _getIsToggledBackground( _isToggledBackground = _getIsToggledBackground(
widget.controller.getSelectionStyle().attributes); widget.controller.getSelectionStyle().attributes);
_isWhite = _isToggledColor && _isWhite = _isToggledColor &&
_selectionStyle.attributes["color"].value == '#ffffff'; _selectionStyle.attributes["color"]!.value == '#ffffff';
_isWhitebackground = _isToggledBackground && _isWhitebackground = _isToggledBackground &&
_selectionStyle.attributes["background"].value == '#ffffff'; _selectionStyle.attributes["background"]!.value == '#ffffff';
}); });
} }
@ -684,9 +668,9 @@ class _ColorButtonState extends State<ColorButton> {
_isToggledColor = _getIsToggledColor(_selectionStyle.attributes); _isToggledColor = _getIsToggledColor(_selectionStyle.attributes);
_isToggledBackground = _getIsToggledBackground(_selectionStyle.attributes); _isToggledBackground = _getIsToggledBackground(_selectionStyle.attributes);
_isWhite = _isToggledColor && _isWhite = _isToggledColor &&
_selectionStyle.attributes["color"].value == '#ffffff'; _selectionStyle.attributes["color"]!.value == '#ffffff';
_isWhitebackground = _isToggledBackground && _isWhitebackground = _isToggledBackground &&
_selectionStyle.attributes["background"].value == '#ffffff'; _selectionStyle.attributes["background"]!.value == '#ffffff';
widget.controller.addListener(_didChangeEditingValue); widget.controller.addListener(_didChangeEditingValue);
} }
@ -708,9 +692,9 @@ class _ColorButtonState extends State<ColorButton> {
_isToggledBackground = _isToggledBackground =
_getIsToggledBackground(_selectionStyle.attributes); _getIsToggledBackground(_selectionStyle.attributes);
_isWhite = _isToggledColor && _isWhite = _isToggledColor &&
_selectionStyle.attributes["color"].value == '#ffffff'; _selectionStyle.attributes["color"]!.value == '#ffffff';
_isWhitebackground = _isToggledBackground && _isWhitebackground = _isToggledBackground &&
_selectionStyle.attributes["background"].value == '#ffffff'; _selectionStyle.attributes["background"]!.value == '#ffffff';
} }
} }
@ -723,13 +707,13 @@ class _ColorButtonState extends State<ColorButton> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
Color iconColor = _isToggledColor && !widget.background && !_isWhite Color? iconColor = _isToggledColor && !widget.background && !_isWhite
? stringToColor(_selectionStyle.attributes["color"].value) ? stringToColor(_selectionStyle.attributes["color"]!.value)
: theme.iconTheme.color; : theme.iconTheme.color;
Color iconColorBackground = Color? iconColorBackground =
_isToggledBackground && widget.background && !_isWhitebackground _isToggledBackground && widget.background && !_isWhitebackground
? stringToColor(_selectionStyle.attributes["background"].value) ? stringToColor(_selectionStyle.attributes["background"]!.value)
: theme.iconTheme.color; : theme.iconTheme.color;
Color fillColor = _isToggledColor && !widget.background && _isWhite Color fillColor = _isToggledColor && !widget.background && _isWhite
@ -785,22 +769,19 @@ class HistoryButton extends StatefulWidget {
final QuillController controller; final QuillController controller;
HistoryButton( HistoryButton(
{Key key, {Key? key,
@required this.icon, required this.icon,
@required this.controller, required this.controller,
@required this.undo}) required this.undo})
: assert(icon != null), : super(key: key);
assert(controller != null),
assert(undo != null),
super(key: key);
@override @override
_HistoryButtonState createState() => _HistoryButtonState(); _HistoryButtonState createState() => _HistoryButtonState();
} }
class _HistoryButtonState extends State<HistoryButton> { class _HistoryButtonState extends State<HistoryButton> {
Color _iconColor; Color? _iconColor;
ThemeData theme; late ThemeData theme;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -860,14 +841,11 @@ class IndentButton extends StatefulWidget {
final bool isIncrease; final bool isIncrease;
IndentButton( IndentButton(
{Key key, {Key? key,
@required this.icon, required this.icon,
@required this.controller, required this.controller,
@required this.isIncrease}) required this.isIncrease})
: assert(icon != null), : super(key: key);
assert(controller != null),
assert(isIncrease != null),
super(key: key);
@override @override
_IndentButtonState createState() => _IndentButtonState(); _IndentButtonState createState() => _IndentButtonState();
@ -917,10 +895,8 @@ class ClearFormatButton extends StatefulWidget {
final QuillController controller; final QuillController controller;
ClearFormatButton({Key key, @required this.icon, @required this.controller}) ClearFormatButton({Key? key, required this.icon, required this.controller})
: assert(icon != null), : super(key: key);
assert(controller != null),
super(key: key);
@override @override
_ClearFormatButtonState createState() => _ClearFormatButtonState(); _ClearFormatButtonState createState() => _ClearFormatButtonState();
@ -950,11 +926,11 @@ class _ClearFormatButtonState extends State<ClearFormatButton> {
class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { class QuillToolbar extends StatefulWidget implements PreferredSizeWidget {
final List<Widget> children; final List<Widget> children;
const QuillToolbar({Key key, @required this.children}) : super(key: key); const QuillToolbar({Key? key, required this.children}) : super(key: key);
factory QuillToolbar.basic( factory QuillToolbar.basic(
{Key key, {Key? key,
@required QuillController controller, required QuillController controller,
double toolbarIconSize = 18.0, double toolbarIconSize = 18.0,
bool showBoldButton = true, bool showBoldButton = true,
bool showItalicButton = true, bool showItalicButton = true,
@ -973,7 +949,7 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget {
bool showLink = true, bool showLink = true,
bool showHistory = true, bool showHistory = true,
bool showHorizontalRule = false, bool showHorizontalRule = false,
OnImagePickCallback onImagePickCallback}) { OnImagePickCallback? onImagePickCallback}) {
iconSize = toolbarIconSize; iconSize = toolbarIconSize;
return QuillToolbar(key: key, children: [ return QuillToolbar(key: key, children: [
Visibility( Visibility(
@ -1192,16 +1168,16 @@ class _QuillToolbarState extends State<QuillToolbar> {
} }
class QuillIconButton extends StatelessWidget { class QuillIconButton extends StatelessWidget {
final VoidCallback onPressed; final VoidCallback? onPressed;
final Widget icon; final Widget? icon;
final double size; final double size;
final Color fillColor; final Color? fillColor;
final double hoverElevation; final double hoverElevation;
final double highlightElevation; final double highlightElevation;
const QuillIconButton({ const QuillIconButton({
Key key, Key? key,
@required this.onPressed, required this.onPressed,
this.icon, this.icon,
this.size = 40, this.size = 40,
this.fillColor, this.fillColor,
@ -1230,7 +1206,7 @@ class QuillIconButton extends StatelessWidget {
class QuillDropdownButton<T> extends StatefulWidget { class QuillDropdownButton<T> extends StatefulWidget {
final double height; final double height;
final Color fillColor; final Color? fillColor;
final double hoverElevation; final double hoverElevation;
final double highlightElevation; final double highlightElevation;
final Widget child; final Widget child;
@ -1239,15 +1215,15 @@ class QuillDropdownButton<T> extends StatefulWidget {
final ValueChanged<T> onSelected; final ValueChanged<T> onSelected;
const QuillDropdownButton({ const QuillDropdownButton({
Key key, Key? key,
this.height = 40, this.height = 40,
this.fillColor, this.fillColor,
this.hoverElevation = 1, this.hoverElevation = 1,
this.highlightElevation = 1, this.highlightElevation = 1,
@required this.child, required this.child,
@required this.initialValue, required this.initialValue,
@required this.items, required this.items,
@required this.onSelected, required this.onSelected,
}) : super(key: key); }) : super(key: key);
@override @override
@ -1276,7 +1252,8 @@ class _QuillDropdownButtonState<T> extends State<QuillDropdownButton<T>> {
void _showMenu() { void _showMenu() {
final popupMenuTheme = PopupMenuTheme.of(context); final popupMenuTheme = PopupMenuTheme.of(context);
final button = context.findRenderObject() as RenderBox; final button = context.findRenderObject() as RenderBox;
final overlay = Overlay.of(context).context.findRenderObject() as RenderBox; final overlay =
Overlay.of(context)!.context.findRenderObject() as RenderBox;
final position = RelativeRect.fromRect( final position = RelativeRect.fromRect(
Rect.fromPoints( Rect.fromPoints(
button.localToGlobal(Offset.zero, ancestor: overlay), button.localToGlobal(Offset.zero, ancestor: overlay),
@ -1296,15 +1273,12 @@ class _QuillDropdownButtonState<T> extends State<QuillDropdownButton<T>> {
// widget.shape ?? popupMenuTheme.shape, // widget.shape ?? popupMenuTheme.shape,
color: popupMenuTheme.color, // widget.color ?? popupMenuTheme.color, color: popupMenuTheme.color, // widget.color ?? popupMenuTheme.color,
// captureInheritedThemes: widget.captureInheritedThemes, // captureInheritedThemes: widget.captureInheritedThemes,
).then((T newValue) { ).then((T? newValue) {
if (!mounted) return null; if (!mounted) return null;
if (newValue == null) { if (newValue == null) {
// if (widget.onCanceled != null) widget.onCanceled(); // if (widget.onCanceled != null) widget.onCanceled();
return null; return null;
} }
if (widget.onSelected != null) {
widget.onSelected(newValue);
}
}); });
} }

@ -56,7 +56,7 @@ packages:
name: csslib name: csslib
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.16.2" version: "0.17.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -91,7 +91,7 @@ packages:
name: filesystem_picker name: filesystem_picker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" version: "2.0.0-nullsafety.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -103,7 +103,7 @@ packages:
name: flutter_colorpicker name: flutter_colorpicker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.5" version: "0.4.0-nullsafety.0"
flutter_keyboard_visibility: flutter_keyboard_visibility:
dependency: "direct main" dependency: "direct main"
description: description:
@ -148,14 +148,14 @@ packages:
name: html name: html
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.14.0+4" version: "0.15.0"
http: http:
dependency: transitive dependency: transitive
description: description:
name: http name: http
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.13.0" version: "0.13.1"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@ -169,7 +169,14 @@ packages:
name: image_picker name: image_picker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.2+1" version: "0.7.3"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -321,7 +328,7 @@ packages:
name: string_validator name: string_validator
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.4" version: "0.3.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@ -356,21 +363,14 @@ packages:
name: universal_html name: universal_html
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.4" version: "2.0.4"
universal_io: universal_io:
dependency: transitive dependency: transitive
description: description:
name: universal_io name: universal_io
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.0.1"
universal_ui:
dependency: "direct main"
description:
name: universal_ui
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.8"
url_launcher: url_launcher:
dependency: "direct main" dependency: "direct main"
description: description:
@ -426,7 +426,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.3" version: "2.0.4"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -434,13 +434,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.0" version: "0.2.0"
zone_local:
dependency: transitive
description:
name: zone_local
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
sdks: sdks:
dart: ">=2.12.0 <3.0.0" dart: ">=2.12.0 <3.0.0"
flutter: ">=1.22.0" flutter: ">=1.24.0-10.2.pre"

@ -6,28 +6,25 @@ homepage: https://bulletjournal.us/home/index.html
repository: https://github.com/singerdmx/flutter-quill repository: https://github.com/singerdmx/flutter-quill
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
quiver: ^3.0.0
collection: ^1.15.0 collection: ^1.15.0
tuple: ^2.0.0
url_launcher: ^6.0.0
flutter_colorpicker: ^0.3.5
image_picker: ^0.7.2
photo_view: ^0.11.0
universal_html: ^1.2.4
file_picker: ^3.0.0 file_picker: ^3.0.0
filesystem_picker: ^1.0.4 filesystem_picker: ^2.0.0-nullsafety.0
path_provider: ^2.0.1 flutter_colorpicker: ^0.4.0-nullsafety.0
string_validator: ^0.1.4
flutter_keyboard_visibility: ^5.0.0 flutter_keyboard_visibility: ^5.0.0
universal_ui: ^0.0.8 image_picker: ^0.7.3
path_provider: ^2.0.1
photo_view: ^0.11.1
quiver: ^3.0.0
string_validator: ^0.3.0
tuple: ^2.0.0
universal_html: ^2.0.4
url_launcher: ^6.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -35,37 +32,34 @@ dev_dependencies:
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter. # The following section is specific to Flutter.
flutter: flutter: null
# To add assets to your package, add an assets section, like this:
# To add assets to your package, add an assets section, like this: # assets:
# assets: # - images/a_dot_burr.jpeg
# - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg
# - images/a_dot_ham.jpeg #
# # For details regarding assets in packages, see
# For details regarding assets in packages, see # https://flutter.dev/assets-and-images/#from-packages
# https://flutter.dev/assets-and-images/#from-packages #
# # An image asset can refer to one or more resolution-specific "variants", see
# An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware.
# https://flutter.dev/assets-and-images/#resolution-aware. # To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# To add custom fonts to your package, add a fonts section here, # "family" key with the font family name, and a "fonts" key with a
# in this "flutter" section. Each entry in this list should have a # list giving the asset and other descriptors for the font. For
# "family" key with the font family name, and a "fonts" key with a # example:
# list giving the asset and other descriptors for the font. For # fonts:
# example: # - family: Schyler
# fonts: # fonts:
# - family: Schyler # - asset: fonts/Schyler-Regular.ttf
# fonts: # - asset: fonts/Schyler-Italic.ttf
# - asset: fonts/Schyler-Regular.ttf # style: italic
# - asset: fonts/Schyler-Italic.ttf # - family: Trajan Pro
# style: italic # fonts:
# - family: Trajan Pro # - asset: fonts/TrajanPro.ttf
# fonts: # - asset: fonts/TrajanPro_Bold.ttf
# - asset: fonts/TrajanPro.ttf # weight: 700
# - asset: fonts/TrajanPro_Bold.ttf #
# weight: 700 # For details regarding fonts in packages, see
# # https://flutter.dev/custom-fonts/#from-packages
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages

Loading…
Cancel
Save