* Revert "Revert "Remove single-view assumption from widgets library (#117480)" (#117545)" This reverts commit b8d5d9c46ca084a707caa1e994d896cb977662d2. * check for mounted
This commit is contained in:
parent
999356b776
commit
6eb002a167
@ -43,6 +43,7 @@ import 'text_editing_intents.dart';
|
|||||||
import 'text_selection.dart';
|
import 'text_selection.dart';
|
||||||
import 'text_selection_toolbar_anchors.dart';
|
import 'text_selection_toolbar_anchors.dart';
|
||||||
import 'ticker_provider.dart';
|
import 'ticker_provider.dart';
|
||||||
|
import 'view.dart';
|
||||||
import 'widget_span.dart';
|
import 'widget_span.dart';
|
||||||
|
|
||||||
export 'package:flutter/services.dart' show SelectionChangedCause, SmartDashesType, SmartQuotesType, TextEditingValue, TextInputType, TextSelection;
|
export 'package:flutter/services.dart' show SelectionChangedCause, SmartDashesType, SmartQuotesType, TextEditingValue, TextInputType, TextSelection;
|
||||||
@ -3303,17 +3304,21 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeMetrics() {
|
void didChangeMetrics() {
|
||||||
if (_lastBottomViewInset != WidgetsBinding.instance.window.viewInsets.bottom) {
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ui.FlutterView view = View.of(context);
|
||||||
|
if (_lastBottomViewInset != view.viewInsets.bottom) {
|
||||||
SchedulerBinding.instance.addPostFrameCallback((Duration _) {
|
SchedulerBinding.instance.addPostFrameCallback((Duration _) {
|
||||||
_selectionOverlay?.updateForScroll();
|
_selectionOverlay?.updateForScroll();
|
||||||
});
|
});
|
||||||
if (_lastBottomViewInset < WidgetsBinding.instance.window.viewInsets.bottom) {
|
if (_lastBottomViewInset < view.viewInsets.bottom) {
|
||||||
// Because the metrics change signal from engine will come here every frame
|
// Because the metrics change signal from engine will come here every frame
|
||||||
// (on both iOS and Android). So we don't need to show caret with animation.
|
// (on both iOS and Android). So we don't need to show caret with animation.
|
||||||
_scheduleShowCaretOnScreen(withAnimation: false);
|
_scheduleShowCaretOnScreen(withAnimation: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
|
_lastBottomViewInset = view.viewInsets.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _performSpellCheck(final String text) async {
|
Future<void> _performSpellCheck(final String text) async {
|
||||||
@ -3516,7 +3521,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||||||
if (_hasFocus) {
|
if (_hasFocus) {
|
||||||
// Listen for changing viewInsets, which indicates keyboard showing up.
|
// Listen for changing viewInsets, which indicates keyboard showing up.
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
|
_lastBottomViewInset = View.of(context).viewInsets.bottom;
|
||||||
if (!widget.readOnly) {
|
if (!widget.readOnly) {
|
||||||
_scheduleShowCaretOnScreen(withAnimation: true);
|
_scheduleShowCaretOnScreen(withAnimation: true);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import 'framework.dart';
|
|||||||
import 'overscroll_indicator.dart';
|
import 'overscroll_indicator.dart';
|
||||||
import 'scroll_metrics.dart';
|
import 'scroll_metrics.dart';
|
||||||
import 'scroll_simulation.dart';
|
import 'scroll_simulation.dart';
|
||||||
|
import 'view.dart';
|
||||||
|
|
||||||
export 'package:flutter/physics.dart' show ScrollSpringSimulation, Simulation, Tolerance;
|
export 'package:flutter/physics.dart' show ScrollSpringSimulation, Simulation, Tolerance;
|
||||||
|
|
||||||
@ -252,7 +253,7 @@ class ScrollPhysics {
|
|||||||
assert(metrics != null);
|
assert(metrics != null);
|
||||||
assert(context != null);
|
assert(context != null);
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
final double maxPhysicalPixels = WidgetsBinding.instance.window.physicalSize.longestSide;
|
final double maxPhysicalPixels = View.of(context).physicalSize.longestSide;
|
||||||
return velocity.abs() > maxPhysicalPixels;
|
return velocity.abs() > maxPhysicalPixels;
|
||||||
}
|
}
|
||||||
return parent!.recommendDeferredLoading(velocity, metrics, context);
|
return parent!.recommendDeferredLoading(velocity, metrics, context);
|
||||||
|
@ -12,6 +12,7 @@ import 'basic.dart';
|
|||||||
import 'binding.dart';
|
import 'binding.dart';
|
||||||
import 'framework.dart';
|
import 'framework.dart';
|
||||||
import 'gesture_detector.dart';
|
import 'gesture_detector.dart';
|
||||||
|
import 'view.dart';
|
||||||
|
|
||||||
/// A widget that visualizes the semantics for the child.
|
/// A widget that visualizes the semantics for the child.
|
||||||
///
|
///
|
||||||
@ -96,7 +97,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
|
|||||||
Offset? _lastPointerDownLocation;
|
Offset? _lastPointerDownLocation;
|
||||||
void _handlePointerDown(PointerDownEvent event) {
|
void _handlePointerDown(PointerDownEvent event) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_lastPointerDownLocation = event.position * WidgetsBinding.instance.window.devicePixelRatio;
|
_lastPointerDownLocation = event.position * View.of(context).devicePixelRatio;
|
||||||
});
|
});
|
||||||
// TODO(ianh): Use a gesture recognizer so that we can reset the
|
// TODO(ianh): Use a gesture recognizer so that we can reset the
|
||||||
// _lastPointerDownLocation when none of the other gesture recognizers win.
|
// _lastPointerDownLocation when none of the other gesture recognizers win.
|
||||||
@ -159,7 +160,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
|
|||||||
_pipelineOwner,
|
_pipelineOwner,
|
||||||
_client.generation,
|
_client.generation,
|
||||||
_lastPointerDownLocation, // in physical pixels
|
_lastPointerDownLocation, // in physical pixels
|
||||||
WidgetsBinding.instance.window.devicePixelRatio,
|
View.of(context).devicePixelRatio,
|
||||||
widget.labelStyle,
|
widget.labelStyle,
|
||||||
),
|
),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
|
@ -10,6 +10,7 @@ import 'dart:math' as math;
|
|||||||
import 'dart:ui' as ui
|
import 'dart:ui' as ui
|
||||||
show
|
show
|
||||||
ClipOp,
|
ClipOp,
|
||||||
|
FlutterView,
|
||||||
Image,
|
Image,
|
||||||
ImageByteFormat,
|
ImageByteFormat,
|
||||||
Paragraph,
|
Paragraph,
|
||||||
@ -30,6 +31,7 @@ import 'debug.dart';
|
|||||||
import 'framework.dart';
|
import 'framework.dart';
|
||||||
import 'gesture_detector.dart';
|
import 'gesture_detector.dart';
|
||||||
import 'service_extensions.dart';
|
import 'service_extensions.dart';
|
||||||
|
import 'view.dart';
|
||||||
|
|
||||||
/// Signature for the builder callback used by
|
/// Signature for the builder callback used by
|
||||||
/// [WidgetInspector.selectButtonBuilder].
|
/// [WidgetInspector.selectButtonBuilder].
|
||||||
@ -2726,7 +2728,8 @@ class _WidgetInspectorState extends State<WidgetInspector>
|
|||||||
// on the edge of the display. If the pointer is being dragged off the edge
|
// on the edge of the display. If the pointer is being dragged off the edge
|
||||||
// of the display we do not want to select anything. A user can still select
|
// of the display we do not want to select anything. A user can still select
|
||||||
// a widget that is only at the exact screen margin by tapping.
|
// a widget that is only at the exact screen margin by tapping.
|
||||||
final Rect bounds = (Offset.zero & (WidgetsBinding.instance.window.physicalSize / WidgetsBinding.instance.window.devicePixelRatio)).deflate(_kOffScreenMargin);
|
final ui.FlutterView view = View.of(context);
|
||||||
|
final Rect bounds = (Offset.zero & (view.physicalSize / view.devicePixelRatio)).deflate(_kOffScreenMargin);
|
||||||
if (!bounds.contains(_lastPointerLocation!)) {
|
if (!bounds.contains(_lastPointerLocation!)) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selection.clear();
|
selection.clear();
|
||||||
|
@ -14398,6 +14398,28 @@ testWidgets('Floating cursor ending with selection', (WidgetTester tester) async
|
|||||||
|
|
||||||
expect(tester.takeException(), null);
|
expect(tester.takeException(), null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('does not crash when didChangeMetrics is called after unmounting', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: EditableText(
|
||||||
|
controller: controller,
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
style: Typography.material2018().black.titleMedium!,
|
||||||
|
cursorColor: Colors.blue,
|
||||||
|
backgroundCursorColor: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||||
|
|
||||||
|
// Disposes the EditableText.
|
||||||
|
await tester.pumpWidget(const Placeholder());
|
||||||
|
|
||||||
|
// Shouldn't crash.
|
||||||
|
state.didChangeMetrics();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnsettableController extends TextEditingController {
|
class UnsettableController extends TextEditingController {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user