mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Adjust the defaults behaviour of scroll views. (#9679)
* Adjust the defaults behaviour of scroll views. Now, primary scroll views scroll by default. Others only scroll if necessary. * apply suggested changes
This commit is contained in:
parent
f739e9e022
commit
89856c0e5b
@ -42,6 +42,7 @@ class _ListDemoState extends State<ListDemo> {
|
||||
),
|
||||
child: new ListView(
|
||||
shrinkWrap: true,
|
||||
primary: false,
|
||||
children: <Widget>[
|
||||
new ListTile(
|
||||
dense: true,
|
||||
|
@ -298,6 +298,6 @@ class GalleryDrawer extends StatelessWidget {
|
||||
));
|
||||
}
|
||||
|
||||
return new Drawer(child: new ListView(children: allDrawerItems));
|
||||
return new Drawer(child: new ListView(primary: false, children: allDrawerItems));
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ class _PagePosition extends ScrollPositionWithSingleContext {
|
||||
/// These physics cause the page view to snap to page boundaries.
|
||||
class PageScrollPhysics extends ScrollPhysics {
|
||||
/// Creates physics for a [PageView].
|
||||
const PageScrollPhysics({ ScrollPhysics parent }) : super(parent);
|
||||
const PageScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);
|
||||
|
||||
@override
|
||||
PageScrollPhysics applyTo(ScrollPhysics parent) => new PageScrollPhysics(parent: parent);
|
||||
|
@ -88,11 +88,6 @@ abstract class ScrollMetrics {
|
||||
/// of the viewport in the scrollable. This is the content below the content
|
||||
/// described by [extentInside].
|
||||
double get extentAfter => math.max(maxScrollExtent - pixels, 0.0);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$runtimeType(${extentBefore.toStringAsFixed(1)}..[${extentInside.toStringAsFixed(1)}]..${extentAfter.toStringAsFixed(1)}})';
|
||||
}
|
||||
}
|
||||
|
||||
/// An immutable snapshot of values associated with a [Scrollable] viewport.
|
||||
@ -131,4 +126,9 @@ class FixedScrollMetrics extends ScrollMetrics {
|
||||
|
||||
@override
|
||||
final AxisDirection axisDirection;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$runtimeType(${extentBefore.toStringAsFixed(1)}..[${extentInside.toStringAsFixed(1)}]..${extentAfter.toStringAsFixed(1)})';
|
||||
}
|
||||
}
|
@ -16,12 +16,12 @@ import 'scroll_simulation.dart';
|
||||
export 'package:flutter/physics.dart' show Tolerance;
|
||||
|
||||
@immutable
|
||||
abstract class ScrollPhysics {
|
||||
const ScrollPhysics(this.parent);
|
||||
class ScrollPhysics {
|
||||
const ScrollPhysics({ this.parent });
|
||||
|
||||
final ScrollPhysics parent;
|
||||
|
||||
ScrollPhysics applyTo(ScrollPhysics parent);
|
||||
ScrollPhysics applyTo(ScrollPhysics parent) => new ScrollPhysics(parent: parent);
|
||||
|
||||
/// Used by [DragScrollActivity] and other user-driven activities to
|
||||
/// convert an offset in logical pixels as provided by the [DragUpdateDetails]
|
||||
@ -172,7 +172,7 @@ abstract class ScrollPhysics {
|
||||
/// clamping behavior.
|
||||
class BouncingScrollPhysics extends ScrollPhysics {
|
||||
/// Creates scroll physics that bounce back from the edge.
|
||||
const BouncingScrollPhysics({ ScrollPhysics parent }) : super(parent);
|
||||
const BouncingScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);
|
||||
|
||||
@override
|
||||
BouncingScrollPhysics applyTo(ScrollPhysics parent) => new BouncingScrollPhysics(parent: parent);
|
||||
@ -251,7 +251,7 @@ class BouncingScrollPhysics extends ScrollPhysics {
|
||||
class ClampingScrollPhysics extends ScrollPhysics {
|
||||
/// Creates scroll physics that prevent the scroll offset from exceeding the
|
||||
/// bounds of the content..
|
||||
const ClampingScrollPhysics({ ScrollPhysics parent }) : super(parent);
|
||||
const ClampingScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);
|
||||
|
||||
@override
|
||||
ClampingScrollPhysics applyTo(ScrollPhysics parent) => new ClampingScrollPhysics(parent: parent);
|
||||
@ -325,13 +325,15 @@ class ClampingScrollPhysics extends ScrollPhysics {
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ScrollPhysics], which can be used instead of this class when the default
|
||||
/// behavior is desired instead.
|
||||
/// * [BouncingScrollPhysics], which provides the bouncing overscroll behavior
|
||||
/// found on iOS.
|
||||
/// * [ClampingScrollPhysics], which provides the clamping overscroll behavior
|
||||
/// found on Android.
|
||||
class AlwaysScrollableScrollPhysics extends ScrollPhysics {
|
||||
/// Creates scroll physics that always lets the user scroll.
|
||||
const AlwaysScrollableScrollPhysics({ ScrollPhysics parent }) : super(parent);
|
||||
const AlwaysScrollableScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);
|
||||
|
||||
@override
|
||||
AlwaysScrollableScrollPhysics applyTo(ScrollPhysics parent) => new AlwaysScrollableScrollPhysics(parent: parent);
|
||||
|
@ -49,9 +49,10 @@ abstract class ScrollView extends StatelessWidget {
|
||||
this.reverse: false,
|
||||
this.controller,
|
||||
bool primary,
|
||||
this.physics,
|
||||
ScrollPhysics physics,
|
||||
this.shrinkWrap: false,
|
||||
}) : primary = primary ?? controller == null && scrollDirection == Axis.vertical,
|
||||
physics = physics ?? (primary == true || (primary == null && controller == null && scrollDirection == Axis.vertical) ? const AlwaysScrollableScrollPhysics() : null),
|
||||
super(key: key) {
|
||||
assert(reverse != null);
|
||||
assert(shrinkWrap != null);
|
||||
@ -90,7 +91,11 @@ abstract class ScrollView extends StatelessWidget {
|
||||
/// Whether this is the primary scroll view associated with the parent
|
||||
/// [PrimaryScrollController].
|
||||
///
|
||||
/// On iOS, this identifies the scroll view that will scroll to top in
|
||||
/// When this is true, the scroll view is scrollable even if it does not have
|
||||
/// sufficient content to actually scroll. Otherwise, by default the user can
|
||||
/// only scroll the view if it has sufficient content. See [physics].
|
||||
///
|
||||
/// On iOS, this also identifies the scroll view that will scroll to top in
|
||||
/// response to a tap in the status bar.
|
||||
///
|
||||
/// Defaults to true when [scrollDirection] is [Axis.vertical] and
|
||||
@ -102,7 +107,26 @@ abstract class ScrollView extends StatelessWidget {
|
||||
/// For example, determines how the scroll view continues to animate after the
|
||||
/// user stops dragging the scroll view.
|
||||
///
|
||||
/// Defaults to matching platform conventions.
|
||||
/// Defaults to matching platform conventions. Furthermore, if [primary] is
|
||||
/// false, then the user cannot scroll if there is insufficient content to
|
||||
/// scroll, while if [primary] is true, they can always attempt to scroll.
|
||||
///
|
||||
/// To force the scroll view to always be scrollable even if there is
|
||||
/// insufficient content, as if [primary] was true but without necessarily
|
||||
/// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics
|
||||
/// object, as in:
|
||||
///
|
||||
/// ```dart
|
||||
/// physics: const AlwaysScrollableScrollPhysics(),
|
||||
/// ```
|
||||
///
|
||||
/// To force the scroll view to use the default platform conventions and not
|
||||
/// be scrollable if there is insufficient content, regardless of the value of
|
||||
/// [primary], provide an explicit [ScrollPhysics] object, as in:
|
||||
///
|
||||
/// ```dart
|
||||
/// physics: const ScrollPhysics(),
|
||||
/// ```
|
||||
final ScrollPhysics physics;
|
||||
|
||||
/// Whether the extent of the scroll view in the [scrollDirection] should be
|
||||
|
@ -48,6 +48,7 @@ void main() {
|
||||
scaffoldKey.currentState.showBottomSheet<Null>((BuildContext context) {
|
||||
return new ListView(
|
||||
shrinkWrap: true,
|
||||
primary: false,
|
||||
children: <Widget>[
|
||||
new Container(height: 100.0, child: const Text('One')),
|
||||
new Container(height: 100.0, child: const Text('Two')),
|
||||
|
@ -278,4 +278,86 @@ void main() {
|
||||
);
|
||||
expect(innerScrollable.controller, isNull);
|
||||
});
|
||||
|
||||
testWidgets('Primary ListViews are always scrollable', (WidgetTester tester) async {
|
||||
final ListView view = new ListView(primary: true);
|
||||
expect(view.physics, const isInstanceOf<AlwaysScrollableScrollPhysics>());
|
||||
});
|
||||
|
||||
testWidgets('Non-primary ListViews are not always scrollable', (WidgetTester tester) async {
|
||||
final ListView view = new ListView(primary: false);
|
||||
expect(view.physics, isNot(const isInstanceOf<AlwaysScrollableScrollPhysics>()));
|
||||
});
|
||||
|
||||
testWidgets('Defaulting-to-primary ListViews are always scrollable', (WidgetTester tester) async {
|
||||
final ListView view = new ListView(scrollDirection: Axis.vertical);
|
||||
expect(view.physics, const isInstanceOf<AlwaysScrollableScrollPhysics>());
|
||||
});
|
||||
|
||||
testWidgets('Defaulting-to-not-primary ListViews are not always scrollable', (WidgetTester tester) async {
|
||||
final ListView view = new ListView(scrollDirection: Axis.horizontal);
|
||||
expect(view.physics, isNot(const isInstanceOf<AlwaysScrollableScrollPhysics>()));
|
||||
});
|
||||
|
||||
testWidgets('primary:true leads to scrolling', (WidgetTester tester) async {
|
||||
bool scrolled = false;
|
||||
await tester.pumpWidget(
|
||||
new NotificationListener<OverscrollNotification>(
|
||||
onNotification: (OverscrollNotification message) { scrolled = true; return false; },
|
||||
child: new ListView(
|
||||
primary: true,
|
||||
children: <Widget>[],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 100.0));
|
||||
expect(scrolled, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('primary:false leads to no scrolling', (WidgetTester tester) async {
|
||||
bool scrolled = false;
|
||||
await tester.pumpWidget(
|
||||
new NotificationListener<OverscrollNotification>(
|
||||
onNotification: (OverscrollNotification message) { scrolled = true; return false; },
|
||||
child: new ListView(
|
||||
primary: false,
|
||||
children: <Widget>[],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 100.0));
|
||||
expect(scrolled, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('physics:AlwaysScrollableScrollPhysics actually overrides primary:false default behaviour', (WidgetTester tester) async {
|
||||
bool scrolled = false;
|
||||
await tester.pumpWidget(
|
||||
new NotificationListener<OverscrollNotification>(
|
||||
onNotification: (OverscrollNotification message) { scrolled = true; return false; },
|
||||
child: new ListView(
|
||||
primary: false,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
children: <Widget>[],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 100.0));
|
||||
expect(scrolled, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('physics:ScrollPhysics actually overrides primary:true default behaviour', (WidgetTester tester) async {
|
||||
bool scrolled = false;
|
||||
await tester.pumpWidget(
|
||||
new NotificationListener<OverscrollNotification>(
|
||||
onNotification: (OverscrollNotification message) { scrolled = true; return false; },
|
||||
child: new ListView(
|
||||
primary: true,
|
||||
physics: const ScrollPhysics(),
|
||||
children: <Widget>[],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 100.0));
|
||||
expect(scrolled, isFalse);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user