Introduce iconAlignment
for the buttons with icon (#137348)
Adds `iconAlignment` property to `ButtonStyleButton` widget. Fixes #89564 ### Example https://github.com/flutter/flutter/assets/13456345/1b5236c4-5c60-4915-b3c6-0a56c43f8a19
This commit is contained in:
parent
82668f1688
commit
10442399fb
@ -0,0 +1,147 @@
|
||||
// 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/material.dart';
|
||||
|
||||
/// Flutter code sample for using [ButtonStyleButton.iconAlignment] parameter.
|
||||
|
||||
void main() {
|
||||
runApp(const ButtonStyleButtonIconAlignmentApp());
|
||||
}
|
||||
|
||||
class ButtonStyleButtonIconAlignmentApp extends StatelessWidget {
|
||||
const ButtonStyleButtonIconAlignmentApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const MaterialApp(
|
||||
home: Scaffold(
|
||||
body: ButtonStyleButtonIconAlignmentExample(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ButtonStyleButtonIconAlignmentExample extends StatefulWidget {
|
||||
const ButtonStyleButtonIconAlignmentExample({super.key});
|
||||
|
||||
@override
|
||||
State<ButtonStyleButtonIconAlignmentExample> createState() => _ButtonStyleButtonIconAlignmentExampleState();
|
||||
}
|
||||
|
||||
class _ButtonStyleButtonIconAlignmentExampleState extends State<ButtonStyleButtonIconAlignmentExample> {
|
||||
TextDirection _textDirection = TextDirection.ltr;
|
||||
IconAlignment _iconAlignment = IconAlignment.start;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Directionality(
|
||||
key: const Key('Directionality'),
|
||||
textDirection: _textDirection,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Spacer(),
|
||||
OverflowBar(
|
||||
spacing: 10,
|
||||
overflowSpacing: 20,
|
||||
alignment: MainAxisAlignment.center,
|
||||
overflowAlignment: OverflowBarAlignment.center,
|
||||
children: <Widget>[
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.sunny),
|
||||
label: const Text('ElevatedButton'),
|
||||
iconAlignment: _iconAlignment,
|
||||
),
|
||||
FilledButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.beach_access),
|
||||
label: const Text('FilledButton'),
|
||||
iconAlignment: _iconAlignment,
|
||||
),
|
||||
FilledButton.tonalIcon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.cloud),
|
||||
label: const Text('FilledButton Tonal'),
|
||||
iconAlignment: _iconAlignment,
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.light),
|
||||
label: const Text('OutlinedButton'),
|
||||
iconAlignment: _iconAlignment,
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.flight_takeoff),
|
||||
label: const Text('TextButton'),
|
||||
iconAlignment: _iconAlignment,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
OverflowBar(
|
||||
alignment: MainAxisAlignment.spaceEvenly,
|
||||
overflowAlignment: OverflowBarAlignment.center,
|
||||
spacing: 10,
|
||||
overflowSpacing: 10,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
children: <Widget>[
|
||||
const Text('Icon alignment'),
|
||||
const SizedBox(height: 10),
|
||||
SegmentedButton<IconAlignment>(
|
||||
onSelectionChanged: (Set<IconAlignment> value) {
|
||||
setState(() {
|
||||
_iconAlignment = value.first;
|
||||
});
|
||||
},
|
||||
selected: <IconAlignment>{ _iconAlignment },
|
||||
segments: IconAlignment.values.map((IconAlignment iconAlignment) {
|
||||
return ButtonSegment<IconAlignment>(
|
||||
value: iconAlignment,
|
||||
label: Text(iconAlignment.name),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: <Widget>[
|
||||
const Text('Text direction'),
|
||||
const SizedBox(height: 10),
|
||||
SegmentedButton<TextDirection>(
|
||||
onSelectionChanged: (Set<TextDirection> value) {
|
||||
setState(() {
|
||||
_textDirection = value.first;
|
||||
});
|
||||
},
|
||||
selected: <TextDirection>{ _textDirection },
|
||||
segments: const <ButtonSegment<TextDirection>>[
|
||||
ButtonSegment<TextDirection>(
|
||||
value: TextDirection.ltr,
|
||||
label: Text('LTR'),
|
||||
),
|
||||
ButtonSegment<TextDirection>(
|
||||
value: TextDirection.rtl,
|
||||
label: Text('RTL'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
// 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/material.dart';
|
||||
import 'package:flutter_api_samples/material/button_style_button/button_style_button.icon_alignment.0.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('ButtonStyleButton.iconAlignment updates button icons alignment', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.ButtonStyleButtonIconAlignmentApp(),
|
||||
);
|
||||
|
||||
Finder findButtonMaterial(String text) {
|
||||
return find.ancestor(
|
||||
of: find.text(text),
|
||||
matching: find.byType(Material),
|
||||
).first;
|
||||
}
|
||||
|
||||
void expectedLeftIconPosition({
|
||||
required double iconOffset,
|
||||
required double textButtonIconOffset,
|
||||
}) {
|
||||
expect(
|
||||
tester.getTopLeft(findButtonMaterial('ElevatedButton')).dx,
|
||||
tester.getTopLeft(find.byIcon(Icons.sunny)).dx - iconOffset,
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(findButtonMaterial('FilledButton')).dx,
|
||||
tester.getTopLeft(find.byIcon(Icons.beach_access)).dx - iconOffset,
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(findButtonMaterial('FilledButton Tonal')).dx,
|
||||
tester.getTopLeft(find.byIcon(Icons.cloud)).dx - iconOffset,
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(findButtonMaterial('OutlinedButton')).dx,
|
||||
tester.getTopLeft(find.byIcon(Icons.light)).dx - iconOffset,
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(findButtonMaterial('TextButton')).dx,
|
||||
tester.getTopLeft(find.byIcon(Icons.flight_takeoff)).dx - textButtonIconOffset,
|
||||
);
|
||||
}
|
||||
|
||||
void expectedRightIconPosition({
|
||||
required double iconOffset,
|
||||
required double textButtonIconOffset,
|
||||
}) {
|
||||
expect(
|
||||
tester.getTopRight(findButtonMaterial('ElevatedButton')).dx,
|
||||
tester.getTopRight(find.byIcon(Icons.sunny)).dx + iconOffset,
|
||||
);
|
||||
expect(
|
||||
tester.getTopRight(findButtonMaterial('FilledButton')).dx,
|
||||
tester.getTopRight(find.byIcon(Icons.beach_access)).dx + iconOffset,
|
||||
);
|
||||
expect(
|
||||
tester.getTopRight(findButtonMaterial('FilledButton Tonal')).dx,
|
||||
tester.getTopRight(find.byIcon(Icons.cloud)).dx + iconOffset,
|
||||
);
|
||||
expect(
|
||||
tester.getTopRight(findButtonMaterial('OutlinedButton')).dx,
|
||||
tester.getTopRight(find.byIcon(Icons.light)).dx + iconOffset,
|
||||
);
|
||||
expect(
|
||||
tester.getTopRight(findButtonMaterial('TextButton')).dx,
|
||||
tester.getTopRight(find.byIcon(Icons.flight_takeoff)).dx + textButtonIconOffset,
|
||||
);
|
||||
}
|
||||
|
||||
// Test initial icon alignment in LTR.
|
||||
expectedLeftIconPosition(iconOffset: 16, textButtonIconOffset: 12);
|
||||
|
||||
// Update icon alignment to end.
|
||||
await tester.tap(find.text('end'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Test icon alignment end in LTR.
|
||||
expectedRightIconPosition(iconOffset: 24, textButtonIconOffset: 16);
|
||||
|
||||
// Reset icon alignment to start.
|
||||
await tester.tap(find.text('start'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Change text direction to RTL.
|
||||
await tester.tap(find.text('RTL'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Test icon alignment start in LTR.
|
||||
expectedRightIconPosition(iconOffset: 16, textButtonIconOffset: 12);
|
||||
|
||||
// Update icon alignment to end.
|
||||
await tester.tap(find.text('end'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Test icon alignment end in LTR.
|
||||
expectedLeftIconPosition(iconOffset: 24, textButtonIconOffset: 16);
|
||||
});
|
||||
}
|
@ -11,11 +11,46 @@ import 'package:flutter/widgets.dart';
|
||||
import 'button_style.dart';
|
||||
import 'colors.dart';
|
||||
import 'constants.dart';
|
||||
import 'elevated_button.dart';
|
||||
import 'filled_button.dart';
|
||||
import 'ink_well.dart';
|
||||
import 'material.dart';
|
||||
import 'material_state.dart';
|
||||
import 'outlined_button.dart';
|
||||
import 'text_button.dart';
|
||||
import 'theme_data.dart';
|
||||
|
||||
/// {@template flutter.material.ButtonStyleButton.iconAlignment}
|
||||
/// Determines the alignment of the icon within the widgets such as:
|
||||
/// - [ElevatedButton.icon],
|
||||
/// - [FilledButton.icon],
|
||||
/// - [FilledButton.tonalIcon].
|
||||
/// - [OutlinedButton.icon],
|
||||
/// - [TextButton.icon],
|
||||
///
|
||||
/// The effect of `iconAlignment` depends on [TextDirection]. If textDirection is
|
||||
/// [TextDirection.ltr] then [IconAlignment.start] and [IconAlignment.end] align the
|
||||
/// icon on the left or right respectively. If textDirection is [TextDirection.rtl] the
|
||||
/// the alignments are reversed.
|
||||
///
|
||||
/// Defaults to [IconAlignment.start].
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample demonstrates how to use `iconAlignment` to align the button icon to the start
|
||||
/// or the end of the button.
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/button_style_button/button_style_button.icon_alignment.0.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// {@endtemplate}
|
||||
enum IconAlignment {
|
||||
/// The icon is placed at the start of the button.
|
||||
start,
|
||||
|
||||
/// The icon is placed at the end of the button.
|
||||
end,
|
||||
}
|
||||
|
||||
/// The base [StatefulWidget] class for buttons whose style is defined by a [ButtonStyle] object.
|
||||
///
|
||||
/// Concrete subclasses must override [defaultStyleOf] and [themeStyleOf].
|
||||
@ -44,6 +79,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
|
||||
this.statesController,
|
||||
this.isSemanticButton = true,
|
||||
required this.child,
|
||||
this.iconAlignment = IconAlignment.start,
|
||||
});
|
||||
|
||||
/// Called when the button is tapped or otherwise activated.
|
||||
@ -117,6 +153,9 @@ abstract class ButtonStyleButton extends StatefulWidget {
|
||||
/// {@macro flutter.widgets.ProxyWidget.child}
|
||||
final Widget? child;
|
||||
|
||||
/// {@macro flutter.material.ButtonStyleButton.iconAlignment}
|
||||
final IconAlignment iconAlignment;
|
||||
|
||||
/// Returns a non-null [ButtonStyle] that's based primarily on the [Theme]'s
|
||||
/// [ThemeData.textTheme] and [ThemeData.colorScheme].
|
||||
///
|
||||
|
@ -74,6 +74,7 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
super.clipBehavior,
|
||||
super.statesController,
|
||||
required super.child,
|
||||
super.iconAlignment,
|
||||
});
|
||||
|
||||
/// Create an elevated button from a pair of widgets that serve as the button's
|
||||
@ -83,6 +84,9 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
/// at the start, and 16 at the end, with an 8 pixel gap in between.
|
||||
///
|
||||
/// If [icon] is null, will create an [ElevatedButton] instead.
|
||||
///
|
||||
/// {@macro flutter.material.ButtonStyleButton.iconAlignment}
|
||||
///
|
||||
factory ElevatedButton.icon({
|
||||
Key? key,
|
||||
required VoidCallback? onPressed,
|
||||
@ -96,6 +100,7 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
MaterialStatesController? statesController,
|
||||
Widget? icon,
|
||||
required Widget label,
|
||||
IconAlignment iconAlignment = IconAlignment.start,
|
||||
}) {
|
||||
if (icon == null) {
|
||||
return ElevatedButton(
|
||||
@ -125,6 +130,7 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
statesController: statesController,
|
||||
icon: icon,
|
||||
label: label,
|
||||
iconAlignment: iconAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@ -532,9 +538,15 @@ class _ElevatedButtonWithIcon extends ElevatedButton {
|
||||
super.statesController,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
super.iconAlignment,
|
||||
}) : super(
|
||||
autofocus: autofocus ?? false,
|
||||
child: _ElevatedButtonWithIconChild(icon: icon, label: label, buttonStyle: style),
|
||||
child: _ElevatedButtonWithIconChild(
|
||||
icon: icon,
|
||||
label: label,
|
||||
buttonStyle: style,
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
@ -563,11 +575,17 @@ class _ElevatedButtonWithIcon extends ElevatedButton {
|
||||
}
|
||||
|
||||
class _ElevatedButtonWithIconChild extends StatelessWidget {
|
||||
const _ElevatedButtonWithIconChild({ required this.label, required this.icon, required this.buttonStyle });
|
||||
const _ElevatedButtonWithIconChild({
|
||||
required this.label,
|
||||
required this.icon,
|
||||
required this.buttonStyle,
|
||||
required this.iconAlignment,
|
||||
});
|
||||
|
||||
final Widget label;
|
||||
final Widget icon;
|
||||
final ButtonStyle? buttonStyle;
|
||||
final IconAlignment iconAlignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -576,7 +594,9 @@ class _ElevatedButtonWithIconChild extends StatelessWidget {
|
||||
final double gap = lerpDouble(8, 4, scale)!;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
|
||||
children: iconAlignment == IconAlignment.start
|
||||
? <Widget>[icon, SizedBox(width: gap), Flexible(child: label)]
|
||||
: <Widget>[Flexible(child: label), SizedBox(width: gap), icon],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ class FilledButton extends ButtonStyleButton {
|
||||
super.clipBehavior = Clip.none,
|
||||
super.statesController,
|
||||
required super.child,
|
||||
super.iconAlignment,
|
||||
}) : _variant = _FilledButtonVariant.filled;
|
||||
|
||||
/// Create a filled button from [icon] and [label].
|
||||
@ -83,6 +84,9 @@ class FilledButton extends ButtonStyleButton {
|
||||
/// and a gap between them.
|
||||
///
|
||||
/// If [icon] is null, will create a [FilledButton] instead.
|
||||
///
|
||||
/// {@macro flutter.material.ButtonStyleButton.iconAlignment}
|
||||
///
|
||||
factory FilledButton.icon({
|
||||
Key? key,
|
||||
required VoidCallback? onPressed,
|
||||
@ -96,6 +100,7 @@ class FilledButton extends ButtonStyleButton {
|
||||
MaterialStatesController? statesController,
|
||||
Widget? icon,
|
||||
required Widget label,
|
||||
IconAlignment iconAlignment = IconAlignment.start,
|
||||
}) {
|
||||
if (icon == null) {
|
||||
return FilledButton(
|
||||
@ -125,6 +130,7 @@ class FilledButton extends ButtonStyleButton {
|
||||
statesController: statesController,
|
||||
icon: icon,
|
||||
label: label,
|
||||
iconAlignment: iconAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@ -167,6 +173,7 @@ class FilledButton extends ButtonStyleButton {
|
||||
MaterialStatesController? statesController,
|
||||
Widget? icon,
|
||||
required Widget label,
|
||||
IconAlignment iconAlignment = IconAlignment.start,
|
||||
}) {
|
||||
if (icon == null) {
|
||||
return FilledButton.tonal(
|
||||
@ -196,6 +203,7 @@ class FilledButton extends ButtonStyleButton {
|
||||
statesController: statesController,
|
||||
icon: icon,
|
||||
label: label,
|
||||
iconAlignment: iconAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@ -535,9 +543,15 @@ class _FilledButtonWithIcon extends FilledButton {
|
||||
super.statesController,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
super.iconAlignment,
|
||||
}) : super(
|
||||
autofocus: autofocus ?? false,
|
||||
child: _FilledButtonWithIconChild(icon: icon, label: label, buttonStyle: style)
|
||||
child: _FilledButtonWithIconChild(
|
||||
icon: icon,
|
||||
label: label,
|
||||
buttonStyle: style,
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
);
|
||||
|
||||
_FilledButtonWithIcon.tonal({
|
||||
@ -553,9 +567,15 @@ class _FilledButtonWithIcon extends FilledButton {
|
||||
super.statesController,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
required IconAlignment iconAlignment,
|
||||
}) : super.tonal(
|
||||
autofocus: autofocus ?? false,
|
||||
child: _FilledButtonWithIconChild(icon: icon, label: label, buttonStyle: style)
|
||||
child: _FilledButtonWithIconChild(
|
||||
icon: icon,
|
||||
label: label,
|
||||
buttonStyle: style,
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
@ -584,11 +604,17 @@ class _FilledButtonWithIcon extends FilledButton {
|
||||
}
|
||||
|
||||
class _FilledButtonWithIconChild extends StatelessWidget {
|
||||
const _FilledButtonWithIconChild({ required this.label, required this.icon, required this.buttonStyle });
|
||||
const _FilledButtonWithIconChild({
|
||||
required this.label,
|
||||
required this.icon,
|
||||
required this.buttonStyle,
|
||||
required this.iconAlignment,
|
||||
});
|
||||
|
||||
final Widget label;
|
||||
final Widget icon;
|
||||
final ButtonStyle? buttonStyle;
|
||||
final IconAlignment iconAlignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -599,7 +625,9 @@ class _FilledButtonWithIconChild extends StatelessWidget {
|
||||
final double gap = lerpDouble(8, 4, scale)!;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
|
||||
children: iconAlignment == IconAlignment.start
|
||||
? <Widget>[icon, SizedBox(width: gap), Flexible(child: label)]
|
||||
: <Widget>[Flexible(child: label), SizedBox(width: gap), icon],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
super.clipBehavior,
|
||||
super.statesController,
|
||||
required super.child,
|
||||
super.iconAlignment,
|
||||
});
|
||||
|
||||
/// Create a text button from a pair of widgets that serve as the button's
|
||||
@ -87,7 +88,10 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
/// at the start, and 16 at the end, with an 8 pixel gap in between.
|
||||
///
|
||||
/// If [icon] is null, will create an [OutlinedButton] instead.
|
||||
factory OutlinedButton.icon({
|
||||
///
|
||||
/// {@macro flutter.material.ButtonStyleButton.iconAlignment}
|
||||
///
|
||||
factory OutlinedButton.icon({
|
||||
Key? key,
|
||||
required VoidCallback? onPressed,
|
||||
VoidCallback? onLongPress,
|
||||
@ -98,6 +102,7 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
MaterialStatesController? statesController,
|
||||
Widget? icon,
|
||||
required Widget label,
|
||||
IconAlignment iconAlignment = IconAlignment.start,
|
||||
}) {
|
||||
if (icon == null) {
|
||||
return OutlinedButton(
|
||||
@ -123,6 +128,7 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
statesController: statesController,
|
||||
icon: icon,
|
||||
label: label,
|
||||
iconAlignment: iconAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@ -455,9 +461,15 @@ class _OutlinedButtonWithIcon extends OutlinedButton {
|
||||
super.statesController,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
super.iconAlignment,
|
||||
}) : super(
|
||||
autofocus: autofocus ?? false,
|
||||
child: _OutlinedButtonWithIconChild(icon: icon, label: label, buttonStyle: style),
|
||||
child: _OutlinedButtonWithIconChild(
|
||||
icon: icon,
|
||||
label: label,
|
||||
buttonStyle: style,
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
@ -486,11 +498,13 @@ class _OutlinedButtonWithIconChild extends StatelessWidget {
|
||||
required this.label,
|
||||
required this.icon,
|
||||
required this.buttonStyle,
|
||||
required this.iconAlignment,
|
||||
});
|
||||
|
||||
final Widget label;
|
||||
final Widget icon;
|
||||
final ButtonStyle? buttonStyle;
|
||||
final IconAlignment iconAlignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -499,7 +513,9 @@ class _OutlinedButtonWithIconChild extends StatelessWidget {
|
||||
final double gap = lerpDouble(8, 4, scale)!;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
|
||||
children: iconAlignment == IconAlignment.start
|
||||
? <Widget>[icon, SizedBox(width: gap), Flexible(child: label)]
|
||||
: <Widget>[Flexible(child: label), SizedBox(width: gap), icon],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ class TextButton extends ButtonStyleButton {
|
||||
super.statesController,
|
||||
super.isSemanticButton,
|
||||
required Widget super.child,
|
||||
super.iconAlignment,
|
||||
});
|
||||
|
||||
/// Create a text button from a pair of widgets that serve as the button's
|
||||
@ -94,6 +95,11 @@ class TextButton extends ButtonStyleButton {
|
||||
///
|
||||
/// The icon and label are arranged in a row and padded by 8 logical pixels
|
||||
/// at the ends, with an 8 pixel gap in between.
|
||||
///
|
||||
/// If [icon] is null, will create a [TextButton] instead.
|
||||
///
|
||||
/// {@macro flutter.material.ButtonStyleButton.iconAlignment}
|
||||
///
|
||||
factory TextButton.icon({
|
||||
Key? key,
|
||||
required VoidCallback? onPressed,
|
||||
@ -107,6 +113,7 @@ class TextButton extends ButtonStyleButton {
|
||||
MaterialStatesController? statesController,
|
||||
Widget? icon,
|
||||
required Widget label,
|
||||
IconAlignment iconAlignment = IconAlignment.start,
|
||||
}) {
|
||||
if (icon == null) {
|
||||
return TextButton(
|
||||
@ -135,6 +142,7 @@ class TextButton extends ButtonStyleButton {
|
||||
statesController: statesController,
|
||||
icon: icon,
|
||||
label: label,
|
||||
iconAlignment: iconAlignment,
|
||||
);
|
||||
}
|
||||
|
||||
@ -497,9 +505,15 @@ class _TextButtonWithIcon extends TextButton {
|
||||
super.statesController,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
super.iconAlignment,
|
||||
}) : super(
|
||||
autofocus: autofocus ?? false,
|
||||
child: _TextButtonWithIconChild(icon: icon, label: label, buttonStyle: style),
|
||||
child: _TextButtonWithIconChild(
|
||||
icon: icon,
|
||||
label: label,
|
||||
buttonStyle: style,
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
@ -525,11 +539,13 @@ class _TextButtonWithIconChild extends StatelessWidget {
|
||||
required this.label,
|
||||
required this.icon,
|
||||
required this.buttonStyle,
|
||||
required this.iconAlignment,
|
||||
});
|
||||
|
||||
final Widget label;
|
||||
final Widget icon;
|
||||
final ButtonStyle? buttonStyle;
|
||||
final IconAlignment iconAlignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -538,7 +554,9 @@ class _TextButtonWithIconChild extends StatelessWidget {
|
||||
final double gap = lerpDouble(8, 4, scale)!;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
|
||||
children: iconAlignment == IconAlignment.start
|
||||
? <Widget>[icon, SizedBox(width: gap), Flexible(child: label)]
|
||||
: <Widget>[Flexible(child: label), SizedBox(width: gap), icon],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2178,6 +2178,118 @@ void main() {
|
||||
|
||||
focusNode.dispose();
|
||||
});
|
||||
|
||||
testWidgets('Default iconAlignment', (WidgetTester tester) async {
|
||||
Widget buildWidget({ required TextDirection textDirection }) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test default iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.ltr));
|
||||
|
||||
final Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
final Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test default iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.rtl));
|
||||
|
||||
final Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
final Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
});
|
||||
|
||||
testWidgets('iconAlignment can be customized', (WidgetTester tester) async {
|
||||
Widget buildWidget({
|
||||
required TextDirection textDirection,
|
||||
required IconAlignment iconAlignment,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 24.0); // 24.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 24.0); // 24.0 - padding between icon and button edge.
|
||||
});
|
||||
}
|
||||
|
||||
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
||||
|
@ -2149,7 +2149,6 @@ void main() {
|
||||
expect(textChildOf(decorations.at(1)).data, 'button');
|
||||
});
|
||||
|
||||
|
||||
testWidgets('FilledButton backgroundBuilder drops button child and foregroundBuilder return value', (WidgetTester tester) async {
|
||||
const Color backgroundColor = Color(0xFF000011);
|
||||
const Color foregroundColor = Color(0xFF000022);
|
||||
@ -2288,6 +2287,230 @@ void main() {
|
||||
|
||||
focusNode.dispose();
|
||||
});
|
||||
|
||||
testWidgets('Default iconAlignment', (WidgetTester tester) async {
|
||||
Widget buildWidget({ required TextDirection textDirection }) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: FilledButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test default iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.ltr));
|
||||
|
||||
final Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
final Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test default iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.rtl));
|
||||
|
||||
final Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
final Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
});
|
||||
|
||||
testWidgets('iconAlignment can be customized', (WidgetTester tester) async {
|
||||
Widget buildWidget({
|
||||
required TextDirection textDirection,
|
||||
required IconAlignment iconAlignment,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: FilledButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 24.0); // 24.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 24.0); // 24.0 - padding between icon and button edge.
|
||||
});
|
||||
|
||||
testWidgets('Tonal icon default iconAlignment', (WidgetTester tester) async {
|
||||
Widget buildWidget({ required TextDirection textDirection }) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: FilledButton.tonalIcon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test default iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.ltr));
|
||||
|
||||
final Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
final Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test default iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.rtl));
|
||||
|
||||
final Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
final Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
});
|
||||
|
||||
testWidgets('Tonal icon iconAlignment can be customized', (WidgetTester tester) async {
|
||||
Widget buildWidget({
|
||||
required TextDirection textDirection,
|
||||
required IconAlignment iconAlignment,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: FilledButton.tonalIcon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 24.0); // 24.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 24.0); // 24.0 - padding between icon and button edge.
|
||||
});
|
||||
}
|
||||
|
||||
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
||||
|
@ -2189,7 +2189,6 @@ void main() {
|
||||
expect(textChildOf(decorations.at(1)).data, 'button');
|
||||
});
|
||||
|
||||
|
||||
testWidgets('OutlinedButton backgroundBuilder drops button child and foregroundBuilder return value', (WidgetTester tester) async {
|
||||
const Color backgroundColor = Color(0xFF000011);
|
||||
const Color foregroundColor = Color(0xFF000022);
|
||||
@ -2318,7 +2317,6 @@ void main() {
|
||||
expect(sameStates(focusedHoveredStates, backgroundStates), isTrue);
|
||||
expect(sameStates(focusedHoveredStates, foregroundStates), isTrue);
|
||||
|
||||
|
||||
// Highlighted (pressed).
|
||||
await gesture.down(center);
|
||||
await tester.pump(); // Start the splash and highlight animations.
|
||||
@ -2357,6 +2355,118 @@ void main() {
|
||||
await tester.pumpWidget(buildFrame()); // onPressed: null - disabled
|
||||
expect(material.color, backgroundColor);
|
||||
});
|
||||
|
||||
testWidgets('Default iconAlignment', (WidgetTester tester) async {
|
||||
Widget buildWidget({ required TextDirection textDirection }) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test default iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.ltr));
|
||||
|
||||
final Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
final Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test default iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.rtl));
|
||||
|
||||
final Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
final Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
});
|
||||
|
||||
testWidgets('iconAlignment can be customized', (WidgetTester tester) async {
|
||||
Widget buildWidget({
|
||||
required TextDirection textDirection,
|
||||
required IconAlignment iconAlignment,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 24.0); // 24.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 24.0); // 24.0 - padding between icon and button edge.
|
||||
});
|
||||
}
|
||||
|
||||
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
||||
|
@ -2022,7 +2022,6 @@ void main() {
|
||||
expect(textChildOf(decorations.at(1)).data, 'button');
|
||||
});
|
||||
|
||||
|
||||
testWidgets('TextButton backgroundBuilder drops button child and foregroundBuilder return value', (WidgetTester tester) async {
|
||||
const Color backgroundColor = Color(0xFF000011);
|
||||
const Color foregroundColor = Color(0xFF000022);
|
||||
@ -2151,7 +2150,6 @@ void main() {
|
||||
expect(sameStates(focusedHoveredStates, backgroundStates), isTrue);
|
||||
expect(sameStates(focusedHoveredStates, foregroundStates), isTrue);
|
||||
|
||||
|
||||
// Highlighted (pressed).
|
||||
await gesture.down(center);
|
||||
await tester.pump(); // Start the splash and highlight animations.
|
||||
@ -2190,6 +2188,118 @@ void main() {
|
||||
await tester.pumpWidget(buildFrame()); // onPressed: null - disabled
|
||||
expect(material.color, backgroundColor);
|
||||
});
|
||||
|
||||
testWidgets('Default iconAlignment', (WidgetTester tester) async {
|
||||
Widget buildWidget({ required TextDirection textDirection }) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test default iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.ltr));
|
||||
|
||||
final Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
final Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 12.0); // 12.0 - padding between icon and button edge.
|
||||
|
||||
// Test default iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(buildWidget(textDirection: TextDirection.rtl));
|
||||
|
||||
final Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
final Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 12.0); // 12.0 - padding between icon and button edge.
|
||||
});
|
||||
|
||||
testWidgets('iconAlignment can be customized', (WidgetTester tester) async {
|
||||
Widget buildWidget({
|
||||
required TextDirection textDirection,
|
||||
required IconAlignment iconAlignment,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('button'),
|
||||
iconAlignment: iconAlignment,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
Offset iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 12.0); // 12.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is ltr.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.ltr,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
Offset buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
Offset iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 16.0); // 16.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.start,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopRight = tester.getTopRight(find.byType(Material).last);
|
||||
iconTopRight = tester.getTopRight(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the right of the button.
|
||||
expect(buttonTopRight.dx, iconTopRight.dx + 12.0); // 12.0 - padding between icon and button edge.
|
||||
|
||||
// Test iconAlignment when textDirection is rtl.
|
||||
await tester.pumpWidget(
|
||||
buildWidget(
|
||||
textDirection: TextDirection.rtl,
|
||||
iconAlignment: IconAlignment.end,
|
||||
),
|
||||
);
|
||||
|
||||
buttonTopLeft = tester.getTopLeft(find.byType(Material).last);
|
||||
iconTopLeft = tester.getTopLeft(find.byIcon(Icons.add));
|
||||
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 16.0); // 16.0 - padding between icon and button edge.
|
||||
});
|
||||
}
|
||||
|
||||
TextStyle? _iconStyle(WidgetTester tester, IconData icon) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user