From 7d17c53992932fad100f03e24030fc5a53c03aaa Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Fri, 29 May 2020 15:48:01 -0700 Subject: [PATCH] Helpful assertion for isAlwaysShown error (#58258) --- .../flutter/lib/src/cupertino/scrollbar.dart | 4 +- .../flutter/lib/src/material/scrollbar.dart | 4 +- .../test/cupertino/scrollbar_test.dart | 52 +++++++++++++++++++ .../flutter/test/material/scrollbar_test.dart | 50 ++++++++++++++++++ 4 files changed, 106 insertions(+), 4 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/scrollbar.dart b/packages/flutter/lib/src/cupertino/scrollbar.dart index 4ae7b948595..a79f7d9b406 100644 --- a/packages/flutter/lib/src/cupertino/scrollbar.dart +++ b/packages/flutter/lib/src/cupertino/scrollbar.dart @@ -62,7 +62,8 @@ class CupertinoScrollbar extends StatefulWidget { this.controller, this.isAlwaysShown = false, @required this.child, - }) : super(key: key); + }) : assert(!isAlwaysShown || controller != null, 'When isAlwaysShown is true, must pass a controller that is attached to a scroll view'), + super(key: key); /// The subtree to place inside the [CupertinoScrollbar]. /// @@ -276,7 +277,6 @@ class _CupertinoScrollbarState extends State with TickerProv void _triggerScrollbar() { WidgetsBinding.instance.addPostFrameCallback((Duration duration) { if (widget.isAlwaysShown) { - assert(widget.controller != null); _fadeoutTimer?.cancel(); widget.controller.position.didUpdateScrollPositionBy(0); } diff --git a/packages/flutter/lib/src/material/scrollbar.dart b/packages/flutter/lib/src/material/scrollbar.dart index e5d0c971766..4e89bb82769 100644 --- a/packages/flutter/lib/src/material/scrollbar.dart +++ b/packages/flutter/lib/src/material/scrollbar.dart @@ -38,7 +38,8 @@ class Scrollbar extends StatefulWidget { @required this.child, this.controller, this.isAlwaysShown = false, - }) : super(key: key); + }) : assert(!isAlwaysShown || controller != null, 'When isAlwaysShown is true, must pass a controller that is attached to a scroll view'), + super(key: key); /// The widget below this widget in the tree. /// @@ -131,7 +132,6 @@ class _ScrollbarState extends State with TickerProviderStateMixin { void _triggerScrollbar() { WidgetsBinding.instance.addPostFrameCallback((Duration duration) { if (widget.isAlwaysShown) { - assert(widget.controller != null); _fadeoutTimer?.cancel(); widget.controller.position.didUpdateScrollPositionBy(0); } diff --git a/packages/flutter/test/cupertino/scrollbar_test.dart b/packages/flutter/test/cupertino/scrollbar_test.dart index 4ca2647f481..2815b5e0dde 100644 --- a/packages/flutter/test/cupertino/scrollbar_test.dart +++ b/packages/flutter/test/cupertino/scrollbar_test.dart @@ -164,6 +164,58 @@ void main() { await tester.pump(_kScrollbarFadeDuration); }); + testWidgets('When isAlwaysShown is true, must pass a controller', + (WidgetTester tester) async { + Widget viewWithScroll() { + return Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(), + child: CupertinoScrollbar( + isAlwaysShown: true, + child: const SingleChildScrollView( + child: SizedBox( + width: 4000.0, + height: 4000.0, + ), + ), + ), + ), + ); + } + + expect(() async { + await tester.pumpWidget(viewWithScroll()); + }, throwsAssertionError); + }); + + testWidgets('When isAlwaysShown is true, must pass a controller that is attached to a scroll view', + (WidgetTester tester) async { + final ScrollController controller = ScrollController(); + Widget viewWithScroll() { + return Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(), + child: CupertinoScrollbar( + controller: controller, + isAlwaysShown: true, + child: const SingleChildScrollView( + child: SizedBox( + width: 4000.0, + height: 4000.0, + ), + ), + ), + ), + ); + } + + await tester.pumpWidget(viewWithScroll()); + final dynamic exception = tester.takeException(); + expect(exception, isAssertionError); + }); + testWidgets('On first render with isAlwaysShown: true, the thumb shows', (WidgetTester tester) async { final ScrollController controller = ScrollController(); diff --git a/packages/flutter/test/material/scrollbar_test.dart b/packages/flutter/test/material/scrollbar_test.dart index ba97a2c54f9..0566aefe5bc 100644 --- a/packages/flutter/test/material/scrollbar_test.dart +++ b/packages/flutter/test/material/scrollbar_test.dart @@ -201,6 +201,56 @@ void main() { expect(scrollbar.controller, isNotNull); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); + testWidgets('When isAlwaysShown is true, must pass a controller', + (WidgetTester tester) async { + Widget viewWithScroll() { + return _buildBoilerplate( + child: Theme( + data: ThemeData(), + child: Scrollbar( + isAlwaysShown: true, + child: const SingleChildScrollView( + child: SizedBox( + width: 4000.0, + height: 4000.0, + ), + ), + ), + ), + ); + } + + expect(() async { + await tester.pumpWidget(viewWithScroll()); + }, throwsAssertionError); + }); + + testWidgets('When isAlwaysShown is true, must pass a controller that is attached to a scroll view', + (WidgetTester tester) async { + final ScrollController controller = ScrollController(); + Widget viewWithScroll() { + return _buildBoilerplate( + child: Theme( + data: ThemeData(), + child: Scrollbar( + isAlwaysShown: true, + controller: controller, + child: const SingleChildScrollView( + child: SizedBox( + width: 4000.0, + height: 4000.0, + ), + ), + ), + ), + ); + } + + await tester.pumpWidget(viewWithScroll()); + final dynamic exception = tester.takeException(); + expect(exception, isAssertionError); + }); + testWidgets('On first render with isAlwaysShown: true, the thumb shows', (WidgetTester tester) async { final ScrollController controller = ScrollController();