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

5
import 'package:flutter/gestures.dart';
6
import 'package:flutter/material.dart';
7 8
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
9
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
10 11 12 13 14 15 16 17 18 19

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

20 21 22 23 24 25 26 27 28
Material getMaterial(WidgetTester tester) {
  return tester.widget<Material>(
    find.descendant(
      of: find.byType(RawChip),
      matching: find.byType(Material),
    ),
  );
}

29 30
DefaultTextStyle getLabelStyle(WidgetTester tester) {
  return tester.widget(
31 32 33 34
    find.descendant(
      of: find.byType(RawChip),
      matching: find.byType(DefaultTextStyle),
    ).last,
35 36 37 38
  );
}

void main() {
39 40 41 42
  test('ChipThemeData copyWith, ==, hashCode basics', () {
    expect(const ChipThemeData(), const ChipThemeData().copyWith());
    expect(const ChipThemeData().hashCode, const ChipThemeData().copyWith().hashCode);
  });
43

44 45 46 47 48 49
  test('ChipThemeData lerp special cases', () {
    expect(ChipThemeData.lerp(null, null, 0), null);
    const ChipThemeData data = ChipThemeData();
    expect(identical(ChipThemeData.lerp(data, data, 0.5), data), true);
  });

50 51
  test('ChipThemeData defaults', () {
    const ChipThemeData themeData = ChipThemeData();
52
    expect(themeData.color, null);
53 54 55 56 57 58
    expect(themeData.backgroundColor, null);
    expect(themeData.deleteIconColor, null);
    expect(themeData.disabledColor, null);
    expect(themeData.selectedColor, null);
    expect(themeData.secondarySelectedColor, null);
    expect(themeData.shadowColor, null);
59
    expect(themeData.surfaceTintColor, null);
60 61 62 63 64 65 66 67 68 69 70 71
    expect(themeData.selectedShadowColor, null);
    expect(themeData.showCheckmark, null);
    expect(themeData.checkmarkColor, null);
    expect(themeData.labelPadding, null);
    expect(themeData.padding, null);
    expect(themeData.side, null);
    expect(themeData.shape, null);
    expect(themeData.labelStyle, null);
    expect(themeData.secondaryLabelStyle, null);
    expect(themeData.brightness, null);
    expect(themeData.elevation, null);
    expect(themeData.pressElevation, null);
72
    expect(themeData.iconTheme, null);
73 74
  });

75
  testWidgetsWithLeakTracking('Default ChipThemeData debugFillProperties', (WidgetTester tester) async {
76 77
    final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
    const ChipThemeData().debugFillProperties(builder);
78

79 80 81 82 83 84
    final List<String> description = builder.properties
      .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
      .map((DiagnosticsNode node) => node.toString())
      .toList();

    expect(description, <String>[]);
85 86
  });

87
  testWidgetsWithLeakTracking('ChipThemeData implements debugFillProperties', (WidgetTester tester) async {
88 89
    final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
    const ChipThemeData(
90 91 92 93 94 95 96 97 98
      color: MaterialStatePropertyAll<Color>(Color(0xfffffff0)),
      backgroundColor: Color(0xfffffff1),
      deleteIconColor: Color(0xfffffff2),
      disabledColor: Color(0xfffffff3),
      selectedColor: Color(0xfffffff4),
      secondarySelectedColor: Color(0xfffffff5),
      shadowColor: Color(0xfffffff6),
      surfaceTintColor: Color(0xfffffff7),
      selectedShadowColor: Color(0xfffffff8),
99
      showCheckmark: true,
100
      checkmarkColor: Color(0xfffffff9),
101 102 103 104 105 106 107 108 109
      labelPadding: EdgeInsets.all(1),
      padding: EdgeInsets.all(2),
      side: BorderSide(width: 10),
      shape: RoundedRectangleBorder(),
      labelStyle: TextStyle(fontSize: 10),
      secondaryLabelStyle: TextStyle(fontSize: 20),
      brightness: Brightness.dark,
      elevation: 5,
      pressElevation: 6,
110
      iconTheme: IconThemeData(color: Color(0xffffff10)),
111 112 113 114 115 116 117
    ).debugFillProperties(builder);

    final List<String> description = builder.properties
        .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
        .map((DiagnosticsNode node) => node.toString())
        .toList();

118
    expect(description, equalsIgnoringHashCodes(<String>[
119 120 121 122 123 124 125 126 127
      'color: MaterialStatePropertyAll(Color(0xfffffff0))',
      'backgroundColor: Color(0xfffffff1)',
      'deleteIconColor: Color(0xfffffff2)',
      'disabledColor: Color(0xfffffff3)',
      'selectedColor: Color(0xfffffff4)',
      'secondarySelectedColor: Color(0xfffffff5)',
      'shadowColor: Color(0xfffffff6)',
      'surfaceTintColor: Color(0xfffffff7)',
      'selectedShadowColor: Color(0xfffffff8)',
128
      'showCheckmark: true',
129
      'checkMarkColor: Color(0xfffffff9)',
130 131
      'labelPadding: EdgeInsets.all(1.0)',
      'padding: EdgeInsets.all(2.0)',
132 133
      'side: BorderSide(width: 10.0)',
      'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)',
134 135 136 137 138
      'labelStyle: TextStyle(inherit: true, size: 10.0)',
      'secondaryLabelStyle: TextStyle(inherit: true, size: 20.0)',
      'brightness: dark',
      'elevation: 5.0',
      'pressElevation: 6.0',
139 140
      'iconTheme: IconThemeData#00000(color: Color(0xffffff10))'
    ]));
141 142
  });

143
  testWidgetsWithLeakTracking('Chip uses ThemeData chip theme', (WidgetTester tester) async {
144 145 146 147 148 149 150
    const ChipThemeData chipTheme = ChipThemeData(
      backgroundColor: Color(0xff112233),
      elevation: 4,
      padding: EdgeInsets.all(50),
      labelPadding: EdgeInsets.all(25),
      shape: RoundedRectangleBorder(),
      labelStyle: TextStyle(fontSize: 32),
151 152
    );

153 154
    await tester.pumpWidget(
      MaterialApp(
155
        theme: ThemeData.light(useMaterial3: false).copyWith(
156 157
          chipTheme: chipTheme,
        ),
158
        home: Directionality(
159
          textDirection: TextDirection.ltr,
nt4f04uNd's avatar
nt4f04uNd committed
160 161
          child: Material(
            child: Center(
162 163 164
              child: RawChip(
                label: const SizedBox(width: 100, height: 100),
                onSelected: (bool newValue) { },
165 166 167 168
              ),
            ),
          ),
        ),
169 170
      ),
    );
171 172

    final RenderBox materialBox = getMaterialBox(tester);
173
    expect(materialBox, paints..rect(color: chipTheme.backgroundColor));
174 175 176 177
    expect(getMaterial(tester).elevation, chipTheme.elevation);
    expect(tester.getSize(find.byType(RawChip)), const Size(250, 250)); // label + padding + labelPadding
    expect(getMaterial(tester).shape, chipTheme.shape);
    expect(getLabelStyle(tester).style.fontSize, 32);
178 179
  });

180
  testWidgetsWithLeakTracking('Chip uses ChipTheme', (WidgetTester tester) async {
181 182 183 184 185 186 187
    const ChipThemeData chipTheme = ChipThemeData(
      backgroundColor: Color(0xff112233),
      elevation: 4,
      padding: EdgeInsets.all(50),
      labelPadding: EdgeInsets.all(25),
      labelStyle: TextStyle(fontSize: 32),
      shape: RoundedRectangleBorder(),
188
    );
189 190 191 192 193 194 195 196

    const ChipThemeData shadowedChipTheme = ChipThemeData(
      backgroundColor: Color(0xff332211),
      elevation: 3,
      padding: EdgeInsets.all(5),
      labelPadding: EdgeInsets.all(10),
      labelStyle: TextStyle(fontSize: 64),
      shape: CircleBorder(),
197
    );
198 199 200

    await tester.pumpWidget(
      MaterialApp(
201
        theme: ThemeData.light(useMaterial3: false).copyWith(
202 203 204 205 206 207 208 209 210 211 212 213 214 215
          chipTheme: shadowedChipTheme,
        ),
        home: ChipTheme(
          data: chipTheme,
          child: Builder(
            builder: (BuildContext context) {
              return Directionality(
                textDirection: TextDirection.ltr,
                child: Material(
                  child: Center(
                    child: RawChip(
                      label: const SizedBox(width: 100, height: 100),
                      onSelected: (bool newValue) { },
                    ),
216 217
                  ),
                ),
218 219
              );
            },
220 221
          ),
        ),
222 223
      ),
    );
224

225
    final RenderBox materialBox = getMaterialBox(tester);
226
    expect(materialBox, paints..rect(color: chipTheme.backgroundColor));
227 228 229 230 231 232
    expect(tester.getSize(find.byType(RawChip)), const Size(250, 250)); // label + padding + labelPadding
    expect(getMaterial(tester).elevation, chipTheme.elevation);
    expect(getMaterial(tester).shape, chipTheme.shape);
    expect(getLabelStyle(tester).style.fontSize, 32);
  });

233
  testWidgetsWithLeakTracking('Chip uses constructor parameters', (WidgetTester tester) async {
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
    const ChipThemeData shadowedChipTheme = ChipThemeData(
      backgroundColor: Color(0xff112233),
      elevation: 4,
      padding: EdgeInsets.all(5),
      labelPadding: EdgeInsets.all(2),
      labelStyle: TextStyle(),
      shape: RoundedRectangleBorder(),
    );

    const Color backgroundColor = Color(0xff332211);
    const double elevation = 3;
    const double fontSize = 32;
    const OutlinedBorder shape = CircleBorder();

    await tester.pumpWidget(
      MaterialApp(
250
        theme: ThemeData(useMaterial3: false),
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
        home: ChipTheme(
          data: shadowedChipTheme,
          child: Builder(
            builder: (BuildContext context) {
              return Directionality(
                textDirection: TextDirection.ltr,
                child: Material(
                  child: Center(
                    child: RawChip(
                      backgroundColor: backgroundColor,
                      elevation: elevation,
                      padding: const EdgeInsets.all(50),
                      labelPadding:const EdgeInsets.all(25),
                      labelStyle: const TextStyle(fontSize: fontSize),
                      shape: shape,
                      label: const SizedBox(width: 100, height: 100),
                      onSelected: (bool newValue) { },
                    ),
                  ),
                ),
              );
            },
          ),
        ),
      ),
    );
277 278

    final RenderBox materialBox = getMaterialBox(tester);
279
    expect(materialBox, paints..circle(color: backgroundColor));
280 281 282 283 284 285
    expect(tester.getSize(find.byType(RawChip)), const Size(250, 250)); // label + padding + labelPadding
    expect(getMaterial(tester).elevation, elevation);
    expect(getMaterial(tester).shape, shape);
    expect(getLabelStyle(tester).style.fontSize, 32);
  });

286
  testWidgetsWithLeakTracking('ChipTheme.fromDefaults', (WidgetTester tester) async {
287
    const TextStyle labelStyle = TextStyle();
288 289 290
    ChipThemeData chipTheme = ChipThemeData.fromDefaults(
      brightness: Brightness.light,
      secondaryColor: Colors.red,
291
      labelStyle: labelStyle,
292
    );
293 294 295 296 297 298
    expect(chipTheme.backgroundColor, Colors.black.withAlpha(0x1f));
    expect(chipTheme.deleteIconColor, Colors.black.withAlpha(0xde));
    expect(chipTheme.disabledColor, Colors.black.withAlpha(0x0c));
    expect(chipTheme.selectedColor, Colors.black.withAlpha(0x3d));
    expect(chipTheme.secondarySelectedColor, Colors.red.withAlpha(0x3d));
    expect(chipTheme.shadowColor, Colors.black);
299
    expect(chipTheme.surfaceTintColor, null);
300 301 302 303 304 305 306 307 308 309 310 311
    expect(chipTheme.selectedShadowColor, Colors.black);
    expect(chipTheme.showCheckmark, true);
    expect(chipTheme.checkmarkColor, null);
    expect(chipTheme.labelPadding, null);
    expect(chipTheme.padding, const EdgeInsets.all(4.0));
    expect(chipTheme.side, null);
    expect(chipTheme.shape, null);
    expect(chipTheme.labelStyle, labelStyle.copyWith(color: Colors.black.withAlpha(0xde)));
    expect(chipTheme.secondaryLabelStyle, labelStyle.copyWith(color: Colors.red.withAlpha(0xde)));
    expect(chipTheme.brightness, Brightness.light);
    expect(chipTheme.elevation, 0.0);
    expect(chipTheme.pressElevation, 8.0);
312

313 314 315 316 317
    chipTheme = ChipThemeData.fromDefaults(
      brightness: Brightness.dark,
      secondaryColor: Colors.tealAccent[200]!,
      labelStyle: const TextStyle(),
    );
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
    expect(chipTheme.backgroundColor, Colors.white.withAlpha(0x1f));
    expect(chipTheme.deleteIconColor, Colors.white.withAlpha(0xde));
    expect(chipTheme.disabledColor, Colors.white.withAlpha(0x0c));
    expect(chipTheme.selectedColor, Colors.white.withAlpha(0x3d));
    expect(chipTheme.secondarySelectedColor, Colors.tealAccent[200]!.withAlpha(0x3d));
    expect(chipTheme.shadowColor, Colors.black);
    expect(chipTheme.selectedShadowColor, Colors.black);
    expect(chipTheme.showCheckmark, true);
    expect(chipTheme.checkmarkColor, null);
    expect(chipTheme.labelPadding, null);
    expect(chipTheme.padding, const EdgeInsets.all(4.0));
    expect(chipTheme.side, null);
    expect(chipTheme.shape, null);
    expect(chipTheme.labelStyle, labelStyle.copyWith(color: Colors.white.withAlpha(0xde)));
    expect(chipTheme.secondaryLabelStyle, labelStyle.copyWith(color: Colors.tealAccent[200]!.withAlpha(0xde)));
    expect(chipTheme.brightness, Brightness.dark);
    expect(chipTheme.elevation, 0.0);
    expect(chipTheme.pressElevation, 8.0);
336
  });
337

338
  testWidgetsWithLeakTracking('ChipThemeData generates correct opacities for defaults', (WidgetTester tester) async {
339 340
    const Color customColor1 = Color(0xcafefeed);
    const Color customColor2 = Color(0xdeadbeef);
341
    final TextStyle customStyle = ThemeData.fallback().textTheme.bodyLarge!.copyWith(color: customColor2);
342

343
    final ChipThemeData lightTheme = ChipThemeData.fromDefaults(
344 345 346 347 348 349 350 351 352 353
      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)));
354
    expect(lightTheme.labelPadding, isNull);
355
    expect(lightTheme.padding, equals(const EdgeInsets.all(4.0)));
356 357
    expect(lightTheme.side, isNull);
    expect(lightTheme.shape, isNull);
358 359
    expect(lightTheme.labelStyle?.color, equals(Colors.black.withAlpha(0xde)));
    expect(lightTheme.secondaryLabelStyle?.color, equals(customColor1.withAlpha(0xde)));
360 361
    expect(lightTheme.brightness, equals(Brightness.light));

362
    final ChipThemeData darkTheme = ChipThemeData.fromDefaults(
363 364 365 366 367 368 369 370 371 372
      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)));
