buttons_test.dart 40.4 KB
Newer Older
1 2 3 4
// Copyright 2015 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.

5
import 'package:flutter/gestures.dart';
6
import 'package:flutter/material.dart';
7
import 'package:flutter/rendering.dart';
8 9 10 11 12 13 14
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

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

void main() {
15 16 17 18
  setUp(() {
    debugResetSemanticsIdCounter();
  });

19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 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 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 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 277 278 279 280 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 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
  testWidgets('MaterialButton defaults', (WidgetTester tester) async {
    final Finder rawButtonMaterial = find.descendant(
      of: find.byType(MaterialButton),
      matching: find.byType(Material),
    );

    // Enabled MaterialButton
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: MaterialButton(
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    Material material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, null);
    expect(material.elevation, 2.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0xdd000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.transparency);

    final Offset center = tester.getCenter(find.byType(MaterialButton));
    await tester.startGesture(center);
    await tester.pumpAndSettle();

    // Only elevation changes when enabled and pressed.
    material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, null);
    expect(material.elevation, 8.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0xdd000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.transparency);

    // Disabled MaterialButton
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: MaterialButton(
          onPressed: null,
          child: Text('button'),
        ),
      ),
    );
    material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, null);
    expect(material.elevation, 0.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0x61000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.transparency);
  });

  testWidgets('FlatButton defaults', (WidgetTester tester) async {
    final Finder rawButtonMaterial = find.descendant(
      of: find.byType(FlatButton),
      matching: find.byType(Material),
    );

    // Enabled FlatButton
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: FlatButton(
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    Material material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, null);
    expect(material.elevation, 0.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0xdd000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.transparency);

    final Offset center = tester.getCenter(find.byType(FlatButton));
    await tester.startGesture(center);
    await tester.pumpAndSettle();

    material = tester.widget<Material>(rawButtonMaterial);
    // No change vs enabled and not pressed.
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, null);
    expect(material.elevation, 0.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0xdd000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.transparency);

    // Disabled FlatButton
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: FlatButton(
          onPressed: null,
          child: Text('button'),
        ),
      ),
    );
    material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, null);
    expect(material.elevation, 0.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0x61000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.transparency);
  });

  testWidgets('RaisedButton defaults', (WidgetTester tester) async {
    final Finder rawButtonMaterial = find.descendant(
      of: find.byType(RaisedButton),
      matching: find.byType(Material),
    );

    // Enabled RaisedButton
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: RaisedButton(
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    Material material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, const Color(0xffe0e0e0));
    expect(material.elevation, 2.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0xdd000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.button);

    final Offset center = tester.getCenter(find.byType(RaisedButton));
    await tester.startGesture(center);
    await tester.pumpAndSettle();

    // Only elevation changes when enabled and pressed.
    material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, const Color(0xffe0e0e0));
    expect(material.elevation, 8.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0xdd000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.button);

    // Disabled RaisedButton
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: RaisedButton(
          onPressed: null,
          child: Text('button'),
        ),
      ),
    );
    material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 200));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, const Color(0x61000000));
    expect(material.elevation, 0.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)));
    expect(material.textStyle.color, const Color(0x61000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.button);
  });

  testWidgets('OutlineButton defaults', (WidgetTester tester) async {
    final Finder rawButtonMaterial = find.descendant(
      of: find.byType(OutlineButton),
      matching: find.byType(Material),
    );

    // Enabled OutlineButton
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: OutlineButton(
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    Material material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 75));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, const Color(0x00000000));
    expect(material.elevation, 0.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.textStyle.color, const Color(0xdd000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.button);

    final Offset center = tester.getCenter(find.byType(OutlineButton));
    await tester.startGesture(center);
    await tester.pumpAndSettle();

    // No change vs enabled and not pressed.
    material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 75));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, const Color(0x00000000));
    expect(material.elevation, 0.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.textStyle.color, const Color(0xdd000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.button);

    // Disabled OutlineButton
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: OutlineButton(
          onPressed: null,
          child: Text('button'),
        ),
      ),
    );
    material = tester.widget<Material>(rawButtonMaterial);
    expect(material.animationDuration, const Duration(milliseconds: 75));
    expect(material.borderOnForeground, true);
    expect(material.borderRadius, null);
    expect(material.clipBehavior, Clip.none);
    expect(material.color, const Color(0x00000000));
    expect(material.elevation, 0.0);
    expect(material.shadowColor, const Color(0xff000000));
    expect(material.textStyle.color, const Color(0x61000000));
    expect(material.textStyle.fontFamily, 'Roboto');
    expect(material.textStyle.fontSize, 14);
    expect(material.textStyle.fontWeight, FontWeight.w500);
    expect(material.type, MaterialType.button);
  });

324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 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 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
  testWidgets('Do buttons work with hover', (WidgetTester tester) async {
    const Color hoverColor = Color(0xff001122);

    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: MaterialButton(
          hoverColor: hoverColor,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );

    final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
    await gesture.addPointer();
    await gesture.moveTo(tester.getCenter(find.byType(MaterialButton)));
    await tester.pumpAndSettle();

    RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: hoverColor));

    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: FlatButton(
          hoverColor: hoverColor,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );

    await tester.pumpAndSettle();
    inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: hoverColor));

    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: OutlineButton(
          hoverColor: hoverColor,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );

    await tester.pumpAndSettle();
    inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: hoverColor));

    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: RaisedButton(
          hoverColor: hoverColor,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );

    await tester.pumpAndSettle();
    inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: hoverColor));

    gesture.removePointer();
  });

  testWidgets('Do buttons work with focus', (WidgetTester tester) async {
    const Color focusColor = Color(0xff001122);

    FocusNode focusNode = FocusNode(debugLabel: 'MaterialButton Node');
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: MaterialButton(
          focusColor: focusColor,
          focusNode: focusNode,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    focusNode.requestFocus();
    await tester.pumpAndSettle();

    RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: focusColor));

    focusNode = FocusNode(debugLabel: 'FlatButton Node');
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: FlatButton(
          focusColor: focusColor,
          focusNode: focusNode,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    focusNode.requestFocus();
    await tester.pumpAndSettle();
    inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: focusColor));

    focusNode = FocusNode(debugLabel: 'RaisedButton Node');
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: RaisedButton(
          focusColor: focusColor,
          focusNode: focusNode,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    focusNode.requestFocus();
    await tester.pumpAndSettle();
    inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: focusColor));

    focusNode = FocusNode(debugLabel: 'OutlineButton Node');
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: OutlineButton(
          focusColor: focusColor,
          focusNode: focusNode,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    focusNode.requestFocus();
    await tester.pumpAndSettle();
    inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: focusColor));
  });

  testWidgets('Button elevation and colors have proper precedence', (WidgetTester tester) async {
    const double elevation = 10.0;
    const double focusElevation = 11.0;
    const double hoverElevation = 12.0;
    const double highlightElevation = 13.0;
    const Color focusColor = Color(0xff001122);
    const Color hoverColor = Color(0xff112233);
    const Color highlightColor = Color(0xff223344);

    final Finder rawButtonMaterial = find.descendant(
      of: find.byType(MaterialButton),
      matching: find.byType(Material),
    );

    final FocusNode focusNode = FocusNode(debugLabel: 'MaterialButton Node');
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: MaterialButton(
          focusColor: focusColor,
          hoverColor: hoverColor,
          highlightColor: highlightColor,
          elevation: elevation,
          focusElevation: focusElevation,
          hoverElevation: hoverElevation,
          highlightElevation: highlightElevation,
          focusNode: focusNode,
          onPressed: () { },
          child: const Text('button'),
        ),
      ),
    );
    await tester.pumpAndSettle();

    // Base elevation
    Material material = tester.widget<Material>(rawButtonMaterial);
    expect(material.elevation, equals(elevation));

    // Focus elevation overrides base
    focusNode.requestFocus();
    await tester.pumpAndSettle();
    material = tester.widget<Material>(rawButtonMaterial);
    RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: focusColor));
    expect(focusNode.hasPrimaryFocus, isTrue);
    expect(material.elevation, equals(focusElevation));

    // Hover elevation overrides focus
    TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
    await gesture.addPointer();
517
    addTearDown(() => gesture?.removePointer());
518 519 520 521 522 523 524
    await gesture.moveTo(tester.getCenter(find.byType(MaterialButton)));
    await tester.pumpAndSettle();
    material = tester.widget<Material>(rawButtonMaterial);
    inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: focusColor)..rect(color: hoverColor));
    expect(material.elevation, equals(hoverElevation));
    await gesture.removePointer();
525
    gesture = null;
526 527

    // Highlight elevation overrides hover
