picker_test.dart 11.3 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
Ian Hickson's avatar
Ian Hickson committed
4

5 6
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
7
import 'package:flutter/rendering.dart';
8
import 'package:flutter/services.dart';
9 10
import 'package:flutter_test/flutter_test.dart';

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

13
void main() {
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
  testWidgets('Picker respects theme styling', (WidgetTester tester) async {
    await tester.pumpWidget(
      CupertinoApp(
        home: Align(
          alignment: Alignment.topLeft,
          child: SizedBox(
            height: 300.0,
            width: 300.0,
            child: CupertinoPicker(
              itemExtent: 50.0,
              onSelectedItemChanged: (_) { },
              children: List<Widget>.generate(3, (int index) {
                return Container(
                  height: 50.0,
                  width: 300.0,
                  child: Text(index.toString()),
                );
              }),
            ),
          ),
        ),
      ),
    );

    final RenderParagraph paragraph = tester.renderObject(find.text('1'));

40 41
    expect(paragraph.text.style.color, isSameColorAs(CupertinoColors.black));
    expect(paragraph.text.style.copyWith(color: CupertinoColors.black), const TextStyle(
42 43
      inherit: false,
      fontFamily: '.SF Pro Display',
44
      fontSize: 21.0,
45 46 47 48 49 50
      fontWeight: FontWeight.w400,
      letterSpacing: -0.41,
      color: CupertinoColors.black,
    ));
  });

51 52 53
  group('layout', () {
    testWidgets('selected item is in the middle', (WidgetTester tester) async {
      final FixedExtentScrollController controller =
54
          FixedExtentScrollController(initialItem: 1);
55 56

      await tester.pumpWidget(
57
        Directionality(
58
          textDirection: TextDirection.ltr,
59
          child: Align(
60
            alignment: Alignment.topLeft,
61
            child: SizedBox(
62 63
              height: 300.0,
              width: 300.0,
64
              child: CupertinoPicker(
65 66
                scrollController: controller,
                itemExtent: 50.0,
67
                onSelectedItemChanged: (_) { },
68 69
                children: List<Widget>.generate(3, (int index) {
                  return Container(
70 71
                    height: 50.0,
                    width: 300.0,
72
                    child: Text(index.toString()),
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
                  );
                }),
              ),
            ),
          ),
        ),
      );

      expect(
        tester.getTopLeft(find.widgetWithText(Container, '1')),
        const Offset(0.0, 125.0),
      );

      controller.jumpToItem(0);
      await tester.pump();

      expect(
        tester.getTopLeft(find.widgetWithText(Container, '1')),
        const Offset(0.0, 175.0),
      );
      expect(
        tester.getTopLeft(find.widgetWithText(Container, '0')),
        const Offset(0.0, 125.0),
      );
    });
  });

100 101 102 103 104 105 106 107 108 109 110 111 112
  testWidgets('picker dark mode', (WidgetTester tester) async {
    await tester.pumpWidget(
      CupertinoApp(
        theme: const CupertinoThemeData(brightness: Brightness.light),
        home: Align(
          alignment: Alignment.topLeft,
          child: SizedBox(
            height: 300.0,
            width: 300.0,
            child: CupertinoPicker(
              backgroundColor: const CupertinoDynamicColor.withBrightness(
                color: Color(0xFF123456), // Set alpha channel to FF to disable under magnifier painting.
                darkColor: Color(0xFF654321),
113
              ),
114 115 116
              itemExtent: 15.0,
              children: const <Widget>[Text('1'), Text('1')],
              onSelectedItemChanged: (int i) { },
117 118 119
            ),
          ),
        ),
120 121
      ),
    );
122

123 124
    expect(find.byType(CupertinoPicker), paints..path(color: const Color(0x33000000), style: PaintingStyle.stroke));
    expect(find.byType(CupertinoPicker), paints..rect(color: const Color(0xFF123456)));
125

126 127 128 129 130 131 132 133 134 135 136 137
    await tester.pumpWidget(
      CupertinoApp(
        theme: const CupertinoThemeData(brightness: Brightness.dark),
        home: Align(
          alignment: Alignment.topLeft,
          child: SizedBox(
            height: 300.0,
            width: 300.0,
            child: CupertinoPicker(
              backgroundColor: const CupertinoDynamicColor.withBrightness(
                color: Color(0xFF123456),
                darkColor: Color(0xFF654321),
138
              ),
139 140 141
              itemExtent: 15.0,
              children: const <Widget>[Text('1'), Text('1')],
              onSelectedItemChanged: (int i) { },
142 143 144
            ),
          ),
        ),
145 146 147 148 149
      ),
    );

    expect(find.byType(CupertinoPicker), paints..path(color: const Color(0x33FFFFFF), style: PaintingStyle.stroke));
    expect(find.byType(CupertinoPicker), paints..rect(color: const Color(0xFF654321)));
150 151
  });

152
  group('scroll', () {
153 154 155 156 157 158 159 160 161 162 163
    testWidgets(
      'scrolling calls onSelectedItemChanged and triggers haptic feedback',
      (WidgetTester tester) async {
        final List<int> selectedItems = <int>[];
        final List<MethodCall> systemCalls = <MethodCall>[];

        SystemChannels.platform.setMockMethodCallHandler((MethodCall methodCall) async {
          systemCalls.add(methodCall);
        });

        await tester.pumpWidget(
164
          Directionality(
165
            textDirection: TextDirection.ltr,
166
            child: CupertinoPicker(
167 168
              itemExtent: 100.0,
              onSelectedItemChanged: (int index) { selectedItems.add(index); },
169 170 171
              children: List<Widget>.generate(100, (int index) {
                return Center(
                  child: Container(
172 173
                    width: 400.0,
                    height: 100.0,
174
                    child: Text(index.toString()),
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
                  ),
                );
              }),
            ),
          ),
        );

        await tester.drag(find.text('0'), const Offset(0.0, -100.0));
        expect(selectedItems, <int>[1]);
        expect(
          systemCalls.single,
          isMethodCall(
            'HapticFeedback.vibrate',
            arguments: 'HapticFeedbackType.selectionClick',
          ),
        );

        await tester.drag(find.text('0'), const Offset(0.0, 100.0));
        expect(selectedItems, <int>[1, 0]);
        expect(systemCalls, hasLength(2));
        expect(
          systemCalls.last,
          isMethodCall(
            'HapticFeedback.vibrate',
            arguments: 'HapticFeedbackType.selectionClick',
          ),
        );
Dan Field's avatar
Dan Field committed
202
    }, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
203 204 205 206 207 208 209 210 211 212 213 214

    testWidgets(
      'do not trigger haptic effects on non-iOS devices',
      (WidgetTester tester) async {
        final List<int> selectedItems = <int>[];
        final List<MethodCall> systemCalls = <MethodCall>[];

        SystemChannels.platform.setMockMethodCallHandler((MethodCall methodCall) async {
          systemCalls.add(methodCall);
        });

        await tester.pumpWidget(
215
          Directionality(
216
            textDirection: TextDirection.ltr,
217
            child: CupertinoPicker(
218 219
              itemExtent: 100.0,
              onSelectedItemChanged: (int index) { selectedItems.add(index); },
220 221 222
              children: List<Widget>.generate(100, (int index) {
                return Center(
                  child: Container(
223 224
                    width: 400.0,
                    height: 100.0,
225
                    child: Text(index.toString()),
226 227 228 229 230 231 232 233 234 235
                  ),
                );
              }),
            ),
          ),
        );

        await tester.drag(find.text('0'), const Offset(0.0, -100.0));
        expect(selectedItems, <int>[1]);
        expect(systemCalls, isEmpty);
Dan Field's avatar
Dan Field committed
236
    }, variant: TargetPlatformVariant(TargetPlatform.values.where((TargetPlatform platform) => platform != TargetPlatform.iOS).toSet()));
237

238 239
    testWidgets('a drag in between items settles back', (WidgetTester tester) async {
      final FixedExtentScrollController controller =
240
          FixedExtentScrollController(initialItem: 10);
241 242 243
      final List<int> selectedItems = <int>[];

      await tester.pumpWidget(
244
        Directionality(
245
          textDirection: TextDirection.ltr,
246
          child: CupertinoPicker(
247 248 249
            scrollController: controller,
            itemExtent: 100.0,
            onSelectedItemChanged: (int index) { selectedItems.add(index); },
250 251 252
            children: List<Widget>.generate(100, (int index) {
              return Center(
                child: Container(
253 254
                  width: 400.0,
                  height: 100.0,
255
                  child: Text(index.toString()),
256 257 258 259 260 261 262 263
                ),
              );
            }),
          ),
        ),
      );

      // Drag it by a bit but not enough to move to the next item.
264
      await tester.drag(find.text('10'), const Offset(0.0, 30.0), touchSlopY: 0.0);
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280

      // The item that was in the center now moved a bit.
      expect(
        tester.getTopLeft(find.widgetWithText(Container, '10')),
        const Offset(200.0, 280.0),
      );

      await tester.pumpAndSettle();

      expect(
        tester.getTopLeft(find.widgetWithText(Container, '10')).dy,
        moreOrLessEquals(250.0, epsilon: 0.5),
      );
      expect(selectedItems.isEmpty, true);

      // Drag it by enough to move to the next item.
281
      await tester.drag(find.text('10'), const Offset(0.0, 70.0), touchSlopY: 0.0);
282 283 284 285 286 287 288 289 290

      await tester.pumpAndSettle();

      expect(
        tester.getTopLeft(find.widgetWithText(Container, '10')).dy,
        // It's down by 100.0 now.
        moreOrLessEquals(350.0, epsilon: 0.5),
      );
      expect(selectedItems, <int>[9]);
Dan Field's avatar
Dan Field committed
291
    }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS,  TargetPlatform.macOS }));
292 293 294

    testWidgets('a big fling that overscrolls springs back', (WidgetTester tester) async {
      final FixedExtentScrollController controller =
295
          FixedExtentScrollController(initialItem: 10);
296 297 298
      final List<int> selectedItems = <int>[];

      await tester.pumpWidget(
299
        Directionality(
300
          textDirection: TextDirection.ltr,
301
          child: CupertinoPicker(
302 303 304
            scrollController: controller,
            itemExtent: 100.0,
            onSelectedItemChanged: (int index) { selectedItems.add(index); },
305 306 307
            children: List<Widget>.generate(100, (int index) {
              return Center(
                child: Container(
308 309
                  width: 400.0,
                  height: 100.0,
310
                  child: Text(index.toString()),
311 312 313 314 315 316 317 318 319 320 321 322 323 324
                ),
              );
            }),
          ),
        ),
      );

      // A wild throw appears.
      await tester.fling(
        find.text('10'),
        const Offset(0.0, 10000.0),
        1000.0,
      );

325 326 327 328
      // Should have been flung far enough that even the first item goes off
      // screen and gets removed.
      expect(find.widgetWithText(Container, '0').evaluate().isEmpty, true);

329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
      expect(
        selectedItems,
        // This specific throw was fast enough that each scroll update landed
        // on every second item.
        <int>[8, 6, 4, 2, 0],
      );

      // Let it spring back.
      await tester.pumpAndSettle();

      expect(
        tester.getTopLeft(find.widgetWithText(Container, '0')).dy,
        // Should have sprung back to the middle now.
        moreOrLessEquals(250.0),
      );
      expect(
        selectedItems,
        // Falling back to 0 shouldn't produce more callbacks.
        <int>[8, 6, 4, 2, 0],
      );
Dan Field's avatar
Dan Field committed
349
    }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS,  TargetPlatform.macOS }));
350 351
  });
}