Unverified Commit b145a533 authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Remove Scrollbar.isAlwaysShown assert based on Scrollbar.controller (#72316)

parent 8db2d853
...@@ -561,10 +561,11 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { ...@@ -561,10 +561,11 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
/// visible. /// visible.
/// ///
/// By default, the thumb will fade in and out as the child scroll view /// By default, the thumb will fade in and out as the child scroll view
/// scrolls. When [isAlwaysShown] is true, and a [controller] is specified, the /// scrolls. When [isAlwaysShown] is true, the scrollbar thumb will remain
/// scrollbar thumb will remain visible without the fade animation. /// visible without the fade animation. This requires that a [ScrollController]
/// is provided to [controller], or that the [PrimaryScrollController] is available.
/// ///
/// Scrollbars are interactive and will use the [PrimaryScrollController] if /// Scrollbars are interactive and will also use the [PrimaryScrollController] if
/// a [controller] is not set. Scrollbar thumbs can be dragged along the main axis /// a [controller] is not set. Scrollbar thumbs can be dragged along the main axis
/// of the [ScrollView] to change the [ScrollPosition]. Tapping along the track /// of the [ScrollView] to change the [ScrollPosition]. Tapping along the track
/// exclusive of the thumb will trigger a [ScrollIncrementType.page] based on /// exclusive of the thumb will trigger a [ScrollIncrementType.page] based on
...@@ -606,10 +607,7 @@ class RawScrollbar extends StatefulWidget { ...@@ -606,10 +607,7 @@ class RawScrollbar extends StatefulWidget {
this.fadeDuration = _kScrollbarFadeDuration, this.fadeDuration = _kScrollbarFadeDuration,
this.timeToFade = _kScrollbarTimeToFade, this.timeToFade = _kScrollbarTimeToFade,
this.pressDuration = Duration.zero, this.pressDuration = Duration.zero,
}) : assert( }) : assert(isAlwaysShown != null),
!isAlwaysShown || controller != null,
'When isAlwaysShown is true, a ScrollController must be provided.',
),
assert(child != null), assert(child != null),
assert(fadeDuration != null), assert(fadeDuration != null),
assert(timeToFade != null), assert(timeToFade != null),
...@@ -681,8 +679,9 @@ class RawScrollbar extends StatefulWidget { ...@@ -681,8 +679,9 @@ class RawScrollbar extends StatefulWidget {
/// When false, the scrollbar will be shown during scrolling /// When false, the scrollbar will be shown during scrolling
/// and will fade out otherwise. /// and will fade out otherwise.
/// ///
/// When true, the scrollbar will always be visible and never fade out. The /// When true, the scrollbar will always be visible and never fade out. If the
/// [controller] property must be set in this case. /// [controller] property has not been set, the [PrimaryScrollController] will
/// be used.
/// ///
/// Defaults to false. /// Defaults to false.
/// ///
...@@ -826,7 +825,13 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv ...@@ -826,7 +825,13 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
// Wait one frame and cause an empty scroll event. This allows the // Wait one frame and cause an empty scroll event. This allows the
// thumb to show immediately when isAlwaysShown is true. A scroll // thumb to show immediately when isAlwaysShown is true. A scroll
// event is required in order to paint the thumb. // event is required in order to paint the thumb.
widget.controller!.position.didUpdateScrollPositionBy(0); final ScrollController? scrollController = widget.controller ?? PrimaryScrollController.of(context);
assert(
scrollController != null,
'A ScrollController is required when Scrollbar.isAlwaysShown is true. '
'Either Scrollbar.controller was not provided, or a PrimaryScrollController could not be found.',
);
scrollController!.position.didUpdateScrollPositionBy(0);
} }
}); });
} }
......
...@@ -263,16 +263,16 @@ void main() { ...@@ -263,16 +263,16 @@ void main() {
await tester.pump(_kScrollbarFadeDuration); await tester.pump(_kScrollbarFadeDuration);
}); });
testWidgets('When isAlwaysShown is true, must pass a controller', testWidgets('When isAlwaysShown is true, must pass a controller or find PrimaryScrollController',
(WidgetTester tester) async { (WidgetTester tester) async {
Widget viewWithScroll() { Widget viewWithScroll() {
return Directionality( return const Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: MediaQuery( child: MediaQuery(
data: const MediaQueryData(), data: MediaQueryData(),
child: CupertinoScrollbar( child: CupertinoScrollbar(
isAlwaysShown: true, isAlwaysShown: true,
child: const SingleChildScrollView( child: SingleChildScrollView(
child: SizedBox( child: SizedBox(
width: 4000.0, width: 4000.0,
height: 4000.0, height: 4000.0,
...@@ -283,12 +283,12 @@ void main() { ...@@ -283,12 +283,12 @@ void main() {
); );
} }
expect(() async { await tester.pumpWidget(viewWithScroll());
await tester.pumpWidget(viewWithScroll()); final dynamic exception = tester.takeException();
}, throwsAssertionError); expect(exception, isAssertionError);
}); });
testWidgets('When isAlwaysShown is true, must pass a controller that is attached to a scroll view', testWidgets('When isAlwaysShown is true, must pass a controller or find PrimarySCrollController that is attached to a scroll view',
(WidgetTester tester) async { (WidgetTester tester) async {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
Widget viewWithScroll() { Widget viewWithScroll() {
...@@ -315,6 +315,39 @@ void main() { ...@@ -315,6 +315,39 @@ void main() {
expect(exception, isAssertionError); expect(exception, isAssertionError);
}); });
testWidgets('On first render with isAlwaysShown: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
Widget viewWithScroll() {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: PrimaryScrollController(
controller: controller,
child: Builder(
builder: (BuildContext context) {
return const CupertinoScrollbar(
isAlwaysShown: true,
child: SingleChildScrollView(
primary: true,
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
);
},
),
),
),
);
}
await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle();
expect(find.byType(CupertinoScrollbar), paints..rect());
});
testWidgets('On first render with isAlwaysShown: true, the thumb shows', testWidgets('On first render with isAlwaysShown: true, the thumb shows',
(WidgetTester tester) async { (WidgetTester tester) async {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
......
...@@ -122,15 +122,15 @@ void main() { ...@@ -122,15 +122,15 @@ void main() {
expect(canvas.invocations.isEmpty, isTrue); expect(canvas.invocations.isEmpty, isTrue);
}); });
testWidgets('When isAlwaysShown is true, must pass a controller', testWidgets('When isAlwaysShown is true, must pass a controller or find PrimaryScrollController',
(WidgetTester tester) async { (WidgetTester tester) async {
Widget viewWithScroll() { Widget viewWithScroll() {
return _buildBoilerplate( return _buildBoilerplate(
child: Theme( child: Theme(
data: ThemeData(), data: ThemeData(),
child: Scrollbar( child: const Scrollbar(
isAlwaysShown: true, isAlwaysShown: true,
child: const SingleChildScrollView( child: SingleChildScrollView(
child: SizedBox( child: SizedBox(
width: 4000.0, width: 4000.0,
height: 4000.0, height: 4000.0,
...@@ -141,12 +141,12 @@ void main() { ...@@ -141,12 +141,12 @@ void main() {
); );
} }
expect(() async { await tester.pumpWidget(viewWithScroll());
await tester.pumpWidget(viewWithScroll()); final dynamic exception = tester.takeException();
}, throwsAssertionError); expect(exception, isAssertionError);
}); });
testWidgets('When isAlwaysShown is true, must pass a controller that is attached to a scroll view', testWidgets('When isAlwaysShown is true, must pass a controller that is attached to a scroll view or find PrimaryScrollController',
(WidgetTester tester) async { (WidgetTester tester) async {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
Widget viewWithScroll() { Widget viewWithScroll() {
...@@ -199,6 +199,38 @@ void main() { ...@@ -199,6 +199,38 @@ void main() {
expect(find.byType(Scrollbar), paints..rect()); expect(find.byType(Scrollbar), paints..rect());
}); });
testWidgets('On first render with isAlwaysShown: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
Widget viewWithScroll() {
return _buildBoilerplate(
child: Theme(
data: ThemeData(),
child: PrimaryScrollController(
controller: controller,
child: Builder(
builder: (BuildContext context) {
return const Scrollbar(
isAlwaysShown: true,
child: SingleChildScrollView(
primary: true,
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
);
},
),
),
),
);
}
await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle();
expect(find.byType(Scrollbar), paints..rect());
});
testWidgets('On first render with isAlwaysShown: false, the thumb is hidden', testWidgets('On first render with isAlwaysShown: false, the thumb is hidden',
(WidgetTester tester) async { (WidgetTester tester) async {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment