flutter 3.44.0

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-05-16 22:51:33 +08:00
parent ac0a34803f
commit e737a50804
71 changed files with 1362 additions and 535 deletions

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: uri_does_not_exist_in_doc_import
// ignore_for_file: prefer_initializing_formals, uri_does_not_exist_in_doc_import
/// @docImport 'package:flutter/cupertino.dart';
/// @docImport 'package:flutter/material.dart';
@@ -394,7 +394,7 @@ class _DiscreteKeyFrameSimulation extends Simulation {
/// ### Customizing User Input Accessibility Announcements
///
/// To customize user input accessibility announcements triggered by text
/// changes, use [SemanticsService.announce] to make the desired
/// changes, use [SemanticsService.sendAnnouncement] to make the desired
/// accessibility announcement.
///
/// On iOS, the on-screen keyboard may announce the most recent input
@@ -525,6 +525,7 @@ class EditableText extends StatefulWidget {
this.spellCheckConfiguration,
this.magnifierConfiguration = TextMagnifierConfiguration.disabled,
this.hintLocales,
this.enableInlinePrediction,
}) : assert(obscuringCharacter.length == 1),
autocorrect =
autocorrect ?? _inferAutocorrect(autofillHints: autofillHints),
@@ -1683,6 +1684,9 @@ class EditableText extends StatefulWidget {
/// {@macro flutter.services.TextInputConfiguration.hintLocales}
final List<Locale>? hintLocales;
/// {@macro flutter.services.TextInputConfiguration.enableInlinePrediction}
final bool? enableInlinePrediction;
/// The default value for [selectionHeightStyle].
///
/// On web platforms, this defaults to [ui.BoxHeightStyle.max].
@@ -2159,6 +2163,13 @@ class EditableText extends StatefulWidget {
defaultValue: true,
),
)
..add(
DiagnosticsProperty<bool?>(
'enableInlinePrediction',
enableInlinePrediction,
defaultValue: null,
),
)
..add(
DiagnosticsProperty<bool>(
'enableInteractiveSelection',
@@ -2494,7 +2505,9 @@ class EditableTextState extends State<EditableText>
final String text =
widget.controller.getSelectionText(selection) ??
selection.textInside(textEditingValue.text);
Clipboard.setData(ClipboardData(text: text));
Clipboard.setData(
ClipboardData(text: text),
).catchError(_reportClipboardError('while copying selection to clipboard'));
if (cause == SelectionChangedCause.toolbar) {
bringIntoView(textEditingValue.selection.extent);
hideToolbar(false);
@@ -2536,7 +2549,9 @@ class EditableTextState extends State<EditableText>
final String text =
widget.controller.getSelectionText(selection) ??
selection.textInside(textEditingValue.text);
Clipboard.setData(ClipboardData(text: text));
Clipboard.setData(
ClipboardData(text: text),
).catchError(_reportClipboardError('while cutting selection to clipboard'));
_replaceText(ReplaceTextIntent(textEditingValue, '', selection, cause));
if (cause == SelectionChangedCause.toolbar) {
// Schedule a call to bringIntoView() after renderEditable updates.
@@ -2550,6 +2565,19 @@ class EditableTextState extends State<EditableText>
clipboardStatus.update();
}
void Function(Object, StackTrace) _reportClipboardError(String context) {
return (Object exception, StackTrace stack) {
FlutterError.reportError(
FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: ErrorDescription(context),
),
);
};
}
bool get _allowPaste {
return !widget.readOnly && textEditingValue.selection.isValid;
}
@@ -2616,6 +2644,21 @@ class EditableTextState extends State<EditableText>
}
}
Future<void> _pasteTextWithReporting(SelectionChangedCause cause) async {
try {
await pasteText(cause);
} catch (error, stack) {
FlutterError.reportError(
FlutterErrorDetails(
exception: error,
stack: stack,
library: 'widgets',
context: ErrorDescription('while pasting text to EditableText'),
),
);
}
}
/// Select the entire text value.
@override
void selectAll(SelectionChangedCause cause) {
@@ -2723,7 +2766,19 @@ class EditableTextState extends State<EditableText>
return;
}
if (_hasInputConnection) {
LiveText.startLiveTextInput();
LiveText.startLiveTextInput().then(
(_) {},
onError: (Object error, StackTrace stack) {
FlutterError.reportError(
FlutterErrorDetails(
exception: error,
stack: stack,
library: 'widgets library',
context: ErrorDescription('while starting Live Text input'),
),
);
},
);
}
if (cause == SelectionChangedCause.toolbar) {
hideToolbar();
@@ -2846,8 +2901,8 @@ class EditableTextState extends State<EditableText>
),
if (toolbarOptions.paste && pasteEnabled)
ContextMenuButtonItem(
onPressed: () {
pasteText(SelectionChangedCause.toolbar);
onPressed: () async {
await _pasteTextWithReporting(SelectionChangedCause.toolbar);
},
type: ContextMenuButtonType.paste,
),
@@ -2972,7 +3027,7 @@ class EditableTextState extends State<EditableText>
? () => cutSelection(SelectionChangedCause.toolbar)
: null,
onPaste: pasteEnabled
? () => pasteText(SelectionChangedCause.toolbar)
? () => _pasteTextWithReporting(SelectionChangedCause.toolbar)
: null,
onSelectAll: selectAllEnabled
? () => selectAll(SelectionChangedCause.toolbar)
@@ -3118,6 +3173,14 @@ class EditableTextState extends State<EditableText>
_effectiveAutofillClient.textInputConfiguration,
);
}
// The style may have changed due to dependency changes
// (e.g. MediaQuery.boldTextOf, MediaQuery.textScalerOf, etc.).
SchedulerBinding.instance.addPostFrameCallback((Duration _) {
if (!mounted || !_hasInputConnection) {
return;
}
_textInputConnection!.updateStyle(_getTextInputStyle(context));
}, debugLabel: 'EditableText.updateStyle');
}
if (defaultTargetPlatform != TargetPlatform.iOS &&
@@ -3217,7 +3280,13 @@ class EditableTextState extends State<EditableText>
}
if (kIsWeb && _hasInputConnection) {
if (oldWidget.readOnly != widget.readOnly) {
final obscureTextChanged = oldWidget.obscureText != widget.obscureText;
if (obscureTextChanged || oldWidget.keyboardType != widget.keyboardType) {
if (obscureTextChanged) {
// When obscureText is toggled, we should reset its state to prevent the last character from being visible between state changes.
_obscureShowCharTicksPending = 0;
_obscureLatestCharIndex = null;
}
_textInputConnection!.updateConfig(
_effectiveAutofillClient.textInputConfiguration,
);
@@ -3240,13 +3309,14 @@ class EditableTextState extends State<EditableText>
? widget.style.merge(const TextStyle(fontWeight: FontWeight.bold))
: widget.style;
if (_hasInputConnection) {
_textInputConnection!.setStyle(
fontFamily: _style.fontFamily,
fontSize: _style.fontSize,
fontWeight: _style.fontWeight,
textDirection: _textDirection,
textAlign: widget.textAlign,
);
// Schedule the style update after layout to ensure preferredLineHeight
// is computed with the new style.
SchedulerBinding.instance.addPostFrameCallback((Duration _) {
if (!mounted || !_hasInputConnection) {
return;
}
_textInputConnection!.updateStyle(_getTextInputStyle(context));
}, debugLabel: 'EditableText.updateStyle');
}
}
@@ -3272,6 +3342,27 @@ class EditableTextState extends State<EditableText>
}
}
TextInputStyle _getTextInputStyle(BuildContext context) {
final double? letterSpacingOverride =
MediaQuery.maybeLetterSpacingOverrideOf(context);
final double? wordSpacingOverride = MediaQuery.maybeWordSpacingOverrideOf(
context,
);
return TextInputStyle(
fontFamily: _style.fontFamily,
fontSize: _style.fontSize,
fontWeight: _style.fontWeight,
textDirection: _textDirection,
textAlign: widget.textAlign,
letterSpacing: letterSpacingOverride ?? _style.letterSpacing,
wordSpacing: wordSpacingOverride ?? _style.wordSpacing,
// preferredLineHeight already includes lineHeightScaleFactor from
// _OverridingTextStyleTextSpanUtils.applyTextSpacingOverrides.
lineHeight: renderEditable.preferredLineHeight,
);
}
@protected
@override
void dispose() {
@@ -3902,13 +3993,7 @@ class EditableTextState extends State<EditableText>
_updateSizeAndTransform();
_schedulePeriodicPostFrameCallbacks();
_textInputConnection!
..setStyle(
fontFamily: _style.fontFamily,
fontSize: _style.fontSize,
fontWeight: _style.fontWeight,
textDirection: _textDirection,
textAlign: widget.textAlign,
)
..updateStyle(_getTextInputStyle(context))
..setEditingState(localValue)
..show();
if (_needsAutofill) {
@@ -3974,13 +4059,7 @@ class EditableTextState extends State<EditableText>
newConnection
..show()
..setStyle(
fontFamily: _style.fontFamily,
fontSize: _style.fontSize,
fontWeight: _style.fontWeight,
textDirection: _textDirection,
textAlign: widget.textAlign,
)
..updateStyle(_getTextInputStyle(context))
..setEditingState(_value);
_lastKnownRemoteTextEditingValue = _value;
}
@@ -3996,6 +4075,15 @@ class EditableTextState extends State<EditableText>
}
}
@override
bool onFocusReceived() {
if (mounted && !_hasFocus && widget.focusNode.canRequestFocus) {
widget.focusNode.requestFocus();
return true;
}
return false;
}
@override
void connectionClosed() {
if (_hasInputConnection) {
@@ -5248,6 +5336,7 @@ class EditableTextState extends State<EditableText>
? const <String>[]
: widget.contentInsertionConfiguration!.allowedMimeTypes,
hintLocales: widget.hintLocales,
enableInlinePrediction: widget.enableInlinePrediction,
);
}
@@ -5300,9 +5389,9 @@ class EditableTextState extends State<EditableText>
: pasteEnabled &&
(widget.selectionControls?.canPaste(this) ?? false)) &&
(clipboardStatus.value == ClipboardStatus.pasteable)
? () {
controls?.handlePaste(this);
pasteText(SelectionChangedCause.toolbar);
? () async {
await controls?.handlePaste(this);
await _pasteTextWithReporting(SelectionChangedCause.toolbar);
}
: null;
}
@@ -5529,70 +5618,6 @@ class EditableTextState extends State<EditableText>
_scrollController.jumpTo(destination);
}
/// Extend the selection down by page if the `forward` parameter is true, or
/// up by page otherwise.
void _extendSelectionByPage(ExtendSelectionByPageIntent intent) {
if (widget.maxLines == 1) {
return;
}
final TextSelection nextSelection;
final Rect extentRect = renderEditable.getLocalRectForCaret(
_value.selection.extent,
);
final state = _scrollableKey.currentState as ScrollableState?;
final double increment = ScrollAction.getDirectionalIncrement(
state!,
ScrollIntent(
direction: intent.forward ? AxisDirection.down : AxisDirection.up,
type: ScrollIncrementType.page,
),
);
final ScrollPosition position = _scrollController.position;
if (intent.forward) {
if (_value.selection.extentOffset >= _value.text.length) {
return;
}
final nextExtentOffset = Offset(
extentRect.left,
extentRect.top + increment,
);
final double height =
position.maxScrollExtent + renderEditable.size.height;
final TextPosition nextExtent =
nextExtentOffset.dy + position.pixels >= height
? TextPosition(offset: _value.text.length)
: renderEditable.getPositionForPoint(
renderEditable.localToGlobal(nextExtentOffset),
);
nextSelection = _value.selection.copyWith(
extentOffset: nextExtent.offset,
);
} else {
if (_value.selection.extentOffset <= 0) {
return;
}
final nextExtentOffset = Offset(
extentRect.left,
extentRect.top + increment,
);
final TextPosition nextExtent = nextExtentOffset.dy + position.pixels <= 0
? const TextPosition(offset: 0)
: renderEditable.getPositionForPoint(
renderEditable.localToGlobal(nextExtentOffset),
);
nextSelection = _value.selection.copyWith(
extentOffset: nextExtent.offset,
);
}
bringIntoView(nextSelection.extent);
userUpdateTextEditingValue(
_value.copyWith(selection: nextSelection),
SelectionChangedCause.keyboard,
);
}
void _updateSelection(UpdateSelectionIntent intent) {
assert(
intent.newSelection.start <= intent.currentTextEditingValue.text.length,
@@ -5730,11 +5755,6 @@ class EditableTextState extends State<EditableText>
ignoreNonCollapsedSelection: false,
),
),
ExtendSelectionByPageIntent: _makeOverridable(
CallbackAction<ExtendSelectionByPageIntent>(
onInvoke: _extendSelectionByPage,
),
),
ExtendSelectionToNextWordBoundaryIntent: _makeOverridable(
_UpdateTextSelectionAction<ExtendSelectionToNextWordBoundaryIntent>(
this,
@@ -6953,7 +6973,7 @@ class _PasteSelectionAction extends ContextAction<PasteTextIntent> {
return;
}
state.pasteText(intent.cause);
state._pasteTextWithReporting(intent.cause);
}
}