ink_paint_test.dart 10.6 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() {
Ian Hickson's avatar
Ian Hickson committed
12
  testWidgets('The InkWell widget renders an ink splash', (WidgetTester tester) async {
13 14
    const Color highlightColor = Color(0xAAFF0000);
    const Color splashColor = Color(0xAA0000FF);
15
    final BorderRadius borderRadius = BorderRadius.circular(6.0);
16 17

    await tester.pumpWidget(
18
      Directionality(
19
        textDirection: TextDirection.ltr,
20 21 22
        child: Material(
          child: Center(
            child: Container(
23 24
              width: 200.0,
              height: 60.0,
25
              child: InkWell(
26 27 28 29 30
                borderRadius: borderRadius,
                highlightColor: highlightColor,
                splashColor: splashColor,
                onTap: () { },
              ),
31 32 33 34 35 36
            ),
          ),
        ),
      ),
    );

37
    final Offset center = tester.getCenter(find.byType(InkWell));
38 39
    final TestGesture gesture = await tester.startGesture(center);
    await tester.pump(); // start gesture
40
    await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way
41

42
    final RenderBox box = Material.of(tester.element(find.byType(InkWell))) as dynamic;
43 44 45
    expect(
      box,
      paints
46 47 48
        ..translate(x: 0.0, y: 0.0)
        ..save()
        ..translate(x: 300.0, y: 270.0)
49
        ..clipRRect(rrect: RRect.fromLTRBR(0.0, 0.0, 200.0, 60.0, const Radius.circular(6.0)))
50 51
        ..circle(x: 100.0, y: 30.0, radius: 21.0, color: splashColor)
        ..restore()
52
        ..rrect(
53
          rrect: RRect.fromLTRBR(300.0, 270.0, 500.0, 330.0, const Radius.circular(6.0)),
54
          color: highlightColor,
55
        ),
56 57 58 59
    );

    await gesture.up();
  });
60

Ian Hickson's avatar
Ian Hickson committed
61
  testWidgets('The InkWell widget renders an ink ripple', (WidgetTester tester) async {
62 63
    const Color highlightColor = Color(0xAAFF0000);
    const Color splashColor = Color(0xB40000FF);
64
    final BorderRadius borderRadius = BorderRadius.circular(6.0);
65 66

    await tester.pumpWidget(
67
      Directionality(
68
        textDirection: TextDirection.ltr,
69 70 71
        child: Material(
          child: Center(
            child: Container(
72 73
              width: 100.0,
              height: 100.0,
74
              child: InkWell(
75 76 77 78 79 80 81
                borderRadius: borderRadius,
                highlightColor: highlightColor,
                splashColor: splashColor,
                onTap: () { },
                radius: 100.0,
                splashFactory: InkRipple.splashFactory,
              ),
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
            ),
          ),
        ),
      ),
    );

    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;

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    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
118
    // Initially the ripple's center is where the tap occurred;
119 120
    // ripplePattern always add a translation of tapDownOffset.
    expect(box, ripplePattern(Offset.zero, 30.0, 0));
121 122

    // The ripple fades in for 75ms. During that time its alpha is eased from
123
    // 0 to the splashColor's alpha value and its center moves towards the
124 125
    // center of the ink well.
    await tester.pump(const Duration(milliseconds: 50));
126
    expect(box, ripplePattern(const Offset(17.0, 17.0), 56.0, 120));
127 128 129 130

    // 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));
131
    expect(box, ripplePattern(const Offset(29.0, 29.0), 73.0, 180));
132 133 134 135

    // 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.
136 137
    // The fade-out begins at 225ms = 50ms + 25ms + 150ms.
    await tester.pump(const Duration(milliseconds: 150));
138
    expect(box, ripplePattern(inkWellCenter - tapDownOffset, 105.0, 180));
139 140 141

    // After another 150ms the fade-out is complete.
    await tester.pump(const Duration(milliseconds: 150));
142
    expect(box, ripplePattern(inkWellCenter - tapDownOffset, 105.0, 0));
Ian Hickson's avatar
Ian Hickson committed
143 144 145 146
  });

  testWidgets('Does the Ink widget render anything', (WidgetTester tester) async {
    await tester.pumpWidget(
147
      Directionality(
148
        textDirection: TextDirection.ltr,
149 150 151
        child: Material(
          child: Center(
            child: Ink(
152 153 154
              color: Colors.blue,
              width: 200.0,
              height: 200.0,
155
              child: InkWell(
156 157 158
                splashColor: Colors.green,
                onTap: () { },
              ),
Ian Hickson's avatar
Ian Hickson committed
159 160 161 162 163
            ),
          ),
        ),
      ),
    );
164

Ian Hickson's avatar
Ian Hickson committed
165 166 167 168 169 170 171 172 173
    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
174
        ..rect(rect: const Rect.fromLTRB(300.0, 200.0, 500.0, 400.0), color: Color(Colors.blue.value))
175
        ..circle(color: Color(Colors.green.value)),
Ian Hickson's avatar
Ian Hickson committed
176 177 178
    );

    await tester.pumpWidget(
179
      Directionality(
180
        textDirection: TextDirection.ltr,
181 182 183
        child: Material(
          child: Center(
            child: Ink(
184 185 186
              color: Colors.red,
              width: 200.0,
              height: 200.0,
187
              child: InkWell(
188 189 190
                splashColor: Colors.green,
                onTap: () { },
              ),
Ian Hickson's avatar
Ian Hickson committed
191 192 193 194 195 196 197 198 199 200 201
            ),
          ),
        ),
      ),
    );

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

    expect(
      box,
      paints
Dan Field's avatar
Dan Field committed
202
        ..rect(rect: const Rect.fromLTRB(300.0, 200.0, 500.0, 400.0), color: Color(Colors.red.value))
203
        ..circle(color: Color(Colors.green.value)),
Ian Hickson's avatar
Ian Hickson committed
204 205 206
    );

    await tester.pumpWidget(
207
      Directionality(
208
        textDirection: TextDirection.ltr,
209 210 211
        child: Material(
          child: Center(
            child: InkWell( // this is at a different depth in the tree so it's now a new InkWell
212 213 214
              splashColor: Colors.green,
              onTap: () { },
            ),
Ian Hickson's avatar
Ian Hickson committed
215 216 217 218 219 220 221 222 223 224 225
          ),
        ),
      ),
    );

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

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

    await gesture.up();
226
  });
227 228 229 230

  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(
231
      Directionality(
232
        textDirection: TextDirection.ltr,
233 234 235
        child: Material(
          child: Center(
            child: Container(
236 237
              width: 100.0,
              height: 100.0,
238
              child: InkWell(
239 240 241 242
                onTap: () { },
                radius: 100.0,
                splashFactory: InkRipple.splashFactory,
              ),
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
            ),
          ),
        ),
      ),
    );

    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();
  });
260 261

  testWidgets('Cancel an InkRipple that was disposed when its animation ended', (WidgetTester tester) async {
262 263
    const Color highlightColor = Color(0xAAFF0000);
    const Color splashColor = Color(0xB40000FF);
264 265 266

    // Regression test for https://github.com/flutter/flutter/issues/14391
    await tester.pumpWidget(
267
      Directionality(
268
        textDirection: TextDirection.ltr,
269 270 271
        child: Material(
          child: Center(
            child: Container(
272 273
              width: 100.0,
              height: 100.0,
274
              child: InkWell(
275 276 277 278 279 280
                splashColor: splashColor,
                highlightColor: highlightColor,
                onTap: () { },
                radius: 100.0,
                splashFactory: InkRipple.splashFactory,
              ),
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
            ),
          ),
        ),
      ),
    );

    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}';
    }));
  });

309
}