528 529
    final TestGesture gesture2 = await tester.startGesture(tester.getCenter(find.byType(MaterialButton)));
    addTearDown(gesture2.removePointer);
530 531 532 533 534
    await tester.pumpAndSettle();
    material = tester.widget<Material>(rawButtonMaterial);
    inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
    expect(inkFeatures, paints..rect(color: focusColor)..rect(color: highlightColor));
    expect(material.elevation, equals(highlightElevation));
535
    await gesture2.up();
536 537
  });

538
  testWidgets('Does FlatButton contribute semantics', (WidgetTester tester) async {
539
    final SemanticsTester semantics = SemanticsTester(tester);
540
    await tester.pumpWidget(
541
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
542
        textDirection: TextDirection.ltr,
543 544 545
        child: Material(
          child: Center(
            child: FlatButton(
Ian Hickson's avatar
Ian Hickson committed
546
              onPressed: () { },
547
              child: const Text('ABC'),
Ian Hickson's avatar
Ian Hickson committed
548 549 550 551
            ),
          ),
        ),
      ),
552 553 554
    );

    expect(semantics, hasSemantics(
555
      TestSemantics.root(
556
        children: <TestSemantics>[
557
          TestSemantics.rootChild(
558 559 560
            actions: <SemanticsAction>[
              SemanticsAction.tap,
            ],
561
            label: 'ABC',
Dan Field's avatar
Dan Field committed
562
            rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),
563
            transform: Matrix4.translationValues(356.0, 276.0, 0.0),
564 565 566 567 568
            flags: <SemanticsFlag>[
              SemanticsFlag.isButton,
              SemanticsFlag.hasEnabledState,
              SemanticsFlag.isEnabled,
            ],
569
          ),
570 571 572 573 574 575 576 577 578
        ],
      ),
      ignoreId: true,
    ));

    semantics.dispose();
  });

  testWidgets('Does RaisedButton contribute semantics', (WidgetTester tester) async {
579
    final SemanticsTester semantics = SemanticsTester(tester);
580
    await tester.pumpWidget(
581
      Directionality(
582
        textDirection: TextDirection.ltr,
583 584 585
        child: Material(
          child: Center(
            child: RaisedButton(
586
              onPressed: () { },
587
              child: const Text('ABC'),
588 589 590 591 592 593 594
            ),
          ),
        ),
      ),
    );

    expect(semantics, hasSemantics(
595
      TestSemantics.root(
596
        children: <TestSemantics>[
597
          TestSemantics.rootChild(
598 599 600
            actions: <SemanticsAction>[
              SemanticsAction.tap,
            ],
601
            label: 'ABC',
Dan Field's avatar
Dan Field committed
602
            rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),
603
            transform: Matrix4.translationValues(356.0, 276.0, 0.0),
604 605 606 607 608
            flags: <SemanticsFlag>[
              SemanticsFlag.isButton,
              SemanticsFlag.hasEnabledState,
              SemanticsFlag.isEnabled,
            ],
609
          ),
610
        ]
611 612
      ),
      ignoreId: true,
613 614 615 616 617
    ));

    semantics.dispose();
  });

