Unverified Commit 4f0c82b7 authored by Onat Çipli's avatar Onat Çipli Committed by GitHub

fixes isAlwaysShown material scrollbar.dart (#54128)

parent 8cb6d5ed
...@@ -238,15 +238,7 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv ...@@ -238,15 +238,7 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv
..color = CupertinoDynamicColor.resolve(_kScrollbarColor, context) ..color = CupertinoDynamicColor.resolve(_kScrollbarColor, context)
..padding = MediaQuery.of(context).padding; ..padding = MediaQuery.of(context).padding;
} }
WidgetsBinding.instance.addPostFrameCallback((Duration duration) { _triggerScrollbar();
if (widget.isAlwaysShown) {
assert(widget.controller != null);
// Wait one frame and cause an empty scroll event. This allows the
// thumb to show immediately when isAlwaysShown is true. A scroll
// event is required in order to paint the thumb.
widget.controller.position.didUpdateScrollPositionBy(0);
}
});
} }
@override @override
...@@ -254,7 +246,7 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv ...@@ -254,7 +246,7 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.isAlwaysShown != oldWidget.isAlwaysShown) { if (widget.isAlwaysShown != oldWidget.isAlwaysShown) {
if (widget.isAlwaysShown == true) { if (widget.isAlwaysShown == true) {
assert(widget.controller != null); _triggerScrollbar();
_fadeoutAnimationController.animateTo(1.0); _fadeoutAnimationController.animateTo(1.0);
} else { } else {
_fadeoutAnimationController.reverse(); _fadeoutAnimationController.reverse();
...@@ -278,6 +270,19 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv ...@@ -278,6 +270,19 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv
); );
} }
// Wait one frame and cause an empty scroll event. This allows the thumb to
// show immediately when isAlwaysShown is true. A scroll event is required in
// order to paint the thumb.
void _triggerScrollbar() {
WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
if (widget.isAlwaysShown) {
assert(widget.controller != null);
_fadeoutTimer?.cancel();
widget.controller.position.didUpdateScrollPositionBy(0);
}
});
}
// Handle a gesture that drags the scrollbar by the given amount. // Handle a gesture that drags the scrollbar by the given amount.
void _dragScrollbar(double primaryDelta) { void _dragScrollbar(double primaryDelta) {
assert(_currentController != null); assert(_currentController != null);
......
...@@ -106,15 +106,7 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin { ...@@ -106,15 +106,7 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
_textDirection = Directionality.of(context); _textDirection = Directionality.of(context);
_materialPainter = _buildMaterialScrollbarPainter(); _materialPainter = _buildMaterialScrollbarPainter();
_useCupertinoScrollbar = false; _useCupertinoScrollbar = false;
WidgetsBinding.instance.addPostFrameCallback((Duration duration) { _triggerScrollbar();
if (widget.isAlwaysShown) {
assert(widget.controller != null);
// Wait one frame and cause an empty scroll event. This allows the
// thumb to show immediately when isAlwaysShown is true. A scroll
// event is required in order to paint the thumb.
widget.controller.position.didUpdateScrollPositionBy(0);
}
});
break; break;
} }
assert(_useCupertinoScrollbar != null); assert(_useCupertinoScrollbar != null);
...@@ -124,15 +116,28 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin { ...@@ -124,15 +116,28 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
void didUpdateWidget(Scrollbar oldWidget) { void didUpdateWidget(Scrollbar oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.isAlwaysShown != oldWidget.isAlwaysShown) { if (widget.isAlwaysShown != oldWidget.isAlwaysShown) {
assert(widget.controller != null);
if (widget.isAlwaysShown == false) { if (widget.isAlwaysShown == false) {
_fadeoutAnimationController.reverse(); _fadeoutAnimationController.reverse();
} else { } else {
_triggerScrollbar();
_fadeoutAnimationController.animateTo(1.0); _fadeoutAnimationController.animateTo(1.0);
} }
} }
} }
// Wait one frame and cause an empty scroll event. This allows the thumb to
// show immediately when isAlwaysShown is true. A scroll event is required in
// order to paint the thumb.
void _triggerScrollbar() {
WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
if (widget.isAlwaysShown) {
assert(widget.controller != null);
_fadeoutTimer?.cancel();
widget.controller.position.didUpdateScrollPositionBy(0);
}
});
}
ScrollbarPainter _buildMaterialScrollbarPainter() { ScrollbarPainter _buildMaterialScrollbarPainter() {
return ScrollbarPainter( return ScrollbarPainter(
color: _themeColor, color: _themeColor,
......
...@@ -286,6 +286,59 @@ void main() { ...@@ -286,6 +286,59 @@ void main() {
expect(find.byType(CupertinoScrollbar), isNot(paints..rrect())); expect(find.byType(CupertinoScrollbar), isNot(paints..rrect()));
}); });
testWidgets(
'With isAlwaysShown: false, set isAlwaysShown: true. The thumb should be always shown directly',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
bool isAlwaysShown = false;
Widget viewWithScroll() {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: Stack(
children: <Widget>[
CupertinoScrollbar(
isAlwaysShown: isAlwaysShown,
controller: controller,
child: SingleChildScrollView(
controller: controller,
child: const SizedBox(
width: 4000.0,
height: 4000.0,
),
),
),
Positioned(
bottom: 10,
child: CupertinoButton(
onPressed: () {
setState(() {
isAlwaysShown = !isAlwaysShown;
});
},
child: const Text('change isAlwaysShown'),
),
)
],
),
),
);
},
);
}
await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle();
expect(find.byType(CupertinoScrollbar), isNot(paints..rrect()));
await tester.tap(find.byType(CupertinoButton));
await tester.pumpAndSettle();
expect(find.byType(CupertinoScrollbar), paints..rrect());
});
testWidgets( testWidgets(
'With isAlwaysShown: false, fling a scroll. While it is still scrolling, set isAlwaysShown: true. The thumb should not fade even after the scrolling stops', 'With isAlwaysShown: false, fling a scroll. While it is still scrolling, set isAlwaysShown: true. The thumb should not fade even after the scrolling stops',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -332,6 +385,7 @@ void main() { ...@@ -332,6 +385,7 @@ void main() {
await tester.pumpWidget(viewWithScroll()); await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(CupertinoScrollbar), isNot(paints..rrect()));
await tester.fling( await tester.fling(
find.byType(SingleChildScrollView), find.byType(SingleChildScrollView),
const Offset(0.0, -10.0), const Offset(0.0, -10.0),
...@@ -340,7 +394,13 @@ void main() { ...@@ -340,7 +394,13 @@ void main() {
expect(find.byType(CupertinoScrollbar), paints..rrect()); expect(find.byType(CupertinoScrollbar), paints..rrect());
await tester.tap(find.byType(CupertinoButton)); await tester.tap(find.byType(CupertinoButton));
await tester.pump();
expect(find.byType(CupertinoScrollbar), paints..rrect());
// Wait for the timer delay to expire.
await tester.pump(const Duration(milliseconds: 600)); // _kScrollbarTimeToFade
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Scrollbar thumb is showing after scroll finishes and timer ends.
expect(find.byType(CupertinoScrollbar), paints..rrect()); expect(find.byType(CupertinoScrollbar), paints..rrect());
}); });
......
...@@ -308,6 +308,54 @@ void main() { ...@@ -308,6 +308,54 @@ void main() {
expect(find.byType(Scrollbar), isNot(paints..rect())); expect(find.byType(Scrollbar), isNot(paints..rect()));
}); });
testWidgets(
'With isAlwaysShown: false, set isAlwaysShown: true. The thumb should be always shown directly',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
bool isAlwaysShown = false;
Widget viewWithScroll() {
return _buildBoilerplate(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Theme(
data: ThemeData(),
child: Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.threed_rotation),
onPressed: () {
setState(() {
isAlwaysShown = !isAlwaysShown;
});
},
),
body: Scrollbar(
isAlwaysShown: isAlwaysShown,
controller: controller,
child: SingleChildScrollView(
controller: controller,
child: const SizedBox(
width: 4000.0,
height: 4000.0,
),
),
),
),
);
},
),
);
}
await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle();
expect(find.byType(Scrollbar), isNot(paints..rect()));
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
// Scrollbar is not showing after scroll finishes
expect(find.byType(Scrollbar), paints..rect());
});
testWidgets( testWidgets(
'With isAlwaysShown: false, fling a scroll. While it is still scrolling, set isAlwaysShown: true. The thumb should not fade even after the scrolling stops', 'With isAlwaysShown: false, fling a scroll. While it is still scrolling, set isAlwaysShown: true. The thumb should not fade even after the scrolling stops',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -348,6 +396,7 @@ void main() { ...@@ -348,6 +396,7 @@ void main() {
await tester.pumpWidget(viewWithScroll()); await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byType(Scrollbar), isNot(paints..rect()));
await tester.fling( await tester.fling(
find.byType(SingleChildScrollView), find.byType(SingleChildScrollView),
const Offset(0.0, -10.0), const Offset(0.0, -10.0),
...@@ -356,8 +405,13 @@ void main() { ...@@ -356,8 +405,13 @@ void main() {
expect(find.byType(Scrollbar), paints..rect()); expect(find.byType(Scrollbar), paints..rect());
await tester.tap(find.byType(FloatingActionButton)); await tester.tap(find.byType(FloatingActionButton));
await tester.pump();
expect(find.byType(Scrollbar), paints..rect());
// Wait for the timer delay to expire.
await tester.pump(const Duration(milliseconds: 600)); // _kScrollbarTimeToFade
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Scrollbar is not showing after scroll finishes // Scrollbar thumb is showing after scroll finishes and timer ends.
expect(find.byType(Scrollbar), paints..rect()); expect(find.byType(Scrollbar), paints..rect());
}); });
......
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