Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 6eb002a

Browse files
authored
Reland "Remove single-view assumption from widgets library (#117480)" (#117549)
* Revert "Revert "Remove single-view assumption from widgets library (#117480)" (#117545)" This reverts commit b8d5d9c. * check for mounted
1 parent 999356b commit 6eb002a

File tree

5 files changed

+40
-8
lines changed

5 files changed

+40
-8
lines changed

packages/flutter/lib/src/widgets/editable_text.dart

+9-4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import 'text_editing_intents.dart';
4343
import 'text_selection.dart';
4444
import 'text_selection_toolbar_anchors.dart';
4545
import 'ticker_provider.dart';
46+
import 'view.dart';
4647
import 'widget_span.dart';
4748

4849
export 'package:flutter/services.dart' show SelectionChangedCause, SmartDashesType, SmartQuotesType, TextEditingValue, TextInputType, TextSelection;
@@ -3303,17 +3304,21 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
33033304

33043305
@override
33053306
void didChangeMetrics() {
3306-
if (_lastBottomViewInset != WidgetsBinding.instance.window.viewInsets.bottom) {
3307+
if (!mounted) {
3308+
return;
3309+
}
3310+
final ui.FlutterView view = View.of(context);
3311+
if (_lastBottomViewInset != view.viewInsets.bottom) {
33073312
SchedulerBinding.instance.addPostFrameCallback((Duration _) {
33083313
_selectionOverlay?.updateForScroll();
33093314
});
3310-
if (_lastBottomViewInset < WidgetsBinding.instance.window.viewInsets.bottom) {
3315+
if (_lastBottomViewInset < view.viewInsets.bottom) {
33113316
// Because the metrics change signal from engine will come here every frame
33123317
// (on both iOS and Android). So we don't need to show caret with animation.
33133318
_scheduleShowCaretOnScreen(withAnimation: false);
33143319
}
33153320
}
3316-
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
3321+
_lastBottomViewInset = view.viewInsets.bottom;
33173322
}
33183323

33193324
Future<void> _performSpellCheck(final String text) async {
@@ -3516,7 +3521,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
35163521
if (_hasFocus) {
35173522
// Listen for changing viewInsets, which indicates keyboard showing up.
35183523
WidgetsBinding.instance.addObserver(this);
3519-
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
3524+
_lastBottomViewInset = View.of(context).viewInsets.bottom;
35203525
if (!widget.readOnly) {
35213526
_scheduleShowCaretOnScreen(withAnimation: true);
35223527
}

packages/flutter/lib/src/widgets/scroll_physics.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'framework.dart';
1414
import 'overscroll_indicator.dart';
1515
import 'scroll_metrics.dart';
1616
import 'scroll_simulation.dart';
17+
import 'view.dart';
1718

1819
export 'package:flutter/physics.dart' show ScrollSpringSimulation, Simulation, Tolerance;
1920

@@ -252,7 +253,7 @@ class ScrollPhysics {
252253
assert(metrics != null);
253254
assert(context != null);
254255
if (parent == null) {
255-
final double maxPhysicalPixels = WidgetsBinding.instance.window.physicalSize.longestSide;
256+
final double maxPhysicalPixels = View.of(context).physicalSize.longestSide;
256257
return velocity.abs() > maxPhysicalPixels;
257258
}
258259
return parent!.recommendDeferredLoading(velocity, metrics, context);

packages/flutter/lib/src/widgets/semantics_debugger.dart

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'basic.dart';
1212
import 'binding.dart';
1313
import 'framework.dart';
1414
import 'gesture_detector.dart';
15+
import 'view.dart';
1516

1617
/// A widget that visualizes the semantics for the child.
1718
///
@@ -96,7 +97,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
9697
Offset? _lastPointerDownLocation;
9798
void _handlePointerDown(PointerDownEvent event) {
9899
setState(() {
99-
_lastPointerDownLocation = event.position * WidgetsBinding.instance.window.devicePixelRatio;
100+
_lastPointerDownLocation = event.position * View.of(context).devicePixelRatio;
100101
});
101102
// TODO(ianh): Use a gesture recognizer so that we can reset the
102103
// _lastPointerDownLocation when none of the other gesture recognizers win.
@@ -159,7 +160,7 @@ class _SemanticsDebuggerState extends State<SemanticsDebugger> with WidgetsBindi
159160
_pipelineOwner,
160161
_client.generation,
161162
_lastPointerDownLocation, // in physical pixels
162-
WidgetsBinding.instance.window.devicePixelRatio,
163+
View.of(context).devicePixelRatio,
163164
widget.labelStyle,
164165
),
165166
child: GestureDetector(

packages/flutter/lib/src/widgets/widget_inspector.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'dart:math' as math;
1010
import 'dart:ui' as ui
1111
show
1212
ClipOp,
13+
FlutterView,
1314
Image,
1415
ImageByteFormat,
1516
Paragraph,
@@ -30,6 +31,7 @@ import 'debug.dart';
3031
import 'framework.dart';
3132
import 'gesture_detector.dart';
3233
import 'service_extensions.dart';
34+
import 'view.dart';
3335

3436
/// Signature for the builder callback used by
3537
/// [WidgetInspector.selectButtonBuilder].
@@ -2726,7 +2728,8 @@ class _WidgetInspectorState extends State<WidgetInspector>
27262728
// on the edge of the display. If the pointer is being dragged off the edge
27272729
// of the display we do not want to select anything. A user can still select
27282730
// a widget that is only at the exact screen margin by tapping.
2729-
final Rect bounds = (Offset.zero & (WidgetsBinding.instance.window.physicalSize / WidgetsBinding.instance.window.devicePixelRatio)).deflate(_kOffScreenMargin);
2731+
final ui.FlutterView view = View.of(context);
2732+
final Rect bounds = (Offset.zero & (view.physicalSize / view.devicePixelRatio)).deflate(_kOffScreenMargin);
27302733
if (!bounds.contains(_lastPointerLocation!)) {
27312734
setState(() {
27322735
selection.clear();

packages/flutter/test/widgets/editable_text_test.dart

+22
Original file line numberDiff line numberDiff line change
@@ -14398,6 +14398,28 @@ testWidgets('Floating cursor ending with selection', (WidgetTester tester) async
1439814398

1439914399
expect(tester.takeException(), null);
1440014400
});
14401+
14402+
testWidgets('does not crash when didChangeMetrics is called after unmounting', (WidgetTester tester) async {
14403+
await tester.pumpWidget(
14404+
MaterialApp(
14405+
home: EditableText(
14406+
controller: controller,
14407+
focusNode: FocusNode(),
14408+
style: Typography.material2018().black.titleMedium!,
14409+
cursorColor: Colors.blue,
14410+
backgroundCursorColor: Colors.grey,
14411+
),
14412+
),
14413+
);
14414+
14415+
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
14416+
14417+
// Disposes the EditableText.
14418+
await tester.pumpWidget(const Placeholder());
14419+
14420+
// Shouldn't crash.
14421+
state.didChangeMetrics();
14422+
});
1440114423
}
1440214424

1440314425
class UnsettableController extends TextEditingController {

0 commit comments

Comments
 (0)