373
    expect(darkTheme.labelPadding, isNull);
374
    expect(darkTheme.padding, equals(const EdgeInsets.all(4.0)));
375 376
    expect(darkTheme.side, isNull);
    expect(darkTheme.shape, isNull);
377 378
    expect(darkTheme.labelStyle?.color, equals(Colors.white.withAlpha(0xde)));
    expect(darkTheme.secondaryLabelStyle?.color, equals(customColor1.withAlpha(0xde)));
379 380
    expect(darkTheme.brightness, equals(Brightness.dark));

381
    final ChipThemeData customTheme = ChipThemeData.fromDefaults(
382 383 384 385 386
      primaryColor: customColor1,
      secondaryColor: customColor2,
      labelStyle: customStyle,
    );

387
    //expect(customTheme.backgroundColor, equals(customColor1.withAlpha(0x1f)));
388 389 390 391
    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)));
392
    expect(customTheme.labelPadding, isNull);
393
    expect(customTheme.padding, equals(const EdgeInsets.all(4.0)));
394 395
    expect(customTheme.side, isNull);
    expect(customTheme.shape, isNull);
396 397
    expect(customTheme.labelStyle?.color, equals(customColor1.withAlpha(0xde)));
    expect(customTheme.secondaryLabelStyle?.color, equals(customColor2.withAlpha(0xde)));
