parent
d325705332
commit
e7772d0e2e
@ -61,6 +61,7 @@ class CupertinoScrollbar extends RawScrollbar {
|
|||||||
this.thicknessWhileDragging = defaultThicknessWhileDragging,
|
this.thicknessWhileDragging = defaultThicknessWhileDragging,
|
||||||
Radius radius = defaultRadius,
|
Radius radius = defaultRadius,
|
||||||
this.radiusWhileDragging = defaultRadiusWhileDragging,
|
this.radiusWhileDragging = defaultRadiusWhileDragging,
|
||||||
|
ScrollNotificationPredicate? notificationPredicate,
|
||||||
}) : assert(thickness != null),
|
}) : assert(thickness != null),
|
||||||
assert(thickness < double.infinity),
|
assert(thickness < double.infinity),
|
||||||
assert(thicknessWhileDragging != null),
|
assert(thicknessWhileDragging != null),
|
||||||
@ -77,6 +78,7 @@ class CupertinoScrollbar extends RawScrollbar {
|
|||||||
fadeDuration: _kScrollbarFadeDuration,
|
fadeDuration: _kScrollbarFadeDuration,
|
||||||
timeToFade: _kScrollbarTimeToFade,
|
timeToFade: _kScrollbarTimeToFade,
|
||||||
pressDuration: const Duration(milliseconds: 100),
|
pressDuration: const Duration(milliseconds: 100),
|
||||||
|
notificationPredicate: notificationPredicate ?? defaultScrollNotificationPredicate,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Default value for [thickness] if it's not specified in [CupertinoScrollbar].
|
/// Default value for [thickness] if it's not specified in [CupertinoScrollbar].
|
||||||
|
@ -69,6 +69,7 @@ class Scrollbar extends StatefulWidget {
|
|||||||
this.hoverThickness,
|
this.hoverThickness,
|
||||||
this.thickness,
|
this.thickness,
|
||||||
this.radius,
|
this.radius,
|
||||||
|
this.notificationPredicate,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// {@macro flutter.widgets.Scrollbar.child}
|
/// {@macro flutter.widgets.Scrollbar.child}
|
||||||
@ -111,6 +112,9 @@ class Scrollbar extends StatefulWidget {
|
|||||||
/// default [Radius.circular] of 8.0 pixels.
|
/// default [Radius.circular] of 8.0 pixels.
|
||||||
final Radius? radius;
|
final Radius? radius;
|
||||||
|
|
||||||
|
/// {@macro flutter.widgets.Scrollbar.notificationPredicate}
|
||||||
|
final ScrollNotificationPredicate? notificationPredicate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ScrollbarState createState() => _ScrollbarState();
|
_ScrollbarState createState() => _ScrollbarState();
|
||||||
}
|
}
|
||||||
@ -129,6 +133,7 @@ class _ScrollbarState extends State<Scrollbar> {
|
|||||||
radius: widget.radius ?? CupertinoScrollbar.defaultRadius,
|
radius: widget.radius ?? CupertinoScrollbar.defaultRadius,
|
||||||
radiusWhileDragging: widget.radius ?? CupertinoScrollbar.defaultRadiusWhileDragging,
|
radiusWhileDragging: widget.radius ?? CupertinoScrollbar.defaultRadiusWhileDragging,
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
|
notificationPredicate: widget.notificationPredicate,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return _MaterialScrollbar(
|
return _MaterialScrollbar(
|
||||||
@ -139,6 +144,7 @@ class _ScrollbarState extends State<Scrollbar> {
|
|||||||
hoverThickness: widget.hoverThickness,
|
hoverThickness: widget.hoverThickness,
|
||||||
thickness: widget.thickness,
|
thickness: widget.thickness,
|
||||||
radius: widget.radius,
|
radius: widget.radius,
|
||||||
|
notificationPredicate: widget.notificationPredicate,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,6 +159,7 @@ class _MaterialScrollbar extends RawScrollbar {
|
|||||||
this.hoverThickness,
|
this.hoverThickness,
|
||||||
double? thickness,
|
double? thickness,
|
||||||
Radius? radius,
|
Radius? radius,
|
||||||
|
ScrollNotificationPredicate? notificationPredicate,
|
||||||
}) : super(
|
}) : super(
|
||||||
key: key,
|
key: key,
|
||||||
child: child,
|
child: child,
|
||||||
@ -163,6 +170,7 @@ class _MaterialScrollbar extends RawScrollbar {
|
|||||||
fadeDuration: _kScrollbarFadeDuration,
|
fadeDuration: _kScrollbarFadeDuration,
|
||||||
timeToFade: _kScrollbarTimeToFade,
|
timeToFade: _kScrollbarTimeToFade,
|
||||||
pressDuration: Duration.zero,
|
pressDuration: Duration.zero,
|
||||||
|
notificationPredicate: notificationPredicate ?? defaultScrollNotificationPredicate,
|
||||||
);
|
);
|
||||||
|
|
||||||
final bool? showTrackOnHover;
|
final bool? showTrackOnHover;
|
||||||
|
@ -565,6 +565,10 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
|
|||||||
/// visible without the fade animation. This requires that a [ScrollController]
|
/// visible without the fade animation. This requires that a [ScrollController]
|
||||||
/// is provided to [controller], or that the [PrimaryScrollController] is available.
|
/// is provided to [controller], or that the [PrimaryScrollController] is available.
|
||||||
///
|
///
|
||||||
|
/// If the scrollbar is wrapped around multiple [ScrollView]s, it only responds to
|
||||||
|
/// the nearest scrollView and shows the corresponding scrollbar thumb by default.
|
||||||
|
/// Set [notificationPredicate] to something else for more complicated behaviors.
|
||||||
|
///
|
||||||
/// Scrollbars are interactive and will also 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
|
||||||
@ -607,6 +611,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,
|
||||||
|
this.notificationPredicate = defaultScrollNotificationPredicate,
|
||||||
}) : assert(child != null),
|
}) : assert(child != null),
|
||||||
assert(fadeDuration != null),
|
assert(fadeDuration != null),
|
||||||
assert(timeToFade != null),
|
assert(timeToFade != null),
|
||||||
@ -767,6 +772,16 @@ class RawScrollbar extends StatefulWidget {
|
|||||||
/// Cannot be null, defaults to [Duration.zero].
|
/// Cannot be null, defaults to [Duration.zero].
|
||||||
final Duration pressDuration;
|
final Duration pressDuration;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.Scrollbar.notificationPredicate}
|
||||||
|
/// A check that specifies whether a [ScrollNotification] should be
|
||||||
|
/// handled by this widget.
|
||||||
|
///
|
||||||
|
/// By default, checks whether `notification.depth == 0`. That means if the
|
||||||
|
/// scrollbar is wrapped around multiple [ScrollView]s, it only responds to the
|
||||||
|
/// nearest scrollView and shows the corresponding scrollbar thumb.
|
||||||
|
/// {@endtemplate}
|
||||||
|
final ScrollNotificationPredicate notificationPredicate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RawScrollbarState<RawScrollbar> createState() => RawScrollbarState<RawScrollbar>();
|
RawScrollbarState<RawScrollbar> createState() => RawScrollbarState<RawScrollbar>();
|
||||||
}
|
}
|
||||||
@ -1031,6 +1046,8 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _handleScrollNotification(ScrollNotification notification) {
|
bool _handleScrollNotification(ScrollNotification notification) {
|
||||||
|
if (!widget.notificationPredicate(notification))
|
||||||
|
return false;
|
||||||
|
|
||||||
final ScrollMetrics metrics = notification.metrics;
|
final ScrollMetrics metrics = notification.metrics;
|
||||||
if (metrics.maxScrollExtent <= metrics.minScrollExtent)
|
if (metrics.maxScrollExtent <= metrics.minScrollExtent)
|
||||||
|
@ -1059,4 +1059,66 @@ void main() {
|
|||||||
final CupertinoScrollbar scrollbar = tester.widget<CupertinoScrollbar>(find.byType(CupertinoScrollbar));
|
final CupertinoScrollbar scrollbar = tester.widget<CupertinoScrollbar>(find.byType(CupertinoScrollbar));
|
||||||
expect(scrollbar.controller, isNotNull);
|
expect(scrollbar.controller, isNotNull);
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
|
||||||
|
|
||||||
|
testWidgets("Scrollbar doesn't show when scroll the inner scrollable widget", (WidgetTester tester) async {
|
||||||
|
final GlobalKey key1 = GlobalKey();
|
||||||
|
final GlobalKey key2 = GlobalKey();
|
||||||
|
final GlobalKey outerKey = GlobalKey();
|
||||||
|
final GlobalKey innerKey = GlobalKey();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: MediaQuery(
|
||||||
|
data: const MediaQueryData(),
|
||||||
|
child: Scrollbar(
|
||||||
|
key: key2,
|
||||||
|
notificationPredicate: null,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
key: outerKey,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 1000.0,
|
||||||
|
width: double.infinity,
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Scrollbar(
|
||||||
|
key: key1,
|
||||||
|
notificationPredicate: null,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 300.0,
|
||||||
|
width: double.infinity,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
key: innerKey,
|
||||||
|
child: const SizedBox(
|
||||||
|
key: Key('Inner scrollable'),
|
||||||
|
height: 1000.0,
|
||||||
|
width: double.infinity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Drag the inner scrollable widget.
|
||||||
|
await tester.drag(find.byKey(innerKey), const Offset(0.0, -25.0));
|
||||||
|
await tester.pump();
|
||||||
|
// Scrollbar fully showing.
|
||||||
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.renderObject(find.byKey(key2)),
|
||||||
|
paintsExactlyCountTimes(#drawRect, 2), // Each bar will call [drawRect] twice.
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.renderObject(find.byKey(key1)),
|
||||||
|
paintsExactlyCountTimes(#drawRect, 2),
|
||||||
|
);
|
||||||
|
}, variant: TargetPlatformVariant.all());
|
||||||
}
|
}
|
||||||
|
@ -804,4 +804,67 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/66444
|
||||||
|
testWidgets("RawScrollbar doesn't show when scroll the inner scrollable widget", (WidgetTester tester) async {
|
||||||
|
final GlobalKey key1 = GlobalKey();
|
||||||
|
final GlobalKey key2 = GlobalKey();
|
||||||
|
final GlobalKey outerKey = GlobalKey();
|
||||||
|
final GlobalKey innerKey = GlobalKey();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: MediaQuery(
|
||||||
|
data: const MediaQueryData(),
|
||||||
|
child: RawScrollbar(
|
||||||
|
key: key2,
|
||||||
|
thumbColor: const Color(0x11111111),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
key: outerKey,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 1000.0,
|
||||||
|
width: double.infinity,
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
RawScrollbar(
|
||||||
|
key: key1,
|
||||||
|
thumbColor: const Color(0x22222222),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 300.0,
|
||||||
|
width: double.infinity,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
key: innerKey,
|
||||||
|
child: const SizedBox(
|
||||||
|
key: Key('Inner scrollable'),
|
||||||
|
height: 1000.0,
|
||||||
|
width: double.infinity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Drag the inner scrollable widget.
|
||||||
|
await tester.drag(find.byKey(innerKey), const Offset(0.0, -25.0));
|
||||||
|
await tester.pump();
|
||||||
|
// Scrollbar fully showing.
|
||||||
|
await tester.pump(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.renderObject(find.byKey(key2)),
|
||||||
|
paintsExactlyCountTimes(#drawRect, 2), // Each bar will call [drawRect] twice.
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.renderObject(find.byKey(key1)),
|
||||||
|
paintsExactlyCountTimes(#drawRect, 2),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user