diff --git a/packages/flutter/lib/src/widgets/nested_scroll_view.dart b/packages/flutter/lib/src/widgets/nested_scroll_view.dart index 5324dbef4f..7a63d6bc90 100644 --- a/packages/flutter/lib/src/widgets/nested_scroll_view.dart +++ b/packages/flutter/lib/src/widgets/nested_scroll_view.dart @@ -795,8 +795,13 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont bool get hasScrolledBody { for (final _NestedScrollPosition position in _innerPositions) { - assert(position.hasContentDimensions && position.hasPixels); - if (position.pixels > position.minScrollExtent) { + if (!position.hasContentDimensions || !position.hasPixels) { + // It's possible that NestedScrollView built twice before layout phase + // in the same frame. This can happen when the FocusManager schedules a microTask + // that marks NestedScrollView dirty during the warm up frame. + // https://github.com/flutter/flutter/pull/75308 + continue; + } else if (position.pixels > position.minScrollExtent) { return true; } } diff --git a/packages/flutter/test/widgets/nested_scroll_view_test.dart b/packages/flutter/test/widgets/nested_scroll_view_test.dart index c55a307739..3bbdf86b1d 100644 --- a/packages/flutter/test/widgets/nested_scroll_view_test.dart +++ b/packages/flutter/test/widgets/nested_scroll_view_test.dart @@ -2303,6 +2303,49 @@ void main() { expect(lastUserScrollingDirection, ScrollDirection.forward); }); + + // Regression test for https://github.com/flutter/flutter/issues/72257 + testWidgets('NestedScrollView works well when rebuilding during scheduleWarmUpFrame', (WidgetTester tester) async { + bool? isScrolled; + final Widget myApp = MaterialApp( + home: Scaffold( + body: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return Focus( + onFocusChange: (_) => setState( (){} ), + child: NestedScrollView( + headerSliverBuilder: (BuildContext context, bool boxIsScrolled) { + isScrolled = boxIsScrolled; + return [ + const SliverAppBar( + expandedHeight: 200, + title: Text('Test'), + ) + ]; + }, + body: CustomScrollView( + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return const Text(''); + }, + childCount: 10, + ), + ) + ], + ), + ), + ); + }, + ), + ), + ); + + await tester.pumpWidget(myApp, Duration.zero, EnginePhase.build); + expect(isScrolled, false); + expect(tester.takeException(), isNull); + }); } class TestHeader extends SliverPersistentHeaderDelegate {