mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Disableable ContextMenuButtonItems (#124253)
Fixes https://github.com/flutter/flutter/issues/124247 | Native | Flutter before | Flutter after | | --- | --- | --- | | <img width="248" alt="Screenshot 2023-04-05 at 9 26 16 AM" src="https://user-images.githubusercontent.com/389558/230177116-154999e8-eef3-441d-9fe9-7063839a6b99.png"> | <img width="240" alt="Screenshot 2023-04-05 at 11 18 01 AM" src="https://user-images.githubusercontent.com/389558/230177125-1680e851-223e-4956-b5b6-1a24e11dc22a.png"> | <img width="226" alt="Screenshot 2023-04-05 at 11 17 36 AM" src="https://user-images.githubusercontent.com/389558/230177123-bde82134-67e1-4ce2-8eec-719eeb779bf4.png"> | Also, it's now possible for anyone to create disabled buttons like this by setting ContextMenuButtonItem.onPressed to `null`.
This commit is contained in:
parent
bd2617ecb9
commit
3128df832a
@ -69,7 +69,7 @@ class CupertinoDesktopTextSelectionToolbarButton extends StatefulWidget {
|
||||
child = null;
|
||||
|
||||
/// {@macro flutter.cupertino.CupertinoTextSelectionToolbarButton.onPressed}
|
||||
final VoidCallback onPressed;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// {@macro flutter.cupertino.CupertinoTextSelectionToolbarButton.child}
|
||||
final Widget? child;
|
||||
|
||||
@ -66,7 +66,7 @@ class CupertinoSpellCheckSuggestionsToolbar extends StatelessWidget {
|
||||
CupertinoLocalizations.of(editableTextState.context);
|
||||
return <ContextMenuButtonItem>[
|
||||
ContextMenuButtonItem(
|
||||
onPressed: () {},
|
||||
onPressed: null,
|
||||
label: localizations.noSpellCheckReplacementsLabel,
|
||||
)
|
||||
];
|
||||
|
||||
@ -51,7 +51,7 @@ class DesktopTextSelectionToolbarButton extends StatelessWidget {
|
||||
);
|
||||
|
||||
/// {@macro flutter.material.TextSelectionToolbarTextButton.onPressed}
|
||||
final VoidCallback onPressed;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// {@macro flutter.material.TextSelectionToolbarTextButton.child}
|
||||
final Widget child;
|
||||
|
||||
@ -47,7 +47,7 @@ class ContextMenuButtonItem {
|
||||
});
|
||||
|
||||
/// The callback to be called when the button is pressed.
|
||||
final VoidCallback onPressed;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// The type of button this represents.
|
||||
final ContextMenuButtonType type;
|
||||
|
||||
@ -14,10 +14,10 @@ void main() {
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
child: CupertinoDesktopTextSelectionToolbarButton(
|
||||
child: const Text('Tap me'),
|
||||
onPressed: () {
|
||||
pressed = true;
|
||||
},
|
||||
child: const Text('Tap me'),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -34,8 +34,8 @@ void main() {
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
child: CupertinoDesktopTextSelectionToolbarButton(
|
||||
child: const Text('Tap me'),
|
||||
onPressed: () { },
|
||||
child: const Text('Tap me'),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -71,4 +71,21 @@ void main() {
|
||||
));
|
||||
expect(opacity.opacity.value, 1.0);
|
||||
});
|
||||
|
||||
testWidgets('passing null to onPressed disables the button', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const CupertinoApp(
|
||||
home: Center(
|
||||
child: CupertinoDesktopTextSelectionToolbarButton(
|
||||
onPressed: null,
|
||||
child: Text('Tap me'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CupertinoButton), findsOneWidget);
|
||||
final CupertinoButton button = tester.widget(find.byType(CupertinoButton));
|
||||
expect(button.enabled, isFalse);
|
||||
});
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ void main() {
|
||||
expect(labels, isNot(contains('yeller')));
|
||||
});
|
||||
|
||||
testWidgets('buildButtonItems builds a "No Replacements Found" button when no suggestions', (WidgetTester tester) async {
|
||||
testWidgets('buildButtonItems builds a disabled "No Replacements Found" button when no suggestions', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: _FakeEditableText(),
|
||||
@ -73,8 +73,9 @@ void main() {
|
||||
CupertinoSpellCheckSuggestionsToolbar.buildButtonItems(editableTextState);
|
||||
|
||||
expect(buttonItems, isNotNull);
|
||||
expect(buttonItems!.length, 1);
|
||||
expect(buttonItems.first.label, 'No Replacements Found');
|
||||
expect(buttonItems, hasLength(1));
|
||||
expect(buttonItems!.first.label, 'No Replacements Found');
|
||||
expect(buttonItems.first.onPressed, isNull);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -71,4 +71,20 @@ void main() {
|
||||
));
|
||||
expect(opacity.opacity.value, 1.0);
|
||||
});
|
||||
|
||||
testWidgets('passing null to onPressed disables the button', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const CupertinoApp(
|
||||
home: Center(
|
||||
child: CupertinoTextSelectionToolbarButton(
|
||||
child: Text('Tap me'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(CupertinoButton), findsOneWidget);
|
||||
final CupertinoButton button = tester.widget(find.byType(CupertinoButton));
|
||||
expect(button.enabled, isFalse);
|
||||
});
|
||||
}
|
||||
|
||||
@ -14,10 +14,10 @@ void main() {
|
||||
MaterialApp(
|
||||
home: Center(
|
||||
child: DesktopTextSelectionToolbarButton(
|
||||
child: const Text('Tap me'),
|
||||
onPressed: () {
|
||||
pressed = true;
|
||||
},
|
||||
child: const Text('Tap me'),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -28,4 +28,21 @@ void main() {
|
||||
await tester.tap(find.byType(DesktopTextSelectionToolbarButton));
|
||||
expect(pressed, true);
|
||||
});
|
||||
|
||||
testWidgets('passing null to onPressed disables the button', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: Center(
|
||||
child: DesktopTextSelectionToolbarButton(
|
||||
onPressed: null,
|
||||
child: Text('Cannot tap me'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(TextButton), findsOneWidget);
|
||||
final TextButton button = tester.widget(find.byType(TextButton));
|
||||
expect(button.enabled, isFalse);
|
||||
});
|
||||
}
|
||||
|
||||
@ -2424,7 +2424,7 @@ void main() {
|
||||
final ContextMenuButtonItem cutButton = items!.first;
|
||||
expect(cutButton.type, ContextMenuButtonType.cut);
|
||||
|
||||
cutButton.onPressed();
|
||||
cutButton.onPressed?.call();
|
||||
await tester.pump();
|
||||
|
||||
expect(controller.text, isEmpty);
|
||||
@ -2492,7 +2492,7 @@ void main() {
|
||||
final ContextMenuButtonItem copyButton = items!.first;
|
||||
expect(copyButton.type, ContextMenuButtonType.copy);
|
||||
|
||||
copyButton.onPressed();
|
||||
copyButton.onPressed?.call();
|
||||
await tester.pump();
|
||||
|
||||
expect(controller.text, equals(text));
|
||||
@ -2560,7 +2560,7 @@ void main() {
|
||||
// Setting data which will be pasted into the clipboard.
|
||||
await Clipboard.setData(const ClipboardData(text: text));
|
||||
|
||||
pasteButton.onPressed();
|
||||
pasteButton.onPressed?.call();
|
||||
await tester.pump();
|
||||
|
||||
expect(controller.text, equals(text + text));
|
||||
@ -2619,7 +2619,7 @@ void main() {
|
||||
final ContextMenuButtonItem selectAllButton = items!.first;
|
||||
expect(selectAllButton.type, ContextMenuButtonType.selectAll);
|
||||
|
||||
selectAllButton.onPressed();
|
||||
selectAllButton.onPressed?.call();
|
||||
await tester.pump();
|
||||
|
||||
expect(controller.text, equals(text));
|
||||
@ -15169,6 +15169,68 @@ testWidgets('Floating cursor ending with selection', (WidgetTester tester) async
|
||||
expect(find.text('DELETE'), matcher);
|
||||
});
|
||||
|
||||
testWidgets('can show spell check suggestions toolbar when there are no spell check results on iOS', (WidgetTester tester) async {
|
||||
tester.binding.platformDispatcher.nativeSpellCheckServiceDefinedTestValue =
|
||||
true;
|
||||
const TextEditingValue value = TextEditingValue(
|
||||
text: 'tset test test',
|
||||
selection: TextSelection(affinity: TextAffinity.upstream, baseOffset: 0, extentOffset: 4),
|
||||
);
|
||||
controller.value = value;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: EditableText(
|
||||
backgroundCursorColor: Colors.grey,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
style: textStyle,
|
||||
cursorColor: cursorColor,
|
||||
selectionControls: materialTextSelectionControls,
|
||||
spellCheckConfiguration:
|
||||
const SpellCheckConfiguration(
|
||||
misspelledTextStyle: CupertinoTextField.cupertinoMisspelledTextStyle,
|
||||
spellCheckSuggestionsToolbarBuilder: CupertinoTextField.defaultSpellCheckSuggestionsToolbarBuilder,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final EditableTextState state =
|
||||
tester.state<EditableTextState>(find.byType(EditableText));
|
||||
|
||||
// Can't show the toolbar when there's no focus.
|
||||
expect(state.showSpellCheckSuggestionsToolbar(), false);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNothing);
|
||||
|
||||
// Can't show the toolbar when there are no spell check results.
|
||||
expect(state.showSpellCheckSuggestionsToolbar(), false);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNothing);
|
||||
|
||||
// Shows 'No Replacements Found' when there are spell check results but no
|
||||
// suggestions.
|
||||
state.spellCheckResults = const SpellCheckResults('test tset test', <SuggestionSpan>[SuggestionSpan(TextRange(start: 0, end: 4), <String>[])]);
|
||||
state.renderEditable.selectWordsInRange(
|
||||
from: Offset.zero,
|
||||
cause: SelectionChangedCause.tap,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
// Toolbar will only show on non-web platforms.
|
||||
expect(state.showSpellCheckSuggestionsToolbar(), isTrue);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(CupertinoTextSelectionToolbarButton), findsOneWidget);
|
||||
expect(find.byType(CupertinoButton), findsOneWidget);
|
||||
expect(find.text('No Replacements Found'), findsOneWidget);
|
||||
final CupertinoButton button = tester.widget(find.byType(CupertinoButton));
|
||||
expect(button.enabled, isFalse);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
|
||||
skip: kIsWeb, // [intended]
|
||||
);
|
||||
|
||||
testWidgets('cupertino spell check suggestions toolbar buttons correctly change the composing region', (WidgetTester tester) async {
|
||||
tester.binding.platformDispatcher.nativeSpellCheckServiceDefinedTestValue =
|
||||
true;
|
||||
|
||||
@ -1754,7 +1754,7 @@ void main() {
|
||||
expect(buttonItems[0].type, ContextMenuButtonType.copy);
|
||||
|
||||
// Press `Copy` item
|
||||
buttonItems[0].onPressed.call();
|
||||
buttonItems[0].onPressed?.call();
|
||||
|
||||
final SelectableRegionState regionState = tester.state<SelectableRegionState>(find.byType(SelectableRegion));
|
||||
|
||||
@ -1808,7 +1808,7 @@ void main() {
|
||||
expect(buttonItems[1].type, ContextMenuButtonType.selectAll);
|
||||
|
||||
// Press `Select All` item
|
||||
buttonItems[1].onPressed.call();
|
||||
buttonItems[1].onPressed?.call();
|
||||
|
||||
final SelectableRegionState regionState = tester.state<SelectableRegionState>(find.byType(SelectableRegion));
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user