mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
[H] Expose "center" on CustomScrollView (#27424)
* Expose "center" on CustomScrollView * Also support anchor
This commit is contained in:
parent
16d3847847
commit
496ddc580c
|
@ -51,6 +51,12 @@ abstract class ScrollView extends StatelessWidget {
|
|||
/// Creates a widget that scrolls.
|
||||
///
|
||||
/// If the [primary] argument is true, the [controller] must be null.
|
||||
///
|
||||
/// If the [shrinkWrap] argument is true, the [center] argument must be null.
|
||||
///
|
||||
/// The [scrollDirection], [reverse], and [shrinkWrap] arguments must not be null.
|
||||
///
|
||||
/// The [anchor] argument must be non-null and in the range 0.0 to 1.0.
|
||||
const ScrollView({
|
||||
Key key,
|
||||
this.scrollDirection = Axis.vertical,
|
||||
|
@ -59,16 +65,22 @@ abstract class ScrollView extends StatelessWidget {
|
|||
bool primary,
|
||||
ScrollPhysics physics,
|
||||
this.shrinkWrap = false,
|
||||
this.center,
|
||||
this.anchor = 0.0,
|
||||
this.cacheExtent,
|
||||
this.semanticChildCount,
|
||||
this.dragStartBehavior = DragStartBehavior.down,
|
||||
}) : assert(reverse != null),
|
||||
}) : assert(scrollDirection != null),
|
||||
assert(reverse != null),
|
||||
assert(shrinkWrap != null),
|
||||
assert(dragStartBehavior != null),
|
||||
assert(!(controller != null && primary == true),
|
||||
'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. '
|
||||
'You cannot both set primary to true and pass an explicit controller.'
|
||||
),
|
||||
assert(!shrinkWrap || center == null),
|
||||
assert(anchor != null),
|
||||
assert(anchor >= 0.0 && anchor <= 1.0),
|
||||
primary = primary ?? controller == null && identical(scrollDirection, Axis.vertical),
|
||||
physics = physics ?? (primary == true || (primary == null && controller == null && identical(scrollDirection, Axis.vertical)) ? const AlwaysScrollableScrollPhysics() : null),
|
||||
super(key: key);
|
||||
|
@ -172,6 +184,32 @@ abstract class ScrollView extends StatelessWidget {
|
|||
/// Defaults to false.
|
||||
final bool shrinkWrap;
|
||||
|
||||
/// The first child in the [GrowthDirection.forward] growth direction.
|
||||
///
|
||||
/// Children after [center] will be placed in the [axisDirection] relative to
|
||||
/// the [center]. Children before [center] will be placed in the opposite of
|
||||
/// the [axisDirection] relative to the [center].
|
||||
///
|
||||
/// The [center] must be the key of one of the slivers built by [buildSlivers].
|
||||
///
|
||||
/// Of the built-in subclasses of [ScrollView], only [CustomScrollView]
|
||||
/// supports [center]; for that class, the given key must be the key of one of
|
||||
/// the slivers in the [CustomScrollView.slivers] list.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [anchor], which controls where the [center] as aligned in the viewport.
|
||||
final Key center;
|
||||
|
||||
/// The relative position of the zero scroll offset.
|
||||
///
|
||||
/// For example, if [anchor] is 0.5 and the [axisDirection] is
|
||||
/// [AxisDirection.down] or [AxisDirection.up], then the zero scroll offset is
|
||||
/// vertically centered within the viewport. If the [anchor] is 1.0, and the
|
||||
/// [axisDirection] is [AxisDirection.right], then the zero scroll offset is
|
||||
/// on the left edge of the viewport.
|
||||
final double anchor;
|
||||
|
||||
/// {@macro flutter.rendering.viewport.cacheExtent}
|
||||
final double cacheExtent;
|
||||
|
||||
|
@ -221,6 +259,14 @@ abstract class ScrollView extends StatelessWidget {
|
|||
/// Subclasses may override this method to change how the viewport is built.
|
||||
/// The default implementation uses a [ShrinkWrappingViewport] if [shrinkWrap]
|
||||
/// is true, and a regular [Viewport] otherwise.
|
||||
///
|
||||
/// The `offset` argument is the value obtained from
|
||||
/// [Scrollable.viewportBuilder].
|
||||
///
|
||||
/// The `axisDirection` argument is the value obtained from [getDirection],
|
||||
/// which by default uses [scrollDirection] and [reverse].
|
||||
///
|
||||
/// The `slivers` argument is the value obtained from [buildSlivers].
|
||||
@protected
|
||||
Widget buildViewport(
|
||||
BuildContext context,
|
||||
|
@ -240,6 +286,8 @@ abstract class ScrollView extends StatelessWidget {
|
|||
offset: offset,
|
||||
slivers: slivers,
|
||||
cacheExtent: cacheExtent,
|
||||
center: center,
|
||||
anchor: anchor,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -392,7 +440,7 @@ abstract class ScrollView extends StatelessWidget {
|
|||
class CustomScrollView extends ScrollView {
|
||||
/// Creates a [ScrollView] that creates custom scroll effects using slivers.
|
||||
///
|
||||
/// If the [primary] argument is true, the [controller] must be null.
|
||||
/// See the [new ScrollView] constructor for more details on these arguments.
|
||||
const CustomScrollView({
|
||||
Key key,
|
||||
Axis scrollDirection = Axis.vertical,
|
||||
|
@ -401,6 +449,8 @@ class CustomScrollView extends ScrollView {
|
|||
bool primary,
|
||||
ScrollPhysics physics,
|
||||
bool shrinkWrap = false,
|
||||
Key center,
|
||||
double anchor = 0.0,
|
||||
double cacheExtent,
|
||||
this.slivers = const <Widget>[],
|
||||
int semanticChildCount,
|
||||
|
@ -413,6 +463,8 @@ class CustomScrollView extends ScrollView {
|
|||
primary: primary,
|
||||
physics: physics,
|
||||
shrinkWrap: shrinkWrap,
|
||||
center: center,
|
||||
anchor: anchor,
|
||||
cacheExtent: cacheExtent,
|
||||
semanticChildCount: semanticChildCount,
|
||||
dragStartBehavior: dragStartBehavior,
|
||||
|
|
91
packages/flutter/test/widgets/custom_scroll_view_test.dart
Normal file
91
packages/flutter/test/widgets/custom_scroll_view_test.dart
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('CustomScrollView.center', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(key: Key('a'), child: SizedBox(height: 100.0)),
|
||||
SliverToBoxAdapter(key: Key('b'), child: SizedBox(height: 100.0)),
|
||||
],
|
||||
center: Key('a'),
|
||||
),
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.getRect(find.descendant(of: find.byKey(const Key('a')), matching: find.byType(SizedBox))),
|
||||
Rect.fromLTRB(0.0, 0.0, 800.0, 100.0));
|
||||
expect(tester.getRect(find.descendant(of: find.byKey(const Key('b')), matching: find.byType(SizedBox))),
|
||||
Rect.fromLTRB(0.0, 100.0, 800.0, 200.0));
|
||||
});
|
||||
|
||||
testWidgets('CustomScrollView.center', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(key: Key('a'), child: SizedBox(height: 100.0)),
|
||||
SliverToBoxAdapter(key: Key('b'), child: SizedBox(height: 100.0)),
|
||||
],
|
||||
center: Key('b'),
|
||||
),
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
tester.getRect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('a'), skipOffstage: false),
|
||||
matching: find.byType(SizedBox, skipOffstage: false),
|
||||
),
|
||||
),
|
||||
Rect.fromLTRB(0.0, -100.0, 800.0, 0.0),
|
||||
);
|
||||
expect(
|
||||
tester.getRect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('b')),
|
||||
matching: find.byType(SizedBox),
|
||||
),
|
||||
),
|
||||
Rect.fromLTRB(0.0, 0.0, 800.0, 100.0),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('CustomScrollView.anchor', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(key: Key('a'), child: SizedBox(height: 100.0)),
|
||||
SliverToBoxAdapter(key: Key('b'), child: SizedBox(height: 100.0)),
|
||||
],
|
||||
center: Key('b'),
|
||||
anchor: 1.0,
|
||||
),
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
tester.getRect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('a')),
|
||||
matching: find.byType(SizedBox),
|
||||
),
|
||||
),
|
||||
Rect.fromLTRB(0.0, 500.0, 800.0, 600.0),
|
||||
);
|
||||
expect(
|
||||
tester.getRect(
|
||||
find.descendant(
|
||||
of: find.byKey(const Key('b'), skipOffstage: false),
|
||||
matching: find.byType(SizedBox, skipOffstage: false),
|
||||
),
|
||||
),
|
||||
Rect.fromLTRB(0.0, 600.0, 800.0, 700.0),
|
||||
);
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue