mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
406 lines
14 KiB
Dart
406 lines
14 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.
|
|
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
test('ButtonThemeData defaults', () {
|
|
const ButtonThemeData theme = ButtonThemeData();
|
|
expect(theme.textTheme, ButtonTextTheme.normal);
|
|
expect(theme.constraints, const BoxConstraints(minWidth: 88.0, minHeight: 36.0));
|
|
expect(theme.padding, const EdgeInsets.symmetric(horizontal: 16.0));
|
|
expect(theme.shape, const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(2.0)),
|
|
));
|
|
expect(theme.alignedDropdown, false);
|
|
expect(theme.layoutBehavior, ButtonBarLayoutBehavior.padded);
|
|
});
|
|
|
|
test('ButtonThemeData default overrides', () {
|
|
const ButtonThemeData theme = ButtonThemeData(
|
|
textTheme: ButtonTextTheme.primary,
|
|
minWidth: 100.0,
|
|
height: 200.0,
|
|
padding: EdgeInsets.zero,
|
|
shape: RoundedRectangleBorder(),
|
|
alignedDropdown: true,
|
|
);
|
|
expect(theme.textTheme, ButtonTextTheme.primary);
|
|
expect(theme.constraints, const BoxConstraints(minWidth: 100.0, minHeight: 200.0));
|
|
expect(theme.padding, EdgeInsets.zero);
|
|
expect(theme.shape, const RoundedRectangleBorder());
|
|
expect(theme.alignedDropdown, true);
|
|
});
|
|
|
|
testWidgets('ButtonTheme defaults', (WidgetTester tester) async {
|
|
late ButtonTextTheme textTheme;
|
|
late ButtonBarLayoutBehavior layoutBehavior;
|
|
late BoxConstraints constraints;
|
|
late EdgeInsets padding;
|
|
late ShapeBorder shape;
|
|
late bool alignedDropdown;
|
|
late ColorScheme colorScheme;
|
|
|
|
await tester.pumpWidget(
|
|
ButtonTheme(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
final ButtonThemeData theme = ButtonTheme.of(context);
|
|
textTheme = theme.textTheme;
|
|
constraints = theme.constraints;
|
|
padding = theme.padding as EdgeInsets;
|
|
shape = theme.shape;
|
|
layoutBehavior = theme.layoutBehavior;
|
|
colorScheme = theme.colorScheme!;
|
|
alignedDropdown = theme.alignedDropdown;
|
|
return Container(
|
|
alignment: Alignment.topLeft,
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: FlatButton(
|
|
onPressed: () { },
|
|
child: const Text('b'), // intrinsic width < minimum width
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(textTheme, ButtonTextTheme.normal);
|
|
expect(layoutBehavior, ButtonBarLayoutBehavior.padded);
|
|
expect(constraints, const BoxConstraints(minWidth: 88.0, minHeight: 36.0));
|
|
expect(padding, const EdgeInsets.symmetric(horizontal: 16.0));
|
|
expect(shape, const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(2.0)),
|
|
));
|
|
expect(alignedDropdown, false);
|
|
expect(colorScheme, ThemeData.light().colorScheme);
|
|
expect(tester.widget<Material>(find.byType(Material)).shape, shape);
|
|
expect(tester.getSize(find.byType(Material)), const Size(88.0, 36.0));
|
|
});
|
|
|
|
test('ButtonThemeData.copyWith', () {
|
|
ButtonThemeData theme = const ButtonThemeData().copyWith();
|
|
expect(theme.textTheme, ButtonTextTheme.normal);
|
|
expect(theme.layoutBehavior, ButtonBarLayoutBehavior.padded);
|
|
expect(theme.constraints, const BoxConstraints(minWidth: 88.0, minHeight: 36.0));
|
|
expect(theme.padding, const EdgeInsets.symmetric(horizontal: 16.0));
|
|
expect(theme.shape, const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(2.0)),
|
|
));
|
|
expect(theme.alignedDropdown, false);
|
|
expect(theme.colorScheme, null);
|
|
|
|
theme = const ButtonThemeData().copyWith(
|
|
textTheme: ButtonTextTheme.primary,
|
|
layoutBehavior: ButtonBarLayoutBehavior.constrained,
|
|
minWidth: 100.0,
|
|
height: 200.0,
|
|
padding: EdgeInsets.zero,
|
|
shape: const StadiumBorder(),
|
|
alignedDropdown: true,
|
|
colorScheme: const ColorScheme.dark(),
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
);
|
|
expect(theme.textTheme, ButtonTextTheme.primary);
|
|
expect(theme.layoutBehavior, ButtonBarLayoutBehavior.constrained);
|
|
expect(theme.constraints, const BoxConstraints(minWidth: 100.0, minHeight: 200.0));
|
|
expect(theme.padding, EdgeInsets.zero);
|
|
expect(theme.shape, const StadiumBorder());
|
|
expect(theme.alignedDropdown, true);
|
|
expect(theme.colorScheme, const ColorScheme.dark());
|
|
});
|
|
|
|
testWidgets('Theme buttonTheme defaults', (WidgetTester tester) async {
|
|
final ThemeData lightTheme = ThemeData.light();
|
|
late ButtonTextTheme textTheme;
|
|
late BoxConstraints constraints;
|
|
late EdgeInsets padding;
|
|
late ShapeBorder shape;
|
|
|
|
const Color disabledColor = Color(0xFF00FF00);
|
|
await tester.pumpWidget(
|
|
Theme(
|
|
data: lightTheme.copyWith(
|
|
disabledColor: disabledColor, // disabled RaisedButton fill color
|
|
buttonTheme: const ButtonThemeData(disabledColor: disabledColor),
|
|
textTheme: lightTheme.textTheme.copyWith(
|
|
button: lightTheme.textTheme.button!.copyWith(
|
|
// The button's height will match because there's no
|
|
// vertical padding by default
|
|
fontSize: 48.0,
|
|
),
|
|
),
|
|
),
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
final ButtonThemeData theme = ButtonTheme.of(context);
|
|
textTheme = theme.textTheme;
|
|
constraints = theme.constraints;
|
|
padding = theme.padding as EdgeInsets;
|
|
shape = theme.shape;
|
|
return Container(
|
|
alignment: Alignment.topLeft,
|
|
child: const Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: RaisedButton(
|
|
onPressed: null,
|
|
child: Text('b'), // intrinsic width < minimum width
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(textTheme, ButtonTextTheme.normal);
|
|
expect(constraints, const BoxConstraints(minWidth: 88.0, minHeight: 36.0));
|
|
expect(padding, const EdgeInsets.symmetric(horizontal: 16.0));
|
|
expect(shape, const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(2.0)),
|
|
));
|
|
|
|
expect(tester.widget<Material>(find.byType(Material)).shape, shape);
|
|
expect(tester.widget<Material>(find.byType(Material)).color, disabledColor);
|
|
expect(tester.getSize(find.byType(Material)), const Size(88.0, 48.0));
|
|
});
|
|
|
|
testWidgets('Theme buttonTheme ButtonTheme overrides', (WidgetTester tester) async {
|
|
late ButtonTextTheme textTheme;
|
|
late BoxConstraints constraints;
|
|
late EdgeInsets padding;
|
|
late ShapeBorder shape;
|
|
|
|
await tester.pumpWidget(
|
|
Theme(
|
|
data: ThemeData.light().copyWith(
|
|
buttonColor: const Color(0xFF00FF00), // enabled RaisedButton fill color
|
|
),
|
|
child: ButtonTheme(
|
|
textTheme: ButtonTextTheme.primary,
|
|
minWidth: 100.0,
|
|
height: 200.0,
|
|
padding: EdgeInsets.zero,
|
|
buttonColor: const Color(0xFF00FF00), // enabled RaisedButton fill color
|
|
shape: const RoundedRectangleBorder(),
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
final ButtonThemeData theme = ButtonTheme.of(context);
|
|
textTheme = theme.textTheme;
|
|
constraints = theme.constraints;
|
|
padding = theme.padding as EdgeInsets;
|
|
shape = theme.shape;
|
|
return Container(
|
|
alignment: Alignment.topLeft,
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: RaisedButton(
|
|
onPressed: () { },
|
|
child: const Text('b'), // intrinsic width < minimum width
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(textTheme, ButtonTextTheme.primary);
|
|
expect(constraints, const BoxConstraints(minWidth: 100.0, minHeight: 200.0));
|
|
expect(padding, EdgeInsets.zero);
|
|
expect(shape, const RoundedRectangleBorder());
|
|
|
|
expect(tester.widget<Material>(find.byType(Material)).shape, shape);
|
|
expect(tester.widget<Material>(find.byType(Material)).color, const Color(0xFF00FF00));
|
|
expect(tester.getSize(find.byType(Material)), const Size(100.0, 200.0));
|
|
});
|
|
|
|
testWidgets('ButtonTheme alignedDropdown', (WidgetTester tester) async {
|
|
final Key dropdownKey = UniqueKey();
|
|
|
|
Widget buildFrame({ required bool alignedDropdown, required TextDirection textDirection }) {
|
|
return MaterialApp(
|
|
builder: (BuildContext context, Widget? child) {
|
|
return Directionality(
|
|
textDirection: textDirection,
|
|
child: child!,
|
|
);
|
|
},
|
|
home: ButtonTheme(
|
|
alignedDropdown: alignedDropdown,
|
|
child: Material(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return Container(
|
|
alignment: Alignment.center,
|
|
child: DropdownButtonHideUnderline(
|
|
child: SizedBox(
|
|
width: 200.0,
|
|
child: DropdownButton<String>(
|
|
key: dropdownKey,
|
|
onChanged: (String? value) { },
|
|
value: 'foo',
|
|
items: const <DropdownMenuItem<String>>[
|
|
DropdownMenuItem<String>(
|
|
value: 'foo',
|
|
child: Text('foo'),
|
|
),
|
|
DropdownMenuItem<String>(
|
|
value: 'bar',
|
|
child: Text('bar'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
final Finder button = find.byKey(dropdownKey);
|
|
final Finder menu = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DropdownMenu<String>');
|
|
|
|
await tester.pumpWidget(
|
|
buildFrame(
|
|
alignedDropdown: false,
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
);
|
|
await tester.tap(button);
|
|
await tester.pumpAndSettle();
|
|
|
|
// 240 = 200.0 (button width) + _kUnalignedMenuMargin (20.0 left and right)
|
|
expect(tester.getSize(button).width, 200.0);
|
|
expect(tester.getSize(menu).width, 240.0);
|
|
|
|
// Dismiss the menu.
|
|
await tester.tapAt(Offset.zero);
|
|
await tester.pumpAndSettle();
|
|
expect(menu, findsNothing);
|
|
|
|
await tester.pumpWidget(
|
|
buildFrame(
|
|
alignedDropdown: true,
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
);
|
|
await tester.tap(button);
|
|
await tester.pumpAndSettle();
|
|
|
|
// Aligneddropdown: true means the button and menu widths match
|
|
expect(tester.getSize(button).width, 200.0);
|
|
expect(tester.getSize(menu).width, 200.0);
|
|
|
|
// There are two 'foo' widgets: the selected menu item's label and the drop
|
|
// down button's label. The should both appear at the same location.
|
|
final Finder fooText = find.text('foo');
|
|
expect(fooText, findsNWidgets(2));
|
|
expect(tester.getRect(fooText.at(0)), tester.getRect(fooText.at(1)));
|
|
|
|
// Dismiss the menu.
|
|
await tester.tapAt(Offset.zero);
|
|
await tester.pumpAndSettle();
|
|
expect(menu, findsNothing);
|
|
|
|
// Same test as above except RTL
|
|
await tester.pumpWidget(
|
|
buildFrame(
|
|
alignedDropdown: true,
|
|
textDirection: TextDirection.rtl,
|
|
),
|
|
);
|
|
await tester.tap(button);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(fooText, findsNWidgets(2));
|
|
expect(tester.getRect(fooText.at(0)), tester.getRect(fooText.at(1)));
|
|
});
|
|
|
|
testWidgets('button theme with stateful color changes color in states', (WidgetTester tester) async {
|
|
final FocusNode focusNode = FocusNode();
|
|
|
|
const Color pressedColor = Color(0x00000001);
|
|
const Color hoverColor = Color(0x00000002);
|
|
const Color focusedColor = Color(0x00000003);
|
|
const Color defaultColor = Color(0x00000004);
|
|
|
|
Color getTextColor(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return pressedColor;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return hoverColor;
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusedColor;
|
|
}
|
|
return defaultColor;
|
|
}
|
|
|
|
const ColorScheme colorScheme = ColorScheme.light();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: Center(
|
|
child: ButtonTheme(
|
|
colorScheme: colorScheme.copyWith(
|
|
primary: MaterialStateColor.resolveWith(getTextColor),
|
|
),
|
|
textTheme: ButtonTextTheme.primary,
|
|
child: FlatButton(
|
|
child: const Text('FlatButton'),
|
|
onPressed: () {},
|
|
focusNode: focusNode,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
Color textColor() {
|
|
return tester.renderObject<RenderParagraph>(find.text('FlatButton')).text.style!.color!;
|
|
}
|
|
|
|
// Default, not disabled.
|
|
expect(textColor(), equals(defaultColor));
|
|
|
|
// Focused.
|
|
focusNode.requestFocus();
|
|
await tester.pumpAndSettle();
|
|
expect(textColor(), focusedColor);
|
|
|
|
// Hovered.
|
|
final Offset center = tester.getCenter(find.byType(FlatButton));
|
|
final TestGesture gesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
await gesture.addPointer(location: Offset.zero);
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.moveTo(center);
|
|
await tester.pumpAndSettle();
|
|
expect(textColor(), hoverColor);
|
|
|
|
// 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(textColor(), pressedColor);
|
|
},
|
|
semanticsEnabled: true,
|
|
);
|
|
}
|