From ad0f7c31203d7f310c53b8e67d7db3c54e12711f Mon Sep 17 00:00:00 2001 From: Alan Mantoux Date: Sun, 29 Dec 2024 12:22:48 +0100 Subject: [PATCH] Checkbox state rendering is handled by checkbox widget --- .../fleather/lib/src/widgets/controller.dart | 19 +++++++++++---- .../lib/src/widgets/editable_text_block.dart | 23 ++++++++++++++----- .../test/widgets/editable_text_test.dart | 15 ++++++------ 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/packages/fleather/lib/src/widgets/controller.dart b/packages/fleather/lib/src/widgets/controller.dart index 67f649a3..eec341d8 100644 --- a/packages/fleather/lib/src/widgets/controller.dart +++ b/packages/fleather/lib/src/widgets/controller.dart @@ -9,7 +9,9 @@ import '../../util.dart'; import 'autoformats.dart'; import 'history.dart'; -/// List of style keys which can be toggled for insertion +/// @docImport 'checkbox.dart'; + +// List of style keys which can be toggled for insertion List _toggleableStyleKeys = [ ParchmentAttribute.bold.key, ParchmentAttribute.italic.key, @@ -34,7 +36,7 @@ class FleatherController extends ChangeNotifier { ParchmentDocument _document; - /// Doument managed by this controller. + /// Document managed by this controller. ParchmentDocument get document => _document; // A list of changes applied to this doc. The changes could be undone or redone. @@ -206,7 +208,16 @@ class FleatherController extends ChangeNotifier { return true; } - void formatText(int index, int length, ParchmentAttribute attribute) { + /// Update format of [length] characters in the document starting at [index] + /// with the provided [attribute]. + /// + /// If [notify] is `true`, the controller will notify widgets to update + /// accordingly; otherwise widgets will not update, this is useful when + /// we do not want a widget to update the document without triggering a + /// rebuild if the editor (e.g.: [FleatherCheckbox] toggling should not cause + /// scrolling to cursor). + void formatText(int index, int length, ParchmentAttribute attribute, + {bool notify = true}) { final change = document.format(index, length, attribute); // _lastChangeSource = ChangeSource.local; const source = ChangeSource.local; @@ -227,7 +238,7 @@ class FleatherController extends ChangeNotifier { _updateSelectionSilent(adjustedSelection, source: source); } _updateHistory(); - notifyListeners(); + if (notify) notifyListeners(); } /// Formats current selection with [attribute]. diff --git a/packages/fleather/lib/src/widgets/editable_text_block.dart b/packages/fleather/lib/src/widgets/editable_text_block.dart index 9ea4dc27..f9bf7359 100644 --- a/packages/fleather/lib/src/widgets/editable_text_block.dart +++ b/packages/fleather/lib/src/widgets/editable_text_block.dart @@ -256,10 +256,7 @@ class EditableTextBlock extends StatelessWidget { void _toggle(LineNode node, bool checked) { final attr = checked ? ParchmentAttribute.checked : ParchmentAttribute.checked.unset; - controller.updateSelection( - TextSelection.collapsed(offset: node.documentOffset), - source: ChangeSource.local); - controller.formatText(node.documentOffset, 0, attr); + controller.formatText(node.documentOffset, 0, attr, notify: false); } } @@ -350,7 +347,7 @@ class _BulletPoint extends StatelessWidget { } } -class _CheckboxPoint extends StatelessWidget { +class _CheckboxPoint extends StatefulWidget { const _CheckboxPoint({ required this.value, required this.enabled, @@ -361,6 +358,13 @@ class _CheckboxPoint extends StatelessWidget { final bool enabled; final ValueChanged onChanged; + @override + State<_CheckboxPoint> createState() => _CheckboxPointState(); +} + +class _CheckboxPointState extends State<_CheckboxPoint> { + late bool value = widget.value; + @override Widget build(BuildContext context) { return Container( @@ -368,7 +372,14 @@ class _CheckboxPoint extends StatelessWidget { padding: const EdgeInsetsDirectional.only(top: 2.0, end: 12.0), child: FleatherCheckbox( value: value, - onChanged: enabled ? (_) => onChanged(!value) : null, + onChanged: widget.enabled + ? (_) { + widget.onChanged(!widget.value); + setState(() { + value = !value; + }); + } + : null, ), ); } diff --git a/packages/fleather/test/widgets/editable_text_test.dart b/packages/fleather/test/widgets/editable_text_test.dart index 5e1f586f..e20a9667 100644 --- a/packages/fleather/test/widgets/editable_text_test.dart +++ b/packages/fleather/test/widgets/editable_text_test.dart @@ -95,10 +95,10 @@ void main() { Operation.insert('\n', {'block': 'cl', 'checked': true})); }); - testWidgets('check list toggle update selection', (tester) async { - const textPreceedingCheckBox = 'some text\n'; + testWidgets('check list toggle', (tester) async { + const textPrecedingCheckBox = 'some text\n'; final delta = Delta() - ..insert(textPreceedingCheckBox) + ..insert(textPrecedingCheckBox) ..insert('an item') ..insert('\n', {'block': 'cl'}); final editor = EditorSandBox( @@ -107,14 +107,15 @@ void main() { expect(find.byType(FleatherCheckbox), findsOneWidget); await editor.updateSelection(base: 0, extent: 0); + editor.controller.addListener(() { + fail('Controller should not notify when checkbox is toggled'); + }); await tester.tap(find.byType(FleatherCheckbox)); await tester.pumpAndSettle(throttleDuration); expect(editor.document.toDelta().last, Operation.insert('\n', {'block': 'cl', 'checked': true})); - expect( - editor.controller.selection, - const TextSelection.collapsed( - offset: textPreceedingCheckBox.length)); + expect(editor.controller.selection, + const TextSelection.collapsed(offset: 0)); }); testWidgets('bullet list', (tester) async {