Update ScrollableDetails for 2D scrolling (#122555)
Update ScrollableDetails for 2D scrolling
This commit is contained in:
parent
dfab19c1fe
commit
4cac07b716
@ -23,6 +23,29 @@
|
|||||||
# * ListWheelScrollView: fix_list_wheel_scroll_view.yaml
|
# * ListWheelScrollView: fix_list_wheel_scroll_view.yaml
|
||||||
version: 1
|
version: 1
|
||||||
transforms:
|
transforms:
|
||||||
|
# Changes made in https://github.com/flutter/flutter/pull/122555
|
||||||
|
- title: "Migrate to 'decorationClipBehavior'"
|
||||||
|
date: 2023-03-13
|
||||||
|
element:
|
||||||
|
uris: [ 'widgets.dart', 'material.dart', 'cupertino.dart' ]
|
||||||
|
field: 'clipBehavior'
|
||||||
|
inClass: 'ScrollableDetails'
|
||||||
|
changes:
|
||||||
|
- kind: 'rename'
|
||||||
|
newName: 'decorationClipBehavior'
|
||||||
|
|
||||||
|
# Changes made in https://github.com/flutter/flutter/pull/122555
|
||||||
|
- title: "Migrate to 'decorationClipBehavior'"
|
||||||
|
date: 2023-03-13
|
||||||
|
element:
|
||||||
|
uris: [ 'widgets.dart', 'material.dart', 'cupertino.dart' ]
|
||||||
|
constructor: ''
|
||||||
|
inClass: 'ScrollableDetails'
|
||||||
|
changes:
|
||||||
|
- kind: 'renameParameter'
|
||||||
|
oldName: 'clipBehavior'
|
||||||
|
newName: 'decorationClipBehavior'
|
||||||
|
|
||||||
# Changes made in https://github.com/flutter/flutter/pull/119647
|
# Changes made in https://github.com/flutter/flutter/pull/119647
|
||||||
- title: "Migrate to 'fromView'"
|
- title: "Migrate to 'fromView'"
|
||||||
date: 2022-10-28
|
date: 2022-10-28
|
||||||
|
@ -460,6 +460,7 @@ class CupertinoScrollBehavior extends ScrollBehavior {
|
|||||||
case TargetPlatform.linux:
|
case TargetPlatform.linux:
|
||||||
case TargetPlatform.macOS:
|
case TargetPlatform.macOS:
|
||||||
case TargetPlatform.windows:
|
case TargetPlatform.windows:
|
||||||
|
assert(details.controller != null);
|
||||||
return CupertinoScrollbar(
|
return CupertinoScrollbar(
|
||||||
controller: details.controller,
|
controller: details.controller,
|
||||||
child: child,
|
child: child,
|
||||||
|
@ -825,6 +825,7 @@ class MaterialScrollBehavior extends ScrollBehavior {
|
|||||||
case TargetPlatform.linux:
|
case TargetPlatform.linux:
|
||||||
case TargetPlatform.macOS:
|
case TargetPlatform.macOS:
|
||||||
case TargetPlatform.windows:
|
case TargetPlatform.windows:
|
||||||
|
assert(details.controller != null);
|
||||||
return Scrollbar(
|
return Scrollbar(
|
||||||
controller: details.controller,
|
controller: details.controller,
|
||||||
child: child,
|
child: child,
|
||||||
|
@ -163,6 +163,7 @@ class ScrollBehavior {
|
|||||||
case TargetPlatform.linux:
|
case TargetPlatform.linux:
|
||||||
case TargetPlatform.macOS:
|
case TargetPlatform.macOS:
|
||||||
case TargetPlatform.windows:
|
case TargetPlatform.windows:
|
||||||
|
assert(details.controller != null);
|
||||||
return RawScrollbar(
|
return RawScrollbar(
|
||||||
controller: details.controller,
|
controller: details.controller,
|
||||||
child: child,
|
child: child,
|
||||||
|
@ -99,18 +99,21 @@ class Scrollable extends StatefulWidget {
|
|||||||
this.clipBehavior = Clip.hardEdge,
|
this.clipBehavior = Clip.hardEdge,
|
||||||
}) : assert(semanticChildCount == null || semanticChildCount >= 0);
|
}) : assert(semanticChildCount == null || semanticChildCount >= 0);
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.Scrollable.axisDirection}
|
||||||
/// The direction in which this widget scrolls.
|
/// The direction in which this widget scrolls.
|
||||||
///
|
///
|
||||||
/// For example, if the [axisDirection] is [AxisDirection.down], increasing
|
/// For example, if the [Scrollable.axisDirection] is [AxisDirection.down],
|
||||||
/// the scroll position will cause content below the bottom of the viewport to
|
/// increasing the scroll position will cause content below the bottom of the
|
||||||
/// become visible through the viewport. Similarly, if [axisDirection] is
|
/// viewport to become visible through the viewport. Similarly, if the
|
||||||
/// [AxisDirection.right], increasing the scroll position will cause content
|
/// axisDirection is [AxisDirection.right], increasing the scroll position
|
||||||
/// beyond the right edge of the viewport to become visible through the
|
/// will cause content beyond the right edge of the viewport to become visible
|
||||||
/// viewport.
|
/// through the viewport.
|
||||||
///
|
///
|
||||||
/// Defaults to [AxisDirection.down].
|
/// Defaults to [AxisDirection.down].
|
||||||
|
/// {@endtemplate}
|
||||||
final AxisDirection axisDirection;
|
final AxisDirection axisDirection;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.Scrollable.controller}
|
||||||
/// An object that can be used to control the position to which this widget is
|
/// An object that can be used to control the position to which this widget is
|
||||||
/// scrolled.
|
/// scrolled.
|
||||||
///
|
///
|
||||||
@ -122,12 +125,17 @@ class Scrollable extends StatefulWidget {
|
|||||||
/// scroll position (see [ScrollController.offset]), or change it (see
|
/// scroll position (see [ScrollController.offset]), or change it (see
|
||||||
/// [ScrollController.animateTo]).
|
/// [ScrollController.animateTo]).
|
||||||
///
|
///
|
||||||
|
/// If null, a [ScrollController] will be created internally by [Scrollable]
|
||||||
|
/// in order to create and manage the [ScrollPosition].
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [ensureVisible], which animates the scroll position to reveal a given
|
/// * [Scrollable.ensureVisible], which animates the scroll position to
|
||||||
/// [BuildContext].
|
/// reveal a given [BuildContext].
|
||||||
|
/// {@endtemplate}
|
||||||
final ScrollController? controller;
|
final ScrollController? controller;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.Scrollable.physics}
|
||||||
/// How the widgets should respond to user input.
|
/// How the widgets should respond to user input.
|
||||||
///
|
///
|
||||||
/// For example, determines how the widget continues to animate after the
|
/// For example, determines how the widget continues to animate after the
|
||||||
@ -136,9 +144,9 @@ class Scrollable extends StatefulWidget {
|
|||||||
/// Defaults to matching platform conventions via the physics provided from
|
/// Defaults to matching platform conventions via the physics provided from
|
||||||
/// the ambient [ScrollConfiguration].
|
/// the ambient [ScrollConfiguration].
|
||||||
///
|
///
|
||||||
/// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the
|
/// If an explicit [ScrollBehavior] is provided to
|
||||||
/// [ScrollPhysics] provided by that behavior will take precedence after
|
/// [Scrollable.scrollBehavior], the [ScrollPhysics] provided by that behavior
|
||||||
/// [physics].
|
/// will take precedence after [Scrollable.physics].
|
||||||
///
|
///
|
||||||
/// The physics can be changed dynamically, but new physics will only take
|
/// The physics can be changed dynamically, but new physics will only take
|
||||||
/// effect if the _class_ of the provided object changes. Merely constructing
|
/// effect if the _class_ of the provided object changes. Merely constructing
|
||||||
@ -153,6 +161,7 @@ class Scrollable extends StatefulWidget {
|
|||||||
/// * [AlwaysScrollableScrollPhysics], which can be used to indicate that the
|
/// * [AlwaysScrollableScrollPhysics], which can be used to indicate that the
|
||||||
/// scrollable should react to scroll requests (and possible overscroll)
|
/// scrollable should react to scroll requests (and possible overscroll)
|
||||||
/// even if the scrollable's contents fit without scrolling being necessary.
|
/// even if the scrollable's contents fit without scrolling being necessary.
|
||||||
|
/// {@endtemplate}
|
||||||
final ScrollPhysics? physics;
|
final ScrollPhysics? physics;
|
||||||
|
|
||||||
/// Builds the viewport through which the scrollable content is displayed.
|
/// Builds the viewport through which the scrollable content is displayed.
|
||||||
@ -902,7 +911,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
|
|||||||
final ScrollableDetails details = ScrollableDetails(
|
final ScrollableDetails details = ScrollableDetails(
|
||||||
direction: widget.axisDirection,
|
direction: widget.axisDirection,
|
||||||
controller: _effectiveScrollController,
|
controller: _effectiveScrollController,
|
||||||
clipBehavior: widget.clipBehavior,
|
decorationClipBehavior: widget.clipBehavior,
|
||||||
);
|
);
|
||||||
|
|
||||||
result = _configuration.buildScrollbar(
|
result = _configuration.buildScrollbar(
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
import 'actions.dart';
|
import 'actions.dart';
|
||||||
@ -15,12 +16,16 @@ import 'primary_scroll_controller.dart';
|
|||||||
import 'scroll_configuration.dart';
|
import 'scroll_configuration.dart';
|
||||||
import 'scroll_controller.dart';
|
import 'scroll_controller.dart';
|
||||||
import 'scroll_metrics.dart';
|
import 'scroll_metrics.dart';
|
||||||
|
import 'scroll_physics.dart';
|
||||||
import 'scrollable.dart';
|
import 'scrollable.dart';
|
||||||
|
|
||||||
export 'package:flutter/physics.dart' show Tolerance;
|
export 'package:flutter/physics.dart' show Tolerance;
|
||||||
|
|
||||||
/// Describes the aspects of a Scrollable widget to inform inherited widgets
|
/// Describes the aspects of a Scrollable widget to inform inherited widgets
|
||||||
/// like [ScrollBehavior] for decorating.
|
/// like [ScrollBehavior] for decorating.
|
||||||
|
// TODO(Piinks): Fix doc with 2DScrollable change.
|
||||||
|
// or enumerate the properties of combined
|
||||||
|
// Scrollables, such as [TwoDimensionalScrollable].
|
||||||
///
|
///
|
||||||
/// Decorations like [GlowingOverscrollIndicator]s and [Scrollbar]s require
|
/// Decorations like [GlowingOverscrollIndicator]s and [Scrollbar]s require
|
||||||
/// information about the Scrollable in order to be initialized.
|
/// information about the Scrollable in order to be initialized.
|
||||||
@ -30,28 +35,118 @@ class ScrollableDetails {
|
|||||||
/// cannot be null.
|
/// cannot be null.
|
||||||
const ScrollableDetails({
|
const ScrollableDetails({
|
||||||
required this.direction,
|
required this.direction,
|
||||||
required this.controller,
|
this.controller,
|
||||||
this.clipBehavior,
|
this.physics,
|
||||||
});
|
@Deprecated(
|
||||||
|
'Migrate to decorationClipBehavior. '
|
||||||
|
'This property was deprecated so that its application is clearer. This clip '
|
||||||
|
'applies to decorators, and does not directly clip a scroll view. '
|
||||||
|
'This feature was deprecated after v3.9.0-1.0.pre.'
|
||||||
|
)
|
||||||
|
Clip? clipBehavior,
|
||||||
|
Clip? decorationClipBehavior,
|
||||||
|
}) : decorationClipBehavior = clipBehavior ?? decorationClipBehavior;
|
||||||
|
|
||||||
/// The direction in which this widget scrolls.
|
/// A constructor specific to a [Scrollable] with an [Axis.vertical].
|
||||||
///
|
const ScrollableDetails.vertical({
|
||||||
/// Cannot be null.
|
bool reverse = false,
|
||||||
|
this.controller,
|
||||||
|
this.physics,
|
||||||
|
this.decorationClipBehavior,
|
||||||
|
}) : direction = reverse ? AxisDirection.up : AxisDirection.down;
|
||||||
|
|
||||||
|
/// A constructor specific to a [Scrollable] with an [Axis.horizontal].
|
||||||
|
const ScrollableDetails.horizontal({
|
||||||
|
bool reverse = false,
|
||||||
|
this.controller,
|
||||||
|
this.physics,
|
||||||
|
this.decorationClipBehavior,
|
||||||
|
}) : direction = reverse ? AxisDirection.left : AxisDirection.right;
|
||||||
|
|
||||||
|
/// {@macro flutter.widgets.Scrollable.axisDirection}
|
||||||
final AxisDirection direction;
|
final AxisDirection direction;
|
||||||
|
|
||||||
/// A [ScrollController] that can be used to control the position of the
|
/// {@macro flutter.widgets.Scrollable.controller}
|
||||||
/// [Scrollable] widget.
|
final ScrollController? controller;
|
||||||
///
|
|
||||||
/// This can be used by [ScrollBehavior] to apply a [Scrollbar] to the associated
|
/// {@macro flutter.widgets.Scrollable.physics}
|
||||||
/// [Scrollable].
|
final ScrollPhysics? physics;
|
||||||
final ScrollController controller;
|
|
||||||
|
|
||||||
/// {@macro flutter.material.Material.clipBehavior}
|
/// {@macro flutter.material.Material.clipBehavior}
|
||||||
///
|
///
|
||||||
/// This can be used by [MaterialScrollBehavior] to clip [StretchingOverscrollIndicator].
|
/// This can be used by [MaterialScrollBehavior] to clip a
|
||||||
|
/// [StretchingOverscrollIndicator].
|
||||||
|
///
|
||||||
|
/// This [Clip] does not affect the [Viewport.clipBehavior], but is rather
|
||||||
|
/// passed from the same value by [Scrollable] so that decorators like
|
||||||
|
/// [StretchingOverscrollIndicator] honor the same clip.
|
||||||
///
|
///
|
||||||
/// Defaults to null.
|
/// Defaults to null.
|
||||||
final Clip? clipBehavior;
|
final Clip? decorationClipBehavior;
|
||||||
|
|
||||||
|
/// Deprecated getter for [decorationClipBehavior].
|
||||||
|
@Deprecated(
|
||||||
|
'Migrate to decorationClipBehavior. '
|
||||||
|
'This property was deprecated so that its application is clearer. This clip '
|
||||||
|
'applies to decorators, and does not directly clip a scroll view. '
|
||||||
|
'This feature was deprecated after v3.9.0-1.0.pre.'
|
||||||
|
)
|
||||||
|
Clip? get clipBehavior => decorationClipBehavior;
|
||||||
|
|
||||||
|
/// Copy the current [ScrollableDetails] with the given values replacing the
|
||||||
|
/// current values.
|
||||||
|
ScrollableDetails copyWith({
|
||||||
|
AxisDirection? direction,
|
||||||
|
ScrollController? controller,
|
||||||
|
ScrollPhysics? physics,
|
||||||
|
Clip? decorationClipBehavior,
|
||||||
|
}) {
|
||||||
|
return ScrollableDetails(
|
||||||
|
direction: direction ?? this.direction,
|
||||||
|
controller: controller ?? this.controller,
|
||||||
|
physics: physics ?? this.physics,
|
||||||
|
decorationClipBehavior: decorationClipBehavior ?? this.decorationClipBehavior,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
final List<String> description = <String>[];
|
||||||
|
description.add('axisDirection: $direction');
|
||||||
|
|
||||||
|
void addIfNonNull(String prefix, Object? value) {
|
||||||
|
if (value != null) {
|
||||||
|
description.add(prefix + value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addIfNonNull('scroll controller: ', controller);
|
||||||
|
addIfNonNull('scroll physics: ', physics);
|
||||||
|
addIfNonNull('decorationClipBehavior: ', decorationClipBehavior);
|
||||||
|
return '${describeIdentity(this)}(${description.join(", ")})';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
direction,
|
||||||
|
controller,
|
||||||
|
physics,
|
||||||
|
decorationClipBehavior,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (other.runtimeType != runtimeType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return other is ScrollableDetails
|
||||||
|
&& other.direction == direction
|
||||||
|
&& other.controller == controller
|
||||||
|
&& other.physics == physics
|
||||||
|
&& other.decorationClipBehavior == decorationClipBehavior;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An auto scroller that scrolls the [scrollable] if a drag gesture drags close
|
/// An auto scroller that scrolls the [scrollable] if a drag gesture drags close
|
||||||
|
@ -424,6 +424,49 @@ void main() {
|
|||||||
|
|
||||||
debugBrightnessOverride = null;
|
debugBrightnessOverride = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Assert in buildScrollbar that controller != null when using it', (WidgetTester tester) async {
|
||||||
|
const ScrollBehavior defaultBehavior = CupertinoScrollBehavior();
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
|
||||||
|
await tester.pumpWidget(ScrollConfiguration(
|
||||||
|
// Avoid the default ones here.
|
||||||
|
behavior: const CupertinoScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
capturedContext = context;
|
||||||
|
return Container(height: 1000.0);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
const ScrollableDetails details = ScrollableDetails(direction: AxisDirection.down);
|
||||||
|
final Widget child = Container();
|
||||||
|
|
||||||
|
switch(defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
case TargetPlatform.fuchsia:
|
||||||
|
case TargetPlatform.iOS:
|
||||||
|
// Does not throw if we aren't using it.
|
||||||
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
||||||
|
break;
|
||||||
|
case TargetPlatform.linux:
|
||||||
|
case TargetPlatform.macOS:
|
||||||
|
case TargetPlatform.windows:
|
||||||
|
expect(
|
||||||
|
() {
|
||||||
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
||||||
|
},
|
||||||
|
throwsA(
|
||||||
|
isA<AssertionError>().having((AssertionError error) => error.toString(),
|
||||||
|
'description', contains('details.controller != null')),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, variant: TargetPlatformVariant.all());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockScrollBehavior extends ScrollBehavior {
|
class MockScrollBehavior extends ScrollBehavior {
|
||||||
|
@ -1445,6 +1445,92 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(capturedContext.dependOnInheritedWidgetOfExactType<MediaQuery>()?.key, uniqueKey);
|
expect(capturedContext.dependOnInheritedWidgetOfExactType<MediaQuery>()?.key, uniqueKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Assert in buildScrollbar that controller != null when using it (vertical)', (WidgetTester tester) async {
|
||||||
|
const ScrollBehavior defaultBehavior = MaterialScrollBehavior();
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: ScrollConfiguration(
|
||||||
|
// Avoid the default ones here.
|
||||||
|
behavior: const MaterialScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
capturedContext = context;
|
||||||
|
return Container(height: 1000.0);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
const ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
);
|
||||||
|
final Widget child = Container();
|
||||||
|
|
||||||
|
switch(defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
case TargetPlatform.fuchsia:
|
||||||
|
case TargetPlatform.iOS:
|
||||||
|
// Does not throw if we aren't using it.
|
||||||
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
||||||
|
break;
|
||||||
|
case TargetPlatform.linux:
|
||||||
|
case TargetPlatform.macOS:
|
||||||
|
case TargetPlatform.windows:
|
||||||
|
expect(
|
||||||
|
() {
|
||||||
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
||||||
|
},
|
||||||
|
throwsA(
|
||||||
|
isA<AssertionError>().having((AssertionError error) => error.toString(),
|
||||||
|
'description', contains('details.controller != null')),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, variant: TargetPlatformVariant.all());
|
||||||
|
|
||||||
|
testWidgets('Assert in buildScrollbar that controller != null when using it (horizontal)', (WidgetTester tester) async {
|
||||||
|
const ScrollBehavior defaultBehavior = MaterialScrollBehavior();
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: ScrollConfiguration(
|
||||||
|
// Avoid the default ones here.
|
||||||
|
behavior: const MaterialScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
capturedContext = context;
|
||||||
|
return Container(height: 1000.0);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
const ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.left,
|
||||||
|
);
|
||||||
|
final Widget child = Container();
|
||||||
|
|
||||||
|
switch(defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
case TargetPlatform.fuchsia:
|
||||||
|
case TargetPlatform.iOS:
|
||||||
|
case TargetPlatform.linux:
|
||||||
|
case TargetPlatform.macOS:
|
||||||
|
case TargetPlatform.windows:
|
||||||
|
// Does not throw if we aren't using it.
|
||||||
|
// Horizontal axis gets no scrollbars for all platforms.
|
||||||
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, variant: TargetPlatformVariant.all());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockScrollBehavior extends ScrollBehavior {
|
class MockScrollBehavior extends ScrollBehavior {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@ -32,6 +33,49 @@ class TestScrollBehavior extends ScrollBehavior {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
testWidgets('Assert in buildScrollbar that controller != null when using it', (WidgetTester tester) async {
|
||||||
|
const ScrollBehavior defaultBehavior = ScrollBehavior();
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
|
||||||
|
await tester.pumpWidget(ScrollConfiguration(
|
||||||
|
// Avoid the default ones here.
|
||||||
|
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
capturedContext = context;
|
||||||
|
return Container(height: 1000.0);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
const ScrollableDetails details = ScrollableDetails(direction: AxisDirection.down);
|
||||||
|
final Widget child = Container();
|
||||||
|
|
||||||
|
switch(defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
case TargetPlatform.fuchsia:
|
||||||
|
case TargetPlatform.iOS:
|
||||||
|
// Does not throw if we aren't using it.
|
||||||
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
||||||
|
break;
|
||||||
|
case TargetPlatform.linux:
|
||||||
|
case TargetPlatform.macOS:
|
||||||
|
case TargetPlatform.windows:
|
||||||
|
expect(
|
||||||
|
() {
|
||||||
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
||||||
|
},
|
||||||
|
throwsA(
|
||||||
|
isA<AssertionError>().having((AssertionError error) => error.toString(),
|
||||||
|
'description', contains('details.controller != null')),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, variant: TargetPlatformVariant.all());
|
||||||
|
|
||||||
// Regression test for https://github.com/flutter/flutter/issues/89681
|
// Regression test for https://github.com/flutter/flutter/issues/89681
|
||||||
testWidgets('_WrappedScrollBehavior shouldNotify test', (WidgetTester tester) async {
|
testWidgets('_WrappedScrollBehavior shouldNotify test', (WidgetTester tester) async {
|
||||||
final ScrollBehavior behavior1 = const ScrollBehavior().copyWith();
|
final ScrollBehavior behavior1 = const ScrollBehavior().copyWith();
|
||||||
|
@ -12,6 +12,80 @@ final LogicalKeyboardKey modifierKey = defaultTargetPlatform == TargetPlatform.m
|
|||||||
: LogicalKeyboardKey.controlLeft;
|
: LogicalKeyboardKey.controlLeft;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
group('ScrollableDetails', (){
|
||||||
|
final ScrollController controller = ScrollController();
|
||||||
|
test('copyWith / == / hashCode', () {
|
||||||
|
final ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
controller: controller,
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
decorationClipBehavior: Clip.hardEdge,
|
||||||
|
);
|
||||||
|
ScrollableDetails copiedDetails = details.copyWith();
|
||||||
|
expect(details, copiedDetails);
|
||||||
|
expect(details.hashCode, copiedDetails.hashCode);
|
||||||
|
|
||||||
|
copiedDetails = details.copyWith(
|
||||||
|
direction: AxisDirection.left,
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
|
decorationClipBehavior: Clip.none,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
copiedDetails,
|
||||||
|
ScrollableDetails(
|
||||||
|
direction: AxisDirection.left,
|
||||||
|
controller: controller,
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
|
decorationClipBehavior: Clip.none,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toString', (){
|
||||||
|
const ScrollableDetails bareDetails = ScrollableDetails(
|
||||||
|
direction: AxisDirection.right,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
bareDetails.toString(),
|
||||||
|
equalsIgnoringHashCodes(
|
||||||
|
'ScrollableDetails#00000(axisDirection: AxisDirection.right)'
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final ScrollableDetails fullDetails = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
controller: controller,
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
decorationClipBehavior: Clip.hardEdge,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
fullDetails.toString(),
|
||||||
|
equalsIgnoringHashCodes(
|
||||||
|
'ScrollableDetails#00000('
|
||||||
|
'axisDirection: AxisDirection.down, '
|
||||||
|
'scroll controller: ScrollController#00000(no clients), '
|
||||||
|
'scroll physics: AlwaysScrollableScrollPhysics, '
|
||||||
|
'decorationClipBehavior: Clip.hardEdge)'
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deprecated clipBehavior is backwards compatible', (){
|
||||||
|
const ScrollableDetails deprecatedClip = ScrollableDetails(
|
||||||
|
direction: AxisDirection.right,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
);
|
||||||
|
expect(deprecatedClip.clipBehavior, Clip.hardEdge);
|
||||||
|
expect(deprecatedClip.decorationClipBehavior, Clip.hardEdge);
|
||||||
|
|
||||||
|
const ScrollableDetails newClip = ScrollableDetails(
|
||||||
|
direction: AxisDirection.right,
|
||||||
|
decorationClipBehavior: Clip.hardEdge,
|
||||||
|
);
|
||||||
|
expect(newClip.clipBehavior, Clip.hardEdge);
|
||||||
|
expect(newClip.decorationClipBehavior, Clip.hardEdge);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets("Keyboard scrolling doesn't happen if scroll physics are set to NeverScrollableScrollPhysics", (WidgetTester tester) async {
|
testWidgets("Keyboard scrolling doesn't happen if scroll physics are set to NeverScrollableScrollPhysics", (WidgetTester tester) async {
|
||||||
final ScrollController controller = ScrollController();
|
final ScrollController controller = ScrollController();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -238,4 +238,11 @@ void main() {
|
|||||||
|
|
||||||
// Changes made in https://github.com/flutter/flutter/pull/114459
|
// Changes made in https://github.com/flutter/flutter/pull/114459
|
||||||
MediaQuery.boldTextOverride(context);
|
MediaQuery.boldTextOverride(context);
|
||||||
|
|
||||||
|
// Changes made in https://github.com/flutter/flutter/pull/122555
|
||||||
|
final ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
);
|
||||||
|
final Clip clip = details.clipBehavior;
|
||||||
}
|
}
|
||||||
|
@ -238,4 +238,11 @@ void main() {
|
|||||||
|
|
||||||
// Changes made in https://github.com/flutter/flutter/pull/114459
|
// Changes made in https://github.com/flutter/flutter/pull/114459
|
||||||
MediaQuery.boldTextOf(context);
|
MediaQuery.boldTextOf(context);
|
||||||
|
|
||||||
|
// Changes made in https://github.com/flutter/flutter/pull/122555
|
||||||
|
final ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
decorationClipBehavior: Clip.none,
|
||||||
|
);
|
||||||
|
final Clip clip = details.decorationClipBehavior;
|
||||||
}
|
}
|
||||||
|
@ -310,4 +310,11 @@ void main() {
|
|||||||
|
|
||||||
// Changes made in https://github.com/flutter/flutter/pull/114459
|
// Changes made in https://github.com/flutter/flutter/pull/114459
|
||||||
MediaQuery.boldTextOverride(context);
|
MediaQuery.boldTextOverride(context);
|
||||||
|
|
||||||
|
// Changes made in https://github.com/flutter/flutter/pull/122555
|
||||||
|
final ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
);
|
||||||
|
final Clip clip = details.clipBehavior;
|
||||||
}
|
}
|
||||||
|
@ -306,4 +306,11 @@ void main() {
|
|||||||
|
|
||||||
// Changes made in https://github.com/flutter/flutter/pull/114459
|
// Changes made in https://github.com/flutter/flutter/pull/114459
|
||||||
MediaQuery.boldTextOf(context);
|
MediaQuery.boldTextOf(context);
|
||||||
|
|
||||||
|
// Changes made in https://github.com/flutter/flutter/pull/122555
|
||||||
|
final ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
decorationClipBehavior: Clip.none,
|
||||||
|
);
|
||||||
|
final Clip clip = details.decorationClipBehavior;
|
||||||
}
|
}
|
||||||
|
@ -160,4 +160,11 @@ void main() {
|
|||||||
|
|
||||||
// Changes made in https://github.com/flutter/flutter/pull/114459
|
// Changes made in https://github.com/flutter/flutter/pull/114459
|
||||||
MediaQuery.boldTextOverride(context);
|
MediaQuery.boldTextOverride(context);
|
||||||
|
|
||||||
|
// Changes made in https://github.com/flutter/flutter/pull/122555
|
||||||
|
final ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
);
|
||||||
|
final Clip clip = details.clipBehavior;
|
||||||
}
|
}
|
||||||
|
@ -160,4 +160,11 @@ void main() {
|
|||||||
|
|
||||||
// Changes made in https://github.com/flutter/flutter/pull/114459
|
// Changes made in https://github.com/flutter/flutter/pull/114459
|
||||||
MediaQuery.boldTextOf(context);
|
MediaQuery.boldTextOf(context);
|
||||||
|
|
||||||
|
// Changes made in https://github.com/flutter/flutter/pull/122555
|
||||||
|
final ScrollableDetails details = ScrollableDetails(
|
||||||
|
direction: AxisDirection.down,
|
||||||
|
decorationClipBehavior: Clip.none,
|
||||||
|
);
|
||||||
|
final Clip clip = details.decorationClipBehavior;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user