Skip to content

Commit 281f142

Browse files
authored
adds trackRadius to ScrollbarPainter and RawScrollbar (#98018)
1 parent 9369bd3 commit 281f142

File tree

2 files changed

+122
-10
lines changed

2 files changed

+122
-10
lines changed

packages/flutter/lib/src/widgets/scrollbar.dart

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
8686
double mainAxisMargin = 0.0,
8787
double crossAxisMargin = 0.0,
8888
Radius? radius,
89+
Radius? trackRadius,
8990
OutlinedBorder? shape,
9091
double minLength = _kMinThumbExtent,
9192
double? minOverscrollLength,
@@ -117,6 +118,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
117118
_minLength = minLength,
118119
_trackColor = trackColor,
119120
_trackBorderColor = trackBorderColor,
121+
_trackRadius = trackRadius,
120122
_scrollbarOrientation = scrollbarOrientation,
121123
_minOverscrollLength = minOverscrollLength ?? minLength,
122124
_ignorePointer = ignorePointer {
@@ -159,6 +161,19 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
159161
notifyListeners();
160162
}
161163

164+
/// [Radius] of corners of the Scrollbar's track.
165+
///
166+
/// Scrollbar's track will be rectangular if [trackRadius] is null.
167+
Radius? get trackRadius => _trackRadius;
168+
Radius? _trackRadius;
169+
set trackRadius(Radius? value) {
170+
if (trackRadius == value)
171+
return;
172+
173+
_trackRadius = value;
174+
notifyListeners();
175+
}
176+
162177
/// [TextDirection] of the [BuildContext] which dictates the side of the
163178
/// screen the scrollbar appears in (the trailing side). Must be set prior to
164179
/// calling paint.
@@ -496,7 +511,11 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
496511
// Paint if the opacity dictates visibility
497512
if (fadeoutOpacityAnimation.value != 0.0) {
498513
// Track
499-
canvas.drawRect(_trackRect!, _paintTrack());
514+
if (trackRadius == null) {
515+
canvas.drawRect(_trackRect!, _paintTrack());
516+
} else {
517+
canvas.drawRRect(RRect.fromRectAndRadius(_trackRect!, trackRadius!), _paintTrack());
518+
}
500519
// Track Border
501520
canvas.drawLine(borderStart, borderEnd, _paintTrack(isBorder: true));
502521
if (radius != null) {
@@ -741,6 +760,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
741760
|| mainAxisMargin != oldDelegate.mainAxisMargin
742761
|| crossAxisMargin != oldDelegate.crossAxisMargin
743762
|| radius != oldDelegate.radius
763+
|| trackRadius != oldDelegate.trackRadius
744764
|| shape != oldDelegate.shape
745765
|| padding != oldDelegate.padding
746766
|| minLength != oldDelegate.minLength
@@ -880,6 +900,7 @@ class RawScrollbar extends StatefulWidget {
880900
this.minThumbLength = _kMinThumbExtent,
881901
this.minOverscrollLength,
882902
this.trackVisibility,
903+
this.trackRadius,
883904
this.trackColor,
884905
this.trackBorderColor,
885906
this.fadeDuration = _kScrollbarFadeDuration,
@@ -1224,6 +1245,12 @@ class RawScrollbar extends StatefulWidget {
12241245
/// [MaterialState]s by using [ScrollbarThemeData.trackVisibility].
12251246
final bool? trackVisibility;
12261247

1248+
/// The [Radius] of the scrollbar track's rounded rectangle corners.
1249+
///
1250+
/// Scrollbar's track will be rectangular if [trackRadius] is null, which is
1251+
/// the default behavior.
1252+
final Radius? trackRadius;
1253+
12271254
/// The color of the scrollbar track.
12281255
///
12291256
/// The scrollbar track will only be visible when [trackVisibility] and
@@ -1380,6 +1407,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
13801407
fadeoutOpacityAnimation: _fadeoutOpacityAnimation,
13811408
thickness: widget.thickness ?? _kScrollbarThickness,
13821409
radius: widget.radius,
1410+
trackRadius: widget.trackRadius,
13831411
scrollbarOrientation: widget.scrollbarOrientation,
13841412
mainAxisMargin: widget.mainAxisMargin,
13851413
shape: widget.shape,
@@ -1513,6 +1541,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
15131541
void updateScrollbarPainter() {
15141542
scrollbarPainter
15151543
..color = widget.thumbColor ?? const Color(0x66BCBCBC)
1544+
..trackRadius = widget.trackRadius
15161545
..trackColor = _showTrack ? const Color(0x08000000) : const Color(0x00000000)
15171546
..trackBorderColor = _showTrack ? const Color(0x1a000000) : const Color(0x00000000)
15181547
..textDirection = Directionality.of(context)

packages/flutter/test/widgets/scrollbar_test.dart

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ ScrollbarPainter _buildPainter({
2525
double mainAxisMargin = 0.0,
2626
double crossAxisMargin = 0.0,
2727
Radius? radius,
28+
Radius? trackRadius,
2829
double minLength = _kMinThumbExtent,
2930
double? minOverscrollLength,
3031
ScrollbarOrientation? scrollbarOrientation,
@@ -38,6 +39,7 @@ ScrollbarPainter _buildPainter({
3839
mainAxisMargin: mainAxisMargin,
3940
crossAxisMargin: crossAxisMargin,
4041
radius: radius,
42+
trackRadius: trackRadius,
4143
minLength: minLength,
4244
minOverscrollLength: minOverscrollLength ?? minLength,
4345
fadeoutOpacityAnimation: kAlwaysCompleteAnimation,
@@ -47,12 +49,18 @@ ScrollbarPainter _buildPainter({
4749

4850
class _DrawRectOnceCanvas extends Fake implements Canvas {
4951
List<Rect> rects = <Rect>[];
52+
List<RRect> rrects = <RRect>[];
5053

5154
@override
5255
void drawRect(Rect rect, Paint paint) {
5356
rects.add(rect);
5457
}
5558

59+
@override
60+
void drawRRect(ui.RRect rrect, ui.Paint paint) {
61+
rrects.add(rrect);
62+
}
63+
5664
@override
5765
void drawLine(Offset p1, Offset p2, Paint paint) {}
5866
}
@@ -62,9 +70,11 @@ void main() {
6270
ScrollbarPainter painter;
6371

6472
Rect captureRect() => testCanvas.rects.removeLast();
73+
RRect captureRRect() => testCanvas.rrects.removeLast();
6574

6675
tearDown(() {
6776
testCanvas.rects.clear();
77+
testCanvas.rrects.clear();
6878
});
6979

7080
final ScrollMetrics defaultMetrics = FixedScrollMetrics(
@@ -629,6 +639,37 @@ void main() {
629639
},
630640
);
631641

642+
test('trackRadius and radius is respected', () {
643+
const double minLen = 3.5;
644+
const Size size = Size(600, 10);
645+
final ScrollMetrics metrics = defaultMetrics.copyWith(
646+
maxScrollExtent: 100000,
647+
viewportDimension: size.height,
648+
);
649+
650+
painter = _buildPainter(
651+
trackRadius: const Radius.circular(2.0),
652+
radius: const Radius.circular(3.0),
653+
minLength: minLen,
654+
minOverscrollLength: minLen,
655+
scrollMetrics: metrics,
656+
);
657+
658+
painter.paint(testCanvas, size);
659+
660+
final RRect thumbRRect = captureRRect(); // thumb
661+
expect(thumbRRect.blRadius, const Radius.circular(3.0));
662+
expect(thumbRRect.brRadius, const Radius.circular(3.0));
663+
expect(thumbRRect.tlRadius, const Radius.circular(3.0));
664+
expect(thumbRRect.trRadius, const Radius.circular(3.0));
665+
666+
final RRect trackRRect = captureRRect(); // track
667+
expect(trackRRect.blRadius, const Radius.circular(2.0));
668+
expect(trackRRect.brRadius, const Radius.circular(2.0));
669+
expect(trackRRect.tlRadius, const Radius.circular(2.0));
670+
expect(trackRRect.trRadius, const Radius.circular(2.0));
671+
});
672+
632673
testWidgets('ScrollbarPainter asserts if no TextDirection has been provided', (WidgetTester tester) async {
633674
final ScrollbarPainter painter = ScrollbarPainter(
634675
color: _kScrollbarColor,
@@ -691,7 +732,7 @@ void main() {
691732
..rect(
692733
rect: const Rect.fromLTRB(794.0, 240.0, 800.0, 600.0),
693734
color: const Color(0x66BCBCBC),
694-
),
735+
),
695736
);
696737

697738
// Tap on the track area above the thumb.
@@ -706,7 +747,7 @@ void main() {
706747
..rect(
707748
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 360.0),
708749
color: const Color(0x66BCBCBC),
709-
),
750+
),
710751
);
711752
});
712753

@@ -736,7 +777,7 @@ void main() {
736777
..rect(
737778
rect: const Rect.fromLTRB(794.0, 3.0, 800.0, 93.0),
738779
color: const Color(0x66BCBCBC),
739-
),
780+
),
740781
);
741782

742783
await tester.pump(const Duration(seconds: 3));
@@ -749,7 +790,7 @@ void main() {
749790
..rect(
750791
rect: const Rect.fromLTRB(794.0, 3.0, 800.0, 93.0),
751792
color: const Color(0x66BCBCBC),
752-
),
793+
),
753794
);
754795

755796
await gesture.up();
@@ -764,7 +805,7 @@ void main() {
764805
..rect(
765806
rect: const Rect.fromLTRB(794.0, 3.0, 800.0, 93.0),
766807
color: const Color(0x4fbcbcbc),
767-
),
808+
),
768809
);
769810
});
770811

@@ -794,7 +835,7 @@ void main() {
794835
..rect(
795836
rect: const Rect.fromLTRB(794.0, 3.0, 800.0, 93.0),
796837
color: const Color(0x66BCBCBC),
797-
),
838+
),
798839
);
799840

800841
final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse);
@@ -811,7 +852,7 @@ void main() {
811852
..rect(
812853
rect: const Rect.fromLTRB(794.0, 3.0, 800.0, 93.0),
813854
color: const Color(0x66BCBCBC),
814-
),
855+
),
815856
);
816857
});
817858

@@ -940,7 +981,7 @@ void main() {
940981
..rect(
941982
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 90.0),
942983
color: const Color(0x66BCBCBC),
943-
),
984+
),
944985
);
945986

946987
// Drag the thumb down to scroll down.
@@ -962,7 +1003,7 @@ void main() {
9621003
..rect(
9631004
rect: const Rect.fromLTRB(794.0, 10.0, 800.0, 100.0),
9641005
color: const Color(0x66BCBCBC),
965-
),
1006+
),
9661007
);
9671008
});
9681009

@@ -2206,6 +2247,7 @@ void main() {
22062247
double mainAxisMargin = 0.0,
22072248
double crossAxisMargin = 0.0,
22082249
Radius? radius,
2250+
Radius? trackRadius,
22092251
OutlinedBorder? shape,
22102252
double minLength = _kMinThumbExtent,
22112253
double? minOverscrollLength,
@@ -2222,6 +2264,7 @@ void main() {
22222264
mainAxisMargin: mainAxisMargin,
22232265
crossAxisMargin: crossAxisMargin,
22242266
radius: radius,
2267+
trackRadius: trackRadius,
22252268
shape: shape,
22262269
minLength: minLength,
22272270
minOverscrollLength: minOverscrollLength,
@@ -2240,6 +2283,7 @@ void main() {
22402283
expect(painter.shouldRepaint(createPainter(mainAxisMargin: 1.0)), true);
22412284
expect(painter.shouldRepaint(createPainter(crossAxisMargin: 1.0)), true);
22422285
expect(painter.shouldRepaint(createPainter(radius: const Radius.circular(1.0))), true);
2286+
expect(painter.shouldRepaint(createPainter(trackRadius: const Radius.circular(1.0))), true);
22432287
expect(painter.shouldRepaint(createPainter(shape: const CircleBorder(side: BorderSide(width: 2.0)))), true);
22442288
expect(painter.shouldRepaint(createPainter(minLength: _kMinThumbExtent + 1.0)), true);
22452289
expect(painter.shouldRepaint(createPainter(minOverscrollLength: 1.0)), true);
@@ -2289,6 +2333,45 @@ void main() {
22892333
);
22902334
});
22912335

2336+
testWidgets('trackRadius and radius properties of RawScrollbar can draw RoundedRectangularRect', (WidgetTester tester) async {
2337+
final ScrollController scrollController = ScrollController();
2338+
await tester.pumpWidget(
2339+
Directionality(
2340+
textDirection: TextDirection.ltr,
2341+
child: MediaQuery(
2342+
data: const MediaQueryData(),
2343+
child: PrimaryScrollController(
2344+
controller: scrollController,
2345+
child: RawScrollbar(
2346+
thumbVisibility: true,
2347+
trackVisibility: true,
2348+
trackRadius: const Radius.circular(1.0),
2349+
radius: const Radius.circular(2.0),
2350+
controller: scrollController,
2351+
child: const SingleChildScrollView(
2352+
child: SizedBox(width: 4000.0, height: 4000.0),
2353+
),
2354+
),
2355+
),
2356+
),
2357+
),
2358+
);
2359+
await tester.pumpAndSettle();
2360+
expect(scrollController.offset, 0.0);
2361+
expect(
2362+
find.byType(RawScrollbar),
2363+
paints
2364+
..rrect(
2365+
rrect: RRect.fromLTRBR(794.0, 0.0, 800.0, 600.0, const Radius.circular(1.0)),
2366+
color: const Color(0x08000000),
2367+
)
2368+
..rrect(
2369+
rrect: RRect.fromLTRBR(794.0, 0.0, 800.0, 90.0, const Radius.circular(2.0)),
2370+
color: const Color(0x66bcbcbc),
2371+
)
2372+
);
2373+
});
2374+
22922375
testWidgets('Scrollbar asserts that a visible track has a visible thumb', (WidgetTester tester) async {
22932376
final ScrollController scrollController = ScrollController();
22942377
Widget _buildApp() {

0 commit comments

Comments
 (0)