
## Description This attempts to re-land #142942 after being reverted in https://github.com/flutter/flutter/pull/149741 because it broke the iOS [platform view UI integration test](https://github.com/flutter/flutter/blob/master/dev/integration_tests/ios_platform_view_tests/ios/PlatformViewUITests/PlatformViewUITests.m?rgh-link-date=2024-06-06T19%3A47%3A27Z). The changes here from the original are that in the Focus widget we no longer set the `onFocus` for the `Semantics` if the platform is iOS. It was not intended to do anything on iOS anyhow. Also, I updated the matchers to not actually do anything yet with the SemanticsAction.focus matching, so that this can be landed without breaking customer tests, and once they have been updated to correctly look for the focus action, we can land a PR that will turn it on. ## Related Issues - https://github.com/flutter/flutter/issues/149838 - https://github.com/flutter/flutter/issues/83809 - https://github.com/flutter/flutter/issues/149842 ## Tests - Updated framework tests to look for the appropriate things using the matchers, even though it doesn't actually test for them yet.
1481 lines
47 KiB
Dart
1481 lines
47 KiB
Dart
// 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.
|
|
|
|
// This file is run as part of a reduced test set in CI on Mac and Windows
|
|
// machines.
|
|
@Tags(<String>['reduced-test-set'])
|
|
@TestOn('!chrome')
|
|
library;
|
|
|
|
import 'dart:ui';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import '../widgets/feedback_tester.dart';
|
|
import '../widgets/semantics_tester.dart';
|
|
|
|
void main() {
|
|
final ThemeData material3Theme = ThemeData(useMaterial3: true);
|
|
final ThemeData material2Theme = ThemeData(useMaterial3: false);
|
|
|
|
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(
|
|
theme: material3Theme,
|
|
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, 6.0);
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
floatingActionButton: FloatingActionButton(
|
|
onPressed: () { },
|
|
highlightElevation: 20.0,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
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, 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(
|
|
theme: material3Theme,
|
|
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, 6.0);
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: material3Theme,
|
|
home: const Scaffold(
|
|
floatingActionButton: FloatingActionButton(
|
|
onPressed: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
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);
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: material3Theme,
|
|
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('Floating Action Button states elevation', (WidgetTester tester) async {
|
|
final FocusNode focusNode = FocusNode();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: material3Theme,
|
|
home: Scaffold(
|
|
body: FloatingActionButton.extended(
|
|
label: const Text('tooltip'),
|
|
onPressed: () {},
|
|
focusNode: focusNode,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder fabFinder = find.byType(PhysicalShape);
|
|
PhysicalShape getFABWidget(Finder finder) => tester.widget<PhysicalShape>(finder);
|
|
|
|
// Default, not disabled.
|
|
expect(getFABWidget(fabFinder).elevation, 6);
|
|
|
|
// Focused.
|
|
focusNode.requestFocus();
|
|
await tester.pumpAndSettle();
|
|
expect(getFABWidget(fabFinder).elevation, 6);
|
|
|
|
// Hovered.
|
|
final Offset center = tester.getCenter(fabFinder);
|
|
final TestGesture gesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
await gesture.addPointer();
|
|
await gesture.moveTo(center);
|
|
await tester.pumpAndSettle();
|
|
expect(getFABWidget(fabFinder).elevation, 8);
|
|
|
|
// Highlighted (pressed).
|
|
await gesture.down(center);
|
|
await tester.pump(); // Start the splash and highlight animations.
|
|
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
|
expect(getFABWidget(fabFinder).elevation, 6);
|
|
|
|
focusNode.dispose();
|
|
});
|
|
|
|
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(
|
|
MaterialApp(
|
|
theme: material3Theme,
|
|
home: const 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 RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)))
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Scaffold(
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
label: SizedBox(
|
|
width: 100.0,
|
|
child: Text('label'),
|
|
),
|
|
icon: Icon(Icons.android),
|
|
onPressed: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(getFabWidget().isExtended, true);
|
|
expect(
|
|
getRawMaterialButtonWidget().shape,
|
|
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)))
|
|
);
|
|
expect(find.text('label'), findsOneWidget);
|
|
expect(find.byType(Icon), findsOneWidget);
|
|
|
|
// Verify that the widget's height is 56 and that its internal
|
|
/// horizontal layout is: 16 icon 8 label 20
|
|
expect(tester.getSize(fabFinder).height, 56.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(
|
|
theme: material3Theme,
|
|
home: const Scaffold(
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
label: SizedBox(
|
|
width: 100.0,
|
|
child: Text('label'),
|
|
),
|
|
onPressed: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(getFabWidget().isExtended, true);
|
|
expect(
|
|
getRawMaterialButtonWidget().shape,
|
|
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)))
|
|
);
|
|
expect(find.text('label'), findsOneWidget);
|
|
expect(find.byType(Icon), findsNothing);
|
|
|
|
// Verify that the widget's height is 56 and that its internal
|
|
/// horizontal layout is: 20 label 20
|
|
expect(tester.getSize(fabFinder).height, 56.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,
|
|
SemanticsAction.focus,
|
|
],
|
|
),
|
|
],
|
|
), 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 tooltip', (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(
|
|
tooltip: 'Add Photo',
|
|
actions: <SemanticsAction>[
|
|
SemanticsAction.tap,
|
|
SemanticsAction.focus,
|
|
],
|
|
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(
|
|
theme: material3Theme,
|
|
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)));
|
|
|
|
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: 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),
|
|
);
|
|
|
|
focusNode.dispose();
|
|
});
|
|
|
|
testWidgets('Can find FloatingActionButton semantics', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: FloatingActionButton(onPressed: () {}),
|
|
));
|
|
|
|
expect(
|
|
tester.getSemantics(find.byType(FloatingActionButton)),
|
|
matchesSemantics(
|
|
hasTapAction: true,
|
|
hasFocusAction: true,
|
|
hasEnabledState: true,
|
|
isButton: true,
|
|
isEnabled: true,
|
|
isFocusable: 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(
|
|
theme: material2Theme,
|
|
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),
|
|
);
|
|
});
|
|
|
|
testWidgets('extended FAB does not show label when isExtended is false', (WidgetTester tester) async {
|
|
const Key iconKey = Key('icon');
|
|
const Key labelKey = Key('label');
|
|
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: FloatingActionButton.extended(
|
|
isExtended: false,
|
|
label: const Text('', key: labelKey),
|
|
icon: const Icon(Icons.add, key: iconKey),
|
|
onPressed: () {},
|
|
),
|
|
),
|
|
);
|
|
|
|
// Verify that Icon is present and label is not.
|
|
expect(find.byKey(iconKey), findsOneWidget);
|
|
expect(find.byKey(labelKey), findsNothing);
|
|
});
|
|
|
|
testWidgets('FloatingActionButton.small configures correct size', (WidgetTester tester) async {
|
|
final Key key = UniqueKey();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
floatingActionButton: FloatingActionButton.small(
|
|
key: key,
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
onPressed: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(tester.getSize(find.byKey(key)), const Size(40.0, 40.0));
|
|
});
|
|
|
|
testWidgets('FloatingActionButton.large configures correct size', (WidgetTester tester) async {
|
|
final Key key = UniqueKey();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
floatingActionButton: FloatingActionButton.large(
|
|
key: key,
|
|
onPressed: null,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(tester.getSize(find.byKey(key)), const Size(96.0, 96.0));
|
|
});
|
|
|
|
testWidgets('FloatingActionButton.extended can customize spacing', (WidgetTester tester) async {
|
|
const Key iconKey = Key('icon');
|
|
const Key labelKey = Key('label');
|
|
const double spacing = 33.0;
|
|
const EdgeInsetsDirectional padding = EdgeInsetsDirectional.only(start: 5.0, end: 6.0);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
label: const Text('', key: labelKey),
|
|
icon: const Icon(Icons.add, key: iconKey),
|
|
extendedIconLabelSpacing: spacing,
|
|
extendedPadding: padding,
|
|
onPressed: () {},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, spacing);
|
|
expect(tester.getTopLeft(find.byKey(iconKey)).dx - tester.getTopLeft(find.byType(FloatingActionButton)).dx, padding.start);
|
|
expect(tester.getTopRight(find.byType(FloatingActionButton)).dx - tester.getTopRight(find.byKey(labelKey)).dx, padding.end);
|
|
});
|
|
|
|
testWidgets('FloatingActionButton.extended can customize text style', (WidgetTester tester) async {
|
|
const Key labelKey = Key('label');
|
|
const TextStyle style = TextStyle(letterSpacing: 2.0);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: material2Theme,
|
|
home: Scaffold(
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
label: const Text('', key: labelKey),
|
|
icon: const Icon(Icons.add),
|
|
extendedTextStyle: style,
|
|
onPressed: () {},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RawMaterialButton rawMaterialButton = tester.widget<RawMaterialButton>(
|
|
find.descendant(
|
|
of: find.byType(FloatingActionButton),
|
|
matching: find.byType(RawMaterialButton),
|
|
),
|
|
);
|
|
// The color comes from the default color scheme's onSecondary value.
|
|
expect(rawMaterialButton.textStyle, style.copyWith(color: const Color(0xffffffff)));
|
|
});
|
|
|
|
group('Material 2', () {
|
|
// These tests are only relevant for Material 2. Once Material 2
|
|
// support is deprecated and the APIs are removed, these tests
|
|
// can be deleted.
|
|
|
|
testWidgets('Floating Action Button elevation when highlighted - effect', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: material2Theme,
|
|
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(
|
|
theme: material2Theme,
|
|
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 while highlighted - effect', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: material2Theme,
|
|
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(
|
|
MaterialApp(
|
|
theme: material2Theme,
|
|
home: const 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(
|
|
theme: material2Theme,
|
|
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('Floating Action Button states elevation', (WidgetTester tester) async {
|
|
final FocusNode focusNode = FocusNode();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: material2Theme,
|
|
home: Scaffold(
|
|
body: FloatingActionButton.extended(
|
|
label: const Text('tooltip'),
|
|
onPressed: () {},
|
|
focusNode: focusNode,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder fabFinder = find.byType(PhysicalShape);
|
|
PhysicalShape getFABWidget(Finder finder) => tester.widget<PhysicalShape>(finder);
|
|
|
|
// Default, not disabled.
|
|
expect(getFABWidget(fabFinder).elevation, 6);
|
|
|
|
// Focused.
|
|
focusNode.requestFocus();
|
|
await tester.pumpAndSettle();
|
|
expect(getFABWidget(fabFinder).elevation, 6);
|
|
|
|
// Hovered.
|
|
final Offset center = tester.getCenter(fabFinder);
|
|
final TestGesture gesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
await gesture.addPointer();
|
|
await gesture.moveTo(center);
|
|
await tester.pumpAndSettle();
|
|
expect(getFABWidget(fabFinder).elevation, 8);
|
|
|
|
// Highlighted (pressed).
|
|
await gesture.down(center);
|
|
await tester.pump(); // Start the splash and highlight animations.
|
|
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
|
expect(getFABWidget(fabFinder).elevation, 12);
|
|
|
|
focusNode.dispose();
|
|
});
|
|
|
|
testWidgets('FloatingActionButton.isExtended', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: material2Theme,
|
|
home: const 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(
|
|
theme: material2Theme,
|
|
home: const Scaffold(
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
label: SizedBox(
|
|
width: 100.0,
|
|
child: Text('label'),
|
|
),
|
|
icon: 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(
|
|
theme: material2Theme,
|
|
home: const Scaffold(
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
label: 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);
|
|
});
|
|
|
|
|
|
// 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(
|
|
theme: material2Theme,
|
|
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_m2.clip.png'),
|
|
);
|
|
});
|
|
});
|
|
|
|
group('feedback', () {
|
|
late FeedbackTester feedback;
|
|
|
|
setUp(() {
|
|
feedback = FeedbackTester();
|
|
});
|
|
|
|
tearDown(() {
|
|
feedback.dispose();
|
|
});
|
|
|
|
testWidgets('FloatingActionButton with enabled feedback', (WidgetTester tester) async {
|
|
const bool enableFeedback = true;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: FloatingActionButton(
|
|
onPressed: () {},
|
|
enableFeedback: enableFeedback,
|
|
child: const Icon(Icons.access_alarm),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.byType(RawMaterialButton));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(feedback.clickSoundCount, 1);
|
|
expect(feedback.hapticCount, 0);
|
|
});
|
|
|
|
testWidgets('FloatingActionButton with disabled feedback', (WidgetTester tester) async {
|
|
const bool enableFeedback = false;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: FloatingActionButton(
|
|
onPressed: () {},
|
|
enableFeedback: enableFeedback,
|
|
child: const Icon(Icons.access_alarm),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.byType(RawMaterialButton));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(feedback.clickSoundCount, 0);
|
|
expect(feedback.hapticCount, 0);
|
|
});
|
|
|
|
testWidgets('FloatingActionButton with enabled feedback by default', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: FloatingActionButton(
|
|
onPressed: () {},
|
|
child: const Icon(Icons.access_alarm),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.byType(RawMaterialButton));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(feedback.clickSoundCount, 1);
|
|
expect(feedback.hapticCount, 0);
|
|
});
|
|
|
|
testWidgets('FloatingActionButton with disabled feedback using FloatingActionButtonTheme', (WidgetTester tester) async {
|
|
const bool enableFeedbackTheme = false;
|
|
final ThemeData theme = ThemeData(
|
|
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
|
enableFeedback: enableFeedbackTheme,
|
|
),
|
|
);
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Theme(
|
|
data: theme,
|
|
child: FloatingActionButton(
|
|
onPressed: () {},
|
|
child: const Icon(Icons.access_alarm),
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.byType(RawMaterialButton));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(feedback.clickSoundCount, 0);
|
|
expect(feedback.hapticCount, 0);
|
|
});
|
|
|
|
testWidgets('FloatingActionButton.enableFeedback is overridden by FloatingActionButtonThemeData.enableFeedback', (WidgetTester tester) async {
|
|
const bool enableFeedbackTheme = false;
|
|
const bool enableFeedback = true;
|
|
final ThemeData theme = ThemeData(
|
|
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
|
enableFeedback: enableFeedbackTheme,
|
|
),
|
|
);
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Theme(
|
|
data: theme,
|
|
child: FloatingActionButton(
|
|
enableFeedback: enableFeedback,
|
|
onPressed: () {},
|
|
child: const Icon(Icons.access_alarm),
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.byType(RawMaterialButton));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(feedback.clickSoundCount, 1);
|
|
expect(feedback.hapticCount, 0);
|
|
});
|
|
});
|
|
}
|
|
|
|
Offset _rightEdgeOfFab(WidgetTester tester) {
|
|
final Finder fab = find.byType(FloatingActionButton);
|
|
return tester.getRect(fab).centerRight - const Offset(1.0, 0.0);
|
|
}
|