mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Fix: Assertion failure in RawScrollbarState with dynamic controller assignment (#173156)
Fix: Assertion failure in RawScrollbarState with dynamic controller assignment fixes: #172614 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
This commit is contained in:
parent
e0bbb2ff05
commit
433e543260
@ -1456,6 +1456,14 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
||||
} else if (_effectiveScrollController != null && enableGestures) {
|
||||
// Interactive scrollbars need to be properly configured. If it is visible
|
||||
// for interaction, ensure we are set up properly.
|
||||
// Don't assert immediately if we're in the middle of updating the widget
|
||||
// as the controller may not be attached yet in that frame.
|
||||
if (_fadeoutAnimationController.status == AnimationStatus.forward &&
|
||||
(widget.thumbVisibility ?? false)) {
|
||||
// When thumbVisibility is true and we're animating forward,
|
||||
// the check is already scheduled by _debugScheduleCheckHasValidScrollPosition.
|
||||
return;
|
||||
}
|
||||
assert(_debugCheckHasValidScrollPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3635,4 +3635,79 @@ The provided ScrollController cannot be shared by multiple ScrollView widgets.''
|
||||
expect(size.height - rtlRect1.bottom, 4);
|
||||
expect(rtlRect1.left, 3);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'RawScrollbar does not assert when dynamically assigning controller with thumbVisibility true',
|
||||
(WidgetTester tester) async {
|
||||
// This test reproduces the issue from https://github.com/flutter/flutter/issues/172614
|
||||
// where dynamically assigning a controller and setting thumbVisibility to true
|
||||
// in the same frame caused an assertion failure.
|
||||
|
||||
final ScrollController scrollController = ScrollController();
|
||||
addTearDown(scrollController.dispose);
|
||||
|
||||
bool useController = false;
|
||||
bool thumbVisible = false;
|
||||
bool interactive = false;
|
||||
|
||||
await tester.pumpWidget(
|
||||
StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: MediaQuery(
|
||||
data: const MediaQueryData(),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
// This reproduces the exact scenario from the linked issue:
|
||||
// Initially the controller is null, thumbVisibility is false, and interactive is false.
|
||||
// Then, in the next frame, the controller is non-null, thumbVisibility is true, and interactive is true.
|
||||
useController = true;
|
||||
thumbVisible = true;
|
||||
interactive = true;
|
||||
});
|
||||
},
|
||||
child: const Text('Enable Scrollbar'),
|
||||
),
|
||||
Expanded(
|
||||
child: RawScrollbar(
|
||||
controller: useController ? scrollController : null,
|
||||
thumbVisibility: thumbVisible,
|
||||
interactive: interactive,
|
||||
child: SingleChildScrollView(
|
||||
controller: useController ? scrollController : null,
|
||||
child: const SizedBox(height: 2000.0, width: 100.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawScrollbar), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Enable Scrollbar'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify scrollbar is now working properly and doesn't throw an assertion.
|
||||
final Finder scrollbarFinder = find.byType(RawScrollbar);
|
||||
expect(scrollbarFinder, findsOneWidget);
|
||||
expect(scrollController.offset, 0.0);
|
||||
|
||||
// Test that scrolling works without assertions.
|
||||
await tester.drag(find.byType(SingleChildScrollView), const Offset(0.0, -100.0));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify the scroll happened.
|
||||
expect(scrollController.offset, greaterThan(0.0));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user