ink_paint_test.dart 11.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
// Copyright 2017 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/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';

import '../rendering/mock_canvas.dart';

void main() {
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
  testWidgets('The Ink widget renders a Container by default', (WidgetTester tester) async {
    await tester.pumpWidget(
      Material(
        child: Ink(),
      ),
    );
    expect(tester.getSize(find.byType(Container)).height, 600.0);
    expect(tester.getSize(find.byType(Container)).width, 800.0);

    const double height = 150.0;
    const double width = 200.0;
    await tester.pumpWidget(
      Material(
        child: Center( // used to constrain to child's size
          child: Ink(
            height: height,
            width: width,
          ),
        ),
      ),
    );
    await tester.pumpAndSettle();
    expect(tester.getSize(find.byType(Container)).height, height);
    expect(tester.getSize(find.byType(Container)).width, width);
  });

Ian Hickson's avatar
Ian Hickson committed
38
  testWidgets('The InkWell widget renders an ink splash', (WidgetTester tester) async {
39 40
    const Color highlightColor = Color(0xAAFF0000);
    const Color splashColor = Color(0xAA0000FF);
41
    final BorderRadius borderRadius = BorderRadius.circular(6.0);
42 43

    await tester.pumpWidget(
44
      Directionality(
45
        textDirection: TextDirection.ltr,
46 47 48
        child: Material(
          child: Center(
            child: Container(
49 50
              width: 200.0,
              height: 60.0,
51
              child: InkWell(
52 53 54 55 56
                borderRadius: borderRadius,
                highlightColor: highlightColor,
                splashColor: splashColor,
                onTap: () { },
              ),
57 58 59 60 61 62
            ),
          ),
        ),
      ),
    );

63
    final Offset center = tester.getCenter(find.byType(InkWell));
64 65
    final TestGesture gesture = await tester.startGesture(center);
    await tester.pump(); // start gesture
66
    await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way
67

68
    final RenderBox box = Material.of(tester.element(find.byType(InkWell))) as dynamic;
69 70 71
    expect(
      box,
      paints
72 73 74
        ..translate(x: 0.0, y: 0.0)
        ..save()
        ..translate(x: 300.0, y: 270.0)
75
        ..clipRRect(rrect: RRect.fromLTRBR(0.0, 0.0, 200.0, 60.0, const Radius.circular(6.0)))
76 77
        ..circle(x: 100.0, y: 30.0, radius: 21.0, color: splashColor)
        ..restore()
78
        ..rrect(
79
          rrect: RRect.fromLTRBR(300.0, 270.0, 500.0, 330.0, const Radius.circular(6.0)),
80
          color: highlightColor,
81
        ),
82 83 84 85
    );

    await gesture.up();
  });
86

Ian Hickson's avatar
Ian Hickson committed
87
  testWidgets('The InkWell widget renders an ink ripple', (WidgetTester tester) async {
88 89
    const Color highlightColor = Color(0xAAFF0000);
    const Color splashColor = Color(0xB40000FF);
90
    final BorderRadius borderRadius = BorderRadius.circular(6.0);
91 92

    await tester.pumpWidget(
93
      Directionality(
94
        textDirection: TextDirection.ltr,
95 96 97
        child: Material(
          child: Center(
            child: Container(
98 99
              width: 100.0,
              height: 100.0,
100
              child: InkWell(
101 102 103 104 105 106 107
                borderRadius: borderRadius,
                highlightColor: highlightColor,
                splashColor: splashColor,
                onTap: () { },
                radius: 100.0,
                splashFactory: InkRipple.splashFactory,
              ),
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
            ),
          ),
        ),
      ),
    );

    final Offset tapDownOffset = tester.getTopLeft(find.byType(InkWell));
    final Offset inkWellCenter = tester.getCenter(find.byType(InkWell));
    //final TestGesture gesture = await tester.startGesture(tapDownOffset);
    await tester.tapAt(tapDownOffset);
    await tester.pump(); // start gesture

    final RenderBox box = Material.of(tester.element(find.byType(InkWell))) as dynamic;

    bool offsetsAreClose(Offset a, Offset b) => (a - b).distance < 1.0;
    bool radiiAreClose(double a, double b) => (a - b).abs() < 1.0;

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    PaintPattern ripplePattern(Offset expectedCenter, double expectedRadius, int expectedAlpha) {
      return paints
        ..translate(x: 0.0, y: 0.0)
        ..translate(x: tapDownOffset.dx, y: tapDownOffset.dy)
        ..something((Symbol method, List<dynamic> arguments) {
          if (method != #drawCircle)
            return false;
          final Offset center = arguments[0];
          final double radius = arguments[1];
          final Paint paint = arguments[2];
          if (offsetsAreClose(center, expectedCenter) && radiiAreClose(radius, expectedRadius) && paint.color.alpha == expectedAlpha)
            return true;
          throw '''
            Expected: center == $expectedCenter, radius == $expectedRadius, alpha == $expectedAlpha
            Found: center == $center radius == $radius alpha == ${paint.color.alpha}''';
        }
      );
    }

Ian Hickson's avatar
Ian Hickson committed
144
    // Initially the ripple's center is where the tap occurred;
145 146
    // ripplePattern always add a translation of tapDownOffset.
    expect(box, ripplePattern(Offset.zero, 30.0, 0));
147 148

    // The ripple fades in for 75ms. During that time its alpha is eased from
149
    // 0 to the splashColor's alpha value and its center moves towards the
150 151
    // center of the ink well.
    await tester.pump(const Duration(milliseconds: 50));
152
    expect(box, ripplePattern(const Offset(17.0, 17.0), 56.0, 120));
153 154 155 156

    // At 75ms the ripple has fade in: it's alpha matches the splashColor's
    // alpha and its center has moved closer to the ink well's center.
    await tester.pump(const Duration(milliseconds: 25));
157
    expect(box, ripplePattern(const Offset(29.0, 29.0), 73.0, 180));
158 159 160 161

    // At this point the splash radius has expanded to its limit: 5 past the
    // ink well's radius parameter. The splash center has moved to its final
    // location at the inkwell's center and the fade-out is about to start.
162 163
    // The fade-out begins at 225ms = 50ms + 25ms + 150ms.
    await tester.pump(const Duration(milliseconds: 150));
164
    expect(box, ripplePattern(inkWellCenter - tapDownOffset, 105.0, 180));
165 166 167

    // After another 150ms the fade-out is complete.
    await tester.pump(const Duration(milliseconds: 150));
168
    expect(box, ripplePattern(inkWellCenter - tapDownOffset, 105.0, 0));
Ian Hickson's avatar
Ian Hickson committed
169 170 171 172
  });

  testWidgets('Does the Ink widget render anything', (WidgetTester tester) async {
    await tester.pumpWidget(
173
      Directionality(
174
        textDirection: TextDirection.ltr,
175 176 177
        child: Material(
          child: Center(
            child: Ink(
178 179 180
              color: Colors.blue,
              width: 200.0,
              height: 200.0,
181
              child: InkWell(
182 183 184
                splashColor: Colors.green,
                onTap: () { },
              ),
Ian Hickson's avatar
Ian Hickson committed
185 186 187 188 189
            ),
          ),
        ),
      ),
    );
190

Ian Hickson's avatar
Ian Hickson committed
191 192 193 194 195 196 197 198 199
    final Offset center = tester.getCenter(find.byType(InkWell));
    final TestGesture gesture = await tester.startGesture(center);
    await tester.pump(); // start gesture
    await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way

    final RenderBox box = Material.of(tester.element(find.byType(InkWell))) as dynamic;
    expect(
      box,
      paints
Dan Field's avatar
Dan Field committed
200
        ..rect(rect: const Rect.fromLTRB(300.0, 200.0, 500.0, 400.0), color: Color(Colors.blue.value))
201
        ..circle(color: Color(Colors.green.value)),
Ian Hickson's avatar
Ian Hickson committed
202 203 204
    );

    await tester.pumpWidget(
205
      Directionality(
206
        textDirection: TextDirection.ltr,
207 208 209
        child: Material(
          child: Center(
            child: Ink(
210 211 212
              color: Colors.red,
              width: 200.0,
              height: 200.0,
213
              child: InkWell(
214 215 216
                splashColor: Colors.green,
                onTap: () { },
              ),
Ian Hickson's avatar
Ian Hickson committed
217 218 219 220 221 222 223 224 225 226 227
            ),
          ),
        ),
      ),
    );

    expect(Material.of(tester.element(find.byType(InkWell))), same(box));

    expect(
      box,
      paints
Dan Field's avatar
Dan Field committed
228
        ..rect(rect: const Rect.fromLTRB(300.0, 200.0, 500.0, 400.0), color: Color(Colors.red.value))
229
        ..circle(color: Color(Colors.green.value)),
Ian Hickson's avatar
Ian Hickson committed
230 231 232
    );

    await tester.pumpWidget(
233
      Directionality(
234
        textDirection: TextDirection.ltr,
235 236 237
        child: Material(
          child: Center(
            child: InkWell( // this is at a different depth in the tree so it's now a new InkWell
238 239 240
              splashColor: Colors.green,
              onTap: () { },
            ),
Ian Hickson's avatar
Ian Hickson committed
241 242 243 244 245 246 247 248 249 250 251
          ),
        ),
      ),
    );

    expect(Material.of(tester.element(find.byType(InkWell))), same(box));

    expect(box, isNot(paints..rect()));
    expect(box, isNot(paints..circle()));

    await gesture.up();
252
  }, skip: isBrowser);
253 254 255 256

  testWidgets('Cancel an InkRipple that was disposed when its animation ended', (WidgetTester tester) async {
    // Regression test for https://github.com/flutter/flutter/issues/14391
    await tester.pumpWidget(
257
      Directionality(
258
        textDirection: TextDirection.ltr,
259 260 261
        child: Material(
          child: Center(
            child: Container(
262 263
              width: 100.0,
              height: 100.0,
264
              child: InkWell(
265 266 267 268
                onTap: () { },
                radius: 100.0,
                splashFactory: InkRipple.splashFactory,
              ),
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
            ),
          ),
        ),
      ),
    );

    final Offset tapDownOffset = tester.getTopLeft(find.byType(InkWell));
    await tester.tapAt(tapDownOffset);
    await tester.pump(); // start splash
    await tester.pump(const Duration(milliseconds: 375)); // _kFadeOutDuration, in_ripple.dart

    final TestGesture gesture = await tester.startGesture(tapDownOffset);
    await tester.pump(); // start gesture
    await gesture.moveTo(const Offset(0.0, 0.0));
    await gesture.up(); // generates a tap cancel
    await tester.pumpAndSettle();
  });
286 287

  testWidgets('Cancel an InkRipple that was disposed when its animation ended', (WidgetTester tester) async {
288 289
    const Color highlightColor = Color(0xAAFF0000);
    const Color splashColor = Color(0xB40000FF);
290 291 292

    // Regression test for https://github.com/flutter/flutter/issues/14391
    await tester.pumpWidget(
293
      Directionality(
294
        textDirection: TextDirection.ltr,
295 296 297
        child: Material(
          child: Center(
            child: Container(
298 299
              width: 100.0,
              height: 100.0,
300
              child: InkWell(
301 302 303 304 305 306
                splashColor: splashColor,
                highlightColor: highlightColor,
                onTap: () { },
                radius: 100.0,
                splashFactory: InkRipple.splashFactory,
              ),
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
            ),
          ),
        ),
      ),
    );

    final Offset tapDownOffset = tester.getTopLeft(find.byType(InkWell));
    await tester.tapAt(tapDownOffset);
    await tester.pump(); // start splash
    // No delay here so _fadeInController.value=1.0 (InkRipple.dart)

    // Generate a tap cancel; Will cancel the ink splash before it started
    final TestGesture gesture = await tester.startGesture(tapDownOffset);
    await tester.pump(); // start gesture
    await gesture.moveTo(const Offset(0.0, 0.0));
    await gesture.up(); // generates a tap cancel

    final RenderBox box = Material.of(tester.element(find.byType(InkWell))) as dynamic;
    expect(box, paints..everything((Symbol method, List<dynamic> arguments) {
      if (method != #drawCircle)
        return true;
      final Paint paint = arguments[2];
      if (paint.color.alpha == 0)
        return true;
      throw 'Expected: paint.color.alpha == 0, found: ${paint.color.alpha}';
    }));
  });

335
}