618 619
  testWidgets('Does FlatButton scale with font scale changes', (WidgetTester tester) async {
    await tester.pumpWidget(
620
      Directionality(
621
        textDirection: TextDirection.ltr,
622 623
        child: Material(
          child: MediaQuery(
624
            data: const MediaQueryData(textScaleFactor: 1.0),
625 626
            child: Center(
              child: FlatButton(
627 628 629 630 631 632 633 634 635
                onPressed: () { },
                child: const Text('ABC'),
              ),
            ),
          ),
        ),
      ),
    );

636
    expect(tester.getSize(find.byType(FlatButton)), equals(const Size(88.0, 48.0)));
637
    expect(tester.getSize(find.byType(Text)), equals(const Size(42.0, 14.0)));
638

639
    // textScaleFactor expands text, but not button.
640
    await tester.pumpWidget(
641
      Directionality(
642
        textDirection: TextDirection.ltr,
643 644
        child: Material(
          child: MediaQuery(
645
            data: const MediaQueryData(textScaleFactor: 1.3),
646 647
            child: Center(
              child: FlatButton(
648 649 650 651 652 653 654 655 656
                onPressed: () { },
                child: const Text('ABC'),
              ),
            ),
          ),
        ),
      ),
    );

657
    expect(tester.getSize(find.byType(FlatButton)), equals(const Size(88.0, 48.0)));
658
    // Scaled text rendering is different on Linux and Mac by one pixel.
659
    // TODO(gspencergoog): Figure out why this is, and fix it. https://github.com/flutter/flutter/issues/12357
660
    expect(tester.getSize(find.byType(Text)).width, isIn(<double>[54.0, 55.0]));
661
    expect(tester.getSize(find.byType(Text)).height, isIn(<double>[18.0, 19.0]));
662 663 664

    // Set text scale large enough to expand text and button.
    await tester.pumpWidget(
665
      Directionality(
666
        textDirection: TextDirection.ltr,
667 668
        child: Material(
          child: MediaQuery(
669
            data: const MediaQueryData(textScaleFactor: 3.0),
670 671
            child: Center(
              child: FlatButton(
672 673 674 675 676 677 678 679 680 681
                onPressed: () { },
                child: const Text('ABC'),
              ),
            ),
          ),
        ),
      ),
    );

    // Scaled text rendering is different on Linux and Mac by one pixel.
682
    // TODO(gspencergoog): Figure out why this is, and fix it. https://github.com/flutter/flutter/issues/12357
683
    expect(tester.getSize(find.byType(FlatButton)).width, isIn(<double>[158.0, 159.0]));
684
    expect(tester.getSize(find.byType(FlatButton)).height, equals(48.0));
685 686
    expect(tester.getSize(find.byType(Text)).width, isIn(<double>[126.0, 127.0]));
    expect(tester.getSize(find.byType(Text)).height, equals(42.0));
687
  }, skip: isBrowser);
688

689 690 691
  // This test is very similar to the '...explicit splashColor and highlightColor' test
  // in icon_button_test.dart. If you change this one, you may want to also change that one.
  testWidgets('MaterialButton with explicit splashColor and highlightColor', (WidgetTester tester) async {
692 693
    const Color directSplashColor = Color(0xFF000011);
    const Color directHighlightColor = Color(0xFF000011);
694

695 696 697
    Widget buttonWidget = Material(
      child: Center(
        child: MaterialButton(
698 699 700
          splashColor: directSplashColor,
          highlightColor: directHighlightColor,
          onPressed: () { /* to make sure the button is enabled */ },
701
          clipBehavior: Clip.antiAlias,
702 703 704 705 706
        ),
      ),
    );

    await tester.pumpWidget(
707
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
708
        textDirection: TextDirection.ltr,
709 710
        child: Theme(
          data: ThemeData(
711 712
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          ),
Ian Hickson's avatar
Ian Hickson committed
713 714
          child: buttonWidget,
        ),
715 716 717
      ),
    );

718
    final Offset center = tester.getCenter(find.byType(MaterialButton));
719 720
    final TestGesture gesture = await tester.startGesture(center);
    await tester.pump(); // start gesture
721
    await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way
722

Dan Field's avatar
Dan Field committed
723
    const Rect expectedClipRect = Rect.fromLTRB(356.0, 282.0, 444.0, 318.0);
724 725
    final Path expectedClipPath = Path()
     ..addRRect(RRect.fromRectAndRadius(
726 727 728
         expectedClipRect,
         const Radius.circular(2.0),
     ));
729 730 731
    expect(
      Material.of(tester.element(find.byType(MaterialButton))),
      paints
732 733 734 735
        ..clipPath(pathMatcher: coversSameAreaAs(
            expectedClipPath,
            areaToCompare: expectedClipRect.inflate(10.0),
        ))
736
        ..circle(color: directSplashColor)
737
        ..rect(color: directHighlightColor),
738 739
    );

740 741
    const Color themeSplashColor1 = Color(0xFF001100);
    const Color themeHighlightColor1 = Color(0xFF001100);
742

743 744 745
    buttonWidget = Material(
      child: Center(
        child: MaterialButton(
746
          onPressed: () { /* to make sure the button is enabled */ },
747
          clipBehavior: Clip.antiAlias,
748 749 750 751 752
        ),
      ),
    );

    await tester.pumpWidget(
753
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
754
        textDirection: TextDirection.ltr,
755 756
        child: Theme(
          data: ThemeData(
Ian Hickson's avatar
Ian Hickson committed
757 758
            highlightColor: themeHighlightColor1,
            splashColor: themeSplashColor1,
759
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
Ian Hickson's avatar
Ian Hickson committed
760 761
          ),
          child: buttonWidget,
762 763 764 765 766 767 768
        ),
      ),
    );

    expect(
      Material.of(tester.element(find.byType(MaterialButton))),
      paints
769 770 771 772
        ..clipPath(pathMatcher: coversSameAreaAs(
            expectedClipPath,
            areaToCompare: expectedClipRect.inflate(10.0),
        ))
773
        ..circle(color: themeSplashColor1)
774
        ..rect(color: themeHighlightColor1),
775 776
    );

777 778
    const Color themeSplashColor2 = Color(0xFF002200);
    const Color themeHighlightColor2 = Color(0xFF002200);
779 780

    await tester.pumpWidget(
781
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
782
        textDirection: TextDirection.ltr,
783 784
        child: Theme(
          data: ThemeData(
Ian Hickson's avatar
Ian Hickson committed
785 786
            highlightColor: themeHighlightColor2,
            splashColor: themeSplashColor2,
787
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
Ian Hickson's avatar
Ian Hickson committed
788 789
          ),
          child: buttonWidget, // same widget, so does not get updated because of us
790 791 792 793 794 795 796 797
        ),
      ),
    );

    expect(
      Material.of(tester.element(find.byType(MaterialButton))),
      paints
        ..circle(color: themeSplashColor2)
798
        ..rect(color: themeHighlightColor2),
799 800 801 802 803
    );

    await gesture.up();
  });

804
  testWidgets('MaterialButton has no clip by default', (WidgetTester tester) async {
805 806 807 808
    final GlobalKey buttonKey = GlobalKey();
    final Widget buttonWidget = Material(
      child: Center(
        child: MaterialButton(
809 810 811 812 813 814 815
          key: buttonKey,
          onPressed: () { /* to make sure the button is enabled */ },
        ),
      ),
    );

    await tester.pumpWidget(
816
      Directionality(
817
        textDirection: TextDirection.ltr,
818 819
        child: Theme(
          data: ThemeData(
820 821 822 823 824 825 826 827 828
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          ),
          child: buttonWidget,
        ),
      ),
    );

    expect(
        tester.renderObject(find.byKey(buttonKey)),
829
        paintsExactlyCountTimes(#clipPath, 0),
830
    );
831
  });
832

833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
  testWidgets('Disabled MaterialButton has correct default text color', (WidgetTester tester) async {
    const String testText = 'Disabled';
    const Widget buttonWidget = Directionality(
      textDirection: TextDirection.ltr,
      child: Material(
        child: Center(
          child: MaterialButton(
            onPressed: null,
            child: Text(testText), // button is disabled
          ),
        ),
      ),
    );

    await tester.pumpWidget(buttonWidget);

    final RichText text = tester.widget<RichText>(find.byType(RichText));
    expect(text.text.style.color, Colors.black38);
  });

853
  testWidgets('Disabled MaterialButton has same semantic size as enabled and exposes disabled semantics', (WidgetTester tester) async {
854
    final SemanticsTester semantics = SemanticsTester(tester);
855

Dan Field's avatar
Dan Field committed
856
    const Rect expectedButtonSize = Rect.fromLTRB(0.0, 0.0, 116.0, 48.0);
857
    // Button is in center of screen
858
    final Matrix4 expectedButtonTransform = Matrix4.identity()
859 860 861 862 863 864
      ..translate(
        TestSemantics.fullScreen.width / 2 - expectedButtonSize.width /2,
        TestSemantics.fullScreen.height / 2 - expectedButtonSize.height /2,
      );

    // enabled button
865
    await tester.pumpWidget(Directionality(
866
      textDirection: TextDirection.ltr,
867 868 869
      child: Material(
        child: Center(
          child: MaterialButton(
870 871 872 873 874 875 876 877
            child: const Text('Button'),
            onPressed: () { /* to make sure the button is enabled */ },
          ),
        ),
      ),
    ));

    expect(semantics, hasSemantics(
878
      TestSemantics.root(
879
        children: <TestSemantics>[
880
          TestSemantics.rootChild(
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
            id: 1,
            rect: expectedButtonSize,
            transform: expectedButtonTransform,
            label: 'Button',
            actions: <SemanticsAction>[
              SemanticsAction.tap,
            ],
            flags: <SemanticsFlag>[
              SemanticsFlag.isButton,
              SemanticsFlag.hasEnabledState,
              SemanticsFlag.isEnabled,
            ],
          ),
        ],
      ),
    ));

    // disabled button
    await tester.pumpWidget(const Directionality(
      textDirection: TextDirection.ltr,
901 902 903 904
      child: Material(
        child: Center(
          child: MaterialButton(
            child: Text('Button'),
905 906 907 908 909 910 911
            onPressed: null, // button is disabled
          ),
        ),
      ),
    ));

    expect(semantics, hasSemantics(
912
      TestSemantics.root(
913
        children: <TestSemantics>[
914
          TestSemantics.rootChild(
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
            id: 1,
            rect: expectedButtonSize,
            transform: expectedButtonTransform,
            label: 'Button',
            flags: <SemanticsFlag>[
              SemanticsFlag.isButton,
              SemanticsFlag.hasEnabledState,
            ],
          ),
        ],
      ),
    ));


    semantics.dispose();
930
  }, skip: isBrowser);
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 989 990 991 992 993 994 995 996 997 998
  testWidgets('MaterialButton minWidth and height parameters', (WidgetTester tester) async {
    Widget buildFrame({ double minWidth, double height, EdgeInsets padding = EdgeInsets.zero, Widget child }) {
      return Directionality(
        textDirection: TextDirection.ltr,
        child: Center(
          child: MaterialButton(
            padding: padding,
            minWidth: minWidth,
            height: height,
            onPressed: null,
            materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
            child: child,
          ),
        ),
      );
    }

    await tester.pumpWidget(buildFrame(minWidth: 8.0, height: 24.0));
    expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 24.0));

    await tester.pumpWidget(buildFrame(minWidth: 8.0));
    // Default minHeight constraint is 36, see RawMaterialButton.
    expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 36.0));

    await tester.pumpWidget(buildFrame(height: 8.0));
    // Default minWidth constraint is 88, see RawMaterialButton.
    expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 8.0));

    await tester.pumpWidget(buildFrame());
    expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 36.0));

    await tester.pumpWidget(buildFrame(padding: const EdgeInsets.all(4.0)));
    expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 36.0));

    // Size is defined by the padding.
    await tester.pumpWidget(
      buildFrame(
        minWidth: 0.0,
        height: 0.0,
        padding: const EdgeInsets.all(4.0),
      ),
    );
    expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 8.0));

    // Size is defined by the padded child.
    await tester.pumpWidget(
      buildFrame(
        minWidth: 0.0,
        height: 0.0,
        padding: const EdgeInsets.all(4.0),
        child: const SizedBox(width: 8.0, height: 8.0),
      ),
    );
    expect(tester.getSize(find.byType(MaterialButton)), const Size(16.0, 16.0));

    // Size is defined by the minWidth, height constraints.
    await tester.pumpWidget(
      buildFrame(
        minWidth: 18.0,
        height: 18.0,
        padding: const EdgeInsets.all(4.0),
        child: const SizedBox(width: 8.0, height: 8.0),
      ),
    );
    expect(tester.getSize(find.byType(MaterialButton)), const Size(18.0, 18.0));
  });

