| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| @TestOn('!chrome') // whole file needs triage. |
| import 'dart:ui'; |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| import '../rendering/mock_canvas.dart'; |
| import '../widgets/semantics_tester.dart'; |
| |
| void main() { |
| testWidgets('Floating Action Button control test', (WidgetTester tester) async { |
| bool didPressButton = false; |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: Center( |
| child: FloatingActionButton( |
| onPressed: () { |
| didPressButton = true; |
| }, |
| child: const Icon(Icons.add), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(didPressButton, isFalse); |
| await tester.tap(find.byType(Icon)); |
| expect(didPressButton, isTrue); |
| }); |
| |
| testWidgets('Floating Action Button tooltip', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () {}, |
| tooltip: 'Add', |
| child: const Icon(Icons.add), |
| ), |
| ), |
| ), |
| ); |
| |
| await tester.tap(find.byType(Icon)); |
| expect(find.byTooltip('Add'), findsOneWidget); |
| }); |
| |
| // Regression test for: https://github.com/flutter/flutter/pull/21084 |
| testWidgets('Floating Action Button tooltip (long press button edge)', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () {}, |
| tooltip: 'Add', |
| child: const Icon(Icons.add), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Add'), findsNothing); |
| await tester.longPressAt(_rightEdgeOfFab(tester)); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Add'), findsOneWidget); |
| }); |
| |
| // Regression test for: https://github.com/flutter/flutter/pull/21084 |
| testWidgets('Floating Action Button tooltip (long press button edge - no child)', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () {}, |
| tooltip: 'Add', |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Add'), findsNothing); |
| await tester.longPressAt(_rightEdgeOfFab(tester)); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Add'), findsOneWidget); |
| }); |
| |
| testWidgets('Floating Action Button tooltip (no child)', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () {}, |
| tooltip: 'Add', |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Add'), findsNothing); |
| |
| // Test hover for tooltip. |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| addTearDown(() => gesture.removePointer()); |
| await gesture.moveTo(tester.getCenter(find.byType(FloatingActionButton))); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Add'), findsOneWidget); |
| |
| await gesture.moveTo(Offset.zero); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Add'), findsNothing); |
| |
| // Test long press for tooltip. |
| await tester.longPress(find.byType(FloatingActionButton)); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Add'), findsOneWidget); |
| }); |
| |
| testWidgets('Floating Action Button tooltip reacts when disabled', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: null, |
| tooltip: 'Add', |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Add'), findsNothing); |
| |
| // Test hover for tooltip. |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| addTearDown(() => gesture.removePointer()); |
| await tester.pumpAndSettle(); |
| await gesture.moveTo(tester.getCenter(find.byType(FloatingActionButton))); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Add'), findsOneWidget); |
| |
| await gesture.moveTo(Offset.zero); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Add'), findsNothing); |
| |
| // Test long press for tooltip. |
| await tester.longPress(find.byType(FloatingActionButton)); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Add'), findsOneWidget); |
| }); |
| |
| testWidgets('Floating Action Button elevation when highlighted - effect', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () { }, |
| ), |
| ), |
| ), |
| ); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| final TestGesture gesture = await tester.press(find.byType(PhysicalShape)); |
| await tester.pump(); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| await tester.pump(const Duration(seconds: 1)); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () { }, |
| highlightElevation: 20.0, |
| ), |
| ), |
| ), |
| ); |
| await tester.pump(); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0); |
| await tester.pump(const Duration(seconds: 1)); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 20.0); |
| await gesture.up(); |
| await tester.pump(); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 20.0); |
| await tester.pump(const Duration(seconds: 1)); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| }); |
| |
| testWidgets('Floating Action Button elevation when disabled - defaults', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: null, |
| ), |
| ), |
| ), |
| ); |
| |
| // Disabled elevation defaults to regular default elevation. |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| }); |
| |
| testWidgets('Floating Action Button elevation when disabled - override', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: null, |
| disabledElevation: 0, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 0.0); |
| }); |
| |
| testWidgets('Floating Action Button elevation when disabled - effect', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: null, |
| ), |
| ), |
| ), |
| ); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: null, |
| disabledElevation: 3.0, |
| ), |
| ), |
| ), |
| ); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| await tester.pump(const Duration(seconds: 1)); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 3.0); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () { }, |
| disabledElevation: 3.0, |
| ), |
| ), |
| ), |
| ); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 3.0); |
| await tester.pump(const Duration(seconds: 1)); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| }); |
| |
| testWidgets('Floating Action Button elevation when disabled while highlighted - effect', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () { }, |
| ), |
| ), |
| ), |
| ); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| await tester.press(find.byType(PhysicalShape)); |
| await tester.pump(); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| await tester.pump(const Duration(seconds: 1)); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0); |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: null, |
| ), |
| ), |
| ), |
| ); |
| await tester.pump(); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0); |
| await tester.pump(const Duration(seconds: 1)); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () { }, |
| ), |
| ), |
| ), |
| ); |
| await tester.pump(); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| await tester.pump(const Duration(seconds: 1)); |
| expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0); |
| }); |
| |
| testWidgets('FlatActionButton mini size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async { |
| final Key key1 = UniqueKey(); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Theme( |
| data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.padded), |
| child: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| key: key1, |
| mini: true, |
| onPressed: null, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byKey(key1)), const Size(48.0, 48.0)); |
| |
| final Key key2 = UniqueKey(); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Theme( |
| data: ThemeData(materialTapTargetSize: MaterialTapTargetSize.shrinkWrap), |
| child: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| key: key2, |
| mini: true, |
| onPressed: null, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byKey(key2)), const Size(40.0, 40.0)); |
| }); |
| |
| testWidgets('FloatingActionButton.isExtended', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton(onPressed: null), |
| ), |
| ), |
| ); |
| |
| final Finder fabFinder = find.byType(FloatingActionButton); |
| |
| FloatingActionButton getFabWidget() { |
| return tester.widget<FloatingActionButton>(fabFinder); |
| } |
| |
| final Finder materialButtonFinder = find.byType(RawMaterialButton); |
| |
| RawMaterialButton getRawMaterialButtonWidget() { |
| return tester.widget<RawMaterialButton>(materialButtonFinder); |
| } |
| |
| expect(getFabWidget().isExtended, false); |
| expect(getRawMaterialButtonWidget().shape, const CircleBorder()); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton.extended( |
| label: const SizedBox( |
| width: 100.0, |
| child: Text('label'), |
| ), |
| icon: const Icon(Icons.android), |
| onPressed: null, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(getFabWidget().isExtended, true); |
| expect(getRawMaterialButtonWidget().shape, const StadiumBorder()); |
| expect(find.text('label'), findsOneWidget); |
| expect(find.byType(Icon), findsOneWidget); |
| |
| // Verify that the widget's height is 48 and that its internal |
| /// horizontal layout is: 16 icon 8 label 20 |
| expect(tester.getSize(fabFinder).height, 48.0); |
| |
| final double fabLeft = tester.getTopLeft(fabFinder).dx; |
| final double fabRight = tester.getTopRight(fabFinder).dx; |
| final double iconLeft = tester.getTopLeft(find.byType(Icon)).dx; |
| final double iconRight = tester.getTopRight(find.byType(Icon)).dx; |
| final double labelLeft = tester.getTopLeft(find.text('label')).dx; |
| final double labelRight = tester.getTopRight(find.text('label')).dx; |
| expect(iconLeft - fabLeft, 16.0); |
| expect(labelLeft - iconRight, 8.0); |
| expect(fabRight - labelRight, 20.0); |
| |
| // The overall width of the button is: |
| // 168 = 16 + 24(icon) + 8 + 100(label) + 20 |
| expect(tester.getSize(find.byType(Icon)).width, 24.0); |
| expect(tester.getSize(find.text('label')).width, 100.0); |
| expect(tester.getSize(fabFinder).width, 168); |
| }); |
| |
| testWidgets('FloatingActionButton.isExtended (without icon)', (WidgetTester tester) async { |
| final Finder fabFinder = find.byType(FloatingActionButton); |
| |
| FloatingActionButton getFabWidget() { |
| return tester.widget<FloatingActionButton>(fabFinder); |
| } |
| |
| final Finder materialButtonFinder = find.byType(RawMaterialButton); |
| |
| RawMaterialButton getRawMaterialButtonWidget() { |
| return tester.widget<RawMaterialButton>(materialButtonFinder); |
| } |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton.extended( |
| label: const SizedBox( |
| width: 100.0, |
| child: Text('label'), |
| ), |
| onPressed: null, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(getFabWidget().isExtended, true); |
| expect(getRawMaterialButtonWidget().shape, const StadiumBorder()); |
| expect(find.text('label'), findsOneWidget); |
| expect(find.byType(Icon), findsNothing); |
| |
| // Verify that the widget's height is 48 and that its internal |
| /// horizontal layout is: 20 label 20 |
| expect(tester.getSize(fabFinder).height, 48.0); |
| |
| final double fabLeft = tester.getTopLeft(fabFinder).dx; |
| final double fabRight = tester.getTopRight(fabFinder).dx; |
| final double labelLeft = tester.getTopLeft(find.text('label')).dx; |
| final double labelRight = tester.getTopRight(find.text('label')).dx; |
| expect(labelLeft - fabLeft, 20.0); |
| expect(fabRight - labelRight, 20.0); |
| |
| // The overall width of the button is: |
| // 140 = 20 + 100(label) + 20 |
| expect(tester.getSize(find.text('label')).width, 100.0); |
| expect(tester.getSize(fabFinder).width, 140); |
| }); |
| |
| testWidgets('Floating Action Button heroTag', (WidgetTester tester) async { |
| late BuildContext theContext; |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Builder( |
| builder: (BuildContext context) { |
| theContext = context; |
| return const FloatingActionButton(heroTag: 1, onPressed: null); |
| }, |
| ), |
| floatingActionButton: const FloatingActionButton(heroTag: 2, onPressed: null), |
| ), |
| ), |
| ); |
| Navigator.push(theContext, PageRouteBuilder<void>( |
| pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { |
| return const Placeholder(); |
| }, |
| )); |
| await tester.pump(); // this would fail if heroTag was the same on both FloatingActionButtons (see below). |
| }); |
| |
| testWidgets('Floating Action Button heroTag - with duplicate', (WidgetTester tester) async { |
| late BuildContext theContext; |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Builder( |
| builder: (BuildContext context) { |
| theContext = context; |
| return const FloatingActionButton(onPressed: null); |
| }, |
| ), |
| floatingActionButton: const FloatingActionButton(onPressed: null), |
| ), |
| ), |
| ); |
| Navigator.push(theContext, PageRouteBuilder<void>( |
| pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { |
| return const Placeholder(); |
| }, |
| )); |
| await tester.pump(); |
| expect(tester.takeException().toString(), contains('FloatingActionButton')); |
| }); |
| |
| testWidgets('Floating Action Button heroTag - with duplicate', (WidgetTester tester) async { |
| late BuildContext theContext; |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Builder( |
| builder: (BuildContext context) { |
| theContext = context; |
| return const FloatingActionButton(heroTag: 'xyzzy', onPressed: null); |
| }, |
| ), |
| floatingActionButton: const FloatingActionButton(heroTag: 'xyzzy', onPressed: null), |
| ), |
| ), |
| ); |
| Navigator.push(theContext, PageRouteBuilder<void>( |
| pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { |
| return const Placeholder(); |
| }, |
| )); |
| await tester.pump(); |
| expect(tester.takeException().toString(), contains('xyzzy')); |
| }); |
| |
| testWidgets('Floating Action Button semantics (enabled)', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: Center( |
| child: FloatingActionButton( |
| onPressed: () { }, |
| child: const Icon(Icons.add, semanticLabel: 'Add'), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(semantics, hasSemantics(TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics.rootChild( |
| label: 'Add', |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasEnabledState, |
| SemanticsFlag.isButton, |
| SemanticsFlag.isEnabled, |
| SemanticsFlag.isFocusable, |
| ], |
| actions: <SemanticsAction>[ |
| SemanticsAction.tap, |
| ], |
| ), |
| ], |
| ), ignoreTransform: true, ignoreId: true, ignoreRect: true)); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('Floating Action Button semantics (disabled)', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| await tester.pumpWidget( |
| const Directionality( |
| textDirection: TextDirection.ltr, |
| child: Center( |
| child: FloatingActionButton( |
| onPressed: null, |
| child: Icon(Icons.add, semanticLabel: 'Add'), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(semantics, hasSemantics(TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics.rootChild( |
| label: 'Add', |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.isButton, |
| SemanticsFlag.hasEnabledState, |
| ], |
| ), |
| ], |
| ), ignoreTransform: true, ignoreId: true, ignoreRect: true)); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('Tooltip is used as semantics label', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: FloatingActionButton( |
| onPressed: () { }, |
| tooltip: 'Add Photo', |
| child: const Icon(Icons.add_a_photo), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(semantics, hasSemantics(TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics.rootChild( |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.scopesRoute, |
| ], |
| children: <TestSemantics>[ |
| TestSemantics( |
| label: 'Add Photo', |
| actions: <SemanticsAction>[ |
| SemanticsAction.tap, |
| ], |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasEnabledState, |
| SemanticsFlag.isButton, |
| SemanticsFlag.isEnabled, |
| SemanticsFlag.isFocusable, |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), ignoreTransform: true, ignoreId: true, ignoreRect: true)); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('extended FAB hero transitions succeed', (WidgetTester tester) async { |
| // Regression test for https://github.com/flutter/flutter/issues/18782 |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| floatingActionButton: Builder( |
| builder: (BuildContext context) { // define context of Navigator.push() |
| return FloatingActionButton.extended( |
| icon: const Icon(Icons.add), |
| label: const Text('A long FAB label'), |
| onPressed: () { |
| Navigator.push(context, MaterialPageRoute<void>( |
| builder: (BuildContext context) { |
| return Scaffold( |
| floatingActionButton: FloatingActionButton.extended( |
| icon: const Icon(Icons.add), |
| label: const Text('X'), |
| onPressed: () { }, |
| ), |
| body: Center( |
| child: ElevatedButton( |
| child: const Text('POP'), |
| onPressed: () { |
| Navigator.pop(context); |
| }, |
| ), |
| ), |
| ); |
| }, |
| )); |
| }, |
| ); |
| }, |
| ), |
| body: const Center( |
| child: Text('Hello World'), |
| ), |
| ), |
| ), |
| ); |
| |
| final Finder longFAB = find.text('A long FAB label'); |
| final Finder shortFAB = find.text('X'); |
| final Finder helloWorld = find.text('Hello World'); |
| |
| expect(longFAB, findsOneWidget); |
| expect(shortFAB, findsNothing); |
| expect(helloWorld, findsOneWidget); |
| |
| await tester.tap(longFAB); |
| await tester.pumpAndSettle(); |
| |
| expect(shortFAB, findsOneWidget); |
| expect(longFAB, findsNothing); |
| |
| // Trigger a hero transition from shortFAB to longFAB. |
| await tester.tap(find.text('POP')); |
| await tester.pumpAndSettle(); |
| |
| expect(longFAB, findsOneWidget); |
| expect(shortFAB, findsNothing); |
| expect(helloWorld, findsOneWidget); |
| }); |
| |
| // This test prevents https://github.com/flutter/flutter/issues/20483 |
| testWidgets('Floating Action Button clips ink splash and highlight', (WidgetTester tester) async { |
| final GlobalKey key = GlobalKey(); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Center( |
| child: RepaintBoundary( |
| key: key, |
| child: FloatingActionButton( |
| onPressed: () { }, |
| child: const Icon(Icons.add), |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| await tester.press(find.byKey(key)); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 1000)); |
| await expectLater( |
| find.byKey(key), |
| matchesGoldenFile('floating_action_button_test.clip.png'), |
| ); |
| }); |
| |
| testWidgets('Floating Action Button changes mouse cursor when hovered', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Align( |
| alignment: Alignment.topLeft, |
| child: FloatingActionButton.extended( |
| onPressed: () { }, |
| mouseCursor: SystemMouseCursors.text, |
| label: const Text('label'), |
| icon: const Icon(Icons.android), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1); |
| await gesture.addPointer(location: tester.getCenter(find.byType(FloatingActionButton))); |
| addTearDown(gesture.removePointer); |
| |
| await tester.pump(); |
| |
| expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Align( |
| alignment: Alignment.topLeft, |
| child: FloatingActionButton( |
| onPressed: () { }, |
| mouseCursor: SystemMouseCursors.text, |
| child: const Icon(Icons.add), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| await gesture.moveTo(tester.getCenter(find.byType(FloatingActionButton))); |
| expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text); |
| |
| // Test default cursor |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Align( |
| alignment: Alignment.topLeft, |
| child: FloatingActionButton( |
| onPressed: () { }, |
| child: const Icon(Icons.add), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click); |
| |
| // Test default cursor when disabled |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold( |
| body: Align( |
| alignment: Alignment.topLeft, |
| child: FloatingActionButton( |
| onPressed: null, |
| child: Icon(Icons.add), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic); |
| }); |
| |
| testWidgets('Floating Action Button has no clip by default', (WidgetTester tester) async { |
| final FocusNode focusNode = FocusNode(); |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: Material( |
| child: FloatingActionButton( |
| focusNode: focusNode, |
| onPressed: () { /* to make sure the button is enabled */ }, |
| ), |
| ), |
| ), |
| ); |
| |
| focusNode.unfocus(); |
| await tester.pump(); |
| |
| expect( |
| tester.renderObject(find.byType(FloatingActionButton)), |
| paintsExactlyCountTimes(#clipPath, 0), |
| ); |
| }); |
| |
| testWidgets('Can find FloatingActionButton semantics', (WidgetTester tester) async { |
| await tester.pumpWidget(MaterialApp( |
| home: FloatingActionButton(onPressed: () {}), |
| )); |
| |
| expect( |
| tester.getSemantics(find.byType(FloatingActionButton)), |
| matchesSemantics( |
| hasTapAction: true, |
| hasEnabledState: true, |
| isButton: true, |
| isEnabled: true, |
| isFocusable: true, |
| ), |
| ); |
| }, semanticsEnabled: true); |
| |
| testWidgets('Foreground color applies to icon on fab', (WidgetTester tester) async { |
| const Color foregroundColor = Color(0xcafefeed); |
| |
| await tester.pumpWidget(MaterialApp( |
| home: FloatingActionButton( |
| onPressed: () {}, |
| foregroundColor: foregroundColor, |
| child: const Icon(Icons.access_alarm), |
| ), |
| )); |
| |
| final RichText iconRichText = tester.widget<RichText>( |
| find.descendant(of: find.byIcon(Icons.access_alarm), matching: find.byType(RichText)), |
| ); |
| expect(iconRichText.text.style!.color, foregroundColor); |
| }); |
| |
| testWidgets('FloatingActionButton uses custom splash color', (WidgetTester tester) async { |
| const Color splashColor = Color(0xcafefeed); |
| |
| await tester.pumpWidget(MaterialApp( |
| home: FloatingActionButton( |
| onPressed: () {}, |
| splashColor: splashColor, |
| child: const Icon(Icons.access_alarm), |
| ), |
| )); |
| |
| await tester.press(find.byType(FloatingActionButton)); |
| await tester.pumpAndSettle(); |
| |
| expect( |
| find.byType(FloatingActionButton), |
| paints..circle(color: splashColor), |
| ); |
| }); |
| } |
| |
| Offset _rightEdgeOfFab(WidgetTester tester) { |
| final Finder fab = find.byType(FloatingActionButton); |
| return tester.getRect(fab).centerRight - const Offset(1.0, 0.0); |
| } |