398 399 400
    expect(customTheme.brightness, equals(Brightness.light));
  });

401
  testWidgetsWithLeakTracking('ChipThemeData lerps correctly', (WidgetTester tester) async {
402
    final ChipThemeData chipThemeBlack = ChipThemeData.fromDefaults(
403 404
      secondaryColor: Colors.black,
      brightness: Brightness.dark,
405
      labelStyle: ThemeData.fallback().textTheme.bodyLarge!.copyWith(color: Colors.black),
406 407
    ).copyWith(
      elevation: 1.0,
408
      labelPadding: const EdgeInsets.symmetric(horizontal: 8.0),
409
      shape: const StadiumBorder(),
410
      side: const BorderSide(),
411
      pressElevation: 4.0,
412
      shadowColor: Colors.black,
413
      surfaceTintColor: Colors.black,
414
      selectedShadowColor: Colors.black,
415
      showCheckmark: false,
416
      checkmarkColor: Colors.black,
417
    );
418
    final ChipThemeData chipThemeWhite = ChipThemeData.fromDefaults(
419 420
      secondaryColor: Colors.white,
      brightness: Brightness.light,
421
      labelStyle: ThemeData.fallback().textTheme.bodyLarge!.copyWith(color: Colors.white),
422 423 424
    ).copyWith(
      padding: const EdgeInsets.all(2.0),
      labelPadding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
425 426
      shape: const BeveledRectangleBorder(),
      side: const BorderSide(color: Colors.white),
427 428
      elevation: 5.0,
      pressElevation: 10.0,
429
      shadowColor: Colors.white,
430
      surfaceTintColor: Colors.white,
431
      selectedShadowColor: Colors.white,
432
      showCheckmark: true,
433
      checkmarkColor: Colors.white,
434 435
    );

436
    final ChipThemeData lerp = ChipThemeData.lerp(chipThemeBlack, chipThemeWhite, 0.5)!;
437
    const Color middleGrey = Color(0xff7f7f7f);
438 439 440 441 442
    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)));
443
    expect(lerp.shadowColor, equals(middleGrey));
444
    expect(lerp.surfaceTintColor, equals(middleGrey));
445
    expect(lerp.selectedShadowColor, equals(middleGrey));
446
    expect(lerp.showCheckmark, equals(true));
447 448
    expect(lerp.labelPadding, equals(const EdgeInsets.all(4.0)));
    expect(lerp.padding, equals(const EdgeInsets.all(3.0)));
449 450
    expect(lerp.side!.color, equals(middleGrey));
    expect(lerp.shape, isA<BeveledRectangleBorder>());
451 452
    expect(lerp.labelStyle?.color, equals(middleGrey.withAlpha(0xde)));
    expect(lerp.secondaryLabelStyle?.color, equals(middleGrey.withAlpha(0xde)));
453
    expect(lerp.brightness, equals(Brightness.light));
454 455
    expect(lerp.elevation, 3.0);
    expect(lerp.pressElevation, 7.0);
456
    expect(lerp.checkmarkColor, equals(middleGrey));
457
    expect(lerp.iconTheme, isNull);
458 459 460

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

461
    final ChipThemeData lerpANull25 = ChipThemeData.lerp(null, chipThemeWhite, 0.25)!;
462 463 464 465 466
    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)));
467
    expect(lerpANull25.shadowColor, equals(Colors.white.withAlpha(0x40)));
468
    expect(lerpANull25.surfaceTintColor, equals(Colors.white.withAlpha(0x40)));
469
    expect(lerpANull25.selectedShadowColor, equals(Colors.white.withAlpha(0x40)));
470
    expect(lerpANull25.showCheckmark, equals(true));
471
    expect(lerpANull25.labelPadding, equals(const EdgeInsets.only(top: 2.0, bottom: 2.0)));
472
    expect(lerpANull25.padding, equals(const EdgeInsets.all(0.5)));
473 474
    expect(lerpANull25.side!.color, equals(Colors.white.withAlpha(0x3f)));
    expect(lerpANull25.shape, isA<BeveledRectangleBorder>());
475 476
    expect(lerpANull25.labelStyle?.color, equals(Colors.black.withAlpha(0x38)));
    expect(lerpANull25.secondaryLabelStyle?.color, equals(Colors.white.withAlpha(0x38)));
477
    expect(lerpANull25.brightness, equals(Brightness.light));
478 479
    expect(lerpANull25.elevation, 1.25);
    expect(lerpANull25.pressElevation, 2.5);
480
    expect(lerpANull25.checkmarkColor, equals(Colors.white.withAlpha(0x40)));
481
    expect(lerp.iconTheme, isNull);
482

483
    final ChipThemeData lerpANull75 = ChipThemeData.lerp(null, chipThemeWhite, 0.75)!;
484 485 486 487 488
    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)));
489
    expect(lerpANull75.shadowColor, equals(Colors.white.withAlpha(0xbf)));
490
    expect(lerpANull75.surfaceTintColor, equals(Colors.white.withAlpha(0xbf)));
491
    expect(lerpANull75.selectedShadowColor, equals(Colors.white.withAlpha(0xbf)));
492
    expect(lerpANull75.showCheckmark, equals(true));
493
    expect(lerpANull75.labelPadding, equals(const EdgeInsets.only(top: 6.0, bottom: 6.0)));
494
    expect(lerpANull75.padding, equals(const EdgeInsets.all(1.5)));
495 496
    expect(lerpANull75.side!.color, equals(Colors.white.withAlpha(0xbf)));
    expect(lerpANull75.shape, isA<BeveledRectangleBorder>());
497 498
    expect(lerpANull75.labelStyle?.color, equals(Colors.black.withAlpha(0xa7)));
    expect(lerpANull75.secondaryLabelStyle?.color, equals(Colors.white.withAlpha(0xa7)));
499
    expect(lerpANull75.brightness, equals(Brightness.light));
500 501
    expect(lerpANull75.elevation, 3.75);
    expect(lerpANull75.pressElevation, 7.5);
502
    expect(lerpANull75.checkmarkColor, equals(Colors.white.withAlpha(0xbf)));
503

504
    final ChipThemeData lerpBNull25 = ChipThemeData.lerp(chipThemeBlack, null, 0.25)!;
505 506 507 508 509
    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)));
510
    expect(lerpBNull25.shadowColor, equals(Colors.black.withAlpha(0xbf)));
511
    expect(lerpBNull25.surfaceTintColor, equals(Colors.black.withAlpha(0xbf)));
512
    expect(lerpBNull25.selectedShadowColor, equals(Colors.black.withAlpha(0xbf)));
513
    expect(lerpBNull25.showCheckmark, equals(false));
514
    expect(lerpBNull25.labelPadding, equals(const EdgeInsets.only(left: 6.0, right: 6.0)));
515
    expect(lerpBNull25.padding, equals(const EdgeInsets.all(3.0)));
516
    expect(lerpBNull25.side!.color, equals(Colors.black.withAlpha(0x3f)));
Dan Field's avatar
Dan Field committed
517
    expect(lerpBNull25.shape, isA<StadiumBorder>());
518 519
    expect(lerpBNull25.labelStyle?.color, equals(Colors.white.withAlpha(0xa7)));
    expect(lerpBNull25.secondaryLabelStyle?.color, equals(Colors.black.withAlpha(0xa7)));
520
    expect(lerpBNull25.brightness, equals(Brightness.dark));
521 522
    expect(lerpBNull25.elevation, 0.75);
    expect(lerpBNull25.pressElevation, 3.0);
523
    expect(lerpBNull25.checkmarkColor, equals(Colors.black.withAlpha(0xbf)));
524
    expect(lerp.iconTheme, isNull);
525

526
    final ChipThemeData lerpBNull75 = ChipThemeData.lerp(chipThemeBlack, null, 0.75)!;
527 528 529 530 531
    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)));
532
    expect(lerpBNull75.shadowColor, equals(Colors.black.withAlpha(0x40)));
533
    expect(lerpBNull75.surfaceTintColor, equals(Colors.black.withAlpha(0x40)));
534
    expect(lerpBNull75.selectedShadowColor, equals(Colors.black.withAlpha(0x40)));
535
    expect(lerpBNull75.showCheckmark, equals(true));
536
    expect(lerpBNull75.labelPadding, equals(const EdgeInsets.only(left: 2.0, right: 2.0)));
537
    expect(lerpBNull75.padding, equals(const EdgeInsets.all(1.0)));
538
    expect(lerpBNull75.side!.color, equals(Colors.black.withAlpha(0xbf)));
Dan Field's avatar
Dan Field committed
539
    expect(lerpBNull75.shape, isA<StadiumBorder>());
540 541
    expect(lerpBNull75.labelStyle?.color, equals(Colors.white.withAlpha(0x38)));
    expect(lerpBNull75.secondaryLabelStyle?.color, equals(Colors.black.withAlpha(0x38)));
542
    expect(lerpBNull75.brightness, equals(Brightness.light));
543 544
    expect(lerpBNull75.elevation, 0.25);
    expect(lerpBNull75.pressElevation, 1.0);
545
    expect(lerpBNull75.checkmarkColor, equals(Colors.black.withAlpha(0x40)));
546
    expect(lerp.iconTheme, isNull);
547
  });
548

549
  testWidgetsWithLeakTracking('Chip uses stateful color from chip theme', (WidgetTester tester) async {
550 551 552 553 554 555 556 557 558 559
    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) {
560
      if (states.contains(MaterialState.disabled)) {
561
        return disabledColor;
562
      }
563

564
      if (states.contains(MaterialState.pressed)) {
565
        return pressedColor;
566
      }
567

568
      if (states.contains(MaterialState.hovered)) {
569
        return hoverColor;
570
      }
571

572
      if (states.contains(MaterialState.focused)) {
573
        return focusedColor;
574
      }
575

576
      if (states.contains(MaterialState.selected)) {
577
        return selectedColor;
578
      }
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606

      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() {
607
      return tester.renderObject<RenderParagraph>(find.text('Chip')).text.style!.color!;
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
    }

    // 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);
643 644

    focusNode.dispose();
645
  });
646

647
  testWidgetsWithLeakTracking('Chip uses stateful border side from resolveWith pattern', (WidgetTester tester) async {
648 649 650 651 652 653
    const Color selectedColor = Color(0x00000001);
    const Color defaultColor = Color(0x00000002);

    BorderSide getBorderSide(Set<MaterialState> states) {
      Color color = defaultColor;

654
      if (states.contains(MaterialState.selected)) {
655
        color = selectedColor;
656
      }
657

658
      return BorderSide(color: color);
659 660 661 662 663
    }

    Widget chipWidget({ bool selected = false }) {
      return MaterialApp(
        theme: ThemeData(
664
          useMaterial3: false,
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
          chipTheme: ThemeData.light().chipTheme.copyWith(
            side: MaterialStateBorderSide.resolveWith(getBorderSide),
          ),
        ),
        home: Scaffold(
          body: ChoiceChip(
            label: const Text('Chip'),
            selected: selected,
            onSelected: (_) {},
          ),
        ),
      );
    }

    // Default.
    await tester.pumpWidget(chipWidget());
681
    expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
682 683 684

    // Selected.
    await tester.pumpWidget(chipWidget(selected: true));
685
    expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor));
686 687
  });

688
  testWidgetsWithLeakTracking('Chip uses stateful border side from chip theme', (WidgetTester tester) async {
689 690 691 692 693
    const Color selectedColor = Color(0x00000001);
    const Color defaultColor = Color(0x00000002);

    BorderSide getBorderSide(Set<MaterialState> states) {
      Color color = defaultColor;
694
      if (states.contains(MaterialState.selected)) {
695
        color = selectedColor;
696
      }
697
      return BorderSide(color: color);
698 699
    }

700 701 702 703 704 705 706 707
    final ChipThemeData chipTheme = ChipThemeData.fromDefaults(
      brightness: Brightness.light,
      secondaryColor: Colors.blue,
      labelStyle: const TextStyle(),
    ).copyWith(
      side: _MaterialStateBorderSide(getBorderSide),
    );

708 709
    Widget chipWidget({ bool selected = false }) {
      return MaterialApp(
710
        theme: ThemeData(useMaterial3: false, chipTheme: chipTheme),
711 712 713 714 715 716 717 718 719 720 721 722
        home: Scaffold(
          body: ChoiceChip(
            label: const Text('Chip'),
            selected: selected,
            onSelected: (_) {},
          ),
        ),
      );
    }

    // Default.
    await tester.pumpWidget(chipWidget());
723
    expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
724 725 726

    // Selected.
    await tester.pumpWidget(chipWidget(selected: true));
727
    expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor));
728 729
  });

730
  testWidgetsWithLeakTracking('Chip uses stateful shape from chip theme', (WidgetTester tester) async {
731
    OutlinedBorder? getShape(Set<MaterialState> states) {
732
      if (states.contains(MaterialState.selected)) {
733
        return const RoundedRectangleBorder();
734
      }
735 736 737 738

      return null;
    }

739 740 741 742 743 744 745 746 747
    final ChipThemeData chipTheme = ChipThemeData.fromDefaults(
      brightness: Brightness.light,
      secondaryColor: Colors.blue,
      labelStyle: const TextStyle(),
    ).copyWith(
      shape: _MaterialStateOutlinedBorder(getShape),
    );


748 749
    Widget chipWidget({ bool selected = false }) {
      return MaterialApp(
750
        theme: ThemeData(useMaterial3: false, chipTheme: chipTheme),
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
        home: Scaffold(
          body: ChoiceChip(
            label: const Text('Chip'),
            selected: selected,
            onSelected: (_) {},
          ),
        ),
      );
    }

    // Default.
    await tester.pumpWidget(chipWidget());
    expect(getMaterial(tester).shape, isA<StadiumBorder>());

    // Selected.
    await tester.pumpWidget(chipWidget(selected: true));
    expect(getMaterial(tester).shape, isA<RoundedRectangleBorder>());
  });
769

770
  testWidgetsWithLeakTracking('RawChip uses material state color from ChipTheme', (WidgetTester tester) async {
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
    const Color disabledSelectedColor = Color(0xffffff00);
    const Color disabledColor = Color(0xff00ff00);
    const Color backgroundColor = Color(0xff0000ff);
    const Color selectedColor = Color(0xffff0000);
    Widget buildApp({ required bool enabled, required bool selected }) {
      return MaterialApp(
        theme: ThemeData(
          chipTheme: ChipThemeData(
            color: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
              if (states.contains(MaterialState.disabled)
                && states.contains(MaterialState.selected)) {
                  return disabledSelectedColor;
              }
              if (states.contains(MaterialState.disabled)) {
                return disabledColor;
              }
              if (states.contains(MaterialState.selected)) {
                return selectedColor;
              }
              return backgroundColor;
            }),
          ),
          useMaterial3: true,
        ),
        home: Material(
          child: RawChip(
            isEnabled: enabled,
            selected: selected,
            label: const Text('RawChip'),
          ),
        ),
      );
    }

    // Check theme color for enabled chip.
    await tester.pumpWidget(buildApp(enabled: true, selected: false));
    await tester.pumpAndSettle();

    // Enabled chip should have the provided backgroundColor.
    expect(getMaterialBox(tester), paints..rrect(color: backgroundColor));

    // Check theme color for disabled chip.
    await tester.pumpWidget(buildApp(enabled: false, selected: false));
    await tester.pumpAndSettle();

    // Disabled chip should have the provided disabledColor.
    expect(getMaterialBox(tester),paints..rrect(color: disabledColor));

    // Check theme color for enabled and selected chip.
    await tester.pumpWidget(buildApp(enabled: true, selected: true));
    await tester.pumpAndSettle();

    // Enabled & selected chip should have the provided selectedColor.
    expect(getMaterialBox(tester), paints..rrect(color: selectedColor));

    // Check theme color for disabled & selected chip.
    await tester.pumpWidget(buildApp(enabled: false, selected: true));
    await tester.pumpAndSettle();

    // Disabled & selected chip should have the provided disabledSelectedColor.
    expect(getMaterialBox(tester), paints..rrect(color: disabledSelectedColor));
  });

