Unverified Commit 03da339f authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Deprecate Scrollbar isAlwaysShown -> thumbVisibility (#96957)

parent 2f77111c
...@@ -45,7 +45,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> { ...@@ -45,7 +45,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> {
radius: const Radius.circular(34.0), radius: const Radius.circular(34.0),
radiusWhileDragging: Radius.zero, radiusWhileDragging: Radius.zero,
controller: _controllerOne, controller: _controllerOne,
isAlwaysShown: true, thumbVisibility: true,
child: ListView.builder( child: ListView.builder(
controller: _controllerOne, controller: _controllerOne,
itemCount: 120, itemCount: 120,
......
...@@ -39,7 +39,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> { ...@@ -39,7 +39,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scrollbar( return Scrollbar(
controller: _controllerOne, controller: _controllerOne,
isAlwaysShown: true, thumbVisibility: true,
child: GridView.builder( child: GridView.builder(
controller: _controllerOne, controller: _controllerOne,
itemCount: 120, itemCount: 120,
......
...@@ -41,7 +41,7 @@ class ScrollMetricsDemoState extends State<ScrollMetricsDemo> { ...@@ -41,7 +41,7 @@ class ScrollMetricsDemoState extends State<ScrollMetricsDemo> {
return false; return false;
}, },
child: Scrollbar( child: Scrollbar(
isAlwaysShown: true, thumbVisibility: true,
child: SizedBox( child: SizedBox(
height: windowSize, height: windowSize,
width: double.infinity, width: double.infinity,
......
...@@ -50,7 +50,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> { ...@@ -50,7 +50,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> {
// unique scroll controller to this scroll view prevents it // unique scroll controller to this scroll view prevents it
// from attaching to the PrimaryScrollController. // from attaching to the PrimaryScrollController.
child: Scrollbar( child: Scrollbar(
isAlwaysShown: true, thumbVisibility: true,
controller: _firstController, controller: _firstController,
child: ListView.builder( child: ListView.builder(
controller: _firstController, controller: _firstController,
...@@ -68,7 +68,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> { ...@@ -68,7 +68,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> {
// ScrollController, so it is using the // ScrollController, so it is using the
// PrimaryScrollController. // PrimaryScrollController.
child: Scrollbar( child: Scrollbar(
isAlwaysShown: true, thumbVisibility: true,
child: ListView.builder( child: ListView.builder(
itemCount: 100, itemCount: 100,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
......
...@@ -39,7 +39,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> { ...@@ -39,7 +39,7 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RawScrollbar( return RawScrollbar(
controller: _controllerOne, controller: _controllerOne,
isAlwaysShown: true, thumbVisibility: true,
child: GridView.builder( child: GridView.builder(
controller: _controllerOne, controller: _controllerOne,
itemCount: 120, itemCount: 120,
......
...@@ -33,7 +33,7 @@ class MyStatelessWidget extends StatelessWidget { ...@@ -33,7 +33,7 @@ class MyStatelessWidget extends StatelessWidget {
side: BorderSide(color: Colors.brown, width: 3.0)), side: BorderSide(color: Colors.brown, width: 3.0)),
thickness: 15.0, thickness: 15.0,
thumbColor: Colors.blue, thumbColor: Colors.blue,
isAlwaysShown: true, thumbVisibility: true,
child: ListView( child: ListView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
children: List<Text>.generate( children: List<Text>.generate(
......
...@@ -14,6 +14,110 @@ ...@@ -14,6 +14,110 @@
version: 1 version: 1
transforms: transforms:
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'cupertino.dart' ]
field: 'isAlwaysShown'
inClass: 'CupertinoScrollbar'
changes:
- kind: 'rename'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'cupertino.dart' ]
constructor: ''
inClass: 'CupertinoScrollbar'
changes:
- kind: 'renameParameter'
oldName: 'isAlwaysShown'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'material.dart' ]
field: 'isAlwaysShown'
inClass: 'Scrollbar'
changes:
- kind: 'rename'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'material.dart' ]
constructor: ''
inClass: 'Scrollbar'
changes:
- kind: 'renameParameter'
oldName: 'isAlwaysShown'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'material.dart' ]
field: 'isAlwaysShown'
inClass: 'ScrollbarThemeData'
changes:
- kind: 'rename'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'material.dart' ]
constructor: ''
inClass: 'ScrollbarThemeData'
changes:
- kind: 'renameParameter'
oldName: 'isAlwaysShown'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'material.dart' ]
method: 'copyWith'
inClass: 'ScrollbarThemeData'
changes:
- kind: 'renameParameter'
oldName: 'isAlwaysShown'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
field: 'isAlwaysShown'
inClass: 'RawScrollbar'
changes:
- kind: 'rename'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96957
- title: "Migrate to 'thumbVisibility'"
date: 2022-01-20
element:
uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
constructor: ''
inClass: 'RawScrollbar'
changes:
- kind: 'renameParameter'
oldName: 'isAlwaysShown'
newName: 'thumbVisibility'
# Changes made in https://github.com/flutter/flutter/pull/96115 # Changes made in https://github.com/flutter/flutter/pull/96115
- title: "Migrate 'Icons.pie_chart_outlined' to 'Icons.pie_chart_outline'" - title: "Migrate 'Icons.pie_chart_outlined' to 'Icons.pie_chart_outline'"
date: 2022-01-04 date: 2022-01-04
......
...@@ -51,9 +51,10 @@ const double _kScrollbarCrossAxisMargin = 3.0; ...@@ -51,9 +51,10 @@ const double _kScrollbarCrossAxisMargin = 3.0;
/// {@end-tool} /// {@end-tool}
/// ///
/// {@tool dartpad} /// {@tool dartpad}
/// When `isAlwaysShown` is true, the scrollbar thumb will remain visible without the /// When [thumbVisibility] is true, the scrollbar thumb will remain visible without the
/// fade animation. This requires that a [ScrollController] is provided to controller, /// fade animation. This requires that a [ScrollController] is provided to controller,
/// or that the [PrimaryScrollController] is available. /// or that the [PrimaryScrollController] is available. [isAlwaysShown] is
/// deprecated in favor of `thumbVisibility`.
/// ///
/// ** See code in examples/api/lib/cupertino/scrollbar/cupertino_scrollbar.1.dart ** /// ** See code in examples/api/lib/cupertino/scrollbar/cupertino_scrollbar.1.dart **
/// {@end-tool} /// {@end-tool}
...@@ -74,24 +75,34 @@ class CupertinoScrollbar extends RawScrollbar { ...@@ -74,24 +75,34 @@ class CupertinoScrollbar extends RawScrollbar {
Key? key, Key? key,
required Widget child, required Widget child,
ScrollController? controller, ScrollController? controller,
bool isAlwaysShown = false, bool? thumbVisibility,
double thickness = defaultThickness, double thickness = defaultThickness,
this.thicknessWhileDragging = defaultThicknessWhileDragging, this.thicknessWhileDragging = defaultThicknessWhileDragging,
Radius radius = defaultRadius, Radius radius = defaultRadius,
this.radiusWhileDragging = defaultRadiusWhileDragging, this.radiusWhileDragging = defaultRadiusWhileDragging,
ScrollNotificationPredicate? notificationPredicate, ScrollNotificationPredicate? notificationPredicate,
ScrollbarOrientation? scrollbarOrientation, ScrollbarOrientation? scrollbarOrientation,
@Deprecated(
'Use thumbVisibility instead. '
'This feature was deprecated after v2.9.0-1.0.pre.',
)
bool? isAlwaysShown,
}) : assert(thickness != null), }) : assert(thickness != null),
assert(thickness < double.infinity), assert(thickness < double.infinity),
assert(thicknessWhileDragging != null), assert(thicknessWhileDragging != null),
assert(thicknessWhileDragging < double.infinity), assert(thicknessWhileDragging < double.infinity),
assert(radius != null), assert(radius != null),
assert(radiusWhileDragging != null), assert(radiusWhileDragging != null),
assert(
isAlwaysShown == null || thumbVisibility == null,
'Scrollbar thumb appearance should only be controlled with thumbVisibility, '
'isAlwaysShown is deprecated.'
),
super( super(
key: key, key: key,
child: child, child: child,
controller: controller, controller: controller,
isAlwaysShown: isAlwaysShown, thumbVisibility: isAlwaysShown ?? thumbVisibility ?? false,
thickness: thickness, thickness: thickness,
radius: radius, radius: radius,
fadeDuration: _kScrollbarFadeDuration, fadeDuration: _kScrollbarFadeDuration,
......
...@@ -303,7 +303,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> { ...@@ -303,7 +303,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
child: PrimaryScrollController( child: PrimaryScrollController(
controller: widget.route.scrollController!, controller: widget.route.scrollController!,
child: Scrollbar( child: Scrollbar(
isAlwaysShown: true, thumbVisibility: true,
child: ListView( child: ListView(
padding: kMaterialListPadding, padding: kMaterialListPadding,
shrinkWrap: true, shrinkWrap: true,
......
...@@ -27,26 +27,30 @@ const Duration _kScrollbarTimeToFade = Duration(milliseconds: 600); ...@@ -27,26 +27,30 @@ const Duration _kScrollbarTimeToFade = Duration(milliseconds: 600);
/// ///
/// {@macro flutter.widgets.Scrollbar} /// {@macro flutter.widgets.Scrollbar}
/// ///
/// Dynamically changes to an iOS style scrollbar that looks like /// Dynamically changes to a [CupertinoScrollbar], an iOS style scrollbar, by
/// [CupertinoScrollbar] on the iOS platform. /// default on the iOS platform.
/// ///
/// The color of the Scrollbar will change when dragged. A hover animation is /// The color of the Scrollbar thumb will change when [MaterialState.dragged],
/// also triggered when used on web and desktop platforms. A scrollbar track /// or [MaterialState.hovered] on desktop and web platforms. These stateful
/// can also been drawn when triggered by a hover event, which is controlled by /// color choices can be changed using [ScrollbarThemeData.thumbColor].
/// [showTrackOnHover]. The thickness of the track and scrollbar thumb will ///
/// become larger when hovering, unless overridden by [hoverThickness]. /// A scrollbar track can been drawn when triggered by a hover event, which is
/// controlled by [showTrackOnHover]. The thickness of the track and scrollbar
/// thumb will become larger when hovering, unless overridden by
/// [hoverThickness].
/// ///
/// {@tool dartpad} /// {@tool dartpad}
/// This sample shows a [Scrollbar] that executes a fade animation as scrolling occurs. /// This sample shows a [Scrollbar] that executes a fade animation as scrolling
/// The Scrollbar will fade into view as the user scrolls, and fade out when scrolling stops. /// occurs. The Scrollbar will fade into view as the user scrolls, and fade out
/// when scrolling stops.
/// ///
/// ** See code in examples/api/lib/material/scrollbar/scrollbar.0.dart ** /// ** See code in examples/api/lib/material/scrollbar/scrollbar.0.dart **
/// {@end-tool} /// {@end-tool}
/// ///
/// {@tool dartpad} /// {@tool dartpad}
/// When isAlwaysShown is true, the scrollbar thumb will remain visible without the /// When [thumbVisibility] is true, the scrollbar thumb will remain visible
/// fade animation. This requires that a ScrollController is provided to controller, /// without the fade animation. This requires that a [ScrollController] is
/// or that the PrimaryScrollController is available. /// provided to controller, or that the [PrimaryScrollController] is available.
/// ///
/// ** See code in examples/api/lib/material/scrollbar/scrollbar.1.dart ** /// ** See code in examples/api/lib/material/scrollbar/scrollbar.1.dart **
/// {@end-tool} /// {@end-tool}
...@@ -78,7 +82,7 @@ class Scrollbar extends StatelessWidget { ...@@ -78,7 +82,7 @@ class Scrollbar extends StatelessWidget {
Key? key, Key? key,
required this.child, required this.child,
this.controller, this.controller,
this.isAlwaysShown, this.thumbVisibility,
this.trackVisibility, this.trackVisibility,
this.showTrackOnHover, this.showTrackOnHover,
this.hoverThickness, this.hoverThickness,
...@@ -87,7 +91,17 @@ class Scrollbar extends StatelessWidget { ...@@ -87,7 +91,17 @@ class Scrollbar extends StatelessWidget {
this.notificationPredicate, this.notificationPredicate,
this.interactive, this.interactive,
this.scrollbarOrientation, this.scrollbarOrientation,
}) : super(key: key); @Deprecated(
'Use thumbVisibility instead. '
'This feature was deprecated after v2.9.0-1.0.pre.',
)
this.isAlwaysShown,
}) : assert(
thumbVisibility == null || isAlwaysShown == null,
'Scrollbar thumb appearance should only be controlled with thumbVisibility, '
'isAlwaysShown is deprecated.'
),
super(key: key);
/// {@macro flutter.widgets.Scrollbar.child} /// {@macro flutter.widgets.Scrollbar.child}
final Widget child; final Widget child;
...@@ -95,7 +109,27 @@ class Scrollbar extends StatelessWidget { ...@@ -95,7 +109,27 @@ class Scrollbar extends StatelessWidget {
/// {@macro flutter.widgets.Scrollbar.controller} /// {@macro flutter.widgets.Scrollbar.controller}
final ScrollController? controller; final ScrollController? controller;
/// {@macro flutter.widgets.Scrollbar.thumbVisibility}
///
/// If this property is null, then [ScrollbarThemeData.thumbVisibility] of
/// [ThemeData.scrollbarTheme] is used. If that is also null, the default value
/// is false.
///
/// If the thumb visibility is related to the scrollbar's material state,
/// use the global [ScrollbarThemeData.thumbVisibility] or override the
/// sub-tree's theme data.
///
/// Replaces deprecated [isAlwaysShown].
final bool? thumbVisibility;
/// {@macro flutter.widgets.Scrollbar.isAlwaysShown} /// {@macro flutter.widgets.Scrollbar.isAlwaysShown}
///
/// To show the scrollbar thumb based on a [MaterialState], use
/// [ScrollbarThemeData.thumbVisibility].
@Deprecated(
'Use thumbVisibility instead. '
'This feature was deprecated after v2.9.0-1.0.pre.',
)
final bool? isAlwaysShown; final bool? isAlwaysShown;
/// Controls the track visibility. /// Controls the track visibility.
...@@ -157,7 +191,7 @@ class Scrollbar extends StatelessWidget { ...@@ -157,7 +191,7 @@ class Scrollbar extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (Theme.of(context).platform == TargetPlatform.iOS) { if (Theme.of(context).platform == TargetPlatform.iOS) {
return CupertinoScrollbar( return CupertinoScrollbar(
isAlwaysShown: isAlwaysShown ?? false, thumbVisibility: isAlwaysShown ?? thumbVisibility ?? false,
thickness: thickness ?? CupertinoScrollbar.defaultThickness, thickness: thickness ?? CupertinoScrollbar.defaultThickness,
thicknessWhileDragging: thickness ?? CupertinoScrollbar.defaultThicknessWhileDragging, thicknessWhileDragging: thickness ?? CupertinoScrollbar.defaultThicknessWhileDragging,
radius: radius ?? CupertinoScrollbar.defaultRadius, radius: radius ?? CupertinoScrollbar.defaultRadius,
...@@ -170,7 +204,7 @@ class Scrollbar extends StatelessWidget { ...@@ -170,7 +204,7 @@ class Scrollbar extends StatelessWidget {
} }
return _MaterialScrollbar( return _MaterialScrollbar(
controller: controller, controller: controller,
isAlwaysShown: isAlwaysShown, thumbVisibility: isAlwaysShown ?? thumbVisibility,
trackVisibility: trackVisibility, trackVisibility: trackVisibility,
showTrackOnHover: showTrackOnHover, showTrackOnHover: showTrackOnHover,
hoverThickness: hoverThickness, hoverThickness: hoverThickness,
...@@ -189,7 +223,7 @@ class _MaterialScrollbar extends RawScrollbar { ...@@ -189,7 +223,7 @@ class _MaterialScrollbar extends RawScrollbar {
Key? key, Key? key,
required Widget child, required Widget child,
ScrollController? controller, ScrollController? controller,
bool? isAlwaysShown, bool? thumbVisibility,
this.trackVisibility, this.trackVisibility,
this.showTrackOnHover, this.showTrackOnHover,
this.hoverThickness, this.hoverThickness,
...@@ -202,7 +236,7 @@ class _MaterialScrollbar extends RawScrollbar { ...@@ -202,7 +236,7 @@ class _MaterialScrollbar extends RawScrollbar {
key: key, key: key,
child: child, child: child,
controller: controller, controller: controller,
isAlwaysShown: isAlwaysShown, thumbVisibility: thumbVisibility,
thickness: thickness, thickness: thickness,
radius: radius, radius: radius,
fadeDuration: _kScrollbarFadeDuration, fadeDuration: _kScrollbarFadeDuration,
...@@ -231,7 +265,7 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> { ...@@ -231,7 +265,7 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> {
late bool _useAndroidScrollbar; late bool _useAndroidScrollbar;
@override @override
bool get showScrollbar => widget.isAlwaysShown ?? _scrollbarTheme.isAlwaysShown ?? false; bool get showScrollbar => widget.thumbVisibility ?? _scrollbarTheme.thumbVisibility?.resolve(_states) ?? _scrollbarTheme.isAlwaysShown ?? false;
@override @override
bool get enableGestures => widget.interactive ?? _scrollbarTheme.interactive ?? !_useAndroidScrollbar; bool get enableGestures => widget.interactive ?? _scrollbarTheme.interactive ?? !_useAndroidScrollbar;
......
...@@ -13,15 +13,15 @@ import 'theme.dart'; ...@@ -13,15 +13,15 @@ import 'theme.dart';
/// Defines default property values for descendant [Scrollbar] widgets. /// Defines default property values for descendant [Scrollbar] widgets.
/// ///
/// Descendant widgets obtain the current [ScrollbarThemeData] object with /// Descendant widgets obtain the current [ScrollbarThemeData] object with
/// `ScrollbarTheme.of(context)`. Instances of [ScrollbarThemeData] can be customized /// `ScrollbarTheme.of(context)`. Instances of [ScrollbarThemeData] can be
/// with [ScrollbarThemeData.copyWith]. /// customized with [ScrollbarThemeData.copyWith].
/// ///
/// Typically the [ScrollbarThemeData] of a [ScrollbarTheme] is specified as part of the overall /// Typically the [ScrollbarThemeData] of a [ScrollbarTheme] is specified as
/// [Theme] with [ThemeData.scrollbarTheme]. /// part of the overall [Theme] with [ThemeData.scrollbarTheme].
/// ///
/// All [ScrollbarThemeData] properties are `null` by default. When null, the [Scrollbar] /// All [ScrollbarThemeData] properties are `null` by default. When null, the
/// computes its own default values, typically based on the overall theme's /// [Scrollbar] computes its own default values, typically based on the overall
/// [ThemeData.colorScheme]. /// theme's [ThemeData.colorScheme].
/// ///
/// See also: /// See also:
/// ///
...@@ -31,10 +31,10 @@ import 'theme.dart'; ...@@ -31,10 +31,10 @@ import 'theme.dart';
class ScrollbarThemeData with Diagnosticable { class ScrollbarThemeData with Diagnosticable {
/// Creates a theme that can be used for [ThemeData.scrollbarTheme]. /// Creates a theme that can be used for [ThemeData.scrollbarTheme].
const ScrollbarThemeData({ const ScrollbarThemeData({
this.thumbVisibility,
this.thickness, this.thickness,
this.trackVisibility, this.trackVisibility,
this.showTrackOnHover, this.showTrackOnHover,
this.isAlwaysShown,
this.radius, this.radius,
this.thumbColor, this.thumbColor,
this.trackColor, this.trackColor,
...@@ -43,7 +43,22 @@ class ScrollbarThemeData with Diagnosticable { ...@@ -43,7 +43,22 @@ class ScrollbarThemeData with Diagnosticable {
this.mainAxisMargin, this.mainAxisMargin,
this.minThumbLength, this.minThumbLength,
this.interactive, this.interactive,
}); @Deprecated(
'Use thumbVisibility instead. '
'This feature was deprecated after v2.9.0-1.0.pre.',
)
this.isAlwaysShown,
}) : assert(
isAlwaysShown == null || thumbVisibility == null,
'Scrollbar thumb appearance should only be controlled with thumbVisibility, '
'isAlwaysShown is deprecated.'
);
/// Overrides the default value of [Scrollbar.thumbVisibility] in all
/// descendant [Scrollbar] widgets.
///
/// Replaces deprecated [isAlwaysShown].
final MaterialStateProperty<bool?>? thumbVisibility;
/// Overrides the default value of [Scrollbar.thickness] in all /// Overrides the default value of [Scrollbar.thickness] in all
/// descendant [Scrollbar] widgets. /// descendant [Scrollbar] widgets.
...@@ -62,6 +77,12 @@ class ScrollbarThemeData with Diagnosticable { ...@@ -62,6 +77,12 @@ class ScrollbarThemeData with Diagnosticable {
/// Overrides the default value of [Scrollbar.isAlwaysShown] in all /// Overrides the default value of [Scrollbar.isAlwaysShown] in all
/// descendant [Scrollbar] widgets. /// descendant [Scrollbar] widgets.
///
/// Deprecated in favor of [thumbVisibility].
@Deprecated(
'Use thumbVisibility instead. '
'This feature was deprecated after v2.9.0-1.0.pre.',
)
final bool? isAlwaysShown; final bool? isAlwaysShown;
/// Overrides the default value of [Scrollbar.interactive] in all /// Overrides the default value of [Scrollbar.interactive] in all
...@@ -126,10 +147,10 @@ class ScrollbarThemeData with Diagnosticable { ...@@ -126,10 +147,10 @@ class ScrollbarThemeData with Diagnosticable {
/// Creates a copy of this object with the given fields replaced with the /// Creates a copy of this object with the given fields replaced with the
/// new values. /// new values.
ScrollbarThemeData copyWith({ ScrollbarThemeData copyWith({
MaterialStateProperty<bool?>? thumbVisibility,
MaterialStateProperty<double?>? thickness, MaterialStateProperty<double?>? thickness,
MaterialStateProperty<bool?>? trackVisibility, MaterialStateProperty<bool?>? trackVisibility,
bool? showTrackOnHover, bool? showTrackOnHover,
bool? isAlwaysShown,
bool? interactive, bool? interactive,
Radius? radius, Radius? radius,
MaterialStateProperty<Color?>? thumbColor, MaterialStateProperty<Color?>? thumbColor,
...@@ -138,8 +159,14 @@ class ScrollbarThemeData with Diagnosticable { ...@@ -138,8 +159,14 @@ class ScrollbarThemeData with Diagnosticable {
double? crossAxisMargin, double? crossAxisMargin,
double? mainAxisMargin, double? mainAxisMargin,
double? minThumbLength, double? minThumbLength,
@Deprecated(
'Use thumbVisibility instead. '
'This feature was deprecated after v2.9.0-1.0.pre.',
)
bool? isAlwaysShown,
}) { }) {
return ScrollbarThemeData( return ScrollbarThemeData(
thumbVisibility: thumbVisibility ?? this.thumbVisibility,
thickness: thickness ?? this.thickness, thickness: thickness ?? this.thickness,
trackVisibility: trackVisibility ?? this.trackVisibility, trackVisibility: trackVisibility ?? this.trackVisibility,
showTrackOnHover: showTrackOnHover ?? this.showTrackOnHover, showTrackOnHover: showTrackOnHover ?? this.showTrackOnHover,
...@@ -163,6 +190,7 @@ class ScrollbarThemeData with Diagnosticable { ...@@ -163,6 +190,7 @@ class ScrollbarThemeData with Diagnosticable {
static ScrollbarThemeData lerp(ScrollbarThemeData? a, ScrollbarThemeData? b, double t) { static ScrollbarThemeData lerp(ScrollbarThemeData? a, ScrollbarThemeData? b, double t) {
assert(t != null); assert(t != null);
return ScrollbarThemeData( return ScrollbarThemeData(
thumbVisibility: _lerpProperties<bool?>(a?.thumbVisibility, b?.thumbVisibility, t, _lerpBool),
thickness: _lerpProperties<double?>(a?.thickness, b?.thickness, t, lerpDouble), thickness: _lerpProperties<double?>(a?.thickness, b?.thickness, t, lerpDouble),
trackVisibility: _lerpProperties<bool?>(a?.trackVisibility, b?.trackVisibility, t, _lerpBool), trackVisibility: _lerpProperties<bool?>(a?.trackVisibility, b?.trackVisibility, t, _lerpBool),
showTrackOnHover: _lerpBool(a?.showTrackOnHover, b?.showTrackOnHover, t), showTrackOnHover: _lerpBool(a?.showTrackOnHover, b?.showTrackOnHover, t),
...@@ -181,6 +209,7 @@ class ScrollbarThemeData with Diagnosticable { ...@@ -181,6 +209,7 @@ class ScrollbarThemeData with Diagnosticable {
@override @override
int get hashCode { int get hashCode {
return hashValues( return hashValues(
thumbVisibility,
thickness, thickness,
trackVisibility, trackVisibility,
showTrackOnHover, showTrackOnHover,
...@@ -203,6 +232,7 @@ class ScrollbarThemeData with Diagnosticable { ...@@ -203,6 +232,7 @@ class ScrollbarThemeData with Diagnosticable {
if (other.runtimeType != runtimeType) if (other.runtimeType != runtimeType)
return false; return false;
return other is ScrollbarThemeData return other is ScrollbarThemeData
&& other.thumbVisibility == thumbVisibility
&& other.thickness == thickness && other.thickness == thickness
&& other.trackVisibility == trackVisibility && other.trackVisibility == trackVisibility
&& other.showTrackOnHover == showTrackOnHover && other.showTrackOnHover == showTrackOnHover
...@@ -220,6 +250,7 @@ class ScrollbarThemeData with Diagnosticable { ...@@ -220,6 +250,7 @@ class ScrollbarThemeData with Diagnosticable {
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties); super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<MaterialStateProperty<bool?>>('thumbVisibility', thumbVisibility, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<double?>>('thickness', thickness, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<double?>>('thickness', thickness, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<bool?>>('trackVisibility', trackVisibility, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<bool?>>('trackVisibility', trackVisibility, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('showTrackOnHover', showTrackOnHover, defaultValue: null)); properties.add(DiagnosticsProperty<bool>('showTrackOnHover', showTrackOnHover, defaultValue: null));
......
...@@ -767,7 +767,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { ...@@ -767,7 +767,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
/// visible. /// visible.
/// ///
/// By default, the thumb will fade in and out as the child scroll view /// By default, the thumb will fade in and out as the child scroll view
/// scrolls. When [isAlwaysShown] is true, the scrollbar thumb will remain /// scrolls. When [thumbVisibility] is true, the scrollbar thumb will remain
/// visible without the fade animation. This requires that the [ScrollController] /// visible without the fade animation. This requires that the [ScrollController]
/// associated with the Scrollable widget is provided to [controller], or that /// associated with the Scrollable widget is provided to [controller], or that
/// the [PrimaryScrollController] is being used by that Scrollable widget. /// the [PrimaryScrollController] is being used by that Scrollable widget.
...@@ -843,7 +843,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { ...@@ -843,7 +843,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
/// {@end-tool} /// {@end-tool}
/// ///
/// {@tool dartpad} /// {@tool dartpad}
/// When `isAlwaysShown` is true, the scrollbar thumb will remain visible without /// When `thumbVisibility` is true, the scrollbar thumb will remain visible without
/// the fade animation. This requires that a [ScrollController] is provided to /// the fade animation. This requires that a [ScrollController] is provided to
/// `controller` for both the [RawScrollbar] and the [GridView]. /// `controller` for both the [RawScrollbar] and the [GridView].
/// Alternatively, the [PrimaryScrollController] can be used automatically so long /// Alternatively, the [PrimaryScrollController] can be used automatically so long
...@@ -868,7 +868,7 @@ class RawScrollbar extends StatefulWidget { ...@@ -868,7 +868,7 @@ class RawScrollbar extends StatefulWidget {
Key? key, Key? key,
required this.child, required this.child,
this.controller, this.controller,
this.isAlwaysShown, this.thumbVisibility,
this.shape, this.shape,
this.radius, this.radius,
this.thickness, this.thickness,
...@@ -882,8 +882,18 @@ class RawScrollbar extends StatefulWidget { ...@@ -882,8 +882,18 @@ class RawScrollbar extends StatefulWidget {
this.interactive, this.interactive,
this.scrollbarOrientation, this.scrollbarOrientation,
this.mainAxisMargin = 0.0, this.mainAxisMargin = 0.0,
this.crossAxisMargin = 0.0 this.crossAxisMargin = 0.0,
@Deprecated(
'Use thumbVisibility instead. '
'This feature was deprecated after v2.9.0-1.0.pre.',
)
this.isAlwaysShown,
}) : assert(child != null), }) : assert(child != null),
assert(
thumbVisibility == null || isAlwaysShown == null,
'Scrollbar thumb appearance should only be controlled with thumbVisibility, '
'isAlwaysShown is deprecated.'
),
assert(minThumbLength != null), assert(minThumbLength != null),
assert(minThumbLength >= 0), assert(minThumbLength >= 0),
assert(minOverscrollLength == null || minOverscrollLength <= minThumbLength), assert(minOverscrollLength == null || minOverscrollLength <= minThumbLength),
...@@ -962,9 +972,91 @@ class RawScrollbar extends StatefulWidget { ...@@ -962,9 +972,91 @@ class RawScrollbar extends StatefulWidget {
/// {@endtemplate} /// {@endtemplate}
final ScrollController? controller; final ScrollController? controller;
/// {@template flutter.widgets.Scrollbar.thumbVisibility}
/// Indicates that the scrollbar thumb should be visible, even when a scroll
/// is not underway.
///
/// When false, the scrollbar will be shown during scrolling
/// and will fade out otherwise.
///
/// When true, the scrollbar will always be visible and never fade out. This
/// requires that the Scrollbar can access the [ScrollController] of the
/// associated Scrollable widget. This can either be the provided [controller],
/// or the [PrimaryScrollController] of the current context.
///
/// * When providing a controller, the same ScrollController must also be
/// provided to the associated Scrollable widget.
/// * The [PrimaryScrollController] is used by default for a [ScrollView]
/// that has not been provided a [ScrollController] and that has an
/// [Axis.vertical] [ScrollDirection]. This automatic behavior does not
/// apply to those with a ScrollDirection of Axis.horizontal. To explicitly
/// use the PrimaryScrollController, set [ScrollView.primary] to true.
///
/// Defaults to false when null.
///
/// {@tool snippet}
///
/// ```dart
/// final ScrollController _controllerOne = ScrollController();
/// final ScrollController _controllerTwo = ScrollController();
///
/// Widget build(BuildContext context) {
/// return Column(
/// children: <Widget>[
/// SizedBox(
/// height: 200,
/// child: Scrollbar(
/// thumbVisibility: true,
/// controller: _controllerOne,
/// child: ListView.builder(
/// controller: _controllerOne,
/// itemCount: 120,
/// itemBuilder: (BuildContext context, int index) {
/// return Text('item $index');
/// },
/// ),
/// ),
/// ),
/// SizedBox(
/// height: 200,
/// child: CupertinoScrollbar(
/// thumbVisibility: true,
/// controller: _controllerTwo,
/// child: SingleChildScrollView(
/// controller: _controllerTwo,
/// child: const SizedBox(
/// height: 2000,
/// width: 500,
/// child: Placeholder(),
/// ),
/// ),
/// ),
/// ),
/// ],
/// );
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [RawScrollbarState.showScrollbar], an overridable getter which uses
/// this value to override the default behavior.
/// * [ScrollView.primary], which indicates whether the ScrollView is the primary
/// scroll view associated with the parent [PrimaryScrollController].
/// * [PrimaryScrollController], which associates a [ScrollController] with
/// a subtree.
///
/// Replaces deprecated [isAlwaysShown].
/// {@endtemplate}
///
/// Subclass [Scrollbar] can hide and show the scrollbar thumb in response to
/// [MaterialState]s by using [ScrollbarThemeData.thumbVisibility].
final bool? thumbVisibility;
/// {@template flutter.widgets.Scrollbar.isAlwaysShown} /// {@template flutter.widgets.Scrollbar.isAlwaysShown}
/// Indicates that the scrollbar should be visible, even when a scroll is not /// Indicates that the scrollbar thumb should be visible, even when a scroll
/// underway. /// is not underway.
/// ///
/// When false, the scrollbar will be shown during scrolling /// When false, the scrollbar will be shown during scrolling
/// and will fade out otherwise. /// and will fade out otherwise.
...@@ -996,7 +1088,7 @@ class RawScrollbar extends StatefulWidget { ...@@ -996,7 +1088,7 @@ class RawScrollbar extends StatefulWidget {
/// SizedBox( /// SizedBox(
/// height: 200, /// height: 200,
/// child: Scrollbar( /// child: Scrollbar(
/// isAlwaysShown: true, /// thumbVisibility: true,
/// controller: _controllerOne, /// controller: _controllerOne,
/// child: ListView.builder( /// child: ListView.builder(
/// controller: _controllerOne, /// controller: _controllerOne,
...@@ -1010,7 +1102,7 @@ class RawScrollbar extends StatefulWidget { ...@@ -1010,7 +1102,7 @@ class RawScrollbar extends StatefulWidget {
/// SizedBox( /// SizedBox(
/// height: 200, /// height: 200,
/// child: CupertinoScrollbar( /// child: CupertinoScrollbar(
/// isAlwaysShown: true, /// thumbVisibility: true,
/// controller: _controllerTwo, /// controller: _controllerTwo,
/// child: SingleChildScrollView( /// child: SingleChildScrollView(
/// controller: _controllerTwo, /// controller: _controllerTwo,
...@@ -1036,7 +1128,13 @@ class RawScrollbar extends StatefulWidget { ...@@ -1036,7 +1128,13 @@ class RawScrollbar extends StatefulWidget {
/// scroll view associated with the parent [PrimaryScrollController]. /// scroll view associated with the parent [PrimaryScrollController].
/// * [PrimaryScrollController], which associates a [ScrollController] with /// * [PrimaryScrollController], which associates a [ScrollController] with
/// a subtree. /// a subtree.
///
/// This is deprecated, [thumbVisibility] should be used instead.
/// {@endtemplate} /// {@endtemplate}
@Deprecated(
'Use thumbVisibility instead. '
'This feature was deprecated after v2.9.0-1.0.pre.',
)
final bool? isAlwaysShown; final bool? isAlwaysShown;
/// The [OutlinedBorder] of the scrollbar's thumb. /// The [OutlinedBorder] of the scrollbar's thumb.
...@@ -1197,13 +1295,14 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv ...@@ -1197,13 +1295,14 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
/// Subclasses can override this getter to make its value depend on an inherited /// Subclasses can override this getter to make its value depend on an inherited
/// theme. /// theme.
/// ///
/// Defaults to false when [RawScrollbar.isAlwaysShown] is null. /// Defaults to false when [RawScrollbar.isAlwaysShown] or
/// [RawScrollbar.thumbVisibility] is null.
/// ///
/// See also: /// See also:
/// ///
/// * [RawScrollbar.isAlwaysShown], which overrides the default behavior. /// * [RawScrollbar.isAlwaysShown], which overrides the default behavior.
@protected @protected
bool get showScrollbar => widget.isAlwaysShown ?? false; bool get showScrollbar => widget.isAlwaysShown ?? widget.thumbVisibility ?? false;
/// Overridable getter to indicate is gestures should be enabled on the /// Overridable getter to indicate is gestures should be enabled on the
/// scrollbar. /// scrollbar.
...@@ -1283,8 +1382,10 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv ...@@ -1283,8 +1382,10 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
: 'provided ScrollController'; : 'provided ScrollController';
String when = ''; String when = '';
if (showScrollbar) { if (widget.isAlwaysShown ?? false) {
when = 'Scrollbar.isAlwaysShown is true'; when = 'Scrollbar.isAlwaysShown is true';
} else if (widget.thumbVisibility ?? false) {
when = 'Scrollbar.thumbVisibility is true';
} else if (enableGestures) { } else if (enableGestures) {
when = 'the scrollbar is interactive'; when = 'the scrollbar is interactive';
} else { } else {
...@@ -1385,8 +1486,9 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv ...@@ -1385,8 +1486,9 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
@override @override
void didUpdateWidget(T oldWidget) { void didUpdateWidget(T oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.isAlwaysShown != oldWidget.isAlwaysShown) { if (widget.isAlwaysShown != oldWidget.isAlwaysShown
if (widget.isAlwaysShown == true) { || widget.thumbVisibility != oldWidget.thumbVisibility) {
if (widget.isAlwaysShown == true || widget.thumbVisibility == true) {
assert(_debugScheduleCheckHasValidScrollPosition()); assert(_debugScheduleCheckHasValidScrollPosition());
_fadeoutTimer?.cancel(); _fadeoutTimer?.cancel();
_fadeoutAnimationController.animateTo(1.0); _fadeoutAnimationController.animateTo(1.0);
......
...@@ -340,9 +340,66 @@ void main() { ...@@ -340,9 +340,66 @@ void main() {
await tester.pump(kScrollbarFadeDuration); await tester.pump(kScrollbarFadeDuration);
}); });
testWidgets( testWidgets('When thumbVisibility is true, must pass a controller or find PrimaryScrollController', (WidgetTester tester) async {
'When isAlwaysShown is true, must pass a controller or find PrimaryScrollController', Widget viewWithScroll() {
(WidgetTester tester) async { return const Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: MediaQueryData(),
child: CupertinoScrollbar(
thumbVisibility: true,
child: SingleChildScrollView(
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
),
),
);
}
await tester.pumpWidget(viewWithScroll());
final AssertionError exception = tester.takeException() as AssertionError;
expect(exception, isAssertionError);
},
);
testWidgets('When thumbVisibility is true, must pass a controller or find PrimaryScrollController that is attached to a scroll view', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
Widget viewWithScroll() {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: CupertinoScrollbar(
controller: controller,
thumbVisibility: true,
child: const SingleChildScrollView(
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
),
),
);
}
final FlutterExceptionHandler? handler = FlutterError.onError;
FlutterErrorDetails? error;
FlutterError.onError = (FlutterErrorDetails details) {
error = details;
};
await tester.pumpWidget(viewWithScroll());
expect(error, isNotNull);
FlutterError.onError = handler;
},
);
testWidgets('When isAlwaysShown is true, must pass a controller or find PrimaryScrollController', (WidgetTester tester) async {
Widget viewWithScroll() { Widget viewWithScroll() {
return const Directionality( return const Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -367,10 +424,7 @@ void main() { ...@@ -367,10 +424,7 @@ void main() {
}, },
); );
testWidgets( testWidgets('When isAlwaysShown is true, must pass a controller or find PrimaryScrollController that is attached to a scroll view', (WidgetTester tester) async {
'When isAlwaysShown is true, '
'must pass a controller or find PrimaryScrollController that is attached to a scroll view',
(WidgetTester tester) async {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
Widget viewWithScroll() { Widget viewWithScroll() {
return Directionality( return Directionality(
...@@ -404,9 +458,74 @@ void main() { ...@@ -404,9 +458,74 @@ void main() {
}, },
); );
testWidgets( testWidgets('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
'On first render with isAlwaysShown: true, the thumb shows with PrimaryScrollController', final ScrollController controller = ScrollController();
(WidgetTester tester) async { Widget viewWithScroll() {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: PrimaryScrollController(
controller: controller,
child: Builder(
builder: (BuildContext context) {
return const CupertinoScrollbar(
thumbVisibility: true,
child: SingleChildScrollView(
primary: true,
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
);
},
),
),
),
);
}
await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle();
expect(find.byType(CupertinoScrollbar), paints..rect());
},
);
testWidgets('On first render with thumbVisibility: true, the thumb shows', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
Widget viewWithScroll() {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: PrimaryScrollController(
controller: controller,
child: CupertinoScrollbar(
thumbVisibility: true,
controller: controller,
child: const SingleChildScrollView(
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
),
),
),
);
}
await tester.pumpWidget(viewWithScroll());
// The scrollbar measures its size on the first frame
// and renders starting in the second,
//
// so pumpAndSettle a frame to allow it to appear.
await tester.pumpAndSettle();
expect(find.byType(CupertinoScrollbar), paints..rrect());
});
testWidgets('On first render with isAlwaysShown: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
Widget viewWithScroll() { Widget viewWithScroll() {
return Directionality( return Directionality(
...@@ -501,9 +620,7 @@ void main() { ...@@ -501,9 +620,7 @@ void main() {
expect(find.byType(CupertinoScrollbar), isNot(paints..rect())); expect(find.byType(CupertinoScrollbar), isNot(paints..rect()));
}); });
testWidgets( testWidgets('With isAlwaysShown: true, fling a scroll. While it is still scrolling, set isAlwaysShown: false. The thumb should not fade out until the scrolling stops.', (WidgetTester tester) async {
'With isAlwaysShown: true, fling a scroll. While it is still scrolling, set isAlwaysShown: false. The thumb should not fade out until the scrolling stops.',
(WidgetTester tester) async {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
bool isAlwaysShown = true; bool isAlwaysShown = true;
Widget viewWithScroll() { Widget viewWithScroll() {
......
...@@ -159,6 +159,112 @@ void main() { ...@@ -159,6 +159,112 @@ void main() {
expect(canvas.invocations.isEmpty, isTrue); expect(canvas.invocations.isEmpty, isTrue);
}); });
testWidgets('When thumbVisibility is true, must pass a controller or find PrimaryScrollController', (WidgetTester tester) async {
Widget viewWithScroll() {
return _buildBoilerplate(
child: Theme(
data: ThemeData(),
child: const Scrollbar(
thumbVisibility: true,
child: SingleChildScrollView(
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
),
),
);
}
await tester.pumpWidget(viewWithScroll());
final AssertionError exception = tester.takeException() as AssertionError;
expect(exception, isAssertionError);
});
testWidgets('When thumbVisibility is true, must pass a controller that is attached to a scroll view or find PrimaryScrollController', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
Widget viewWithScroll() {
return _buildBoilerplate(
child: Theme(
data: ThemeData(),
child: Scrollbar(
thumbVisibility: true,
controller: controller,
child: const SingleChildScrollView(
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
),
),
);
}
await tester.pumpWidget(viewWithScroll());
final AssertionError exception = tester.takeException() as AssertionError;
expect(exception, isAssertionError);
});
testWidgets('On first render with thumbVisibility: true, the thumb shows', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
Widget viewWithScroll() {
return _buildBoilerplate(
child: Theme(
data: ThemeData(),
child: Scrollbar(
thumbVisibility: true,
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), paints..rect());
});
testWidgets('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
Widget viewWithScroll() {
return _buildBoilerplate(
child: Theme(
data: ThemeData(),
child: PrimaryScrollController(
controller: controller,
child: Builder(
builder: (BuildContext context) {
return const Scrollbar(
thumbVisibility: true,
child: SingleChildScrollView(
primary: true,
child: SizedBox(
width: 4000.0,
height: 4000.0,
),
),
);
},
),
),
),
);
}
await tester.pumpWidget(viewWithScroll());
await tester.pumpAndSettle();
expect(find.byType(Scrollbar), paints..rect());
});
testWidgets( testWidgets(
'When isAlwaysShown is true, must pass a controller or find PrimaryScrollController', 'When isAlwaysShown is true, must pass a controller or find PrimaryScrollController',
(WidgetTester tester) async { (WidgetTester tester) async {
......
...@@ -320,7 +320,7 @@ void main() { ...@@ -320,7 +320,7 @@ void main() {
child: Scrollbar( child: Scrollbar(
thickness: thickness, thickness: thickness,
hoverThickness: hoverThickness, hoverThickness: hoverThickness,
isAlwaysShown: true, thumbVisibility: true,
showTrackOnHover: showTrackOnHover, showTrackOnHover: showTrackOnHover,
radius: radius, radius: radius,
controller: scrollController, controller: scrollController,
...@@ -637,7 +637,7 @@ void main() { ...@@ -637,7 +637,7 @@ void main() {
ScrollbarThemeData( ScrollbarThemeData(
thickness: MaterialStateProperty.resolveWith(_getThickness), thickness: MaterialStateProperty.resolveWith(_getThickness),
showTrackOnHover: true, showTrackOnHover: true,
isAlwaysShown: true, thumbVisibility: MaterialStateProperty.resolveWith(_getThumbVisibility),
radius: const Radius.circular(3.0), radius: const Radius.circular(3.0),
thumbColor: MaterialStateProperty.resolveWith(_getThumbColor), thumbColor: MaterialStateProperty.resolveWith(_getThumbColor),
trackColor: MaterialStateProperty.resolveWith(_getTrackColor), trackColor: MaterialStateProperty.resolveWith(_getTrackColor),
...@@ -653,16 +653,16 @@ void main() { ...@@ -653,16 +653,16 @@ void main() {
.toList(); .toList();
expect(description, <String>[ expect(description, <String>[
"thumbVisibility: Instance of '_MaterialStatePropertyWith<bool?>'",
"thickness: Instance of '_MaterialStatePropertyWith<double?>'", "thickness: Instance of '_MaterialStatePropertyWith<double?>'",
'showTrackOnHover: true', 'showTrackOnHover: true',
'isAlwaysShown: true',
'radius: Radius.circular(3.0)', 'radius: Radius.circular(3.0)',
"thumbColor: Instance of '_MaterialStatePropertyWith<Color?>'", "thumbColor: Instance of '_MaterialStatePropertyWith<Color?>'",
"trackColor: Instance of '_MaterialStatePropertyWith<Color?>'", "trackColor: Instance of '_MaterialStatePropertyWith<Color?>'",
"trackBorderColor: Instance of '_MaterialStatePropertyWith<Color?>'", "trackBorderColor: Instance of '_MaterialStatePropertyWith<Color?>'",
'crossAxisMargin: 3.0', 'crossAxisMargin: 3.0',
'mainAxisMargin: 6.0', 'mainAxisMargin: 6.0',
'minThumbLength: 120.0', 'minThumbLength: 120.0'
]); ]);
// On the web, Dart doubles and ints are backed by the same kind of object because // On the web, Dart doubles and ints are backed by the same kind of object because
...@@ -684,7 +684,7 @@ ScrollbarThemeData _scrollbarTheme({ ...@@ -684,7 +684,7 @@ ScrollbarThemeData _scrollbarTheme({
MaterialStateProperty<double?>? thickness, MaterialStateProperty<double?>? thickness,
MaterialStateProperty<bool?>? trackVisibility, MaterialStateProperty<bool?>? trackVisibility,
bool showTrackOnHover = true, bool showTrackOnHover = true,
bool isAlwaysShown = true, MaterialStateProperty<bool?>? thumbVisibility,
Radius radius = const Radius.circular(6.0), Radius radius = const Radius.circular(6.0),
MaterialStateProperty<Color?>? thumbColor, MaterialStateProperty<Color?>? thumbColor,
MaterialStateProperty<Color?>? trackColor, MaterialStateProperty<Color?>? trackColor,
...@@ -697,7 +697,7 @@ ScrollbarThemeData _scrollbarTheme({ ...@@ -697,7 +697,7 @@ ScrollbarThemeData _scrollbarTheme({
thickness: thickness ?? MaterialStateProperty.resolveWith(_getThickness), thickness: thickness ?? MaterialStateProperty.resolveWith(_getThickness),
trackVisibility: trackVisibility, trackVisibility: trackVisibility,
showTrackOnHover: showTrackOnHover, showTrackOnHover: showTrackOnHover,
isAlwaysShown: isAlwaysShown, thumbVisibility: thumbVisibility,
radius: radius, radius: radius,
thumbColor: thumbColor ?? MaterialStateProperty.resolveWith(_getThumbColor), thumbColor: thumbColor ?? MaterialStateProperty.resolveWith(_getThumbColor),
trackColor: trackColor ?? MaterialStateProperty.resolveWith(_getTrackColor), trackColor: trackColor ?? MaterialStateProperty.resolveWith(_getTrackColor),
...@@ -714,6 +714,8 @@ double? _getThickness(Set<MaterialState> states) { ...@@ -714,6 +714,8 @@ double? _getThickness(Set<MaterialState> states) {
return 10.0; return 10.0;
} }
bool? _getThumbVisibility(Set<MaterialState> states) => true;
Color? _getThumbColor(Set<MaterialState> states) { Color? _getThumbColor(Set<MaterialState> states) {
if (states.contains(MaterialState.dragged)) if (states.contains(MaterialState.dragged))
return Colors.red; return Colors.red;
......
...@@ -1304,6 +1304,44 @@ void main() { ...@@ -1304,6 +1304,44 @@ void main() {
); );
}); });
testWidgets('RawScrollbar.thumbVisibility asserts that a ScrollPosition is attached', (WidgetTester tester) async {
final FlutterExceptionHandler? handler = FlutterError.onError;
FlutterErrorDetails? error;
FlutterError.onError = (FlutterErrorDetails details) {
error = details;
};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: RawScrollbar(
thumbVisibility: true,
controller: ScrollController(),
thumbColor: const Color(0x11111111),
child: const SingleChildScrollView(
child: SizedBox(
height: 1000.0,
width: 50.0,
),
),
),
),
),
);
await tester.pumpAndSettle();
expect(error, isNotNull);
final AssertionError exception = error!.exception as AssertionError;
expect(
exception.message,
contains("The Scrollbar's ScrollController has no ScrollPosition attached."),
);
FlutterError.onError = handler;
});
testWidgets('RawScrollbar.isAlwaysShown asserts that a ScrollPosition is attached', (WidgetTester tester) async { testWidgets('RawScrollbar.isAlwaysShown asserts that a ScrollPosition is attached', (WidgetTester tester) async {
final FlutterExceptionHandler? handler = FlutterError.onError; final FlutterExceptionHandler? handler = FlutterError.onError;
FlutterErrorDetails? error; FlutterErrorDetails? error;
......
...@@ -220,4 +220,10 @@ void main() { ...@@ -220,4 +220,10 @@ void main() {
OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true); OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true);
notification = OverscrollIndicatorNotification(error: ''); notification = OverscrollIndicatorNotification(error: '');
notification.disallowGlow(); notification.disallowGlow();
// Changes made in https://github.com/flutter/flutter/pull/96957
CupertinoScrollbar scrollbar = CupertinoScrollbar(isAlwaysShown: true);
bool nowShowing = scrollbar.isAlwaysShown;
RawScrollbar rawScrollbar = RawScrollbar(isAlwaysShown: true);
nowShowing = rawScrollbar.isAlwaysShown;
} }
...@@ -220,4 +220,10 @@ void main() { ...@@ -220,4 +220,10 @@ void main() {
OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true); OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true);
notification = OverscrollIndicatorNotification(error: ''); notification = OverscrollIndicatorNotification(error: '');
notification.disallowIndicator(); notification.disallowIndicator();
// Changes made in https://github.com/flutter/flutter/pull/96957
CupertinoScrollbar scrollbar = CupertinoScrollbar(thumbVisibility: true);
bool nowShowing = scrollbar.thumbVisibility;
RawScrollbar rawScrollbar = RawScrollbar(thumbVisibility: true);
nowShowing = rawScrollbar.thumbVisibility;
} }
...@@ -521,4 +521,13 @@ void main() { ...@@ -521,4 +521,13 @@ void main() {
// Changes made in https://github.com/flutter/flutter/pull/96115 // Changes made in https://github.com/flutter/flutter/pull/96115
Icon icon = Icons.pie_chart_outlined; Icon icon = Icons.pie_chart_outlined;
// Changes made in https://github.com/flutter/flutter/pull/96957
Scrollbar scrollbar = Scrollbar(isAlwaysShown: true);
bool nowShowing = scrollbar.isAlwaysShown;
ScrollbarThemeData scrollbarTheme = ScrollbarThemeData(isAlwaysShown: nowShowing);
scrollbarTheme.copyWith(isAlwaysShown: nowShowing);
scrollbarTheme.isAlwaysShown;
RawScrollbar rawScrollbar = RawScrollbar(isAlwaysShown: true);
nowShowing = rawScrollbar.isAlwaysShown;
} }
...@@ -494,4 +494,13 @@ void main() { ...@@ -494,4 +494,13 @@ void main() {
// Changes made in https://github.com/flutter/flutter/pull/96115 // Changes made in https://github.com/flutter/flutter/pull/96115
Icon icon = Icons.pie_chart_outline; Icon icon = Icons.pie_chart_outline;
// Changes made in https://github.com/flutter/flutter/pull/96957
Scrollbar scrollbar = Scrollbar(thumbVisibility: true);
bool nowShowing = scrollbar.thumbVisibility;
ScrollbarThemeData scrollbarTheme = ScrollbarThemeData(thumbVisibility: nowShowing);
scrollbarTheme.copyWith(thumbVisibility: nowShowing);
scrollbarTheme.thumbVisibility;
RawScrollbar rawScrollbar = RawScrollbar(thumbVisibility: true);
nowShowing = rawScrollbar.thumbVisibility;
} }
...@@ -177,4 +177,8 @@ void main() { ...@@ -177,4 +177,8 @@ void main() {
final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true); final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true);
final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(error: ''); final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(error: '');
notification.disallowGlow(); notification.disallowGlow();
// Changes made in https://github.com/flutter/flutter/pull/96957
RawScrollbar rawScrollbar = RawScrollbar(isAlwaysShown: true);
nowShowing = rawScrollbar.isAlwaysShown;
} }
...@@ -177,4 +177,8 @@ void main() { ...@@ -177,4 +177,8 @@ void main() {
final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true); final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true);
final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(error: ''); final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(error: '');
notification.disallowIndicator(); notification.disallowIndicator();
// Changes made in https://github.com/flutter/flutter/pull/96957
RawScrollbar rawScrollbar = RawScrollbar(thumbVisibility: true);
nowShowing = rawScrollbar.thumbVisibility;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment