update the scrollbar that support always show the track even not on hover (#90178)
This commit is contained in:
parent
d0e0052939
commit
313b5f30d9
@ -77,6 +77,7 @@ class Scrollbar extends StatelessWidget {
|
||||
required this.child,
|
||||
this.controller,
|
||||
this.isAlwaysShown,
|
||||
this.trackVisibility,
|
||||
this.showTrackOnHover,
|
||||
this.hoverThickness,
|
||||
this.thickness,
|
||||
@ -95,11 +96,26 @@ class Scrollbar extends StatelessWidget {
|
||||
/// {@macro flutter.widgets.Scrollbar.isAlwaysShown}
|
||||
final bool? isAlwaysShown;
|
||||
|
||||
/// Controls the track visibility.
|
||||
///
|
||||
/// If this property is null, then [ScrollbarThemeData.trackVisibility] of
|
||||
/// [ThemeData.scrollbarTheme] is used. If that is also null, the default value
|
||||
/// is false.
|
||||
///
|
||||
/// If the track visibility is related to the scrollbar's material state,
|
||||
/// use the global [ScrollbarThemeData.trackVisibility] or override the
|
||||
/// sub-tree's theme data.
|
||||
///
|
||||
/// [showTrackOnHover] can be replaced by this and will be deprecated.
|
||||
final bool? trackVisibility;
|
||||
|
||||
/// Controls if the track will show on hover and remain, including during drag.
|
||||
///
|
||||
/// If this property is null, then [ScrollbarThemeData.showTrackOnHover] of
|
||||
/// [ThemeData.scrollbarTheme] is used. If that is also null, the default value
|
||||
/// is false.
|
||||
///
|
||||
/// This will be deprecated, and [trackVisibility] is recommended.
|
||||
final bool? showTrackOnHover;
|
||||
|
||||
/// The thickness of the scrollbar when a hover state is active and
|
||||
@ -153,6 +169,7 @@ class Scrollbar extends StatelessWidget {
|
||||
return _MaterialScrollbar(
|
||||
controller: controller,
|
||||
isAlwaysShown: isAlwaysShown,
|
||||
trackVisibility: trackVisibility,
|
||||
showTrackOnHover: showTrackOnHover,
|
||||
hoverThickness: hoverThickness,
|
||||
thickness: thickness,
|
||||
@ -171,6 +188,7 @@ class _MaterialScrollbar extends RawScrollbar {
|
||||
required Widget child,
|
||||
ScrollController? controller,
|
||||
bool? isAlwaysShown,
|
||||
this.trackVisibility,
|
||||
this.showTrackOnHover,
|
||||
this.hoverThickness,
|
||||
double? thickness,
|
||||
@ -193,6 +211,7 @@ class _MaterialScrollbar extends RawScrollbar {
|
||||
scrollbarOrientation: scrollbarOrientation,
|
||||
);
|
||||
|
||||
final bool? trackVisibility;
|
||||
final bool? showTrackOnHover;
|
||||
final double? hoverThickness;
|
||||
|
||||
@ -217,6 +236,13 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> {
|
||||
|
||||
bool get _showTrackOnHover => widget.showTrackOnHover ?? _scrollbarTheme.showTrackOnHover ?? false;
|
||||
|
||||
MaterialStateProperty<bool> get _trackVisibility => MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.hovered) && _showTrackOnHover) {
|
||||
return true;
|
||||
}
|
||||
return widget.trackVisibility ?? _scrollbarTheme.trackVisibility?.resolve(states) ?? false;
|
||||
});
|
||||
|
||||
Set<MaterialState> get _states => <MaterialState>{
|
||||
if (_dragIsActive) MaterialState.dragged,
|
||||
if (_hoverIsActive) MaterialState.hovered,
|
||||
@ -251,7 +277,7 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> {
|
||||
|
||||
// If the track is visible, the thumb color hover animation is ignored and
|
||||
// changes immediately.
|
||||
if (states.contains(MaterialState.hovered) && _showTrackOnHover)
|
||||
if (_trackVisibility.resolve(states))
|
||||
return _scrollbarTheme.thumbColor?.resolve(states) ?? hoverColor;
|
||||
|
||||
return Color.lerp(
|
||||
@ -266,7 +292,7 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> {
|
||||
final Color onSurface = _colorScheme.onSurface;
|
||||
final Brightness brightness = _colorScheme.brightness;
|
||||
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.hovered) && _showTrackOnHover) {
|
||||
if (_trackVisibility.resolve(states)) {
|
||||
return _scrollbarTheme.trackColor?.resolve(states)
|
||||
?? (brightness == Brightness.light
|
||||
? onSurface.withOpacity(0.03)
|
||||
@ -280,7 +306,7 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> {
|
||||
final Color onSurface = _colorScheme.onSurface;
|
||||
final Brightness brightness = _colorScheme.brightness;
|
||||
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.hovered) && _showTrackOnHover) {
|
||||
if (_trackVisibility.resolve(states)) {
|
||||
return _scrollbarTheme.trackBorderColor?.resolve(states)
|
||||
?? (brightness == Brightness.light
|
||||
? onSurface.withOpacity(0.1)
|
||||
@ -292,7 +318,7 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> {
|
||||
|
||||
MaterialStateProperty<double> get _thickness {
|
||||
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.hovered) && _showTrackOnHover)
|
||||
if (states.contains(MaterialState.hovered) && _trackVisibility.resolve(states))
|
||||
return widget.hoverThickness
|
||||
?? _scrollbarTheme.thickness?.resolve(states)
|
||||
?? _kScrollbarThicknessWithTrack;
|
||||
|
@ -32,6 +32,7 @@ class ScrollbarThemeData with Diagnosticable {
|
||||
/// Creates a theme that can be used for [ThemeData.scrollbarTheme].
|
||||
const ScrollbarThemeData({
|
||||
this.thickness,
|
||||
this.trackVisibility,
|
||||
this.showTrackOnHover,
|
||||
this.isAlwaysShown,
|
||||
this.radius,
|
||||
@ -51,6 +52,10 @@ class ScrollbarThemeData with Diagnosticable {
|
||||
/// * [MaterialState.hovered] on web and desktop platforms.
|
||||
final MaterialStateProperty<double?>? thickness;
|
||||
|
||||
/// Overrides the default value of [Scrollbar.trackVisibility] in all
|
||||
/// descendant [Scrollbar] widgets.
|
||||
final MaterialStateProperty<bool?>? trackVisibility;
|
||||
|
||||
/// Overrides the default value of [Scrollbar.showTrackOnHover] in all
|
||||
/// descendant [Scrollbar] widgets.
|
||||
final bool? showTrackOnHover;
|
||||
@ -122,6 +127,7 @@ class ScrollbarThemeData with Diagnosticable {
|
||||
/// new values.
|
||||
ScrollbarThemeData copyWith({
|
||||
MaterialStateProperty<double?>? thickness,
|
||||
MaterialStateProperty<bool?>? trackVisibility,
|
||||
bool? showTrackOnHover,
|
||||
bool? isAlwaysShown,
|
||||
bool? interactive,
|
||||
@ -135,6 +141,7 @@ class ScrollbarThemeData with Diagnosticable {
|
||||
}) {
|
||||
return ScrollbarThemeData(
|
||||
thickness: thickness ?? this.thickness,
|
||||
trackVisibility: trackVisibility ?? this.trackVisibility,
|
||||
showTrackOnHover: showTrackOnHover ?? this.showTrackOnHover,
|
||||
isAlwaysShown: isAlwaysShown ?? this.isAlwaysShown,
|
||||
interactive: interactive ?? this.interactive,
|
||||
@ -157,9 +164,10 @@ class ScrollbarThemeData with Diagnosticable {
|
||||
assert(t != null);
|
||||
return ScrollbarThemeData(
|
||||
thickness: _lerpProperties<double?>(a?.thickness, b?.thickness, t, lerpDouble),
|
||||
showTrackOnHover: t < 0.5 ? a?.showTrackOnHover : b?.showTrackOnHover,
|
||||
isAlwaysShown: t < 0.5 ? a?.isAlwaysShown : b?.isAlwaysShown,
|
||||
interactive: t < 0.5 ? a?.interactive : b?.interactive,
|
||||
trackVisibility: _lerpProperties<bool?>(a?.trackVisibility, b?.trackVisibility, t, _lerpBool),
|
||||
showTrackOnHover: _lerpBool(a?.showTrackOnHover, b?.showTrackOnHover, t),
|
||||
isAlwaysShown: _lerpBool(a?.isAlwaysShown, b?.isAlwaysShown, t),
|
||||
interactive: _lerpBool(a?.interactive, b?.interactive, t),
|
||||
radius: Radius.lerp(a?.radius, b?.radius, t),
|
||||
thumbColor: _lerpProperties<Color?>(a?.thumbColor, b?.thumbColor, t, Color.lerp),
|
||||
trackColor: _lerpProperties<Color?>(a?.trackColor, b?.trackColor, t, Color.lerp),
|
||||
@ -174,6 +182,7 @@ class ScrollbarThemeData with Diagnosticable {
|
||||
int get hashCode {
|
||||
return hashValues(
|
||||
thickness,
|
||||
trackVisibility,
|
||||
showTrackOnHover,
|
||||
isAlwaysShown,
|
||||
interactive,
|
||||
@ -195,6 +204,7 @@ class ScrollbarThemeData with Diagnosticable {
|
||||
return false;
|
||||
return other is ScrollbarThemeData
|
||||
&& other.thickness == thickness
|
||||
&& other.trackVisibility == trackVisibility
|
||||
&& other.showTrackOnHover == showTrackOnHover
|
||||
&& other.isAlwaysShown == isAlwaysShown
|
||||
&& other.interactive == interactive
|
||||
@ -211,6 +221,7 @@ class ScrollbarThemeData with Diagnosticable {
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<MaterialStateProperty<double?>>('thickness', thickness, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<MaterialStateProperty<bool?>>('trackVisibility', trackVisibility, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('showTrackOnHover', showTrackOnHover, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('isAlwaysShown', isAlwaysShown, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('interactive', interactive, defaultValue: null));
|
||||
@ -252,6 +263,8 @@ class _LerpProperties<T> implements MaterialStateProperty<T> {
|
||||
}
|
||||
}
|
||||
|
||||
bool? _lerpBool(bool? a, bool? b, double t) => t < 0.5 ? a : b;
|
||||
|
||||
/// Applies a scrollbar theme to descendant [Scrollbar] widgets.
|
||||
///
|
||||
/// Descendant widgets obtain the current theme's [ScrollbarThemeData] using
|
||||
|
@ -574,6 +574,52 @@ void main() {
|
||||
}),
|
||||
);
|
||||
|
||||
testWidgets('ScrollbarThemeData.trackVisibility test', (WidgetTester tester) async {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
bool? _getTrackVisibility(Set<MaterialState> states) {
|
||||
return true;
|
||||
}
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData().copyWith(
|
||||
scrollbarTheme: _scrollbarTheme(
|
||||
trackVisibility: MaterialStateProperty.resolveWith(_getTrackVisibility),
|
||||
),
|
||||
),
|
||||
home: ScrollConfiguration(
|
||||
behavior: const NoScrollbarBehavior(),
|
||||
child: Scrollbar(
|
||||
isAlwaysShown: true,
|
||||
showTrackOnHover: true,
|
||||
controller: scrollController,
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
child: const SizedBox(width: 4000.0, height: 4000.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.byType(Scrollbar),
|
||||
paints
|
||||
..rect(color: const Color(0x08000000))
|
||||
..line(
|
||||
strokeWidth: 1.0,
|
||||
color: const Color(0x1a000000),
|
||||
)
|
||||
..rrect(color: const Color(0xff4caf50)),
|
||||
);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
||||
TargetPlatform.linux,
|
||||
TargetPlatform.macOS,
|
||||
TargetPlatform.windows,
|
||||
TargetPlatform.fuchsia,
|
||||
}),
|
||||
);
|
||||
|
||||
testWidgets('Default ScrollbarTheme debugFillProperties', (WidgetTester tester) async {
|
||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||
const ScrollbarThemeData().debugFillProperties(builder);
|
||||
@ -636,6 +682,7 @@ class NoScrollbarBehavior extends ScrollBehavior {
|
||||
|
||||
ScrollbarThemeData _scrollbarTheme({
|
||||
MaterialStateProperty<double?>? thickness,
|
||||
MaterialStateProperty<bool?>? trackVisibility,
|
||||
bool showTrackOnHover = true,
|
||||
bool isAlwaysShown = true,
|
||||
Radius radius = const Radius.circular(6.0),
|
||||
@ -648,6 +695,7 @@ ScrollbarThemeData _scrollbarTheme({
|
||||
}) {
|
||||
return ScrollbarThemeData(
|
||||
thickness: thickness ?? MaterialStateProperty.resolveWith(_getThickness),
|
||||
trackVisibility: trackVisibility,
|
||||
showTrackOnHover: showTrackOnHover,
|
||||
isAlwaysShown: isAlwaysShown,
|
||||
radius: radius,
|
||||
|
Loading…
x
Reference in New Issue
Block a user