Various improvements to text-editing-related documentation. (#142561)

This commit is contained in:
Ian Hickson 2024-02-07 11:58:06 -08:00 committed by GitHub
parent 4becae25b0
commit 9fccb32a58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 126 additions and 115 deletions

View file

@ -763,16 +763,15 @@ class CupertinoTextField extends StatefulWidget {
);
}
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
/// Configuration for the text field magnifier.
///
/// By default (when this property is set to null), a [CupertinoTextMagnifier]
/// is used on mobile platforms, and nothing on desktop platforms. To suppress
/// the magnifier on all platforms, consider passing
/// [TextMagnifierConfiguration.disabled] explicitly.
///
/// {@macro flutter.widgets.magnifier.intro}
///
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
///
/// By default, builds a [CupertinoTextMagnifier] on iOS and Android nothing on all other
/// platforms. If it is desired to suppress the magnifier, consider passing
/// [TextMagnifierConfiguration.disabled].
///
/// {@tool dartpad}
/// This sample demonstrates how to customize the magnifier that this text field uses.
///

View file

@ -502,15 +502,13 @@ class SelectableText extends StatefulWidget {
);
}
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
///
/// {@macro flutter.widgets.magnifier.intro}
///
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
/// The configuration for the magnifier used when the text is selected.
///
/// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier]
/// on Android, and builds nothing on all other platforms. If it is desired to
/// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled].
/// on Android, and builds nothing on all other platforms. To suppress the
/// magnifier, consider passing [TextMagnifierConfiguration.disabled].
///
/// {@macro flutter.widgets.magnifier.intro}
final TextMagnifierConfiguration? magnifierConfiguration;
@override

View file

@ -33,6 +33,7 @@ import 'theme.dart';
/// {@end-tool}
///
/// See also:
///
/// * [SelectableRegion], which provides an overview of the selection system.
class SelectionArea extends StatefulWidget {
/// Creates a [SelectionArea].
@ -48,15 +49,13 @@ class SelectionArea extends StatefulWidget {
required this.child,
});
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
///
/// {@macro flutter.widgets.magnifier.intro}
///
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
/// The configuration for the magnifier in the selection region.
///
/// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier]
/// on Android, and builds nothing on all other platforms. If it is desired to
/// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled].
/// on Android, and builds nothing on all other platforms. To suppress the
/// magnifier, consider passing [TextMagnifierConfiguration.disabled].
///
/// {@macro flutter.widgets.magnifier.intro}
final TextMagnifierConfiguration? magnifierConfiguration;
/// {@macro flutter.widgets.Focus.focusNode}

View file

@ -349,15 +349,13 @@ class TextField extends StatefulWidget {
keyboardType = keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline),
enableInteractiveSelection = enableInteractiveSelection ?? (!readOnly || !obscureText);
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
///
/// {@macro flutter.widgets.magnifier.intro}
///
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
/// The configuration for the magnifier of this text field.
///
/// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier]
/// on Android, and builds nothing on all other platforms. If it is desired to
/// suppress the magnifier, consider passing [TextMagnifierConfiguration.disabled].
/// on Android, and builds nothing on all other platforms. To suppress the
/// magnifier, consider passing [TextMagnifierConfiguration.disabled].
///
/// {@macro flutter.widgets.magnifier.intro}
///
/// {@tool dartpad}
/// This sample demonstrates how to customize the magnifier that this text field uses.

View file

@ -1799,11 +1799,28 @@ class EditableText extends StatefulWidget {
/// {@template flutter.widgets.EditableText.contextMenuBuilder}
/// Builds the text selection toolbar when requested by the user.
///
/// `primaryAnchor` is the desired anchor position for the context menu, while
/// `secondaryAnchor` is the fallback location if the menu doesn't fit.
/// The context menu is built when [EditableTextState.showToolbar] is called,
/// typically by one of the callbacks installed by the widget created by
/// [TextSelectionGestureDetectorBuilder.buildGestureDetector]. The widget
/// returned by [contextMenuBuilder] is passed to a [ContextMenuController].
///
/// `buttonItems` represents the buttons that would be built by default for
/// this widget.
/// If no callback is provided, no context menu will be shown.
///
/// The [EditableTextContextMenuBuilder] signature used by the
/// [contextMenuBuilder] callback has two parameters, the [BuildContext] of
/// the [EditableText] and the [EditableTextState] of the [EditableText].
///
/// The [EditableTextState] has two properties that are especially useful when
/// building the widgets for the context menu:
///
/// * [EditableTextState.contextMenuAnchors] specifies the desired anchor
/// position for the context menu.
///
/// * [EditableTextState.contextMenuButtonItems] represents the buttons that
/// should typically be built for this widget (e.g. cut, copy, paste).
///
/// The [TextSelectionToolbarLayoutDelegate] class may be particularly useful
/// in honoring the preferred anchor positions.
///
/// For backwards compatibility, when [selectionControls] is set to an object
/// that does not mix in [TextSelectionHandleControls], [contextMenuBuilder]
@ -1834,8 +1851,6 @@ class EditableText extends StatefulWidget {
/// * [BrowserContextMenu], which allows the browser's context menu on web
/// to be disabled and Flutter-rendered context menus to appear.
/// {@endtemplate}
///
/// If not provided, no context menu will be shown.
final EditableTextContextMenuBuilder? contextMenuBuilder;
/// {@template flutter.widgets.EditableText.spellCheckConfiguration}
@ -1852,11 +1867,10 @@ class EditableText extends StatefulWidget {
/// {@endtemplate}
final SpellCheckConfiguration? spellCheckConfiguration;
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
/// The configuration for the magnifier to use with selections in this text
/// field.
///
/// {@macro flutter.widgets.magnifier.intro}
///
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
final TextMagnifierConfiguration magnifierConfiguration;
bool get _userSelectionEnabled => enableInteractiveSelection && (!readOnly || !obscureText);

View file

@ -15,19 +15,17 @@ import 'inherited_theme.dart';
import 'navigator.dart';
import 'overlay.dart';
/// {@template flutter.widgets.magnifier.MagnifierBuilder}
/// Signature for a builder that builds a [Widget] with a [MagnifierController].
///
/// Consuming [MagnifierController] or [ValueNotifier]<[MagnifierInfo]> is not
/// required, although if a Widget intends to have entry or exit animations, it should take
/// [MagnifierController] and provide it an [AnimationController], so that [MagnifierController]
/// can wait before removing it from the overlay.
/// {@endtemplate}
/// The builder is called exactly once per magnifier.
///
/// See also:
/// If the `controller` parameter's [MagnifierController.animationController]
/// field is set (by the builder) to an [AnimationController], the
/// [MagnifierController] will drive the animation during entry and exit.
///
/// - [MagnifierInfo], the data class that updates the
/// magnifier.
/// The `magnifierInfo` parameter is updated with new [MagnifierInfo] instances
/// during the lifetime of the built magnifier, e.g. as the user moves their
/// finger around the text field.
typedef MagnifierBuilder = Widget? Function(
BuildContext context,
MagnifierController controller,
@ -57,9 +55,9 @@ class MagnifierInfo {
/// The offset of the gesture position that the magnifier should be shown at.
final Offset globalGesturePosition;
/// The rect of the current line the magnifier should be shown at,
/// without taking into account any padding of the field; only the position
/// of the first and last character.
/// The rect of the current line the magnifier should be shown at, without
/// taking into account any padding of the field; only the position of the
/// first and last character.
final Rect currentLineBoundaries;
/// The rect of the handle that the magnifier should follow.
@ -89,48 +87,45 @@ class MagnifierInfo {
);
}
/// {@template flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
/// A configuration object for a magnifier.
/// {@endtemplate}
/// A configuration object for a magnifier (e.g. in a text field).
///
/// {@macro flutter.widgets.magnifier.intro}
///
/// {@template flutter.widgets.magnifier.TextMagnifierConfiguration.details}
/// In general, most features of the magnifier can be configured through
/// [MagnifierBuilder]. [TextMagnifierConfiguration] is used to configure
/// the magnifier's behavior through the [SelectionOverlay].
/// {@endtemplate}
/// In general, most features of the magnifier can be configured by controlling
/// the widgets built by the [magnifierBuilder].
class TextMagnifierConfiguration {
/// Constructs a [TextMagnifierConfiguration] from parts.
///
/// If [magnifierBuilder] is null, a default [MagnifierBuilder] will be used
/// that never builds a magnifier.
/// that does not build a magnifier.
const TextMagnifierConfiguration({
MagnifierBuilder? magnifierBuilder,
this.shouldDisplayHandlesInMagnifier = true
this.shouldDisplayHandlesInMagnifier = true,
}) : _magnifierBuilder = magnifierBuilder;
/// The passed in [MagnifierBuilder].
///
/// This is nullable because [disabled] needs to be static const,
/// so that it can be used as a default parameter. If left null,
/// the [magnifierBuilder] getter will be a function that always returns
/// null.
/// The builder callback that creates the widget that renders the magnifier.
MagnifierBuilder get magnifierBuilder => _magnifierBuilder ?? _none;
final MagnifierBuilder? _magnifierBuilder;
/// {@macro flutter.widgets.magnifier.MagnifierBuilder}
MagnifierBuilder get magnifierBuilder => _magnifierBuilder ?? (_, __, ___) => null;
static Widget? _none(
BuildContext context,
MagnifierController controller,
ValueNotifier<MagnifierInfo> magnifierInfo,
) => null;
/// Determines whether a magnifier should show the text editing handles or not.
/// Whether a magnifier should show the text editing handles or not.
///
/// This flag is used by [SelectionOverlay.showMagnifier] to control the order
/// of layers in the rendering; specifically, whether to place the layer
/// containing the handles above or below the layer containing the magnifier
/// in the [Overlay].
final bool shouldDisplayHandlesInMagnifier;
/// A constant for a [TextMagnifierConfiguration] that is disabled.
///
/// In particular, this [TextMagnifierConfiguration] is considered disabled
/// because it never builds anything, regardless of platform.
/// A constant for a [TextMagnifierConfiguration] that is disabled, meaning it
/// never builds anything, regardless of platform.
static const TextMagnifierConfiguration disabled = TextMagnifierConfiguration();
}
/// A controller for a magnifier.
///
/// [MagnifierController]'s main benefit over holding a raw [OverlayEntry] is that
/// [MagnifierController] will handle logic around waiting for a magnifier to animate in or out.
///
@ -156,11 +151,11 @@ class MagnifierController {
/// The magnifier's [OverlayEntry], if currently in the overlay.
///
/// This is public in case other overlay entries need to be positioned
/// above or below this [overlayEntry]. Anything in the paint order after
/// the [RawMagnifier] will not be displayed in the magnifier; this means that if it
/// is desired for an overlay entry to be displayed in the magnifier,
/// it _must_ be positioned below the magnifier.
/// This is exposed so that other overlay entries can be positioned above or
/// below this [overlayEntry]. Anything in the paint order after the
/// [RawMagnifier] in this [OverlayEntry] will not be displayed in the
/// magnifier; if it is desired for an overlay entry to be displayed in the
/// magnifier, it _must_ be positioned below the magnifier.
///
/// {@tool snippet}
/// ```dart
@ -198,20 +193,22 @@ class MagnifierController {
/// ```
/// {@end-tool}
///
/// A null check on [overlayEntry] will not suffice to check if a magnifier is in the
/// overlay or not; instead, you should check [shown]. This is because it is possible,
/// such as in cases where [hide] was called with `removeFromOverlay` false, that the magnifier
/// is not shown, but the entry is not null.
/// To check if a magnifier is in the overlay, use [shown]. The [overlayEntry]
/// field may be non-null even when the magnifier is not visible.
OverlayEntry? get overlayEntry => _overlayEntry;
OverlayEntry? _overlayEntry;
/// If the magnifier is shown or not.
/// Whether the magnifier is currently being shown.
///
/// [shown] is:
/// - false when nothing is in the overlay.
/// - false when [animationController] is [AnimationStatus.dismissed].
/// - false when [animationController] is animating out.
/// and true in all other circumstances.
/// This is false when nothing is in the overlay, when the
/// [animationController] is in the [AnimationStatus.dismissed] state, or when
/// the [animationController] is animating out (i.e. in the
/// [AnimationStatus.reverse] state).
///
/// It is true in the opposite cases, i.e. when the overlay is not empty, and
/// either the [animationController] is null, in the
/// [AnimationStatus.completed] state, or in the [AnimationStatus.forward]
/// state.
bool get shown {
if (overlayEntry == null) {
return false;
@ -225,17 +222,17 @@ class MagnifierController {
return true;
}
/// Shows the [RawMagnifier] that this controller controls.
/// Displays the magnifier.
///
/// Returns a future that completes when the magnifier is fully shown, i.e. done
/// with its entry animation.
///
/// To control what overlays are shown in the magnifier, utilize [below]. See
/// [overlayEntry] for more details on how to utilize [below].
/// To control what overlays are shown in the magnifier, use `below`. See
/// [overlayEntry] for more details on how to utilize `below`.
///
/// If the magnifier already exists (i.e. [overlayEntry] != null), then [show] will
/// override the old overlay and not play an exit animation. Consider awaiting [hide]
/// first, to guarantee
/// If the magnifier already exists (i.e. [overlayEntry] != null), then [show]
/// will replace the old overlay without playing an exit animation. Consider
/// awaiting [hide] first, to animate from the old magnifier to the new one.
Future<void> show({
required BuildContext context,
required WidgetBuilder builder,

View file

@ -192,6 +192,7 @@ const double _kSelectableVerticalComparingThreshold = 3.0;
/// ```
///
/// See also:
///
/// * [SelectionArea], which creates a [SelectableRegion] with
/// platform-adaptive selection controls.
/// * [SelectionHandler], which contains APIs to handle selection events from the
@ -216,13 +217,13 @@ class SelectableRegion extends StatefulWidget {
this.onSelectionChanged,
});
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
///
/// {@macro flutter.widgets.magnifier.intro}
/// The configuration for the magnifier used with selections in this region.
///
/// By default, [SelectableRegion]'s [TextMagnifierConfiguration] is disabled.
/// For a version of [SelectableRegion] that adapts automatically to the
/// current platform, consider [SelectionArea].
///
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
/// {@macro flutter.widgets.magnifier.intro}
final TextMagnifierConfiguration magnifierConfiguration;
/// {@macro flutter.widgets.Focus.focusNode}

View file

@ -989,19 +989,18 @@ class SelectionOverlay {
final ValueNotifier<MagnifierInfo> _magnifierInfo =
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty);
/// [MagnifierController.show] and [MagnifierController.hide] should not be called directly, except
/// from inside [showMagnifier] and [hideMagnifier]. If it is desired to show or hide the magnifier,
/// call [showMagnifier] or [hideMagnifier]. This is because the magnifier needs to orchestrate
/// with other properties in [SelectionOverlay].
// [MagnifierController.show] and [MagnifierController.hide] should not be
// called directly, except from inside [showMagnifier] and [hideMagnifier]. If
// it is desired to show or hide the magnifier, call [showMagnifier] or
// [hideMagnifier]. This is because the magnifier needs to orchestrate with
// other properties in [SelectionOverlay].
final MagnifierController _magnifierController = MagnifierController();
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.intro}
///
/// {@macro flutter.widgets.magnifier.intro}
/// The configuration for the magnifier.
///
/// By default, [SelectionOverlay]'s [TextMagnifierConfiguration] is disabled.
///
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
/// {@macro flutter.widgets.magnifier.intro}
final TextMagnifierConfiguration magnifierConfiguration;
/// {@template flutter.widgets.SelectionOverlay.toolbarIsVisible}
@ -1047,11 +1046,12 @@ class SelectionOverlay {
}
_magnifierController.show(
context: context,
below: magnifierConfiguration.shouldDisplayHandlesInMagnifier
? null
: _handles?.start,
builder: (_) => builtMagnifier);
context: context,
below: magnifierConfiguration.shouldDisplayHandlesInMagnifier
? null
: _handles?.start,
builder: (_) => builtMagnifier,
);
}
/// {@template flutter.widgets.SelectionOverlay.hideMagnifier}

View file

@ -6,9 +6,12 @@ import 'dart:math' as math;
import 'package:flutter/rendering.dart';
/// Positions the toolbar above [anchorAbove] if it fits, or otherwise below
/// A [SingleChildLayoutDelegate] for use with [CustomSingleChildLayout] that
/// positions its child above [anchorAbove] if it fits, or otherwise below
/// [anchorBelow].
///
/// Primarily intended for use with toolbars or context menus.
///
/// See also:
///
/// * [TextSelectionToolbar], which uses this to position itself.
@ -16,6 +19,8 @@ import 'package:flutter/rendering.dart';
/// itself.
class TextSelectionToolbarLayoutDelegate extends SingleChildLayoutDelegate {
/// Creates an instance of TextSelectionToolbarLayoutDelegate.
///
/// The [fitsAbove] parameter is optional; if omitted, it will be calculated.
TextSelectionToolbarLayoutDelegate({
required this.anchorAbove,
required this.anchorBelow,
@ -41,8 +46,8 @@ class TextSelectionToolbarLayoutDelegate extends SingleChildLayoutDelegate {
/// If not provided, it will be calculated.
final bool? fitsAbove;
/// Return the value that centers width as closely as possible to position
/// while fitting inside of min and max.
/// Return the distance from zero that centers `width` as closely as possible
/// to `position` from zero while fitting between zero and `max`.
static double centerOn(double position, double width, double max) {
// If it overflows on the left, put it as far left as possible.
if (position - width / 2.0 < 0.0) {