blob: fd1e1fe6496402362329a2f474fe1c31a05484f9 [file] [log] [blame]
// 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: Container(
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,
);
}