1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/gestures.dart';
6import 'package:flutter/material.dart';
7import 'package:flutter/rendering.dart';
8import 'package:flutter_test/flutter_test.dart';
9import '../widgets/semantics_tester.dart';
10
11void main() {
12 setUp(() {
13 debugResetSemanticsIdCounter();
14 });
15
16 testWidgets('MaterialButton defaults', (WidgetTester tester) async {
17 final Finder rawButtonMaterial = find.descendant(
18 of: find.byType(MaterialButton),
19 matching: find.byType(Material),
20 );
21
22 // Enabled MaterialButton
23 await tester.pumpWidget(
24 Theme(
25 data: ThemeData(useMaterial3: false),
26 child: Directionality(
27 textDirection: TextDirection.ltr,
28 child: MaterialButton(
29 onPressed: () { },
30 child: const Text('button'),
31 ),
32 ),
33 ),
34 );
35 Material material = tester.widget<Material>(rawButtonMaterial);
36 expect(material.animationDuration, const Duration(milliseconds: 200));
37 expect(material.borderOnForeground, true);
38 expect(material.borderRadius, null);
39 expect(material.clipBehavior, Clip.none);
40 expect(material.color, null);
41 expect(material.elevation, 2.0);
42 expect(material.shadowColor, null);
43 expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))));
44 expect(material.textStyle!.color, const Color(0xdd000000));
45 expect(material.textStyle!.fontFamily, 'Roboto');
46 expect(material.textStyle!.fontSize, 14);
47 expect(material.textStyle!.fontWeight, FontWeight.w500);
48 expect(material.type, MaterialType.transparency);
49
50 final Offset center = tester.getCenter(find.byType(MaterialButton));
51 final TestGesture gesture = await tester.startGesture(center);
52 await tester.pumpAndSettle();
53
54 // Only elevation changes when enabled and pressed.
55 material = tester.widget<Material>(rawButtonMaterial);
56 expect(material.animationDuration, const Duration(milliseconds: 200));
57 expect(material.borderOnForeground, true);
58 expect(material.borderRadius, null);
59 expect(material.clipBehavior, Clip.none);
60 expect(material.color, null);
61 expect(material.elevation, 8.0);
62 expect(material.shadowColor, null);
63 expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))));
64 expect(material.textStyle!.color, const Color(0xdd000000));
65 expect(material.textStyle!.fontFamily, 'Roboto');
66 expect(material.textStyle!.fontSize, 14);
67 expect(material.textStyle!.fontWeight, FontWeight.w500);
68 expect(material.type, MaterialType.transparency);
69
70 // Disabled MaterialButton
71 await tester.pumpWidget(
72 Theme(
73 data: ThemeData(useMaterial3: false),
74 child: const Directionality(
75 textDirection: TextDirection.ltr,
76 child: MaterialButton(
77 onPressed: null,
78 child: Text('button'),
79 ),
80 ),
81 ),
82 );
83 material = tester.widget<Material>(rawButtonMaterial);
84 expect(material.animationDuration, const Duration(milliseconds: 200));
85 expect(material.borderOnForeground, true);
86 expect(material.borderRadius, null);
87 expect(material.clipBehavior, Clip.none);
88 expect(material.color, null);
89 expect(material.elevation, 0.0);
90 expect(material.shadowColor, null);
91 expect(material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))));
92 expect(material.textStyle!.color, const Color(0x61000000));
93 expect(material.textStyle!.fontFamily, 'Roboto');
94 expect(material.textStyle!.fontSize, 14);
95 expect(material.textStyle!.fontWeight, FontWeight.w500);
96 expect(material.type, MaterialType.transparency);
97
98 // Finish gesture to release resources.
99 await gesture.up();
100 await tester.pumpAndSettle();
101 });
102
103 testWidgets('Does MaterialButton work with hover', (WidgetTester tester) async {
104 const Color hoverColor = Color(0xff001122);
105
106 await tester.pumpWidget(
107 Directionality(
108 textDirection: TextDirection.ltr,
109 child: MaterialButton(
110 hoverColor: hoverColor,
111 onPressed: () { },
112 child: const Text('button'),
113 ),
114 ),
115 );
116
117 final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
118 await gesture.addPointer();
119 await gesture.moveTo(tester.getCenter(find.byType(MaterialButton)));
120 await tester.pumpAndSettle();
121
122 final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
123 expect(inkFeatures, paints..rect(color: hoverColor));
124 });
125
126 testWidgets('Does MaterialButton work with focus', (WidgetTester tester) async {
127 const Color focusColor = Color(0xff001122);
128
129 final FocusNode focusNode = FocusNode(debugLabel: 'MaterialButton Node');
130 await tester.pumpWidget(
131 Directionality(
132 textDirection: TextDirection.ltr,
133 child: MaterialButton(
134 focusColor: focusColor,
135 focusNode: focusNode,
136 onPressed: () { },
137 child: const Text('button'),
138 ),
139 ),
140 );
141
142 FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
143 focusNode.requestFocus();
144 await tester.pumpAndSettle();
145
146 final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
147 expect(inkFeatures, paints..rect(color: focusColor));
148
149 focusNode.dispose();
150 });
151
152 testWidgets('MaterialButton elevation and colors have proper precedence', (WidgetTester tester) async {
153 const double elevation = 10.0;
154 const double focusElevation = 11.0;
155 const double hoverElevation = 12.0;
156 const double highlightElevation = 13.0;
157 const Color focusColor = Color(0xff001122);
158 const Color hoverColor = Color(0xff112233);
159 const Color highlightColor = Color(0xff223344);
160
161 final Finder rawButtonMaterial = find.descendant(
162 of: find.byType(MaterialButton),
163 matching: find.byType(Material),
164 );
165
166 final FocusNode focusNode = FocusNode(debugLabel: 'MaterialButton Node');
167 await tester.pumpWidget(
168 Directionality(
169 textDirection: TextDirection.ltr,
170 child: MaterialButton(
171 focusColor: focusColor,
172 hoverColor: hoverColor,
173 highlightColor: highlightColor,
174 elevation: elevation,
175 focusElevation: focusElevation,
176 hoverElevation: hoverElevation,
177 highlightElevation: highlightElevation,
178 focusNode: focusNode,
179 onPressed: () { },
180 child: const Text('button'),
181 ),
182 ),
183 );
184 await tester.pumpAndSettle();
185 FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
186
187 // Base elevation
188 Material material = tester.widget<Material>(rawButtonMaterial);
189 expect(material.elevation, equals(elevation));
190
191 // Focus elevation overrides base
192 focusNode.requestFocus();
193 await tester.pumpAndSettle();
194 material = tester.widget<Material>(rawButtonMaterial);
195 RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
196 expect(inkFeatures, paints..rect(color: focusColor));
197 expect(focusNode.hasPrimaryFocus, isTrue);
198 expect(material.elevation, equals(focusElevation));
199
200 // Hover elevation overrides focus
201 TestGesture? gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
202 await gesture.addPointer();
203 addTearDown(() => gesture?.removePointer());
204 await gesture.moveTo(tester.getCenter(find.byType(MaterialButton)));
205 await tester.pumpAndSettle();
206 material = tester.widget<Material>(rawButtonMaterial);
207 inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
208 expect(inkFeatures, paints..rect(color: focusColor)..rect(color: hoverColor));
209 expect(material.elevation, equals(hoverElevation));
210 await gesture.removePointer();
211 gesture = null;
212
213 // Highlight elevation overrides hover
214 final TestGesture gesture2 = await tester.startGesture(tester.getCenter(find.byType(MaterialButton)));
215 addTearDown(gesture2.removePointer);
216 await tester.pumpAndSettle();
217 material = tester.widget<Material>(rawButtonMaterial);
218 inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
219 expect(inkFeatures, paints..rect(color: focusColor)..rect(color: highlightColor));
220 expect(material.elevation, equals(highlightElevation));
221 await gesture2.up();
222
223 focusNode.dispose();
224 });
225
226 testWidgets("MaterialButton's disabledColor takes precedence over its default disabled color.", (WidgetTester tester) async {
227 // Regression test for https://github.com/flutter/flutter/issues/30012.
228
229 final Finder rawButtonMaterial = find.descendant(
230 of: find.byType(MaterialButton),
231 matching: find.byType(Material),
232 );
233
234 await tester.pumpWidget(
235 const Directionality(
236 textDirection: TextDirection.ltr,
237 child: MaterialButton(
238 disabledColor: Color(0xff00ff00),
239 onPressed: null,
240 child: Text('button'),
241 ),
242 ),
243 );
244
245 final Material material = tester.widget<Material>(rawButtonMaterial);
246 expect(material.color, const Color(0xff00ff00));
247 });
248
249 testWidgets('Default MaterialButton meets a11y contrast guidelines', (WidgetTester tester) async {
250 await tester.pumpWidget(
251 MaterialApp(
252 home: Scaffold(
253 body: Center(
254 child: MaterialButton(
255 child: const Text('MaterialButton'),
256 onPressed: () { },
257 ),
258 ),
259 ),
260 ),
261 );
262
263 // Default, not disabled.
264 await expectLater(tester, meetsGuideline(textContrastGuideline));
265
266 // Highlighted (pressed).
267 final Offset center = tester.getCenter(find.byType(MaterialButton));
268 final TestGesture gesture = await tester.startGesture(center);
269 await tester.pump(); // Start the splash and highlight animations.
270 await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
271 await expectLater(tester, meetsGuideline(textContrastGuideline));
272
273 // Finish gesture to release resources.
274 await gesture.up();
275 await tester.pumpAndSettle();
276 },
277 skip: isBrowser, // https://github.com/flutter/flutter/issues/44115
278 );
279
280 testWidgets('MaterialButton gets focus when autofocus is set.', (WidgetTester tester) async {
281 final FocusNode focusNode = FocusNode(debugLabel: 'MaterialButton');
282 await tester.pumpWidget(
283 MaterialApp(
284 home: Center(
285 child: MaterialButton(
286 focusNode: focusNode,
287 onPressed: () {},
288 child: Container(width: 100, height: 100, color: const Color(0xffff0000)),
289 ),
290 ),
291 ),
292 );
293
294 await tester.pump();
295 expect(focusNode.hasPrimaryFocus, isFalse);
296
297 await tester.pumpWidget(
298 MaterialApp(
299 home: Center(
300 child: MaterialButton(
301 autofocus: true,
302 focusNode: focusNode,
303 onPressed: () {},
304 child: Container(width: 100, height: 100, color: const Color(0xffff0000)),
305 ),
306 ),
307 ),
308 );
309
310 await tester.pump();
311 expect(focusNode.hasPrimaryFocus, isTrue);
312
313 focusNode.dispose();
314 });
315
316 testWidgets('MaterialButton onPressed and onLongPress callbacks are correctly called when non-null', (WidgetTester tester) async {
317
318 bool wasPressed;
319 Finder materialButton;
320
321 Widget buildFrame({ VoidCallback? onPressed, VoidCallback? onLongPress }) {
322 return Directionality(
323 textDirection: TextDirection.ltr,
324 child: MaterialButton(
325 onPressed: onPressed,
326 onLongPress: onLongPress,
327 child: const Text('button'),
328 ),
329 );
330 }
331
332 // onPressed not null, onLongPress null.
333 wasPressed = false;
334 await tester.pumpWidget(
335 buildFrame(onPressed: () { wasPressed = true; }),
336 );
337 materialButton = find.byType(MaterialButton);
338 expect(tester.widget<MaterialButton>(materialButton).enabled, true);
339 await tester.tap(materialButton);
340 expect(wasPressed, true);
341
342 // onPressed null, onLongPress not null.
343 wasPressed = false;
344 await tester.pumpWidget(
345 buildFrame(onLongPress: () { wasPressed = true; }),
346 );
347 materialButton = find.byType(MaterialButton);
348 expect(tester.widget<MaterialButton>(materialButton).enabled, true);
349 await tester.longPress(materialButton);
350 expect(wasPressed, true);
351
352 // onPressed null, onLongPress null.
353 await tester.pumpWidget(
354 buildFrame(),
355 );
356 materialButton = find.byType(MaterialButton);
357 expect(tester.widget<MaterialButton>(materialButton).enabled, false);
358 });
359
360 testWidgets('MaterialButton onPressed and onLongPress callbacks are distinctly recognized', (WidgetTester tester) async {
361 bool didPressButton = false;
362 bool didLongPressButton = false;
363
364 await tester.pumpWidget(
365 Directionality(
366 textDirection: TextDirection.ltr,
367 child: MaterialButton(
368 onPressed: () {
369 didPressButton = true;
370 },
371 onLongPress: () {
372 didLongPressButton = true;
373 },
374 child: const Text('button'),
375 ),
376 ),
377 );
378
379 final Finder materialButton = find.byType(MaterialButton);
380 expect(tester.widget<MaterialButton>(materialButton).enabled, true);
381
382 expect(didPressButton, isFalse);
383 await tester.tap(materialButton);
384 expect(didPressButton, isTrue);
385
386 expect(didLongPressButton, isFalse);
387 await tester.longPress(materialButton);
388 expect(didLongPressButton, isTrue);
389 });
390
391 testWidgets('MaterialButton changes mouse cursor when hovered', (WidgetTester tester) async {
392 await tester.pumpWidget(
393 Directionality(
394 textDirection: TextDirection.ltr,
395 child: MouseRegion(
396 cursor: SystemMouseCursors.forbidden,
397 child: MaterialButton(
398 onPressed: () {},
399 mouseCursor: SystemMouseCursors.text,
400 ),
401 ),
402 ),
403 );
404
405 final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1);
406 await gesture.addPointer(location: Offset.zero);
407
408 await tester.pump();
409
410 expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
411
412 // Test default cursor
413 await tester.pumpWidget(
414 Directionality(
415 textDirection: TextDirection.ltr,
416 child: MouseRegion(
417 cursor: SystemMouseCursors.forbidden,
418 child: MaterialButton(
419 onPressed: () {},
420 ),
421 ),
422 ),
423 );
424
425 expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click);
426
427 // Test default cursor when disabled
428 await tester.pumpWidget(
429 const Directionality(
430 textDirection: TextDirection.ltr,
431 child: MouseRegion(
432 cursor: SystemMouseCursors.forbidden,
433 child: MaterialButton(
434 onPressed: null,
435 ),
436 ),
437 ),
438 );
439
440 expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
441 });
442
443 // This test is very similar to the '...explicit splashColor and highlightColor' test
444 // in icon_button_test.dart. If you change this one, you may want to also change that one.
445 testWidgets('MaterialButton with explicit splashColor and highlightColor', (WidgetTester tester) async {
446 const Color directSplashColor = Color(0xFF000011);
447 const Color directHighlightColor = Color(0xFF000011);
448
449 Widget buttonWidget = Center(
450 child: MaterialButton(
451 splashColor: directSplashColor,
452 highlightColor: directHighlightColor,
453 onPressed: () { /* to make sure the button is enabled */ },
454 clipBehavior: Clip.antiAlias,
455 ),
456 );
457
458 await tester.pumpWidget(
459 Directionality(
460 textDirection: TextDirection.ltr,
461 child: Theme(
462 data: ThemeData(
463 useMaterial3: false,
464 materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
465 ),
466 child: buttonWidget,
467 ),
468 ),
469 );
470
471 final Offset center = tester.getCenter(find.byType(MaterialButton));
472 final TestGesture gesture = await tester.startGesture(center);
473 await tester.pump(); // start gesture
474 await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way
475
476 // Painter is translated to the center by the Center widget and not
477 // the Material widget.
478 const Rect expectedClipRect = Rect.fromLTRB(0.0, 0.0, 88.0, 36.0);
479 final Path expectedClipPath = Path()
480 ..addRRect(RRect.fromRectAndRadius(
481 expectedClipRect,
482 const Radius.circular(2.0),
483 ));
484 expect(
485 Material.of(tester.element(find.byType(InkWell))),
486 paints
487 ..clipPath(pathMatcher: coversSameAreaAs(
488 expectedClipPath,
489 areaToCompare: expectedClipRect.inflate(10.0),
490 ))
491 ..circle(color: directSplashColor)
492 ..rect(color: directHighlightColor),
493 );
494
495 const Color themeSplashColor1 = Color(0xFF001100);
496 const Color themeHighlightColor1 = Color(0xFF001100);
497
498 buttonWidget = Center(
499 child: MaterialButton(
500 onPressed: () { /* to make sure the button is enabled */ },
501 clipBehavior: Clip.antiAlias,
502 ),
503 );
504
505 await tester.pumpWidget(
506 Directionality(
507 textDirection: TextDirection.ltr,
508 child: Theme(
509 data: ThemeData(
510 useMaterial3: false,
511 highlightColor: themeHighlightColor1,
512 splashColor: themeSplashColor1,
513 materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
514 ),
515 child: buttonWidget,
516 ),
517 ),
518 );
519
520 expect(
521 Material.of(tester.element(find.byType(InkWell))),
522 paints
523 ..clipPath(pathMatcher: coversSameAreaAs(
524 expectedClipPath,
525 areaToCompare: expectedClipRect.inflate(10.0),
526 ))
527 ..circle(color: themeSplashColor1)
528 ..rect(color: themeHighlightColor1),
529 );
530
531 const Color themeSplashColor2 = Color(0xFF002200);
532 const Color themeHighlightColor2 = Color(0xFF002200);
533
534 await tester.pumpWidget(
535 Directionality(
536 textDirection: TextDirection.ltr,
537 child: Theme(
538 data: ThemeData(
539 useMaterial3: false,
540 highlightColor: themeHighlightColor2,
541 splashColor: themeSplashColor2,
542 materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
543 ),
544 child: buttonWidget, // same widget, so does not get updated because of us
545 ),
546 ),
547 );
548
549 expect(
550 Material.of(tester.element(find.byType(InkWell))),
551 paints
552 ..circle(color: themeSplashColor2)
553 ..rect(color: themeHighlightColor2),
554 );
555
556 await gesture.up();
557 });
558
559 testWidgets('MaterialButton has no clip by default', (WidgetTester tester) async {
560 final GlobalKey buttonKey = GlobalKey();
561 final Widget buttonWidget = Center(
562 child: MaterialButton(
563 key: buttonKey,
564 onPressed: () { /* to make sure the button is enabled */ },
565 ),
566 );
567
568 await tester.pumpWidget(
569 Directionality(
570 textDirection: TextDirection.ltr,
571 child: Theme(
572 data: ThemeData(
573 materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
574 ),
575 child: buttonWidget,
576 ),
577 ),
578 );
579
580 expect(
581 tester.renderObject(find.byKey(buttonKey)),
582 paintsExactlyCountTimes(#clipPath, 0),
583 );
584 });
585
586 testWidgets('Disabled MaterialButton has same semantic size as enabled and exposes disabled semantics', (WidgetTester tester) async {
587 final SemanticsTester semantics = SemanticsTester(tester);
588
589 const Rect expectedButtonSize = Rect.fromLTRB(0.0, 0.0, 116.0, 48.0);
590 // Button is in center of screen
591 final Matrix4 expectedButtonTransform = Matrix4.identity()
592 ..translate(
593 TestSemantics.fullScreen.width / 2 - expectedButtonSize.width /2,
594 TestSemantics.fullScreen.height / 2 - expectedButtonSize.height /2,
595 );
596
597 // enabled button
598 await tester.pumpWidget(
599 Theme(
600 data: ThemeData(useMaterial3: false),
601 child: Directionality(
602 textDirection: TextDirection.ltr,
603 child: Center(
604 child: MaterialButton(
605 child: const Text('Button'),
606 onPressed: () { /* to make sure the button is enabled */ },
607 ),
608 ),
609 ),
610 ),
611 );
612
613 expect(semantics, hasSemantics(
614 TestSemantics.root(
615 children: <TestSemantics>[
616 TestSemantics.rootChild(
617 id: 1,
618 rect: expectedButtonSize,
619 transform: expectedButtonTransform,
620 label: 'Button',
621 actions: <SemanticsAction>[
622 SemanticsAction.tap,
623 SemanticsAction.focus,
624 ],
625 flags: <SemanticsFlag>[
626 SemanticsFlag.hasEnabledState,
627 SemanticsFlag.isButton,
628 SemanticsFlag.isEnabled,
629 SemanticsFlag.isFocusable,
630 ],
631 ),
632 ],
633 ),
634 ));
635
636 // disabled button
637 await tester.pumpWidget(
638 Theme(
639 data: ThemeData(useMaterial3: false),
640 child: const Directionality(
641 textDirection: TextDirection.ltr,
642 child: Center(
643 child: MaterialButton(
644 onPressed: null, // button is disabled
645 child: Text('Button'),
646 ),
647 ),
648 ),
649 ),
650 );
651
652 expect(semantics, hasSemantics(
653 TestSemantics.root(
654 children: <TestSemantics>[
655 TestSemantics.rootChild(
656 id: 1,
657 rect: expectedButtonSize,
658 transform: expectedButtonTransform,
659 label: 'Button',
660 flags: <SemanticsFlag>[
661 SemanticsFlag.hasEnabledState,
662 SemanticsFlag.isButton,
663 SemanticsFlag.isFocusable,
664 ],
665 actions: <SemanticsAction>[SemanticsAction.focus],
666 ),
667 ],
668 ),
669 ));
670
671
672 semantics.dispose();
673 });
674
675 testWidgets('MaterialButton minWidth and height parameters', (WidgetTester tester) async {
676 Widget buildFrame({ double? minWidth, double? height, EdgeInsets padding = EdgeInsets.zero, Widget? child }) {
677 return Directionality(
678 textDirection: TextDirection.ltr,
679 child: Center(
680 child: MaterialButton(
681 padding: padding,
682 minWidth: minWidth,
683 height: height,
684 onPressed: null,
685 materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
686 child: child,
687 ),
688 ),
689 );
690 }
691
692 await tester.pumpWidget(buildFrame(minWidth: 8.0, height: 24.0));
693 expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 24.0));
694
695 await tester.pumpWidget(buildFrame(minWidth: 8.0));
696 // Default minHeight constraint is 36, see RawMaterialButton.
697 expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 36.0));
698
699 await tester.pumpWidget(buildFrame(height: 8.0));
700 // Default minWidth constraint is 88, see RawMaterialButton.
701 expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 8.0));
702
703 await tester.pumpWidget(buildFrame());
704 expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 36.0));
705
706 await tester.pumpWidget(buildFrame(padding: const EdgeInsets.all(4.0)));
707 expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 36.0));
708
709 // Size is defined by the padding.
710 await tester.pumpWidget(
711 buildFrame(
712 minWidth: 0.0,
713 height: 0.0,
714 padding: const EdgeInsets.all(4.0),
715 ),
716 );
717 expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 8.0));
718
719 // Size is defined by the padded child.
720 await tester.pumpWidget(
721 buildFrame(
722 minWidth: 0.0,
723 height: 0.0,
724 padding: const EdgeInsets.all(4.0),
725 child: const SizedBox(width: 8.0, height: 8.0),
726 ),
727 );
728 expect(tester.getSize(find.byType(MaterialButton)), const Size(16.0, 16.0));
729
730 // Size is defined by the minWidth, height constraints.
731 await tester.pumpWidget(
732 buildFrame(
733 minWidth: 18.0,
734 height: 18.0,
735 padding: const EdgeInsets.all(4.0),
736 child: const SizedBox(width: 8.0, height: 8.0),
737 ),
738 );
739 expect(tester.getSize(find.byType(MaterialButton)), const Size(18.0, 18.0));
740 });
741
742 testWidgets('MaterialButton size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
743 final Key key1 = UniqueKey();
744 await tester.pumpWidget(
745 Theme(
746 data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.padded),
747 child: Directionality(
748 textDirection: TextDirection.ltr,
749 child: Center(
750 child: MaterialButton(
751 key: key1,
752 child: const SizedBox(width: 50.0, height: 8.0),
753 onPressed: () { },
754 ),
755 ),
756 ),
757 ),
758 );
759
760 expect(tester.getSize(find.byKey(key1)), const Size(88.0, 48.0));
761
762 final Key key2 = UniqueKey();
763 await tester.pumpWidget(
764 Theme(
765 data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap),
766 child: Directionality(
767 textDirection: TextDirection.ltr,
768 child: Center(
769 child: MaterialButton(
770 key: key2,
771 child: const SizedBox(width: 50.0, height: 8.0),
772 onPressed: () { },
773 ),
774 ),
775 ),
776 ),
777 );
778
779 expect(tester.getSize(find.byKey(key2)), const Size(88.0, 36.0));
780 });
781
782 testWidgets('MaterialButton shape overrides ButtonTheme shape', (WidgetTester tester) async {
783 // Regression test for https://github.com/flutter/flutter/issues/29146
784 await tester.pumpWidget(
785 Directionality(
786 textDirection: TextDirection.ltr,
787 child: MaterialButton(
788 onPressed: () { },
789 shape: const StadiumBorder(),
790 child: const Text('button'),
791 ),
792 ),
793 );
794
795 final Finder rawButtonMaterial = find.descendant(
796 of: find.byType(MaterialButton),
797 matching: find.byType(Material),
798 );
799 expect(tester.widget<Material>(rawButtonMaterial).shape, const StadiumBorder());
800 });
801
802 testWidgets('MaterialButton responds to density changes.', (WidgetTester tester) async {
803 const Key key = Key('test');
804 const Key childKey = Key('test child');
805
806 Future<void> buildTest(VisualDensity visualDensity, {bool useText = false}) async {
807 return tester.pumpWidget(
808 MaterialApp(
809 theme: ThemeData(useMaterial3: false),
810 home: Directionality(
811 textDirection: TextDirection.rtl,
812 child: Center(
813 child: MaterialButton(
814 visualDensity: visualDensity,
815 key: key,
816 onPressed: () {},
817 child: useText ? const Text('Text', key: childKey) : Container(key: childKey, width: 100, height: 100, color: const Color(0xffff0000)),
818 ),
819 ),
820 ),
821 ),
822 );
823 }
824
825 await buildTest(VisualDensity.standard);
826 final RenderBox box = tester.renderObject(find.byKey(key));
827 Rect childRect = tester.getRect(find.byKey(childKey));
828 await tester.pumpAndSettle();
829 expect(box.size, equals(const Size(132, 100)));
830 expect(childRect, equals(const Rect.fromLTRB(350, 250, 450, 350)));
831
832 await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0));
833 await tester.pumpAndSettle();
834 childRect = tester.getRect(find.byKey(childKey));
835 expect(box.size, equals(const Size(156, 124)));
836 expect(childRect, equals(const Rect.fromLTRB(350, 250, 450, 350)));
837
838 await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0));
839 await tester.pumpAndSettle();
840 childRect = tester.getRect(find.byKey(childKey));
841 expect(box.size, equals(const Size(108, 100)));
842 expect(childRect, equals(const Rect.fromLTRB(350, 250, 450, 350)));
843
844 await buildTest(VisualDensity.standard, useText: true);
845 await tester.pumpAndSettle();
846 childRect = tester.getRect(find.byKey(childKey));
847 expect(box.size, equals(const Size(88, 48)));
848 expect(childRect, equals(const Rect.fromLTRB(372.0, 293.0, 428.0, 307.0)));
849
850 await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0), useText: true);
851 await tester.pumpAndSettle();
852 childRect = tester.getRect(find.byKey(childKey));
853 expect(box.size, equals(const Size(112, 60)));
854 expect(childRect, equals(const Rect.fromLTRB(372.0, 293.0, 428.0, 307.0)));
855
856 await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0), useText: true);
857 await tester.pumpAndSettle();
858 childRect = tester.getRect(find.byKey(childKey));
859 expect(box.size, equals(const Size(76, 36)));
860 expect(childRect, equals(const Rect.fromLTRB(372.0, 293.0, 428.0, 307.0)));
861 });
862
863 testWidgets('disabledElevation is passed to RawMaterialButton', (WidgetTester tester) async {
864 const double disabledElevation = 16;
865
866 final Finder rawMaterialButtonFinder = find.descendant(
867 of: find.byType(MaterialButton),
868 matching: find.byType(RawMaterialButton),
869 );
870
871 await tester.pumpWidget(
872 const Directionality(
873 textDirection: TextDirection.ltr,
874 child: MaterialButton(
875 disabledElevation: disabledElevation,
876 onPressed: null, // disabled button
877 child: Text('button'),
878 ),
879 ),
880 );
881
882 final RawMaterialButton rawMaterialButton = tester.widget(rawMaterialButtonFinder);
883 expect(rawMaterialButton.disabledElevation, equals(disabledElevation));
884 });
885
886 testWidgets('MaterialButton.disabledElevation defaults to 0.0 when not provided', (WidgetTester tester) async {
887 final Finder rawMaterialButtonFinder = find.descendant(
888 of: find.byType(MaterialButton),
889 matching: find.byType(RawMaterialButton),
890 );
891
892 await tester.pumpWidget(
893 const Directionality(
894 textDirection: TextDirection.ltr,
895 child: MaterialButton(
896 onPressed: null, // disabled button
897 child: Text('button'),
898 ),
899 ),
900 );
901
902 final RawMaterialButton rawMaterialButton = tester.widget(rawMaterialButtonFinder);
903 expect(rawMaterialButton.disabledElevation, equals(0.0));
904 });
905}
906

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com