fixes isAlwaysShown material scrollbar.dart (#54128)

This commit is contained in:
Onat Çipli 2020-05-28 21:23:05 +03:00 committed by GitHub
parent 8cb6d5edbf
commit 4f0c82b7a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 145 additions and 21 deletions

View file

@ -238,15 +238,7 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv
..color = CupertinoDynamicColor.resolve(_kScrollbarColor, context)
..padding = MediaQuery.of(context).padding;
}
WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
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);
}
});
_triggerScrollbar();
}
@override
@ -254,7 +246,7 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv
super.didUpdateWidget(oldWidget);
if (widget.isAlwaysShown != oldWidget.isAlwaysShown) {
if (widget.isAlwaysShown == true) {
assert(widget.controller != null);
_triggerScrollbar();
_fadeoutAnimationController.animateTo(1.0);
} else {
_fadeoutAnimationController.reverse();
@ -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.
void _dragScrollbar(double primaryDelta) {
assert(_currentController != null);

View file

@ -106,15 +106,7 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
_textDirection = Directionality.of(context);
_materialPainter = _buildMaterialScrollbarPainter();
_useCupertinoScrollbar = false;
WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
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);
}
});
_triggerScrollbar();
break;
}
assert(_useCupertinoScrollbar != null);
@ -124,15 +116,28 @@ class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
void didUpdateWidget(Scrollbar oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isAlwaysShown != oldWidget.isAlwaysShown) {
assert(widget.controller != null);
if (widget.isAlwaysShown == false) {
_fadeoutAnimationController.reverse();
} else {
_triggerScrollbar();
_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() {
return ScrollbarPainter(
color: _themeColor,

View file

@ -286,6 +286,59 @@ void main() {
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(
'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 {
@ -332,6 +385,7 @@ void main() {
await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle();
expect(find.byType(CupertinoScrollbar), isNot(paints..rrect()));
await tester.fling(
find.byType(SingleChildScrollView),
const Offset(0.0, -10.0),
@ -340,7 +394,13 @@ void main() {
expect(find.byType(CupertinoScrollbar), paints..rrect());
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();
// Scrollbar thumb is showing after scroll finishes and timer ends.
expect(find.byType(CupertinoScrollbar), paints..rrect());
});

View file

@ -308,6 +308,54 @@ void main() {
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(
'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 {
@ -348,6 +396,7 @@ void main() {
await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle();
expect(find.byType(Scrollbar), isNot(paints..rect()));
await tester.fling(
find.byType(SingleChildScrollView),
const Offset(0.0, -10.0),
@ -356,8 +405,13 @@ void main() {
expect(find.byType(Scrollbar), paints..rect());
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();
// Scrollbar is not showing after scroll finishes
// Scrollbar thumb is showing after scroll finishes and timer ends.
expect(find.byType(Scrollbar), paints..rect());
});