Make disposing a ScrollPosition with pixels=null legal (#44617)

diff --git a/packages/flutter/lib/src/widgets/page_view.dart b/packages/flutter/lib/src/widgets/page_view.dart
index 1e7ab53..9580501 100644
--- a/packages/flutter/lib/src/widgets/page_view.dart
+++ b/packages/flutter/lib/src/widgets/page_view.dart
@@ -333,29 +333,6 @@
   final int initialPage;
   double _pageToUseOnStartup;
 
-  /// If [pixels] isn't set by [applyViewportDimension] before [dispose] is
-  /// called, this could throw an assert as [pixels] will be set to null.
-  ///
-  /// With [Tab]s, this happens when there are nested [TabBarView]s and there
-  /// is an attempt to warp over the nested tab to a tab adjacent to it.
-  ///
-  /// This flag will be set to true once the dimensions have been established
-  /// and [pixels] is set.
-  bool isInitialPixelsValueSet = false;
-
-  @override
-  void dispose() {
-    // TODO(shihaohong): remove workaround once these issues have been
-    // resolved, https://github.com/flutter/flutter/issues/32054,
-    // https://github.com/flutter/flutter/issues/32056
-    // Sets `pixels` to a non-null value before `ScrollPosition.dispose` is
-    // invoked if it was never set by `applyViewportDimension`.
-    if (pixels == null && !isInitialPixelsValueSet) {
-      correctPixels(0);
-    }
-    super.dispose();
-  }
-
   @override
   double get viewportFraction => _viewportFraction;
   double _viewportFraction;
@@ -422,7 +399,6 @@
 
     if (newPixels != oldPixels) {
       correctPixels(newPixels);
-      isInitialPixelsValueSet = true;
       return false;
     }
     return result;
diff --git a/packages/flutter/lib/src/widgets/scroll_position.dart b/packages/flutter/lib/src/widgets/scroll_position.dart
index f1ade32..a661a33 100644
--- a/packages/flutter/lib/src/widgets/scroll_position.dart
+++ b/packages/flutter/lib/src/widgets/scroll_position.dart
@@ -693,7 +693,6 @@
 
   @override
   void dispose() {
-    assert(pixels != null);
     activity?.dispose(); // it will be null if it got absorbed by another ScrollPosition
     _activity = null;
     super.dispose();
diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart
index f43a22c..8c2ceef 100644
--- a/packages/flutter/test/material/tabs_test.dart
+++ b/packages/flutter/test/material/tabs_test.dart
@@ -978,8 +978,7 @@
     expect(tabController.index, 0);
   });
 
-  testWidgets('Nested TabBarView sets ScrollController pixels to non-null value '
-  'when disposed before it is set by the applyViewportDimension', (WidgetTester tester) async {
+  testWidgets('Can switch to non-neighboring tab in nested TabBarView without crashing', (WidgetTester tester) async {
     // This is a regression test for https://github.com/flutter/flutter/issues/18756
     final TabController _mainTabController = TabController(length: 4, vsync: const TestVSync());
     final TabController _nestedTabController = TabController(length: 2, vsync: const TestVSync());
diff --git a/packages/flutter/test/widgets/scrollable_in_overlay_test.dart b/packages/flutter/test/widgets/scrollable_in_overlay_test.dart
new file mode 100644
index 0000000..41e6bad
--- /dev/null
+++ b/packages/flutter/test/widgets/scrollable_in_overlay_test.dart
@@ -0,0 +1,64 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter/material.dart';
+
+void main() {
+  test('Can dispose ScrollPosition when pixels is null', () {
+    final ScrollPosition position = ScrollPositionWithSingleContext(
+      initialPixels: null,
+      keepScrollOffset: false,
+      physics: const AlwaysScrollableScrollPhysics(),
+      context: ScrollableState(),
+    );
+
+    expect(position.pixels, isNull);
+    position.dispose(); // Should not throw/assert.
+  });
+
+  testWidgets('scrollable in hidden overlay does not crash when unhidden', (WidgetTester tester) async {
+    // Regression test for https://github.com/flutter/flutter/issues/44269.
+    final TabController controller = TabController(vsync: const TestVSync(), length: 1);
+
+    final OverlayEntry entry1 = OverlayEntry(
+      maintainState: true,
+      opaque: true,
+      builder: (BuildContext context) {
+        return TabBar(
+          isScrollable: true,
+          controller: controller,
+          tabs: const <Tab>[
+            Tab(text: 'Main'),
+          ],
+        );
+      },
+    );
+    final OverlayEntry entry2 = OverlayEntry(
+      maintainState: true,
+      opaque: true,
+      builder: (BuildContext context) {
+        return const Text('number2');
+      },
+    );
+
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Material(
+          child: Overlay(
+            initialEntries: <OverlayEntry>[
+              entry1,
+              entry2,
+            ],
+          ),
+        ),
+      ),
+    );
+
+    entry2.remove();
+    await tester.pump();
+    expect(tester.takeException(), isNull);
+  });
+}