Skip to content

Commit

Permalink
update the scrollbar that support always show the track even not on h…
Browse files Browse the repository at this point in the history
…over (#90178)
  • Loading branch information
xu-baolin committed Dec 4, 2021
1 parent d0e0052 commit 313b5f3
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 7 deletions.
34 changes: 30 additions & 4 deletions packages/flutter/lib/src/material/scrollbar.dart
Expand Up @@ -77,6 +77,7 @@ class Scrollbar extends StatelessWidget {
required this.child,
this.controller,
this.isAlwaysShown,
this.trackVisibility,
this.showTrackOnHover,
this.hoverThickness,
this.thickness,
Expand All @@ -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
Expand Down Expand Up @@ -153,6 +169,7 @@ class Scrollbar extends StatelessWidget {
return _MaterialScrollbar(
controller: controller,
isAlwaysShown: isAlwaysShown,
trackVisibility: trackVisibility,
showTrackOnHover: showTrackOnHover,
hoverThickness: hoverThickness,
thickness: thickness,
Expand All @@ -171,6 +188,7 @@ class _MaterialScrollbar extends RawScrollbar {
required Widget child,
ScrollController? controller,
bool? isAlwaysShown,
this.trackVisibility,
this.showTrackOnHover,
this.hoverThickness,
double? thickness,
Expand All @@ -193,6 +211,7 @@ class _MaterialScrollbar extends RawScrollbar {
scrollbarOrientation: scrollbarOrientation,
);

final bool? trackVisibility;
final bool? showTrackOnHover;
final double? hoverThickness;

Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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;
Expand Down
19 changes: 16 additions & 3 deletions packages/flutter/lib/src/material/scrollbar_theme.dart
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -122,6 +127,7 @@ class ScrollbarThemeData with Diagnosticable {
/// new values.
ScrollbarThemeData copyWith({
MaterialStateProperty<double?>? thickness,
MaterialStateProperty<bool?>? trackVisibility,
bool? showTrackOnHover,
bool? isAlwaysShown,
bool? interactive,
Expand All @@ -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,
Expand All @@ -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),
Expand All @@ -174,6 +182,7 @@ class ScrollbarThemeData with Diagnosticable {
int get hashCode {
return hashValues(
thickness,
trackVisibility,
showTrackOnHover,
isAlwaysShown,
interactive,
Expand All @@ -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
Expand All @@ -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));
Expand Down Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions packages/flutter/test/material/scrollbar_theme_test.dart
Expand Up @@ -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);
Expand Down Expand Up @@ -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),
Expand All @@ -648,6 +695,7 @@ ScrollbarThemeData _scrollbarTheme({
}) {
return ScrollbarThemeData(
thickness: thickness ?? MaterialStateProperty.resolveWith(_getThickness),
trackVisibility: trackVisibility,
showTrackOnHover: showTrackOnHover,
isAlwaysShown: isAlwaysShown,
radius: radius,
Expand Down

0 comments on commit 313b5f3

Please sign in to comment.