Unverified Commit 13f4fe40 authored by Justin McCandless's avatar Justin McCandless Committed by GitHub

Show iOS menu on iOS for TextField by default (#124895)

Even in TextField using the Material library, iOS devices will still show the iOS-style spell check toolbar by default when using spell check.
parent 0d023144
......@@ -18,6 +18,10 @@ const int _kMaxSuggestions = 3;
///
/// Tries to position itself below the [anchors], but if it doesn't fit, then it
/// readjusts to fit above bottom view insets.
///
/// See also:
/// * [SpellCheckSuggestionsToolbar], which is similar but for both the
/// Material and Cupertino libraries.
class CupertinoSpellCheckSuggestionsToolbar extends StatelessWidget {
/// Constructs a [CupertinoSpellCheckSuggestionsToolbar].
///
......@@ -28,6 +32,18 @@ class CupertinoSpellCheckSuggestionsToolbar extends StatelessWidget {
required this.buttonItems,
}) : assert(buttonItems.length <= _kMaxSuggestions);
/// Constructs a [CupertinoSpellCheckSuggestionsToolbar] with the default
/// children for an [EditableText].
///
/// See also:
/// * [SpellCheckSuggestionsToolbar.editableText], which is similar but
/// builds an Android-style toolbar.
CupertinoSpellCheckSuggestionsToolbar.editableText({
super.key,
required EditableTextState editableTextState,
}) : buttonItems = buildButtonItems(editableTextState) ?? <ContextMenuButtonItem>[],
anchors = editableTextState.contextMenuAnchors;
/// The location on which to anchor the menu.
final TextSelectionToolbarAnchors anchors;
......@@ -129,6 +145,10 @@ class CupertinoSpellCheckSuggestionsToolbar extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (buttonItems.isEmpty) {
return const SizedBox.shrink();
}
final List<Widget> children = _buildToolbarButtons(context);
return CupertinoTextSelectionToolbar(
anchorAbove: anchors.primaryAnchor,
......
......@@ -793,25 +793,19 @@ class CupertinoTextField extends StatefulWidget {
/// style.
///
/// See also:
/// * [spellCheckConfiguration], where this is typically specified for
/// [CupertinoTextField].
/// * [SpellCheckConfiguration.spellCheckSuggestionsToolbarBuilder], the
/// builder configured to show a spell check suggestions toolbar.
/// * [TextField.defaultSpellCheckSuggestionsToolbarBuilder], the builder
/// configured to show the Material style spell check suggestions toolbar.
/// parameter for which this is the default value for [CupertinoTextField].
/// * [TextField.defaultSpellCheckSuggestionsToolbarBuilder], which is like
/// this but specifies the default for [CupertinoTextField].
@visibleForTesting
static Widget defaultSpellCheckSuggestionsToolbarBuilder(
BuildContext context,
EditableTextState editableTextState,
) {
final List<ContextMenuButtonItem>? buttonItems =
CupertinoSpellCheckSuggestionsToolbar.buildButtonItems(editableTextState);
if (buttonItems == null || buttonItems.isEmpty){
return const SizedBox.shrink();
}
return CupertinoSpellCheckSuggestionsToolbar(
anchors: editableTextState.contextMenuAnchors,
buttonItems: buttonItems,
return CupertinoSpellCheckSuggestionsToolbar.editableText(
editableTextState: editableTextState,
);
}
......
......@@ -25,6 +25,11 @@ const int _kMaxSuggestions = 3;
///
/// Tries to position itself below the [anchor], but if it doesn't fit, then it
/// readjusts to fit above bottom view insets.
///
/// See also:
///
/// * [CupertinoSpellCheckSuggestionsToolbar], which is similar but builds an
/// iOS-style spell check toolbar.
class SpellCheckSuggestionsToolbar extends StatelessWidget {
/// Constructs a [SpellCheckSuggestionsToolbar].
///
......@@ -36,6 +41,18 @@ class SpellCheckSuggestionsToolbar extends StatelessWidget {
required this.buttonItems,
}) : assert(buttonItems.length <= _kMaxSuggestions + 1);
/// Constructs a [SpellCheckSuggestionsToolbar] with the default children for
/// an [EditableText].
///
/// See also:
/// * [CupertinoSpellCheckSuggestionsToolbar.editableText], which is similar
/// but builds an iOS-style toolbar.
SpellCheckSuggestionsToolbar.editableText({
super.key,
required EditableTextState editableTextState,
}) : buttonItems = buildButtonItems(editableTextState) ?? <ContextMenuButtonItem>[],
anchor = getToolbarAnchor(editableTextState.contextMenuAnchors);
/// {@template flutter.material.SpellCheckSuggestionsToolbar.anchor}
/// The focal point below which the toolbar attempts to position itself.
/// {@endtemplate}
......@@ -165,6 +182,10 @@ class SpellCheckSuggestionsToolbar extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (buttonItems.isEmpty){
return const SizedBox.shrink();
}
// Adjust toolbar height if needed.
final double spellCheckSuggestionsToolbarHeight =
_kDefaultToolbarHeight - (48.0 * (4 - buttonItems.length));
......
......@@ -800,32 +800,37 @@ class TextField extends StatefulWidget {
decorationStyle: TextDecorationStyle.wavy,
);
/// Default builder for the spell check suggestions toolbar in the Material
/// style.
/// Default builder for [TextField]'s spell check suggestions toolbar.
///
/// On Apple platforms, builds an iOS-style toolbar. Everywhere else, builds
/// an Android-style toolbar.
///
/// See also:
/// * [spellCheckConfiguration], where this is typically specified for
/// [TextField].
/// * [SpellCheckConfiguration.spellCheckSuggestionsToolbarBuilder], the
/// builder configured to show a spell check suggestions toolbar.
/// * [CupertinoTextField.defaultSpellCheckSuggestionsToolbarBuilder], the builder
/// configured to show the Material style spell check suggestions toolbar.
/// parameter for which this is the default value for [TextField].
/// * [CupertinoTextField.defaultSpellCheckSuggestionsToolbarBuilder], which
/// is like this but specifies the default for [CupertinoTextField].
@visibleForTesting
static Widget defaultSpellCheckSuggestionsToolbarBuilder(
BuildContext context,
EditableTextState editableTextState,
) {
final Offset anchor =
SpellCheckSuggestionsToolbar.getToolbarAnchor(editableTextState.contextMenuAnchors);
final List<ContextMenuButtonItem>? buttonItems =
SpellCheckSuggestionsToolbar.buildButtonItems(editableTextState);
if (buttonItems == null){
return const SizedBox.shrink();
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
return CupertinoSpellCheckSuggestionsToolbar.editableText(
editableTextState: editableTextState,
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
return SpellCheckSuggestionsToolbar.editableText(
editableTextState: editableTextState,
);
}
return SpellCheckSuggestionsToolbar(
anchor: anchor,
buttonItems: buttonItems,
);
}
@override
......
......@@ -16153,6 +16153,58 @@ void main() {
}, variant: TargetPlatformVariant.all());
}
});
testWidgets('Builds the corresponding default spell check toolbar by platform', (WidgetTester tester) async {
tester.binding.platformDispatcher.nativeSpellCheckServiceDefinedTestValue =
true;
late final BuildContext builderContext;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: Builder(
builder: (BuildContext context) {
builderContext = context;
return const TextField(
autofocus: true,
spellCheckConfiguration: SpellCheckConfiguration(),
);
},
),
),
),
),
);
// Allow the autofocus to take effect.
await tester.pump();
final EditableTextState editableTextState =
tester.state<EditableTextState>(find.byType(EditableText));
editableTextState.spellCheckResults =
const SpellCheckResults(
'',
<SuggestionSpan>[
SuggestionSpan(TextRange(start: 0, end: 0), <String>['something']),
],
);
final Widget spellCheckToolbar =
TextField.defaultSpellCheckSuggestionsToolbarBuilder(
builderContext,
editableTextState,
);
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
expect(spellCheckToolbar, isA<CupertinoSpellCheckSuggestionsToolbar>());
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(spellCheckToolbar, isA<SpellCheckSuggestionsToolbar>());
}
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));
}
/// A Simple widget for testing the obscure text.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment