slivers_appbar_floating_test.dart 12.3 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1 2 3 4 5 6 7 8 9
// Copyright 2016 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/rendering.dart';
import 'package:flutter/widgets.dart';

void verifyPaintPosition(GlobalKey key, Offset ideal, bool visible) {
10
  final RenderSliver target = key.currentContext.findRenderObject();
Adam Barth's avatar
Adam Barth committed
11
  expect(target.parent, const isInstanceOf<RenderViewport>());
12 13
  final SliverPhysicalParentData parentData = target.parentData;
  final Offset actual = parentData.paintOffset;
Ian Hickson's avatar
Ian Hickson committed
14
  expect(actual, ideal);
15
  final SliverGeometry geometry = target.geometry;
Ian Hickson's avatar
Ian Hickson committed
16 17 18 19
  expect(geometry.visible, visible);
}

void verifyActualBoxPosition(WidgetTester tester, Finder finder, int index, Rect ideal) {
20
  final RenderBox box = tester.renderObjectList<RenderBox>(finder).elementAt(index);
21
  final Rect rect = new Rect.fromPoints(box.localToGlobal(Offset.zero), box.localToGlobal(box.size.bottomRight(Offset.zero)));
Ian Hickson's avatar
Ian Hickson committed
22 23 24 25 26 27 28
  expect(rect, equals(ideal));
}

void main() {
  testWidgets('Sliver appbars - floating - scroll offset doesn\'t change', (WidgetTester tester) async {
    const double bigHeight = 1000.0;
    await tester.pumpWidget(
29
      new CustomScrollView(
30
        slivers: <Widget>[
31
          const BigSliver(height: bigHeight),
32
          new SliverPersistentHeader(delegate: new TestDelegate(), floating: true),
33
          const BigSliver(height: bigHeight),
Ian Hickson's avatar
Ian Hickson committed
34 35 36
        ],
      ),
    );
37
    final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
Ian Hickson's avatar
Ian Hickson committed
38 39 40 41 42 43
    final double max = bigHeight * 2.0 + new TestDelegate().maxExtent - 600.0; // 600 is the height of the test viewport
    assert(max < 10000.0);
    expect(max, 1600.0);
    expect(position.pixels, 0.0);
    expect(position.minScrollExtent, 0.0);
    expect(position.maxScrollExtent, max);
44
    position.animateTo(10000.0, curve: Curves.linear, duration: const Duration(minutes: 1));
45
    await tester.pumpAndSettle(const Duration(milliseconds: 50));
Ian Hickson's avatar
Ian Hickson committed
46 47 48 49 50 51 52 53 54 55
    expect(position.pixels, max);
    expect(position.minScrollExtent, 0.0);
    expect(position.maxScrollExtent, max);
  });

  testWidgets('Sliver appbars - floating - normal behavior works', (WidgetTester tester) async {
    final TestDelegate delegate = new TestDelegate();
    const double bigHeight = 1000.0;
    GlobalKey key1, key2, key3;
    await tester.pumpWidget(
56
      new CustomScrollView(
57
        slivers: <Widget>[
Ian Hickson's avatar
Ian Hickson committed
58
          new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
59
          new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: delegate, floating: true),
Ian Hickson's avatar
Ian Hickson committed
60 61 62 63
          new BigSliver(key: key3 = new GlobalKey(), height: bigHeight),
        ],
      ),
    );
64
    final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
Ian Hickson's avatar
Ian Hickson committed
65

66 67 68
    verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
    verifyPaintPosition(key2, const Offset(0.0, 600.0), false);
    verifyPaintPosition(key3, const Offset(0.0, 600.0), false);
Ian Hickson's avatar
Ian Hickson committed
69

70
    position.animateTo(bigHeight - 600.0 + delegate.maxExtent, curve: Curves.linear, duration: const Duration(minutes: 1));
71
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
72
    verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
73 74
    verifyPaintPosition(key2, new Offset(0.0, 600.0 - delegate.maxExtent), true);
    verifyActualBoxPosition(tester, find.byType(Container), 0, new Rect.fromLTWH(0.0, 600.0 - delegate.maxExtent, 800.0, delegate.maxExtent));
75
    verifyPaintPosition(key3, const Offset(0.0, 600.0), false);
Ian Hickson's avatar
Ian Hickson committed
76 77

    assert(delegate.maxExtent * 2.0 < 600.0); // make sure this fits on the test screen...
78
    position.animateTo(bigHeight - 600.0 + delegate.maxExtent * 2.0, curve: Curves.linear, duration: const Duration(minutes: 1));
79
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
80
    verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
81 82 83 84
    verifyPaintPosition(key2, new Offset(0.0, 600.0 - delegate.maxExtent * 2.0), true);
    verifyActualBoxPosition(tester, find.byType(Container), 0, new Rect.fromLTWH(0.0, 600.0 - delegate.maxExtent * 2.0, 800.0, delegate.maxExtent));
    verifyPaintPosition(key3, new Offset(0.0, 600.0 - delegate.maxExtent), true);

85
    position.animateTo(bigHeight, curve: Curves.linear, duration: const Duration(minutes: 1));
86
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
87 88
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
89 90 91
    verifyActualBoxPosition(tester, find.byType(Container), 0, new Rect.fromLTWH(0.0, 0.0, 800.0, delegate.maxExtent));
    verifyPaintPosition(key3, new Offset(0.0, delegate.maxExtent), true);

92
    position.animateTo(bigHeight + delegate.maxExtent * 0.1, curve: Curves.linear, duration: const Duration(minutes: 1));
93
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
94 95
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
96 97 98
    verifyActualBoxPosition(tester, find.byType(Container), 0, new Rect.fromLTWH(0.0, 0.0, 800.0, delegate.maxExtent * 0.9));
    verifyPaintPosition(key3, new Offset(0.0, delegate.maxExtent * 0.9), true);

99
    position.animateTo(bigHeight + delegate.maxExtent * 0.5, curve: Curves.linear, duration: const Duration(minutes: 1));
100
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
101 102
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
103 104 105
    verifyActualBoxPosition(tester, find.byType(Container), 0, new Rect.fromLTWH(0.0, 0.0, 800.0, delegate.maxExtent * 0.5));
    verifyPaintPosition(key3, new Offset(0.0, delegate.maxExtent * 0.5), true);

106
    position.animateTo(bigHeight + delegate.maxExtent * 0.9, curve: Curves.linear, duration: const Duration(minutes: 1));
107
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
108 109
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
110 111 112
    verifyActualBoxPosition(tester, find.byType(Container), 0, new Rect.fromLTWH(0.0, -delegate.maxExtent * 0.4, 800.0, delegate.maxExtent * 0.5));
    verifyPaintPosition(key3, new Offset(0.0, delegate.maxExtent * 0.1), true);

113
    position.animateTo(bigHeight + delegate.maxExtent * 2.0, curve: Curves.linear, duration: const Duration(minutes: 1));
114
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
115 116 117
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key3, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
118 119 120 121 122 123 124
  });

  testWidgets('Sliver appbars - floating - no floating behavior when animating', (WidgetTester tester) async {
    final TestDelegate delegate = new TestDelegate();
    const double bigHeight = 1000.0;
    GlobalKey key1, key2, key3;
    await tester.pumpWidget(
125
      new CustomScrollView(
126
        slivers: <Widget>[
Ian Hickson's avatar
Ian Hickson committed
127
          new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
128
          new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: delegate, floating: true),
Ian Hickson's avatar
Ian Hickson committed
129 130 131 132
          new BigSliver(key: key3 = new GlobalKey(), height: bigHeight),
        ],
      ),
    );
133
    final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
Ian Hickson's avatar
Ian Hickson committed
134

135 136 137
    verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
    verifyPaintPosition(key2, const Offset(0.0, 600.0), false);
    verifyPaintPosition(key3, const Offset(0.0, 600.0), false);
Ian Hickson's avatar
Ian Hickson committed
138

139
    position.animateTo(bigHeight + delegate.maxExtent * 2.0, curve: Curves.linear, duration: const Duration(minutes: 1));
140
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
141 142 143
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key3, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
144

145
    position.animateTo(bigHeight + delegate.maxExtent * 1.9, curve: Curves.linear, duration: const Duration(minutes: 1));
146
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
147 148 149
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key3, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
150 151 152 153 154 155 156
  });

  testWidgets('Sliver appbars - floating - floating behavior when dragging down', (WidgetTester tester) async {
    final TestDelegate delegate = new TestDelegate();
    const double bigHeight = 1000.0;
    GlobalKey key1, key2, key3;
    await tester.pumpWidget(
157
      new CustomScrollView(
158
        slivers: <Widget>[
Ian Hickson's avatar
Ian Hickson committed
159
          new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
160
          new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: delegate, floating: true),
Ian Hickson's avatar
Ian Hickson committed
161 162 163 164
          new BigSliver(key: key3 = new GlobalKey(), height: bigHeight),
        ],
      ),
    );
165
    final ScrollPositionWithSingleContext position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
Ian Hickson's avatar
Ian Hickson committed
166

167 168 169
    verifyPaintPosition(key1, const Offset(0.0, 0.0), true);
    verifyPaintPosition(key2, const Offset(0.0, 600.0), false);
    verifyPaintPosition(key3, const Offset(0.0, 600.0), false);
Ian Hickson's avatar
Ian Hickson committed
170

171
    position.animateTo(bigHeight + delegate.maxExtent * 2.0, curve: Curves.linear, duration: const Duration(minutes: 1));
172
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
173 174 175
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key3, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
176

177
    position.animateTo(bigHeight + delegate.maxExtent * 1.9, curve: Curves.linear, duration: const Duration(minutes: 1));
178
    position.updateUserScrollDirection(ScrollDirection.forward);
179
    await tester.pumpAndSettle(const Duration(milliseconds: 1000));
180 181
    verifyPaintPosition(key1, const Offset(0.0, 0.0), false);
    verifyPaintPosition(key2, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
182
    verifyActualBoxPosition(tester, find.byType(Container), 0, new Rect.fromLTWH(0.0, -delegate.maxExtent * 0.4, 800.0, delegate.maxExtent * 0.5));
183
    verifyPaintPosition(key3, const Offset(0.0, 0.0), true);
Ian Hickson's avatar
Ian Hickson committed
184
  });
185 186 187 188 189 190 191 192 193

  testWidgets('Sliver appbars - floating - overscroll gap is below header', (WidgetTester tester) async {
    await tester.pumpWidget(
      new CustomScrollView(
        physics: const BouncingScrollPhysics(),
        slivers: <Widget>[
          new SliverPersistentHeader(delegate: new TestDelegate(), floating: true),
          new SliverList(
            delegate: new SliverChildListDelegate(<Widget>[
194
              const SizedBox(
195
                height: 300.0,
196
                child: const Text('X'),
197 198 199 200 201 202 203
              ),
            ]),
          ),
        ],
      ),
    );

204 205
    expect(tester.getTopLeft(find.byType(Container)), Offset.zero);
    expect(tester.getTopLeft(find.text('X')), const Offset(0.0, 200.0));
206

207
    final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
208 209 210
    position.jumpTo(-50.0);
    await tester.pump();

211 212
    expect(tester.getTopLeft(find.byType(Container)), Offset.zero);
    expect(tester.getTopLeft(find.text('X')), const Offset(0.0, 250.0));
213
  });
Ian Hickson's avatar
Ian Hickson committed
214 215
}

216
class TestDelegate extends SliverPersistentHeaderDelegate {
Ian Hickson's avatar
Ian Hickson committed
217 218 219 220
  @override
  double get maxExtent => 200.0;

  @override
221 222 223 224 225
  double get minExtent => 100.0;

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new Container(constraints: new BoxConstraints(minHeight: minExtent, maxHeight: maxExtent));
Ian Hickson's avatar
Ian Hickson committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
  }

  @override
  bool shouldRebuild(TestDelegate oldDelegate) => false;
}


class RenderBigSliver extends RenderSliver {
  RenderBigSliver(double height) : _height = height;

  double get height => _height;
  double _height;
  set height(double value) {
    if (value == _height)
      return;
    _height = value;
    markNeedsLayout();
  }

  double get paintExtent => (height - constraints.scrollOffset).clamp(0.0, constraints.remainingPaintExtent);

  @override
  void performLayout() {
    geometry = new SliverGeometry(
      scrollExtent: height,
      paintExtent: paintExtent,
      maxPaintExtent: height,
    );
  }
}

class BigSliver extends LeafRenderObjectWidget {
258
  const BigSliver({ Key key, this.height }) : super(key: key);
Ian Hickson's avatar
Ian Hickson committed
259 260 261 262 263 264 265 266 267 268 269 270 271

  final double height;

  @override
  RenderBigSliver createRenderObject(BuildContext context) {
    return new RenderBigSliver(height);
  }

  @override
  void updateRenderObject(BuildContext context, RenderBigSliver renderObject) {
    renderObject.height = height;
  }
}