999
  testWidgets('MaterialButton size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
1000
    final Key key1 = UniqueKey();
1001
    await tester.pumpWidget(
1002 1003 1004
      Theme(
        data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.padded),
        child: Directionality(
1005
          textDirection: TextDirection.ltr,
1006 1007 1008
          child: Material(
            child: Center(
              child: MaterialButton(
1009 1010
                key: key1,
                child: const SizedBox(width: 50.0, height: 8.0),
1011
                onPressed: () { },
1012 1013 1014 1015 1016 1017 1018 1019 1020
              ),
            ),
          ),
        ),
      ),
    );

    expect(tester.getSize(find.byKey(key1)), const Size(88.0, 48.0));

1021
    final Key key2 = UniqueKey();
1022
    await tester.pumpWidget(
1023 1024 1025
      Theme(
        data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap),
        child: Directionality(
1026
          textDirection: TextDirection.ltr,
1027 1028 1029
          child: Material(
            child: Center(
              child: MaterialButton(
1030 1031
                key: key2,
                child: const SizedBox(width: 50.0, height: 8.0),
1032
                onPressed: () { },
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
              ),
            ),
          ),
        ),
      ),
    );

    expect(tester.getSize(find.byKey(key2)), const Size(88.0, 36.0));
  });

  testWidgets('FlatButton size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
1044
    final Key key1 = UniqueKey();
1045
    await tester.pumpWidget(
1046 1047 1048
      Theme(
        data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.padded),
        child: Directionality(
1049
          textDirection: TextDirection.ltr,
1050 1051 1052
          child: Material(
            child: Center(
              child: FlatButton(
1053 1054
                key: key1,
                child: const SizedBox(width: 50.0, height: 8.0),
1055
                onPressed: () { },
1056 1057 1058 1059 1060 1061 1062 1063 1064
              ),
            ),
          ),
        ),
      ),
    );

    expect(tester.getSize(find.byKey(key1)), const Size(88.0, 48.0));

1065
    final Key key2 = UniqueKey();
1066
    await tester.pumpWidget(
1067 1068 1069
      Theme(
        data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap),
        child: Directionality(
1070
          textDirection: TextDirection.ltr,
1071 1072 1073
          child: Material(
            child: Center(
              child: FlatButton(
1074 1075
                key: key2,
                child: const SizedBox(width: 50.0, height: 8.0),
1076
                onPressed: () { },
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
              ),
            ),
          ),
        ),
      ),
    );

    expect(tester.getSize(find.byKey(key2)), const Size(88.0, 36.0));
  });

  testWidgets('RaisedButton size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
1088
    final Key key1 = UniqueKey();
1089
    await tester.pumpWidget(
1090 1091 1092
      Theme(
        data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.padded),
        child: Directionality(
1093
          textDirection: TextDirection.ltr,
1094 1095 1096
          child: Material(
            child: Center(
              child: RaisedButton(
1097 1098
                key: key1,
                child: const SizedBox(width: 50.0, height: 8.0),
1099
                onPressed: () { },
1100 1101 1102 1103 1104 1105 1106 1107 1108
              ),
            ),
          ),
        ),
      ),
    );

    expect(tester.getSize(find.byKey(key1)), const Size(88.0, 48.0));

1109
    final Key key2 = UniqueKey();
1110
    await tester.pumpWidget(
1111 1112 1113
      Theme(
        data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap),
        child: Directionality(
1114
          textDirection: TextDirection.ltr,
1115 1116 1117
          child: Material(
            child: Center(
              child: RaisedButton(
1118 1119
                key: key2,
                child: const SizedBox(width: 50.0, height: 8.0),
1120
                onPressed: () { },
1121 1122 1123 1124 1125 1126 1127 1128 1129
              ),
            ),
          ),
        ),
      ),
    );

    expect(tester.getSize(find.byKey(key2)), const Size(88.0, 36.0));
  });
1130

1131
  testWidgets('RaisedButton has no clip by default', (WidgetTester tester) async {
1132
    await tester.pumpWidget(
1133
      Directionality(
1134
          textDirection: TextDirection.ltr,
1135 1136
          child: Material(
            child: RaisedButton(
1137 1138
              onPressed: () { /* to make sure the button is enabled */ },
            ),
1139
          ),
1140 1141 1142 1143 1144
      ),
    );

    expect(
        tester.renderObject(find.byType(RaisedButton)),
1145
        paintsExactlyCountTimes(#clipPath, 0),
1146 1147
    );
  });
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167

  testWidgets('MaterialButton shape overrides ButtonTheme shape', (WidgetTester tester) async {
    // Regression test for https://github.com/flutter/flutter/issues/29146
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: MaterialButton(
          onPressed: () { },
          shape: const StadiumBorder(),
          child: const Text('button'),
        ),
      ),
    );

    final Finder rawButtonMaterial = find.descendant(
      of: find.byType(MaterialButton),
      matching: find.byType(Material),
    );
    expect(tester.widget<Material>(rawButtonMaterial).shape, const StadiumBorder());
  });
1168

1169
  testWidgets('MaterialButton disabled default is correct.', (WidgetTester tester) async {
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
    // Regression test for https://github.com/flutter/flutter/issues/30012.

    final Finder rawButtonMaterial = find.descendant(
      of: find.byType(MaterialButton),
      matching: find.byType(Material),
    );

    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: MaterialButton(
          disabledColor: Color(0xff00ff00),
          onPressed: null,
          child: Text('button'),
        ),
      ),
    );

    final Material material = tester.widget<Material>(rawButtonMaterial);
    expect(material.color, const Color(0xff00ff00));
  });
1191
}