834
  testWidgetsWithLeakTracking('RawChip uses state colors from ChipTheme', (WidgetTester tester) async {
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
    const ChipThemeData chipTheme = ChipThemeData(
      disabledColor: Color(0xadfefafe),
      backgroundColor: Color(0xcafefeed),
      selectedColor: Color(0xbeefcafe),
    );
    Widget buildApp({ required bool enabled, required bool selected }) {
      return MaterialApp(
        theme: ThemeData(chipTheme: chipTheme, useMaterial3: true),
        home: Material(
          child: RawChip(
            isEnabled: enabled,
            selected: selected,
            label: const Text('RawChip'),
          ),
        ),
      );
    }

    // Check theme color for enabled chip.
    await tester.pumpWidget(buildApp(enabled: true, selected: false));
    await tester.pumpAndSettle();

    // Enabled chip should have the provided backgroundColor.
    expect(getMaterialBox(tester), paints..rrect(color: chipTheme.backgroundColor));

    // Check theme color for disabled chip.
    await tester.pumpWidget(buildApp(enabled: false, selected: false));
    await tester.pumpAndSettle();

    // Disabled chip should have the provided disabledColor.
    expect(getMaterialBox(tester),paints..rrect(color: chipTheme.disabledColor));

    // Check theme color for enabled and selected chip.
    await tester.pumpWidget(buildApp(enabled: true, selected: true));
    await tester.pumpAndSettle();

    // Enabled & selected chip should have the provided selectedColor.
    expect(getMaterialBox(tester), paints..rrect(color: chipTheme.selectedColor));
  });
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930

  // This is a regression test for https://github.com/flutter/flutter/issues/119163.
  testWidgetsWithLeakTracking('RawChip respects checkmark properties from ChipTheme', (WidgetTester tester) async {
    Widget buildRawChip({ChipThemeData? chipTheme}) {
      return MaterialApp(
        theme: ThemeData.light(useMaterial3: false).copyWith(
          chipTheme: chipTheme,
        ),
        home: Directionality(
          textDirection: TextDirection.ltr,
          child: Material(
          child: Center(
              child: RawChip(
                selected: true,
                label: const SizedBox(width: 100, height: 100),
                onSelected: (bool newValue) { },
              ),
            ),
          ),
        ),
      );
    }

    // Test that the checkmark is painted.
    await tester.pumpWidget(buildRawChip(
      chipTheme: const ChipThemeData(
        checkmarkColor: Color(0xffff0000),
      ),
    ));

    RenderBox materialBox = getMaterialBox(tester);
    expect(
      materialBox,
      paints..path(
        color: const Color(0xffff0000),
        style: PaintingStyle.stroke,
      ),
    );

    // Test that the checkmark is not painted when ChipThemeData.showCheckmark is false.
    await tester.pumpWidget(buildRawChip(
      chipTheme: const ChipThemeData(
        showCheckmark: false,
        checkmarkColor: Color(0xffff0000),
      ),
    ));
    await tester.pumpAndSettle();

    materialBox = getMaterialBox(tester);
    expect(
      materialBox,
      isNot(paints..path(
        color: const Color(0xffff0000),
        style: PaintingStyle.stroke,
      )),
    );
  });
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988

  testWidgets("Material3 - RawChip.shape's side is used when provided", (WidgetTester tester) async {
    Widget buildChip({ OutlinedBorder? shape, BorderSide? side }) {
      return MaterialApp(
        theme: ThemeData(
          useMaterial3: true,
          chipTheme: ChipThemeData(
            shape: shape,
            side: side,
          ),
        ),
        home: const Material(
          child: Center(
            child: RawChip(
              label: Text('RawChip'),
            ),
          ),
        ),
      );
    }

    // Test [RawChip.shape] with a side.
    await tester.pumpWidget(buildChip(
      shape: const RoundedRectangleBorder(
        side: BorderSide(color: Color(0xffff00ff)),
        borderRadius: BorderRadius.all(Radius.circular(7.0)),
      )),
    );

    // Chip should have the provided shape and the side from [RawChip.shape].
    expect(
      getMaterial(tester).shape,
      const RoundedRectangleBorder(
        side: BorderSide(color: Color(0xffff00ff)),
        borderRadius: BorderRadius.all(Radius.circular(7.0)),
      ),
    );

    // Test [RawChip.shape] with a side and [RawChip.side].
    await tester.pumpWidget(buildChip(
      shape: const RoundedRectangleBorder(
        side: BorderSide(color: Color(0xffff00ff)),
        borderRadius: BorderRadius.all(Radius.circular(7.0)),
      ),
      side: const BorderSide(color: Color(0xfffff000))),
    );
    await tester.pumpAndSettle();

    // Chip use shape from [RawChip.shape] and the side from [RawChip.side].
    // [RawChip.shape]'s side should be ignored.
    expect(
      getMaterial(tester).shape,
      const RoundedRectangleBorder(
        side: BorderSide(color: Color(0xfffff000)),
        borderRadius: BorderRadius.all(Radius.circular(7.0)),
      ),
    );
  });
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
}

class _MaterialStateOutlinedBorder extends StadiumBorder implements MaterialStateOutlinedBorder {
  const _MaterialStateOutlinedBorder(this.resolver);

  final MaterialPropertyResolver<OutlinedBorder?> resolver;

  @override
  OutlinedBorder? resolve(Set<MaterialState> states) => resolver(states);
}

class _MaterialStateBorderSide extends MaterialStateBorderSide {
  const _MaterialStateBorderSide(this.resolver);

  final MaterialPropertyResolver<BorderSide?> resolver;

  @override
  BorderSide? resolve(Set<MaterialState> states) => resolver(states);
1007
}