Unverified Commit 418b1535 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Update style for CupertinoRefreshControl. (#19025)

All slivers must start with Sliver (or CupertinoSliver).
All widgets must have a key argument.

Also, some minor tweaks here and there to style and docs.
parent cf932490
......@@ -51,7 +51,7 @@ class _CupertinoRefreshControlDemoState extends State<CupertinoRefreshControlDem
const CupertinoSliverNavigationBar(
largeTitle: const Text('Cupertino Refresh'),
),
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
onRefresh: () {
return new Future<void>.delayed(const Duration(seconds: 2))
..then((_) => setState(() => repopulateList()));
......
......@@ -13,34 +13,35 @@ import 'activity_indicator.dart';
import 'colors.dart';
import 'icons.dart';
class _CupertinoRefreshSliver extends SingleChildRenderObjectWidget {
const _CupertinoRefreshSliver({
class _CupertinoSliverRefresh extends SingleChildRenderObjectWidget {
const _CupertinoSliverRefresh({
Key key,
this.refreshIndicatorLayoutExtent = 0.0,
this.hasLayoutExtent = false,
Widget child,
}) : assert(refreshIndicatorLayoutExtent != null),
assert(refreshIndicatorLayoutExtent >= 0.0),
assert(hasLayoutExtent != null),
super(child: child);
super(key: key, child: child);
// The amount of space the indicator should occupy in the sliver in a
// resting state when in the refreshing mode.
final double refreshIndicatorLayoutExtent;
// _RenderCupertinoRefreshSliver will paint the child in the available
// space either way but this instructs the _RenderCupertinoRefreshSliver
// _RenderCupertinoSliverRefresh will paint the child in the available
// space either way but this instructs the _RenderCupertinoSliverRefresh
// on whether to also occupy any layoutExtent space or not.
final bool hasLayoutExtent;
@override
_RenderCupertinoRefreshSliver createRenderObject(BuildContext context) {
return new _RenderCupertinoRefreshSliver(
_RenderCupertinoSliverRefresh createRenderObject(BuildContext context) {
return new _RenderCupertinoSliverRefresh(
refreshIndicatorExtent: refreshIndicatorLayoutExtent,
hasLayoutExtent: hasLayoutExtent,
);
}
@override
void updateRenderObject(BuildContext context, covariant _RenderCupertinoRefreshSliver renderObject) {
void updateRenderObject(BuildContext context, covariant _RenderCupertinoSliverRefresh renderObject) {
renderObject
..refreshIndicatorLayoutExtent = refreshIndicatorLayoutExtent
..hasLayoutExtent = hasLayoutExtent;
......@@ -53,10 +54,9 @@ class _CupertinoRefreshSliver extends SingleChildRenderObjectWidget {
//
// The [layoutExtentOffsetCompensation] field keeps internal accounting to
// prevent scroll position jumps as the [layoutExtent] is set and unset.
class _RenderCupertinoRefreshSliver
extends RenderSliver
class _RenderCupertinoSliverRefresh extends RenderSliver
with RenderObjectWithChildMixin<RenderBox> {
_RenderCupertinoRefreshSliver({
_RenderCupertinoSliverRefresh({
@required double refreshIndicatorExtent,
@required bool hasLayoutExtent,
RenderBox child,
......@@ -185,13 +185,17 @@ enum RefreshIndicatorMode {
/// Initial state, when not being overscrolled into, or after the overscroll
/// is canceled or after done and the sliver retracted away.
inactive,
/// While being overscrolled but not far enough yet to trigger the refresh.
drag,
/// Dragged far enough that the onRefresh callback will run and the dragged
/// displacement is not yet at the final refresh resting state.
armed,
/// While the onRefresh task is running.
refresh,
/// While the indicator is animating away after refreshing.
done,
}
......@@ -201,7 +205,7 @@ enum RefreshIndicatorMode {
/// control and the space available.
///
/// The `refreshTriggerPullDistance` and `refreshIndicatorExtent` parameters are
/// the same values passed into the [CupertinoRefreshControl].
/// the same values passed into the [CupertinoSliverRefreshControl].
///
/// The `pulledExtent` parameter is the currently available space either from
/// overscrolling or as held by the sliver during refresh.
......@@ -213,9 +217,9 @@ typedef Widget RefreshControlIndicatorBuilder(
double refreshIndicatorExtent,
);
/// A callback function that's invoked when the [CupertinoRefreshControl] is
/// A callback function that's invoked when the [CupertinoSliverRefreshControl] is
/// pulled a `refreshTriggerPullDistance`. Must return a [Future]. Upon
/// completion of the [Future], the [CupertinoRefreshControl] enters the
/// completion of the [Future], the [CupertinoSliverRefreshControl] enters the
/// [RefreshIndicatorMode.done] state and will start to go away.
typedef Future<void> RefreshCallback();
......@@ -254,19 +258,22 @@ typedef Future<void> RefreshCallback();
/// * [RefreshIndicator], a Material Design version of the pull-to-refresh
/// paradigm. This widget works differently than [RefreshIndicator] because
/// instead of being an overlay on top of the scrollable, the
/// [CupertinoRefreshControl] is part of the scrollable and actively occupies
/// [CupertinoSliverRefreshControl] is part of the scrollable and actively occupies
/// scrollable space.
class CupertinoRefreshControl extends StatefulWidget {
/// Create a new [CupertinoRefreshControl] for inserting into a list of slivers.
class CupertinoSliverRefreshControl extends StatefulWidget {
/// Create a new refresh control for inserting into a list of slivers.
///
/// [refreshTriggerPullDistance], [refreshIndicatorExtent] both have reasonable
/// defaults and cannot be null.
/// The [refreshTriggerPullDistance] and [refreshIndicatorExtent] arguments
/// must not be null.
///
/// [builder] has a default indicator builder but can be null, in which case
/// no indicator UI will be shown but the [onRefresh] will still be invoked.
/// The [builder] argument may be null, in which case no indicator UI will be
/// shown but the [onRefresh] will still be invoked. By default, [builder]
/// shows a [CupertinoActivityIndicator].
///
/// [onRefresh] will be called when pulled far enough to trigger a refresh.
const CupertinoRefreshControl({
/// The [onRefresh] argument will be called when pulled far enough to trigger
/// a refresh.
const CupertinoSliverRefreshControl({
Key key,
this.refreshTriggerPullDistance = _defaultRefreshTriggerPullDistance,
this.refreshIndicatorExtent = _defaultRefreshIndicatorExtent,
this.builder = buildSimpleRefreshIndicator,
......@@ -279,7 +286,8 @@ class CupertinoRefreshControl extends StatefulWidget {
refreshTriggerPullDistance >= refreshIndicatorExtent,
'The refresh indicator cannot take more space in its final state '
'than the amount initially created by overscrolling.'
);
),
super(key: key);
/// The amount of overscroll the scrollable must be dragged to trigger a reload.
///
......@@ -327,12 +335,12 @@ class CupertinoRefreshControl extends StatefulWidget {
static const double _defaultRefreshTriggerPullDistance = 100.0;
static const double _defaultRefreshIndicatorExtent = 60.0;
/// Retrieve the current state of the CupertinoRefreshControl. The same as the
/// Retrieve the current state of the CupertinoSliverRefreshControl. The same as the
/// state that gets passed into the [builder] function. Used for testing.
@visibleForTesting
static RefreshIndicatorMode state(BuildContext context) {
final _CupertinoRefreshControlState state
= context.ancestorStateOfType(const TypeMatcher<_CupertinoRefreshControlState>());
final _CupertinoSliverRefreshControlState state
= context.ancestorStateOfType(const TypeMatcher<_CupertinoSliverRefreshControlState>());
return state.refreshState;
}
......@@ -373,10 +381,10 @@ class CupertinoRefreshControl extends StatefulWidget {
}
@override
_CupertinoRefreshControlState createState() => new _CupertinoRefreshControlState();
_CupertinoSliverRefreshControlState createState() => new _CupertinoSliverRefreshControlState();
}
class _CupertinoRefreshControlState extends State<CupertinoRefreshControl> {
class _CupertinoSliverRefreshControlState extends State<CupertinoSliverRefreshControl> {
/// Reset the state from done to inactive when only this fraction of the
/// original `refreshTriggerPullDistance` is left.
static const double _inactiveResetOverscrollFraction = 0.1;
......@@ -498,7 +506,7 @@ class _CupertinoRefreshControlState extends State<CupertinoRefreshControl> {
@override
Widget build(BuildContext context) {
return new _CupertinoRefreshSliver(
return new _CupertinoSliverRefresh(
refreshIndicatorLayoutExtent: widget.refreshIndicatorExtent,
hasLayoutExtent: hasSliverLayoutExtent,
// A LayoutBuilder lets the sliver's layout changes be fed back out to
......@@ -515,9 +523,8 @@ class _CupertinoRefreshControlState extends State<CupertinoRefreshControl> {
widget.refreshTriggerPullDistance,
widget.refreshIndicatorExtent,
);
} else {
return new Container();
}
return new Container();
},
)
);
......
......@@ -75,7 +75,7 @@ enum _RefreshIndicatorMode {
/// * [RefreshIndicatorState], can be used to programmatically show the refresh indicator.
/// * [RefreshProgressIndicator], widget used by [RefreshIndicator] to show
/// the inner circular progress spinner during refreshes.
/// * [CupertinoRefreshControl], an iOS equivalent of the pull-to-refresh pattern.
/// * [CupertinoSliverRefreshControl], an iOS equivalent of the pull-to-refresh pattern.
/// Must be used as a sliver inside a [CustomScrollView] instead of wrapping
/// around a [ScrollView] because it's a part of the scrollable instead of
/// being overlaid on top of it.
......
......@@ -12,10 +12,10 @@ import 'package:mockito/mockito.dart';
void main() {
MockHelper mockHelper;
/// Completer that holds the future given to the CupertinoRefreshControl.
/// Completer that holds the future given to the CupertinoSliverRefreshControl.
Completer<void> refreshCompleter;
/// The widget that the indicator builder given to the CupertinoRefreshControl
/// The widget that the indicator builder given to the CupertinoSliverRefreshControl
/// returns.
Widget refreshIndicator;
......@@ -86,7 +86,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
),
buildAListOfStuff(),
......@@ -113,7 +113,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
),
buildAListOfStuff(),
......@@ -126,7 +126,7 @@ void main() {
await tester.drag(find.text('0'), const Offset(0.0, 50.0));
await tester.pump();
// The function is referenced once while passing into CupertinoRefreshControl
// The function is referenced once while passing into CupertinoSliverRefreshControl
// and is called.
verify(mockHelper.builder(
any,
......@@ -155,7 +155,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
),
buildAListOfStuff(),
......@@ -187,7 +187,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
),
buildAListOfStuff(),
......@@ -251,7 +251,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -313,7 +313,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -390,7 +390,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -470,7 +470,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -547,7 +547,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -636,7 +636,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -697,7 +697,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -785,7 +785,7 @@ void main() {
child: new CustomScrollView(
slivers: <Widget>[
buildAListOfStuff(),
new CupertinoRefreshControl( // it's in the middle now.
new CupertinoSliverRefreshControl( // it's in the middle now.
builder: builder,
onRefresh: onRefresh,
),
......@@ -817,7 +817,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
),
buildAListOfStuff(),
......@@ -866,7 +866,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
),
buildAListOfStuff(),
......@@ -876,7 +876,7 @@ void main() {
);
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.inactive,
);
......@@ -891,7 +891,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
),
buildAListOfStuff(),
......@@ -904,14 +904,14 @@ void main() {
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.drag,
);
await tester.pump(const Duration(seconds: 2));
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.inactive,
);
......@@ -926,7 +926,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
refreshTriggerPullDistance: 80.0,
),
......@@ -940,14 +940,14 @@ void main() {
await gesture.moveBy(const Offset(0.0, 79.0));
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.drag,
);
await gesture.moveBy(const Offset(0.0, 3.0)); // Overscrolling, need to move more than 1px.
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.armed,
);
......@@ -964,7 +964,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
refreshTriggerPullDistance: 90.0,
......@@ -980,7 +980,7 @@ void main() {
await gesture.moveBy(const Offset(0.0, 90.0)); // Arm it.
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.armed,
);
......@@ -991,7 +991,7 @@ void main() {
moreOrLessEquals(49.775111111111116), // Below 50 now.
);
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.refresh,
);
......@@ -1009,7 +1009,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -1022,7 +1022,7 @@ void main() {
await tester.drag(find.text('0'), const Offset(0.0, 100.0));
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.armed,
);
// The sliver scroll offset correction is applied on the next frame.
......@@ -1030,7 +1030,7 @@ void main() {
await tester.pump(const Duration(seconds: 2));
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.refresh,
);
expect(
......@@ -1043,7 +1043,7 @@ void main() {
// right away even though the sliver gets a new offset correction the
// next frame.
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.done,
);
......@@ -1061,7 +1061,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -1075,13 +1075,13 @@ void main() {
await gesture.moveBy(const Offset(0.0, 150.0));
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.armed,
);
refreshCompleter.complete(null);
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.done,
);
await tester.pump();
......@@ -1095,7 +1095,7 @@ void main() {
);
// Need to bring it to 100 * 0.1 to reset to inactive.
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.done,
);
......@@ -1106,7 +1106,7 @@ void main() {
moreOrLessEquals(9.313890708161875),
);
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.inactive,
);
......@@ -1124,7 +1124,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
......@@ -1138,7 +1138,7 @@ void main() {
await gesture.moveBy(const Offset(0.0, 150.0));
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.armed,
);
await tester.pump(); // Sliver scroll offset correction is applied one frame later.
......@@ -1151,7 +1151,7 @@ void main() {
moreOrLessEquals(-145.0332383665717),
);
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.refresh,
);
......@@ -1159,7 +1159,7 @@ void main() {
// The sliver layout extent is removed on next frame.
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.inactive,
);
// Nothing moved.
......@@ -1190,7 +1190,7 @@ void main() {
textDirection: TextDirection.ltr,
child: new CustomScrollView(
slivers: <Widget>[
new CupertinoRefreshControl(
new CupertinoSliverRefreshControl(
builder: null,
onRefresh: onRefresh,
refreshIndicatorExtent: 0.0,
......@@ -1204,7 +1204,7 @@ void main() {
await tester.drag(find.text('0'), const Offset(0.0, 150.0));
await tester.pump();
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
RefreshIndicatorMode.armed,
);
......@@ -1212,7 +1212,7 @@ void main() {
await tester.pump(const Duration(seconds: 5));
// In refresh mode but has no UI.
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.refresh,
);
expect(
......@@ -1225,7 +1225,7 @@ void main() {
await tester.pump();
// Goes to inactive right away since the sliver is already collapsed.
expect(
CupertinoRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder, skipOffstage: false))),
RefreshIndicatorMode.inactive,
);
......
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