1472 lines
47 KiB
Dart
1472 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 '../rendering/mock_canvas.dart';
|
|
import '../widgets/semantics_tester.dart';
|
|
import 'feedback_tester.dart';
|
|
|
|
void main() {
|
|
|
|
final ThemeData material3Theme = ThemeData.light().copyWith(useMaterial3: true);
|
|
final ThemeData material2Theme = ThemeData.light().copyWith(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);
|
|
});
|
|
|
|
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,
|
|
],
|
|
),
|
|
],
|
|
), 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,
|
|
],
|
|
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),
|
|
);
|
|
});
|
|
|
|
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,
|
|
),
|
|
);
|
|
});
|
|
|
|
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),
|
|
);
|
|
});
|
|
|
|
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(
|
|
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', () {
|
|
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
|
|
// is turned on by default, these tests can be removed.
|
|
|
|
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);
|
|
});
|
|
|
|
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);
|
|
}
|