feat: integrate more lint rules and missing dispose calls (#1174)
* chore: enable additional lint rules and update dependencies Enable new lint rules including no_equal_then_else, no_equal_conditions, avoid_contradictory_expressions, and resource cleanup rules. Update awesome_lints dependency to use local path reference. * fix: add missing dispose calls for proper resource cleanup Add dispose() calls for controllers, notifiers, and other resources across multiple components to prevent memory leaks. Also fix parameter type in table_node.dart and collection equality check in scroll service. * refactor: remove deprecated document and node classes Delete deprecated DocumentV0 and NodeV0 classes and remove the deprecated Document.empty() factory method in favor of Document.blank(). * fix: remove deprecated exports and duplicate lint ignores Remove exports for deleted deprecated document and node files from core.dart and remove duplicate dispose_fields ignore comments. * chore: point awesome_lints to git main branch Change awesome_lints dependency from local path to git repository reference pointing to main branch. * fix: remove duplicate dispose calls in find replace widget Remove duplicate findTextEditingController.dispose() call and remove searchService.dispose() from FindMenu since it doesn't own the service. The searchService is owned and disposed by the parent widget. * fix: correct overlay entry disposal in toolbar widgets Remove overlay entries from overlay before disposing. OverlayEntry must be removed with remove() before dispose() is called. Move cleanup logic to proper order in dispose methods. * refactor: inline overlay cleanup logic in toolbar dispose methods Replace helper method calls with inline cleanup logic to make the overlay entry disposal order explicit (remove, dispose, set to null).
This commit is contained in:
parent
f882d958d9
commit
b1e7853608
21 changed files with 98 additions and 507 deletions
|
|
@ -18,7 +18,21 @@ analyzer:
|
||||||
custom_lint:
|
custom_lint:
|
||||||
enable_all_lint_rules: false
|
enable_all_lint_rules: false
|
||||||
rules:
|
rules:
|
||||||
|
# Formatting rules
|
||||||
- newline_before_case
|
- newline_before_case
|
||||||
- newline_before_constructor
|
- newline_before_constructor
|
||||||
- newline_before_method
|
- newline_before_method
|
||||||
- newline_before_return
|
- newline_before_return
|
||||||
|
|
||||||
|
- no_equal_then_else
|
||||||
|
- no_equal_conditions
|
||||||
|
- avoid_contradictory_expressions
|
||||||
|
- avoid_constant_conditions
|
||||||
|
- avoid_collection_equality_checks
|
||||||
|
- avoid_collection_methods_with_unrelated_types
|
||||||
|
# - avoid_late_context
|
||||||
|
- avoid_mounted_in_setstate
|
||||||
|
- proper_super_calls
|
||||||
|
- dispose_fields
|
||||||
|
# - avoid_undisposed_instances
|
||||||
|
# - dispose_class_fields
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
export 'document/attributes.dart';
|
export 'document/attributes.dart';
|
||||||
export 'document/deprecated/document.dart';
|
|
||||||
export 'document/deprecated/node.dart';
|
|
||||||
export 'document/diff.dart';
|
export 'document/diff.dart';
|
||||||
export 'document/document.dart';
|
export 'document/document.dart';
|
||||||
export 'document/node.dart';
|
export 'document/node.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
import 'dart:collection';
|
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/core/document/attributes.dart';
|
|
||||||
import 'package:appflowy_editor/src/core/document/deprecated/node.dart';
|
|
||||||
import 'package:appflowy_editor/src/core/document/path.dart';
|
|
||||||
import 'package:appflowy_editor/src/core/document/text_delta.dart';
|
|
||||||
|
|
||||||
///
|
|
||||||
/// ⚠️ THIS FILE HAS BEEN DEPRECATED.
|
|
||||||
/// ⚠️ THIS FILE HAS BEEN DEPRECATED.
|
|
||||||
/// ⚠️ THIS FILE HAS BEEN DEPRECATED.
|
|
||||||
///
|
|
||||||
/// ONLY USE FOR MIGRATION.
|
|
||||||
///
|
|
||||||
class DocumentV0 {
|
|
||||||
DocumentV0({
|
|
||||||
required this.root,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory DocumentV0.fromJson(Map<String, dynamic> json) {
|
|
||||||
assert(json['document'] is Map);
|
|
||||||
|
|
||||||
final document = Map<String, Object>.from(json['document'] as Map);
|
|
||||||
final root = NodeV0.fromJson(document);
|
|
||||||
|
|
||||||
return DocumentV0(root: root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a empty document with a single text node.
|
|
||||||
factory DocumentV0.empty() {
|
|
||||||
final root = NodeV0(
|
|
||||||
type: 'editor',
|
|
||||||
children: LinkedList<NodeV0>()..add(TextNodeV0.empty()),
|
|
||||||
);
|
|
||||||
|
|
||||||
return DocumentV0(
|
|
||||||
root: root,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final NodeV0 root;
|
|
||||||
|
|
||||||
/// Returns the node at the given [path].
|
|
||||||
NodeV0? nodeAtPath(Path path) {
|
|
||||||
return root.childAtPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a [NodeV0]s at the given [Path].
|
|
||||||
bool insert(Path path, Iterable<NodeV0> nodes) {
|
|
||||||
if (path.isEmpty || nodes.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final target = nodeAtPath(path);
|
|
||||||
if (target != null) {
|
|
||||||
for (final node in nodes) {
|
|
||||||
target.insertBefore(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final parent = nodeAtPath(path.parent);
|
|
||||||
if (parent != null) {
|
|
||||||
for (var i = 0; i < nodes.length; i++) {
|
|
||||||
parent.insert(nodes.elementAt(i), index: path.last + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deletes the [NodeV0]s at the given [Path].
|
|
||||||
bool delete(Path path, [int length = 1]) {
|
|
||||||
if (path.isEmpty || length <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var target = nodeAtPath(path);
|
|
||||||
if (target == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
while (target != null && length > 0) {
|
|
||||||
final next = target.next;
|
|
||||||
target.unlink();
|
|
||||||
target = next;
|
|
||||||
length--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the [NodeV0] at the given [Path]
|
|
||||||
bool update(Path path, Attributes attributes) {
|
|
||||||
if (path.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final target = nodeAtPath(path);
|
|
||||||
if (target == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
target.updateAttributes(attributes);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the [TextNodeV0] at the given [Path]
|
|
||||||
bool updateText(Path path, Delta delta) {
|
|
||||||
if (path.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final target = nodeAtPath(path);
|
|
||||||
if (target == null || target is! TextNodeV0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
target.delta = target.delta.compose(delta);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get isEmpty {
|
|
||||||
if (root.children.isEmpty) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root.children.length > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final node = root.children.first;
|
|
||||||
if (node is TextNodeV0 &&
|
|
||||||
(node.delta.isEmpty || node.delta.toPlainText().isEmpty)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> toJson() {
|
|
||||||
return {
|
|
||||||
'document': root.toJson(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,318 +0,0 @@
|
||||||
import 'dart:collection';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/core/document/attributes.dart';
|
|
||||||
import 'package:appflowy_editor/src/core/document/path.dart';
|
|
||||||
import 'package:appflowy_editor/src/core/document/text_delta.dart';
|
|
||||||
import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart';
|
|
||||||
|
|
||||||
///
|
|
||||||
/// ⚠️ THIS FILE HAS BEEN DEPRECATED.
|
|
||||||
/// ⚠️ THIS FILE HAS BEEN DEPRECATED.
|
|
||||||
/// ⚠️ THIS FILE HAS BEEN DEPRECATED.
|
|
||||||
///
|
|
||||||
/// ONLY USE FOR MIGRATION.
|
|
||||||
///
|
|
||||||
final class NodeV0 extends ChangeNotifier with LinkedListEntry<NodeV0> {
|
|
||||||
NodeV0({
|
|
||||||
required this.type,
|
|
||||||
Attributes? attributes,
|
|
||||||
this.parent,
|
|
||||||
LinkedList<NodeV0>? children,
|
|
||||||
}) : children = children ?? LinkedList<NodeV0>(),
|
|
||||||
_attributes = attributes ?? {} {
|
|
||||||
for (final child in this.children) {
|
|
||||||
child.parent = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
factory NodeV0.fromJson(Map<String, Object> json) {
|
|
||||||
assert(json['type'] is String);
|
|
||||||
|
|
||||||
final jType = json['type'] as String;
|
|
||||||
final jChildren = json['children'] as List?;
|
|
||||||
final jAttributes = json['attributes'] != null
|
|
||||||
? Attributes.from(json['attributes'] as Map)
|
|
||||||
: Attributes.from({});
|
|
||||||
|
|
||||||
final children = LinkedList<NodeV0>();
|
|
||||||
if (jChildren != null) {
|
|
||||||
children.addAll(
|
|
||||||
jChildren.map(
|
|
||||||
(jChild) => NodeV0.fromJson(
|
|
||||||
Map<String, Object>.from(jChild),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeV0 node;
|
|
||||||
|
|
||||||
if (jType == 'text') {
|
|
||||||
final jDelta = json['delta'] as List<dynamic>?;
|
|
||||||
final delta = jDelta == null ? Delta() : Delta.fromJson(jDelta);
|
|
||||||
node = TextNodeV0(
|
|
||||||
children: children,
|
|
||||||
attributes: jAttributes,
|
|
||||||
delta: delta,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
node = NodeV0(
|
|
||||||
type: jType,
|
|
||||||
children: children,
|
|
||||||
attributes: jAttributes,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final child in children) {
|
|
||||||
child.parent = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String type;
|
|
||||||
final LinkedList<NodeV0> children;
|
|
||||||
NodeV0? parent;
|
|
||||||
Attributes _attributes;
|
|
||||||
|
|
||||||
// Renderable
|
|
||||||
final key = GlobalKey();
|
|
||||||
final layerLink = LayerLink();
|
|
||||||
|
|
||||||
Attributes get attributes => {..._attributes};
|
|
||||||
|
|
||||||
String get id {
|
|
||||||
if (subtype != null) {
|
|
||||||
return '$type/$subtype';
|
|
||||||
}
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
String? get subtype {
|
|
||||||
if (attributes[BuiltInAttributeKey.subtype] is String) {
|
|
||||||
return attributes[BuiltInAttributeKey.subtype] as String;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path get path => _computePath();
|
|
||||||
|
|
||||||
void updateAttributes(Attributes attributes) {
|
|
||||||
final oldAttributes = this.attributes;
|
|
||||||
|
|
||||||
_attributes = composeAttributes(this.attributes, attributes) ?? {};
|
|
||||||
|
|
||||||
// Notifies the new attributes
|
|
||||||
// if attributes contains 'subtype', should notify parent to rebuild node
|
|
||||||
// else, just notify current node.
|
|
||||||
bool shouldNotifyParent =
|
|
||||||
this.attributes['subtype'] != oldAttributes['subtype'];
|
|
||||||
shouldNotifyParent ? parent?.notifyListeners() : notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeV0? childAtIndex(int index) {
|
|
||||||
if (children.length <= index || index < 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return children.elementAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeV0? childAtPath(Path path) {
|
|
||||||
if (path.isEmpty) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return childAtIndex(path.first)?.childAtPath(path.sublist(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void insert(NodeV0 entry, {int? index}) {
|
|
||||||
final length = children.length;
|
|
||||||
index ??= length;
|
|
||||||
|
|
||||||
if (children.isEmpty) {
|
|
||||||
entry.parent = this;
|
|
||||||
children.add(entry);
|
|
||||||
notifyListeners();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If index is out of range, insert at the end.
|
|
||||||
// If index is negative, insert at the beginning.
|
|
||||||
// If index is positive, insert at the index.
|
|
||||||
if (index >= length) {
|
|
||||||
children.last.insertAfter(entry);
|
|
||||||
} else if (index <= 0) {
|
|
||||||
children.first.insertBefore(entry);
|
|
||||||
} else {
|
|
||||||
childAtIndex(index)?.insertBefore(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void insertAfter(NodeV0 entry) {
|
|
||||||
entry.parent = parent;
|
|
||||||
super.insertAfter(entry);
|
|
||||||
|
|
||||||
// Notifies the new node.
|
|
||||||
parent?.notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void insertBefore(NodeV0 entry) {
|
|
||||||
entry.parent = parent;
|
|
||||||
super.insertBefore(entry);
|
|
||||||
|
|
||||||
// Notifies the new node.
|
|
||||||
parent?.notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void unlink() {
|
|
||||||
super.unlink();
|
|
||||||
|
|
||||||
parent?.notifyListeners();
|
|
||||||
parent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> toJson() {
|
|
||||||
var map = <String, Object>{
|
|
||||||
'type': type,
|
|
||||||
};
|
|
||||||
if (children.isNotEmpty) {
|
|
||||||
map['children'] =
|
|
||||||
children.map((node) => node.toJson()).toList(growable: false);
|
|
||||||
}
|
|
||||||
if (attributes.isNotEmpty) {
|
|
||||||
map['attributes'] = attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeV0 copyWith({
|
|
||||||
String? type,
|
|
||||||
LinkedList<NodeV0>? children,
|
|
||||||
Attributes? attributes,
|
|
||||||
}) {
|
|
||||||
final node = NodeV0(
|
|
||||||
type: type ?? this.type,
|
|
||||||
attributes: attributes ?? {...this.attributes},
|
|
||||||
children: children,
|
|
||||||
);
|
|
||||||
if (children == null && this.children.isNotEmpty) {
|
|
||||||
for (final child in this.children) {
|
|
||||||
node.children.add(
|
|
||||||
child.copyWith()..parent = node,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path _computePath([Path previous = const []]) {
|
|
||||||
if (parent == null) {
|
|
||||||
return previous;
|
|
||||||
}
|
|
||||||
var index = 0;
|
|
||||||
for (final child in parent!.children) {
|
|
||||||
if (child == this) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent!._computePath([index, ...previous]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class TextNodeV0 extends NodeV0 {
|
|
||||||
TextNodeV0({
|
|
||||||
required Delta delta,
|
|
||||||
super.children,
|
|
||||||
Attributes? attributes,
|
|
||||||
}) : _delta = delta,
|
|
||||||
super(
|
|
||||||
type: 'text',
|
|
||||||
attributes: attributes ?? {},
|
|
||||||
);
|
|
||||||
|
|
||||||
TextNodeV0.empty({Attributes? attributes})
|
|
||||||
: _delta = Delta(operations: [TextInsert('')]),
|
|
||||||
super(
|
|
||||||
type: 'text',
|
|
||||||
attributes: attributes ?? {},
|
|
||||||
);
|
|
||||||
|
|
||||||
Delta _delta;
|
|
||||||
|
|
||||||
Delta get delta => _delta;
|
|
||||||
|
|
||||||
set delta(Delta v) {
|
|
||||||
_delta = v;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, Object> toJson() {
|
|
||||||
final map = super.toJson();
|
|
||||||
map['delta'] = delta.toJson();
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TextNodeV0 copyWith({
|
|
||||||
String? type = 'text',
|
|
||||||
LinkedList<NodeV0>? children,
|
|
||||||
Attributes? attributes,
|
|
||||||
Delta? delta,
|
|
||||||
}) {
|
|
||||||
final textNode = TextNodeV0(
|
|
||||||
children: children,
|
|
||||||
attributes: attributes ?? this.attributes,
|
|
||||||
delta: delta ?? this.delta,
|
|
||||||
);
|
|
||||||
if (children == null && this.children.isNotEmpty) {
|
|
||||||
for (final child in this.children) {
|
|
||||||
textNode.children.add(
|
|
||||||
child.copyWith()..parent = textNode,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return textNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
String toPlainText() => _delta.toPlainText();
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NodeV0Equality on Iterable<NodeV0> {
|
|
||||||
bool equals(Iterable<NodeV0> other) {
|
|
||||||
if (length != other.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < length; i++) {
|
|
||||||
if (!_nodeEquals(elementAt(i), other.elementAt(i))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _nodeEquals<T, U>(T base, U other) {
|
|
||||||
if (identical(this, other)) return true;
|
|
||||||
|
|
||||||
return base is NodeV0 &&
|
|
||||||
other is NodeV0 &&
|
|
||||||
other.type == base.type &&
|
|
||||||
other.children.equals(base.children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -45,19 +45,6 @@ class Document {
|
||||||
return Document(root: root);
|
return Document(root: root);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a empty document with a single text node.
|
|
||||||
@Deprecated('use Document.blank() instead')
|
|
||||||
factory Document.empty() {
|
|
||||||
final root = Node(
|
|
||||||
type: 'document',
|
|
||||||
children: LinkedList<Node>()..add(TextNode.empty()),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Document(
|
|
||||||
root: root,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a blank [Document] containing an empty root [Node].
|
/// Creates a blank [Document] containing an empty root [Node].
|
||||||
///
|
///
|
||||||
/// If [withInitialText] is true, the document will contain an empty
|
/// If [withInitialText] is true, the document will contain an empty
|
||||||
|
|
|
||||||
|
|
@ -23,26 +23,36 @@ class _AnimatedSelectionAreaPaintState extends State<AnimatedSelectionAreaPaint>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late Animation<double> animation;
|
late Animation<double> animation;
|
||||||
late AnimationController controller;
|
late AnimationController controller;
|
||||||
|
late CurvedAnimation curvedAnimation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
if (widget.withAnimation) {
|
if (widget.withAnimation) {
|
||||||
|
curvedAnimation = CurvedAnimation(
|
||||||
|
parent: controller,
|
||||||
|
curve: Curves.bounceInOut,
|
||||||
|
);
|
||||||
controller = AnimationController(
|
controller = AnimationController(
|
||||||
duration: const Duration(seconds: 4),
|
duration: const Duration(seconds: 4),
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
animation = Tween<double>(begin: 0, end: 1.0).animate(
|
animation = Tween<double>(begin: 0, end: 1.0).animate(curvedAnimation);
|
||||||
CurvedAnimation(
|
|
||||||
parent: controller,
|
|
||||||
curve: Curves.bounceInOut,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
controller.repeat();
|
controller.repeat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
if (widget.withAnimation) {
|
||||||
|
curvedAnimation.dispose();
|
||||||
|
controller.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget builder(double value) {
|
Widget builder(double value) {
|
||||||
|
|
@ -70,15 +80,6 @@ class _AnimatedSelectionAreaPaintState extends State<AnimatedSelectionAreaPaint>
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
if (widget.withAnimation) {
|
|
||||||
controller.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SelectionAreaPaint extends StatelessWidget {
|
class SelectionAreaPaint extends StatelessWidget {
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,13 @@ class ImageBlockComponentWidgetState extends State<ImageBlockComponentWidget>
|
||||||
|
|
||||||
bool alwaysShowMenu = false;
|
bool alwaysShowMenu = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
showActionsNotifier.dispose();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final node = widget.node;
|
final node = widget.node;
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_focusNode.dispose();
|
_focusNode.dispose();
|
||||||
|
_textEditingController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,12 @@ class _TableBlockComponentWidgetState extends State<TableBlockComponentWidget>
|
||||||
late final editorState = Provider.of<EditorState>(context, listen: false);
|
late final editorState = Provider.of<EditorState>(context, listen: false);
|
||||||
final _scrollController = ScrollController();
|
final _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = Scrollbar(
|
Widget child = Scrollbar(
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ class TableNode {
|
||||||
return TableNode(node: node);
|
return TableNode(node: node);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node getCell(int col, row) => _cells[col][row];
|
Node getCell(int col, int row) => _cells[col][row];
|
||||||
|
|
||||||
TableConfig get config => _config;
|
TableConfig get config => _config;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class KeyboardServiceWidget extends StatefulWidget {
|
||||||
class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
|
class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
|
||||||
implements AppFlowyKeyboardService {
|
implements AppFlowyKeyboardService {
|
||||||
late final SelectionGestureInterceptor interceptor;
|
late final SelectionGestureInterceptor interceptor;
|
||||||
late final EditorState editorState;
|
late final EditorState editorState = context.read<EditorState>();
|
||||||
late final TextInputService textInputService;
|
late final TextInputService textInputService;
|
||||||
late final FocusNode focusNode;
|
late final FocusNode focusNode;
|
||||||
|
|
||||||
|
|
@ -52,7 +52,6 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
editorState = Provider.of<EditorState>(context, listen: false);
|
|
||||||
editorState.selectionNotifier.addListener(_onSelectionChanged);
|
editorState.selectionNotifier.addListener(_onSelectionChanged);
|
||||||
|
|
||||||
interceptor = SelectionGestureInterceptor(
|
interceptor = SelectionGestureInterceptor(
|
||||||
|
|
@ -78,6 +77,7 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
textInputService.close();
|
||||||
editorState.selectionNotifier.removeListener(_onSelectionChanged);
|
editorState.selectionNotifier.removeListener(_onSelectionChanged);
|
||||||
editorState.service.selectionService.unregisterGestureInterceptor(
|
editorState.service.selectionService.unregisterGestureInterceptor(
|
||||||
'keyboard',
|
'keyboard',
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ class _ScrollServiceWidgetState extends State<ScrollServiceWidget>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
scrollController.dispose();
|
||||||
editorState.selectionNotifier.removeListener(_onSelectionChanged);
|
editorState.selectionNotifier.removeListener(_onSelectionChanged);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -123,10 +124,13 @@ class _ScrollServiceWidgetState extends State<ScrollServiceWidget>
|
||||||
default:
|
default:
|
||||||
targetRect = selectionRects.last;
|
targetRect = selectionRects.last;
|
||||||
|
|
||||||
/// sometimes moving up in a long single node may be not working
|
// sometimes moving up in a long single node may be not working
|
||||||
/// so we need to special handle this case.
|
// so we need to special handle this case.
|
||||||
final isInSingleNode = (lastSelection?.isSingle ?? false) &&
|
final isLastSelectionSingle = lastSelection?.isSingle ?? false;
|
||||||
lastSelection?.start.path == selection.start.path;
|
final isLastSelectionPathEqual =
|
||||||
|
lastSelection?.start.path.equals(selection.start.path) ?? false;
|
||||||
|
final isInSingleNode =
|
||||||
|
isLastSelectionSingle && isLastSelectionPathEqual;
|
||||||
if (selection.isForward && isInSingleNode) {
|
if (selection.isForward && isInSingleNode) {
|
||||||
targetRect = selectionRects.first;
|
targetRect = selectionRects.first;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,8 @@ class _DesktopSelectionServiceWidgetState
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
_dropTargetEntry?.dispose();
|
||||||
|
_dropTargetEntry = null;
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
editorState.selectionNotifier.removeListener(_updateSelection);
|
editorState.selectionNotifier.removeListener(_updateSelection);
|
||||||
editorState.removeScrollViewScrolledListener(
|
editorState.removeScrollViewScrolledListener(
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,8 @@ class _MobileSelectionServiceWidgetState
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
_lastPanOffset.dispose();
|
||||||
|
currentSelection.dispose();
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
selectionNotifierAfterLayout.dispose();
|
selectionNotifierAfterLayout.dispose();
|
||||||
editorState.selectionNotifier.removeListener(_updateSelection);
|
editorState.selectionNotifier.removeListener(_updateSelection);
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,12 @@ class _FindAndReplaceMenuWidgetState extends State<FindAndReplaceMenuWidget> {
|
||||||
showReplaceMenu = widget.showReplaceMenu;
|
showReplaceMenu = widget.showReplaceMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
searchService.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
|
|
@ -187,12 +193,13 @@ class _FindMenuState extends State<FindMenu> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
widget.searchService.matchWrappers.removeListener(_setState);
|
findTextFieldFocusNode.dispose();
|
||||||
widget.searchService.currentSelectedIndex.removeListener(_setState);
|
|
||||||
widget.searchService.dispose();
|
|
||||||
findTextEditingController.removeListener(_searchPattern);
|
findTextEditingController.removeListener(_searchPattern);
|
||||||
findTextEditingController.dispose();
|
findTextEditingController.dispose();
|
||||||
|
|
||||||
|
widget.searchService.matchWrappers.removeListener(_setState);
|
||||||
|
widget.searchService.currentSelectedIndex.removeListener(_setState);
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -389,6 +396,13 @@ class _ReplaceMenuState extends State<ReplaceMenu> {
|
||||||
final replaceTextFieldFocusNode = FocusNode();
|
final replaceTextFieldFocusNode = FocusNode();
|
||||||
final replaceTextEditingController = TextEditingController();
|
final replaceTextEditingController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
replaceTextFieldFocusNode.dispose();
|
||||||
|
replaceTextEditingController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
|
|
|
||||||
|
|
@ -100,13 +100,17 @@ class _FloatingToolbarState extends State<FloatingToolbar>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
Debounce.cancel(_debounceKey);
|
||||||
|
|
||||||
|
_toolbarContainer?.remove();
|
||||||
|
_toolbarContainer?.dispose();
|
||||||
|
_toolbarContainer = null;
|
||||||
editorState.selectionNotifier.removeListener(_onSelectionChanged);
|
editorState.selectionNotifier.removeListener(_onSelectionChanged);
|
||||||
widget.editorScrollController.offsetNotifier.removeListener(
|
widget.editorScrollController.offsetNotifier.removeListener(
|
||||||
_onScrollPositionChanged,
|
_onScrollPositionChanged,
|
||||||
);
|
);
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
|
||||||
_clear();
|
|
||||||
_toolbarWidget = null;
|
_toolbarWidget = null;
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,13 @@ class _ColorPickerState extends State<ColorPicker> {
|
||||||
_convertHexToOpacity(customColorHex ?? selectedColorHex) ?? '100';
|
_convertHexToOpacity(customColorHex ?? selectedColorHex) ?? '100';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_colorHexController.dispose();
|
||||||
|
_colorOpacityController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return basicOverlay(
|
return basicOverlay(
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ class _LinkMenuState extends State<LinkMenu> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_focusNode.dispose();
|
||||||
_textEditingController.dispose();
|
_textEditingController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,10 @@ class _MobileFloatingToolbarState extends State<MobileFloatingToolbar>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_toolbarContainer?.remove();
|
||||||
|
_toolbarContainer?.dispose();
|
||||||
|
_toolbarContainer = null;
|
||||||
|
|
||||||
editorState.selectionNotifier.removeListener(_onSelectionChanged);
|
editorState.selectionNotifier.removeListener(_onSelectionChanged);
|
||||||
widget.editorScrollController.offsetNotifier.removeListener(
|
widget.editorScrollController.offsetNotifier.removeListener(
|
||||||
_onScrollPositionChanged,
|
_onScrollPositionChanged,
|
||||||
|
|
@ -90,8 +94,6 @@ class _MobileFloatingToolbarState extends State<MobileFloatingToolbar>
|
||||||
_onTapSelectionAreaSubscription.cancel();
|
_onTapSelectionAreaSubscription.cancel();
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
|
||||||
_clear();
|
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,10 @@ class _MobileToolbarV2State extends State<MobileToolbarV2> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_removeKeyboardToolbar();
|
isKeyboardShow.dispose();
|
||||||
|
toolbarOverlay?.remove();
|
||||||
|
toolbarOverlay?.dispose();
|
||||||
|
toolbarOverlay = null;
|
||||||
KeyboardHeightObserver.instance.removeListener(_onKeyboardHeightChanged);
|
KeyboardHeightObserver.instance.removeListener(_onKeyboardHeightChanged);
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue