slivers_appbar_floating_test.dart 11.5 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1 2 3 4 5 6 7 8 9 10
// 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) {
  RenderSliver target = key.currentContext.findRenderObject();
11
  expect(target.parent, const isInstanceOf<RenderViewport2>());
Ian Hickson's avatar
Ian Hickson committed
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
  SliverPhysicalParentData parentData = target.parentData;
  Offset actual = parentData.paintOffset;
  expect(actual, ideal);
  SliverGeometry geometry = target.geometry;
  expect(geometry.visible, visible);
}

void verifyActualBoxPosition(WidgetTester tester, Finder finder, int index, Rect ideal) {
  RenderBox box = tester.renderObjectList<RenderBox>(finder).elementAt(index);
  Rect rect = new Rect.fromPoints(box.localToGlobal(Point.origin), box.localToGlobal(box.size.bottomRight(Point.origin)));
  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>[
Ian Hickson's avatar
Ian Hickson committed
31
          new BigSliver(height: bigHeight),
32
          new SliverPersistentHeader(delegate: new TestDelegate(), floating: true),
Ian Hickson's avatar
Ian Hickson committed
33 34 35 36
          new BigSliver(height: bigHeight),
        ],
      ),
    );
37
    ScrollPosition position = tester.state<Scrollable2State>(find.byType(Scrollable2)).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));
Ian Hickson's avatar
Ian Hickson committed
45 46 47 48 49 50 51 52 53 54 55
    await tester.pumpUntilNoTransientCallbacks(const Duration(milliseconds: 10));
    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
    ScrollPosition position = tester.state<Scrollable2State>(find.byType(Scrollable2)).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));
Ian Hickson's avatar
Ian Hickson committed
71
    await tester.pumpUntilNoTransientCallbacks(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));
Ian Hickson's avatar
Ian Hickson committed
79
    await tester.pumpUntilNoTransientCallbacks(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));
Ian Hickson's avatar
Ian Hickson committed
86
    await tester.pumpUntilNoTransientCallbacks(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));
Ian Hickson's avatar
Ian Hickson committed
93
    await tester.pumpUntilNoTransientCallbacks(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));
Ian Hickson's avatar
Ian Hickson committed
100
    await tester.pumpUntilNoTransientCallbacks(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));
Ian Hickson's avatar
Ian Hickson committed
107
    await tester.pumpUntilNoTransientCallbacks(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));
Ian Hickson's avatar
Ian Hickson committed
114
    await tester.pumpUntilNoTransientCallbacks(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
    ScrollPosition position = tester.state<Scrollable2State>(find.byType(Scrollable2)).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));
Ian Hickson's avatar
Ian Hickson committed
140
    await tester.pumpUntilNoTransientCallbacks(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));
Ian Hickson's avatar
Ian Hickson committed
146
    await tester.pumpUntilNoTransientCallbacks(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
    ScrollPosition position = tester.state<Scrollable2State>(find.byType(Scrollable2)).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));
Ian Hickson's avatar
Ian Hickson committed
172
    await tester.pumpUntilNoTransientCallbacks(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));
Ian Hickson's avatar
Ian Hickson committed
178 179
    position.updateUserScrollDirection(ScrollDirection.forward); // ignore: INVALID_USE_OF_PROTECTED_MEMBER, since this is using a protected method for testing purposes
    await tester.pumpUntilNoTransientCallbacks(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
class TestDelegate extends SliverPersistentHeaderDelegate {
Ian Hickson's avatar
Ian Hickson committed
188 189 190 191
  @override
  double get maxExtent => 200.0;

  @override
192 193 194 195 196
  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
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
  }

  @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 {
  BigSliver({ Key key, this.height }) : super(key: key);

  final double height;

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

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