From 4ce03965d1414c58c3b7c47767bd49f9302c40de Mon Sep 17 00:00:00 2001 From: Shivansh Yadav <105140714+shivansh00011@users.noreply.github.com> Date: Mon, 16 Jun 2025 09:14:31 +0530 Subject: [PATCH] fix: use end-start instead of length for TextSelection in toggle style button --- .../toolbar/buttons/toggle_style_button.dart | 11 +-- test/common/utils/quill_test_app.dart | 53 ++++---------- .../buttons/toggle_style_button_test.dart | 72 +++++++++++++++++++ 3 files changed, 89 insertions(+), 47 deletions(-) create mode 100644 test/toolbar/buttons/toggle_style_button_test.dart diff --git a/lib/src/toolbar/buttons/toggle_style_button.dart b/lib/src/toolbar/buttons/toggle_style_button.dart index dcc120fa8..d0df477e1 100644 --- a/lib/src/toolbar/buttons/toggle_style_button.dart +++ b/lib/src/toolbar/buttons/toggle_style_button.dart @@ -135,15 +135,8 @@ class QuillToolbarToggleStyleButtonState } bool _getIsToggled(Map attrs) { - if (widget.attribute.key == Attribute.list.key || - widget.attribute.key == Attribute.header.key || - widget.attribute.key == Attribute.script.key || - widget.attribute.key == Attribute.align.key) { - final attribute = attrs[widget.attribute.key]; - if (attribute == null) { - return false; - } - return attribute.value == widget.attribute.value; + if (controller.selection.end - controller.selection.start == 1) { + return false; } return attrs.containsKey(widget.attribute.key); } diff --git a/test/common/utils/quill_test_app.dart b/test/common/utils/quill_test_app.dart index aaa2fddf4..d38b91bf7 100644 --- a/test/common/utils/quill_test_app.dart +++ b/test/common/utils/quill_test_app.dart @@ -35,61 +35,38 @@ class QuillTestApp extends StatelessWidget { /// /// Either [home] or [scaffoldBody] must be provided. /// Throws an [ArgumentError] if both are provided. - QuillTestApp({ - required this.home, - required this.scaffoldBody, - this.onLocalizationsAvailable, + const QuillTestApp({ + required this.child, super.key, - }) { - if (home != null && scaffoldBody != null) { - throw ArgumentError('Either the home or scaffoldBody must be null'); - } - } + }); /// Creates a [QuillTestApp] with a [Scaffold] wrapping the given [body] widget. - factory QuillTestApp.withScaffold(Widget body, - {LocalizationsAvailableCallback? onLocalizationsAvailable}) => - QuillTestApp( - home: null, - scaffoldBody: body, - onLocalizationsAvailable: onLocalizationsAvailable, - ); +static Widget withScaffold(Widget child) { + return MaterialApp( + localizationsDelegates: FlutterQuillLocalizations.localizationsDelegates, + supportedLocales: FlutterQuillLocalizations.supportedLocales, + home: Scaffold( + body: child, + ), + ); +} /// Creates a [QuillTestApp] with the specified [home] widget. factory QuillTestApp.home(Widget home, {LocalizationsAvailableCallback? onLocalizationsAvailable}) => QuillTestApp( - home: home, - scaffoldBody: null, - onLocalizationsAvailable: onLocalizationsAvailable, + child: home, ); /// The home widget for the application. - /// - /// If [home] is not null, [scaffoldBody] must be null. - final Widget? home; - - /// The body widget for a [Scaffold] used as the application home. - /// - /// If [scaffoldBody] is not null, [home] must be null. - final Widget? scaffoldBody; - - final LocalizationsAvailableCallback? onLocalizationsAvailable; + final Widget child; @override Widget build(BuildContext context) { return MaterialApp( localizationsDelegates: FlutterQuillLocalizations.localizationsDelegates, supportedLocales: FlutterQuillLocalizations.supportedLocales, - home: Builder(builder: (context) { - if (onLocalizationsAvailable != null) { - onLocalizationsAvailable?.call(context.loc); - } - return home ?? - Scaffold( - body: scaffoldBody, - ); - }), + home: child, ); } } diff --git a/test/toolbar/buttons/toggle_style_button_test.dart b/test/toolbar/buttons/toggle_style_button_test.dart new file mode 100644 index 000000000..45ee1c279 --- /dev/null +++ b/test/toolbar/buttons/toggle_style_button_test.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_quill/flutter_quill.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../common/utils/quill_test_app.dart'; + +void main() { + group('QuillToolbarToggleStyleButton', () { + testWidgets('should not toggle toolbar state when formatting single character', + (tester) async { + final controller = QuillController.basic(); + controller.document.insert(0, 'Hello World'); + + await tester.pumpWidget( + QuillTestApp.withScaffold( + QuillToolbarToggleStyleButton( + controller: controller, + attribute: Attribute.bold, + ), + ), + ); + + // Format a single character + controller.formatText(0, 1, Attribute.bold); + + // Verify the toolbar button is not toggled + final button = tester.widget( + find.byType(QuillToolbarIconButton), + ); + expect(button.isSelected, false); + + // Format multiple characters + controller.formatText(0, 5, Attribute.bold); + + // Verify the toolbar button is now toggled + await tester.pump(); + final buttonAfterMultiFormat = tester.widget( + find.byType(QuillToolbarIconButton), + ); + expect(buttonAfterMultiFormat.isSelected, true); + }); + + testWidgets('should maintain formatting for single character', + (tester) async { + final controller = QuillController.basic(); + controller.document.insert(0, 'Hello World'); + + await tester.pumpWidget( + QuillTestApp.withScaffold( + QuillToolbarToggleStyleButton( + controller: controller, + attribute: Attribute.bold, + ), + ), + ); + + // Format a single character + controller.formatText(0, 1, Attribute.bold); + + // Verify the character is bold + final style = controller.document.collectStyle(0, 1); + expect(style.attributes.containsKey(Attribute.bold.key), true); + + // Type new text after the bold character + controller.replaceText(1, 0, 'New text', null); + + // Verify the new text is not bold + final newStyle = controller.document.collectStyle(1, 9); + expect(newStyle.attributes.containsKey(Attribute.bold.key), false); + }); + }); +} \ No newline at end of file