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

import 'dart:ui' show window;

7
import 'package:flutter/gestures.dart';
8
import 'package:flutter/material.dart';
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/painting.dart';

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

RenderBox getMaterialBox(WidgetTester tester) {
  return tester.firstRenderObject<RenderBox>(
    find.descendant(
      of: find.byType(RawChip),
      matching: find.byType(CustomPaint),
    ),
  );
}

24 25 26 27 28 29 30 31 32
Material getMaterial(WidgetTester tester) {
  return tester.widget<Material>(
    find.descendant(
      of: find.byType(RawChip),
      matching: find.byType(Material),
    ),
  );
}

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
IconThemeData getIconData(WidgetTester tester) {
  final IconTheme iconTheme = tester.firstWidget(
    find.descendant(
      of: find.byType(RawChip),
      matching: find.byType(IconTheme),
    ),
  );
  return iconTheme.data;
}

DefaultTextStyle getLabelStyle(WidgetTester tester) {
  return tester.widget(
    find
        .descendant(
          of: find.byType(RawChip),
          matching: find.byType(DefaultTextStyle),
        )
        .last,
  );
}

void main() {
  testWidgets('Chip theme is built by ThemeData', (WidgetTester tester) async {
56
    final ThemeData theme = ThemeData(
57 58 59 60 61 62 63 64 65 66 67
      platform: TargetPlatform.android,
      primarySwatch: Colors.red,
    );
    final ChipThemeData chipTheme = theme.chipTheme;

    expect(chipTheme.backgroundColor, equals(Colors.black.withAlpha(0x1f)));
    expect(chipTheme.selectedColor, equals(Colors.black.withAlpha(0x3d)));
    expect(chipTheme.deleteIconColor, equals(Colors.black.withAlpha(0xde)));
  });

  testWidgets('Chip uses ThemeData chip theme if present', (WidgetTester tester) async {
68
    final ThemeData theme = ThemeData(
69 70 71 72 73 74 75
      platform: TargetPlatform.android,
      primarySwatch: Colors.red,
      backgroundColor: Colors.blue,
    );
    final ChipThemeData chipTheme = theme.chipTheme;

    Widget buildChip(ChipThemeData data) {
76 77 78
      return MaterialApp(
        locale: const Locale('en', 'us'),
        home: Directionality(
79
        textDirection: TextDirection.ltr,
80 81 82 83 84
        child: MediaQuery(
          data: MediaQueryData.fromWindow(window),
          child: Material(
            child: Center(
              child: Theme(
85
                data: theme,
86
                child: RawChip(
87
                  showCheckmark: true,
88
                  onDeleted: () { },
89 90 91 92
                  tapEnabled: true,
                  avatar: const Placeholder(),
                  deleteIcon: const Placeholder(),
                  isEnabled: true,
93 94
                  selected: false,
                  label: const Text('Chip'),
95
                  onSelected: (bool newValue) { },
96 97 98 99 100 101
                  onPressed: null,
                ),
              ),
            ),
          ),
        ),
102
      ));
103 104 105 106 107 108 109
    }

    await tester.pumpWidget(buildChip(chipTheme));
    await tester.pumpAndSettle();

    final RenderBox materialBox = getMaterialBox(tester);

110
    expect(materialBox, paints..path(color: chipTheme.backgroundColor));
111 112 113
  });

  testWidgets('Chip overrides ThemeData theme if ChipTheme present', (WidgetTester tester) async {
114
    final ThemeData theme = ThemeData(
115 116 117 118 119 120 121
      platform: TargetPlatform.android,
      primarySwatch: Colors.red,
    );
    final ChipThemeData chipTheme = theme.chipTheme;
    final ChipThemeData customTheme = chipTheme.copyWith(
      backgroundColor: Colors.purple,
      deleteIconColor: Colors.purple.withAlpha(0x3d),
122
      elevation: 3.0,
123
      shadowColor: Colors.pink,
124 125 126
    );
    const bool value = false;
    Widget buildChip(ChipThemeData data) {
127 128
      return MaterialApp(
        home: Directionality(
129
        textDirection: TextDirection.ltr,
130 131 132 133 134
        child: MediaQuery(
          data: MediaQueryData.fromWindow(window),
          child: Material(
            child: Center(
              child: Theme(
135
                data: theme,
136
                child: ChipTheme(
137
                  data: customTheme,
138
                  child: RawChip(
139
                    showCheckmark: true,
140
                    onDeleted: () { },
141 142 143 144 145 146
                    tapEnabled: true,
                    avatar: const Placeholder(),
                    deleteIcon: const Placeholder(),
                    isEnabled: true,
                    selected: value,
                    label: const Text('$value'),
147
                    onSelected: (bool newValue) { },
148 149 150 151 152 153 154
                    onPressed: null,
                  ),
                ),
              ),
            ),
          ),
        ),
155
      ));
156 157 158 159 160 161
    }

    await tester.pumpWidget(buildChip(chipTheme));
    await tester.pumpAndSettle();

    final RenderBox materialBox = getMaterialBox(tester);
162
    final Material material = getMaterial(tester);
163

164
    expect(materialBox, paints..path(color: Color(customTheme.backgroundColor.value)));
165
    expect(material.elevation, customTheme.elevation);
166
    expect(material.shadowColor, customTheme.shadowColor);
167
  }, skip: isBrowser);
168 169

  testWidgets('ChipThemeData generates correct opacities for defaults', (WidgetTester tester) async {
170 171
    const Color customColor1 = Color(0xcafefeed);
    const Color customColor2 = Color(0xdeadbeef);
172
    final TextStyle customStyle = ThemeData.fallback().textTheme.body2.copyWith(color: customColor2);
173

174
    final ChipThemeData lightTheme = ChipThemeData.fromDefaults(
175 176 177 178 179 180 181 182 183 184 185 186
      secondaryColor: customColor1,
      brightness: Brightness.light,
      labelStyle: customStyle,
    );

    expect(lightTheme.backgroundColor, equals(Colors.black.withAlpha(0x1f)));
    expect(lightTheme.deleteIconColor, equals(Colors.black.withAlpha(0xde)));
    expect(lightTheme.disabledColor, equals(Colors.black.withAlpha(0x0c)));
    expect(lightTheme.selectedColor, equals(Colors.black.withAlpha(0x3d)));
    expect(lightTheme.secondarySelectedColor, equals(customColor1.withAlpha(0x3d)));
    expect(lightTheme.labelPadding, equals(const EdgeInsets.symmetric(horizontal: 8.0)));
    expect(lightTheme.padding, equals(const EdgeInsets.all(4.0)));
187
    expect(lightTheme.shape, equals(isInstanceOf<StadiumBorder>()));
188 189 190 191
    expect(lightTheme.labelStyle.color, equals(Colors.black.withAlpha(0xde)));
    expect(lightTheme.secondaryLabelStyle.color, equals(customColor1.withAlpha(0xde)));
    expect(lightTheme.brightness, equals(Brightness.light));

192
    final ChipThemeData darkTheme = ChipThemeData.fromDefaults(
193 194 195 196 197 198 199 200 201 202 203 204
      secondaryColor: customColor1,
      brightness: Brightness.dark,
      labelStyle: customStyle,
    );

    expect(darkTheme.backgroundColor, equals(Colors.white.withAlpha(0x1f)));
    expect(darkTheme.deleteIconColor, equals(Colors.white.withAlpha(0xde)));
    expect(darkTheme.disabledColor, equals(Colors.white.withAlpha(0x0c)));
    expect(darkTheme.selectedColor, equals(Colors.white.withAlpha(0x3d)));
    expect(darkTheme.secondarySelectedColor, equals(customColor1.withAlpha(0x3d)));
    expect(darkTheme.labelPadding, equals(const EdgeInsets.symmetric(horizontal: 8.0)));
    expect(darkTheme.padding, equals(const EdgeInsets.all(4.0)));
205
    expect(darkTheme.shape, equals(isInstanceOf<StadiumBorder>()));
206 207 208 209
    expect(darkTheme.labelStyle.color, equals(Colors.white.withAlpha(0xde)));
    expect(darkTheme.secondaryLabelStyle.color, equals(customColor1.withAlpha(0xde)));
    expect(darkTheme.brightness, equals(Brightness.dark));

210
    final ChipThemeData customTheme = ChipThemeData.fromDefaults(
211 212 213 214 215 216 217 218 219 220 221 222
      primaryColor: customColor1,
      secondaryColor: customColor2,
      labelStyle: customStyle,
    );

    expect(customTheme.backgroundColor, equals(customColor1.withAlpha(0x1f)));
    expect(customTheme.deleteIconColor, equals(customColor1.withAlpha(0xde)));
    expect(customTheme.disabledColor, equals(customColor1.withAlpha(0x0c)));
    expect(customTheme.selectedColor, equals(customColor1.withAlpha(0x3d)));
    expect(customTheme.secondarySelectedColor, equals(customColor2.withAlpha(0x3d)));
    expect(customTheme.labelPadding, equals(const EdgeInsets.symmetric(horizontal: 8.0)));
    expect(customTheme.padding, equals(const EdgeInsets.all(4.0)));
223
    expect(customTheme.shape, equals(isInstanceOf<StadiumBorder>()));
224 225 226 227 228 229
    expect(customTheme.labelStyle.color, equals(customColor1.withAlpha(0xde)));
    expect(customTheme.secondaryLabelStyle.color, equals(customColor2.withAlpha(0xde)));
    expect(customTheme.brightness, equals(Brightness.light));
  });

  testWidgets('ChipThemeData lerps correctly', (WidgetTester tester) async {
230
    final ChipThemeData chipThemeBlack = ChipThemeData.fromDefaults(
231 232
      secondaryColor: Colors.black,
      brightness: Brightness.dark,
233
      labelStyle: ThemeData.fallback().textTheme.body2.copyWith(color: Colors.black),
234 235 236
    ).copyWith(
      elevation: 1.0,
      pressElevation: 4.0,
237 238
      shadowColor: Colors.black,
      selectedShadowColor: Colors.black,
239
      checkmarkColor: Colors.black,
240
    );
241
    final ChipThemeData chipThemeWhite = ChipThemeData.fromDefaults(
242 243
      secondaryColor: Colors.white,
      brightness: Brightness.light,
244
      labelStyle: ThemeData.fallback().textTheme.body2.copyWith(color: Colors.white),
245 246 247 248 249
    ).copyWith(
      padding: const EdgeInsets.all(2.0),
      labelPadding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
      elevation: 5.0,
      pressElevation: 10.0,
250 251
      shadowColor: Colors.white,
      selectedShadowColor: Colors.white,
252
      checkmarkColor: Colors.white,
253 254
    );

255
    final ChipThemeData lerp = ChipThemeData.lerp(chipThemeBlack, chipThemeWhite, 0.5);
256
    const Color middleGrey = Color(0xff7f7f7f);
257 258 259 260 261
    expect(lerp.backgroundColor, equals(middleGrey.withAlpha(0x1f)));
    expect(lerp.deleteIconColor, equals(middleGrey.withAlpha(0xde)));
    expect(lerp.disabledColor, equals(middleGrey.withAlpha(0x0c)));
    expect(lerp.selectedColor, equals(middleGrey.withAlpha(0x3d)));
    expect(lerp.secondarySelectedColor, equals(middleGrey.withAlpha(0x3d)));
262 263
    expect(lerp.shadowColor, equals(middleGrey));
    expect(lerp.selectedShadowColor, equals(middleGrey));
264 265
    expect(lerp.labelPadding, equals(const EdgeInsets.all(4.0)));
    expect(lerp.padding, equals(const EdgeInsets.all(3.0)));
266
    expect(lerp.shape, equals(isInstanceOf<StadiumBorder>()));
267 268 269
    expect(lerp.labelStyle.color, equals(middleGrey.withAlpha(0xde)));
    expect(lerp.secondaryLabelStyle.color, equals(middleGrey.withAlpha(0xde)));
    expect(lerp.brightness, equals(Brightness.light));
270 271
    expect(lerp.elevation, 3.0);
    expect(lerp.pressElevation, 7.0);
272
    expect(lerp.checkmarkColor, equals(middleGrey));
273 274 275 276 277 278 279 280 281

    expect(ChipThemeData.lerp(null, null, 0.25), isNull);

    final ChipThemeData lerpANull25 = ChipThemeData.lerp(null, chipThemeWhite, 0.25);
    expect(lerpANull25.backgroundColor, equals(Colors.black.withAlpha(0x08)));
    expect(lerpANull25.deleteIconColor, equals(Colors.black.withAlpha(0x38)));
    expect(lerpANull25.disabledColor, equals(Colors.black.withAlpha(0x03)));
    expect(lerpANull25.selectedColor, equals(Colors.black.withAlpha(0x0f)));
    expect(lerpANull25.secondarySelectedColor, equals(Colors.white.withAlpha(0x0f)));
282 283
    expect(lerpANull25.shadowColor, equals(Colors.white.withAlpha(0x40)));
    expect(lerpANull25.selectedShadowColor, equals(Colors.white.withAlpha(0x40)));
284 285
    expect(lerpANull25.labelPadding, equals(const EdgeInsets.only(left: 0.0, top: 2.0, right: 0.0, bottom: 2.0)));
    expect(lerpANull25.padding, equals(const EdgeInsets.all(0.5)));
286
    expect(lerpANull25.shape, equals(isInstanceOf<StadiumBorder>()));
287 288 289
    expect(lerpANull25.labelStyle.color, equals(Colors.black.withAlpha(0x38)));
    expect(lerpANull25.secondaryLabelStyle.color, equals(Colors.white.withAlpha(0x38)));
    expect(lerpANull25.brightness, equals(Brightness.light));
290 291
    expect(lerpANull25.elevation, 1.25);
    expect(lerpANull25.pressElevation, 2.5);
292
    expect(lerpANull25.checkmarkColor, equals(Colors.white.withAlpha(0x40)));
293 294 295 296 297 298 299

    final ChipThemeData lerpANull75 = ChipThemeData.lerp(null, chipThemeWhite, 0.75);
    expect(lerpANull75.backgroundColor, equals(Colors.black.withAlpha(0x17)));
    expect(lerpANull75.deleteIconColor, equals(Colors.black.withAlpha(0xa7)));
    expect(lerpANull75.disabledColor, equals(Colors.black.withAlpha(0x09)));
    expect(lerpANull75.selectedColor, equals(Colors.black.withAlpha(0x2e)));
    expect(lerpANull75.secondarySelectedColor, equals(Colors.white.withAlpha(0x2e)));
300 301
    expect(lerpANull75.shadowColor, equals(Colors.white.withAlpha(0xbf)));
    expect(lerpANull75.selectedShadowColor, equals(Colors.white.withAlpha(0xbf)));
302 303
    expect(lerpANull75.labelPadding, equals(const EdgeInsets.only(left: 0.0, top: 6.0, right: 0.0, bottom: 6.0)));
    expect(lerpANull75.padding, equals(const EdgeInsets.all(1.5)));
304
    expect(lerpANull75.shape, equals(isInstanceOf<StadiumBorder>()));
305 306 307
    expect(lerpANull75.labelStyle.color, equals(Colors.black.withAlpha(0xa7)));
    expect(lerpANull75.secondaryLabelStyle.color, equals(Colors.white.withAlpha(0xa7)));
    expect(lerpANull75.brightness, equals(Brightness.light));
308 309
    expect(lerpANull75.elevation, 3.75);
    expect(lerpANull75.pressElevation, 7.5);
310
    expect(lerpANull75.checkmarkColor, equals(Colors.white.withAlpha(0xbf)));
311 312 313 314 315 316 317

    final ChipThemeData lerpBNull25 = ChipThemeData.lerp(chipThemeBlack, null, 0.25);
    expect(lerpBNull25.backgroundColor, equals(Colors.white.withAlpha(0x17)));
    expect(lerpBNull25.deleteIconColor, equals(Colors.white.withAlpha(0xa7)));
    expect(lerpBNull25.disabledColor, equals(Colors.white.withAlpha(0x09)));
    expect(lerpBNull25.selectedColor, equals(Colors.white.withAlpha(0x2e)));
    expect(lerpBNull25.secondarySelectedColor, equals(Colors.black.withAlpha(0x2e)));
318 319
    expect(lerpBNull25.shadowColor, equals(Colors.black.withAlpha(0xbf)));
    expect(lerpBNull25.selectedShadowColor, equals(Colors.black.withAlpha(0xbf)));
320 321
    expect(lerpBNull25.labelPadding, equals(const EdgeInsets.only(left: 6.0, top: 0.0, right: 6.0, bottom: 0.0)));
    expect(lerpBNull25.padding, equals(const EdgeInsets.all(3.0)));
322
    expect(lerpBNull25.shape, equals(isInstanceOf<StadiumBorder>()));
323 324 325
    expect(lerpBNull25.labelStyle.color, equals(Colors.white.withAlpha(0xa7)));
    expect(lerpBNull25.secondaryLabelStyle.color, equals(Colors.black.withAlpha(0xa7)));
    expect(lerpBNull25.brightness, equals(Brightness.dark));
326 327
    expect(lerpBNull25.elevation, 0.75);
    expect(lerpBNull25.pressElevation, 3.0);
328
    expect(lerpBNull25.checkmarkColor, equals(Colors.black.withAlpha(0xbf)));
329 330 331 332 333 334 335

    final ChipThemeData lerpBNull75 = ChipThemeData.lerp(chipThemeBlack, null, 0.75);
    expect(lerpBNull75.backgroundColor, equals(Colors.white.withAlpha(0x08)));
    expect(lerpBNull75.deleteIconColor, equals(Colors.white.withAlpha(0x38)));
    expect(lerpBNull75.disabledColor, equals(Colors.white.withAlpha(0x03)));
    expect(lerpBNull75.selectedColor, equals(Colors.white.withAlpha(0x0f)));
    expect(lerpBNull75.secondarySelectedColor, equals(Colors.black.withAlpha(0x0f)));
336 337
    expect(lerpBNull75.shadowColor, equals(Colors.black.withAlpha(0x40)));
    expect(lerpBNull75.selectedShadowColor, equals(Colors.black.withAlpha(0x40)));
338 339
    expect(lerpBNull75.labelPadding, equals(const EdgeInsets.only(left: 2.0, top: 0.0, right: 2.0, bottom: 0.0)));
    expect(lerpBNull75.padding, equals(const EdgeInsets.all(1.0)));
340
    expect(lerpBNull75.shape, equals(isInstanceOf<StadiumBorder>()));
341 342 343
    expect(lerpBNull75.labelStyle.color, equals(Colors.white.withAlpha(0x38)));
    expect(lerpBNull75.secondaryLabelStyle.color, equals(Colors.black.withAlpha(0x38)));
    expect(lerpBNull75.brightness, equals(Brightness.light));
344 345
    expect(lerpBNull75.elevation, 0.25);
    expect(lerpBNull75.pressElevation, 1.0);
346
    expect(lerpBNull75.checkmarkColor, equals(Colors.black.withAlpha(0x40)));
347
  });
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441

  testWidgets('Chip uses stateful color from chip theme', (WidgetTester tester) async {
    final FocusNode focusNode = FocusNode();

    const Color pressedColor = Color(0x00000001);
    const Color hoverColor = Color(0x00000002);
    const Color focusedColor = Color(0x00000003);
    const Color defaultColor = Color(0x00000004);
    const Color selectedColor = Color(0x00000005);
    const Color disabledColor = Color(0x00000006);

    Color getTextColor(Set<MaterialState> states) {
      if (states.contains(MaterialState.disabled))
        return disabledColor;

      if (states.contains(MaterialState.pressed))
        return pressedColor;

      if (states.contains(MaterialState.hovered))
        return hoverColor;

      if (states.contains(MaterialState.focused))
        return focusedColor;

      if (states.contains(MaterialState.selected))
        return selectedColor;

      return defaultColor;
    }

    final TextStyle labelStyle =  TextStyle(
      color: MaterialStateColor.resolveWith(getTextColor),
    );
    Widget chipWidget({ bool enabled = true, bool selected = false }) {
      return MaterialApp(
        theme: ThemeData(
          chipTheme: ThemeData.light().chipTheme.copyWith(
            labelStyle: labelStyle,
            secondaryLabelStyle: labelStyle,
          ),
        ),
        home: Scaffold(
          body: Focus(
            focusNode: focusNode,
            child: ChoiceChip(
              label: const Text('Chip'),
              selected: selected,
              onSelected: enabled ? (_) {} : null,
            ),
          ),
        ),
      );
    }
    Color textColor() {
      return tester.renderObject<RenderParagraph>(find.text('Chip')).text.style.color;
    }

    // Default, not disabled.
    await tester.pumpWidget(chipWidget());
    expect(textColor(), equals(defaultColor));

    // Selected.
    await tester.pumpWidget(chipWidget(selected: true));
    expect(textColor(), selectedColor);

    // Focused.
    final FocusNode chipFocusNode = focusNode.children.first;
    chipFocusNode.requestFocus();
    await tester.pumpAndSettle();
    expect(textColor(), focusedColor);

    // Hovered.
    final Offset center = tester.getCenter(find.byType(ChoiceChip));
    final TestGesture gesture = await tester.createGesture(
      kind: PointerDeviceKind.mouse,
    );
    await gesture.addPointer();
    await gesture.moveTo(center);
    await tester.pumpAndSettle();
    expect(textColor(), hoverColor);

    // Pressed.
    await gesture.down(center);
    await tester.pumpAndSettle();
    expect(textColor(), pressedColor);

    // Disabled.
    await tester.pumpWidget(chipWidget(enabled: false));
    await tester.pumpAndSettle();
    expect(textColor(), disabledColor);

    // Teardown.
    await gesture.removePointer();
  });
442
}