Unverified Commit a939d9d7 authored by Jiun Kim's avatar Jiun Kim Committed by GitHub

[Stepper] Add Alternative Label on horizontal-type Stepper (#91496)

* Add label on Step

* Add _buildLabelText, _labelStyle

* Update _buildHorizontal with _buildLabelText

* Fix Redundant Center widgets

* Fix Unnecessary Column Widget

* Fix Unnecessary import

* Fix Unnecessary Container

* Set label style to bodyText1 to match title style

* Update test for Alternative Label

* Fix code format

* Update label docstring

* Update to test textstyle of label

* Update to Test `TextStyles` of Label

* Fix typo in inline comments

* Update for Stepper type to `horizontal`

* Restore `_buildVerticalHeader`

* Update docstring, `Only [StepperType.horizontal]`

* Update for Readability

* Add `_isLabel` method

* Update `Horizontal height` with `_isLabel`

* Update to make `_buildIcon` centered

* Fix for `curly_braces_in_flow_control_structures`
parent fd294fa8
...@@ -138,6 +138,7 @@ class Step { ...@@ -138,6 +138,7 @@ class Step {
required this.content, required this.content,
this.state = StepState.indexed, this.state = StepState.indexed,
this.isActive = false, this.isActive = false,
this.label,
}) : assert(title != null), }) : assert(title != null),
assert(content != null), assert(content != null),
assert(state != null); assert(state != null);
...@@ -162,6 +163,10 @@ class Step { ...@@ -162,6 +163,10 @@ class Step {
/// Whether or not the step is active. The flag only influences styling. /// Whether or not the step is active. The flag only influences styling.
final bool isActive; final bool isActive;
/// Only [StepperType.horizontal], Optional widget that appears under the [title].
/// By default, uses the `bodyText1` theme.
final Widget? label;
} }
/// A material stepper widget that displays progress through a sequence of /// A material stepper widget that displays progress through a sequence of
...@@ -353,6 +358,15 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin { ...@@ -353,6 +358,15 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
return Theme.of(context).brightness == Brightness.dark; return Theme.of(context).brightness == Brightness.dark;
} }
bool _isLabel() {
for (final Step step in widget.steps) {
if (step.label != null) {
return true;
}
}
return false;
}
Widget _buildLine(bool visible) { Widget _buildLine(bool visible) {
return Container( return Container(
width: visible ? 1.0 : 0.0, width: visible ? 1.0 : 0.0,
...@@ -573,6 +587,27 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin { ...@@ -573,6 +587,27 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
} }
} }
TextStyle _labelStyle(int index) {
final ThemeData themeData = Theme.of(context);
final TextTheme textTheme = themeData.textTheme;
assert(widget.steps[index].state != null);
switch (widget.steps[index].state) {
case StepState.indexed:
case StepState.editing:
case StepState.complete:
return textTheme.bodyText1!;
case StepState.disabled:
return textTheme.bodyText1!.copyWith(
color: _isDark() ? _kDisabledDark : _kDisabledLight,
);
case StepState.error:
return textTheme.bodyText1!.copyWith(
color: _isDark() ? _kErrorDark : _kErrorLight,
);
}
}
Widget _buildHeaderText(int index) { Widget _buildHeaderText(int index) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
...@@ -598,6 +633,17 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin { ...@@ -598,6 +633,17 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
); );
} }
Widget _buildLabelText(int index) {
if (widget.steps[index].label != null) {
return AnimatedDefaultTextStyle(
style: _labelStyle(index),
duration: kThemeAnimationDuration,
child: widget.steps[index].label!,
);
}
return const SizedBox();
}
Widget _buildVerticalHeader(int index) { Widget _buildVerticalHeader(int index) {
return Container( return Container(
margin: const EdgeInsets.symmetric(horizontal: 24.0), margin: const EdgeInsets.symmetric(horizontal: 24.0),
...@@ -709,9 +755,14 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin { ...@@ -709,9 +755,14 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
SizedBox( SizedBox(
height: 72.0, height: _isLabel() ? 104.0 : 72.0,
child: Center( child: Column(
child: _buildIcon(i), mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (widget.steps[i].label != null) const SizedBox(height: 24.0,),
Center(child: _buildIcon(i)),
if (widget.steps[i].label != null) SizedBox(height : 24.0, child: _buildLabelText(i),),
],
), ),
), ),
Container( Container(
......
...@@ -1144,6 +1144,90 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async ...@@ -1144,6 +1144,90 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
expect(material.margin, equals(margin)); expect(material.margin, equals(margin));
}); });
testWidgets('Stepper with Alternative Label', (WidgetTester tester) async {
int index = 0;
late TextStyle bodyText1Style;
late TextStyle bodyText2Style;
late TextStyle captionStyle;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
bodyText1Style = Theme.of(context).textTheme.bodyText1!;
bodyText2Style = Theme.of(context).textTheme.bodyText2!;
captionStyle = Theme.of(context).textTheme.caption!;
return Stepper(
type: StepperType.horizontal,
currentStep: index,
onStepTapped: (int i) {
setState(() {
index = i;
});
},
steps: <Step>[
Step(
title: const Text('Title 1'),
content: const Text('Content 1'),
label: Text('Label 1', style: Theme.of(context).textTheme.caption),
),
Step(
title: const Text('Title 2'),
content: const Text('Content 2'),
label: Text('Label 2', style: Theme.of(context).textTheme.bodyText1),
),
Step(
title: const Text('Title 3'),
content: const Text('Content 3'),
label: Text('Label 3', style: Theme.of(context).textTheme.bodyText2),
),
],
);
}),
),
),
);
// Check Styles of Label Text Widgets before tapping steps
final Text label1TextWidget =
tester.widget<Text>(find.text('Label 1'));
final Text label3TextWidget =
tester.widget<Text>(find.text('Label 3'));
expect(captionStyle, label1TextWidget.style);
expect(bodyText2Style, label3TextWidget.style);
late Text selectedLabelTextWidget;
late Text nextLabelTextWidget;
// Tap to Step1 Label then, `index` become 0
await tester.tap(find.text('Label 1'));
expect(index, 0);
// Check Styles of Selected Label Text Widgets and Another Label Text Widget
selectedLabelTextWidget =
tester.widget<Text>(find.text('Label ${index + 1}'));
expect(captionStyle, selectedLabelTextWidget.style);
nextLabelTextWidget =
tester.widget<Text>(find.text('Label ${index + 2}'));
expect(bodyText1Style, nextLabelTextWidget.style);
// Tap to Step2 Label then, `index` become 1
await tester.tap(find.text('Label 2'));
expect(index, 1);
// Check Styles of Selected Label Text Widgets and Another Label Text Widget
selectedLabelTextWidget =
tester.widget<Text>(find.text('Label ${index + 1}'));
expect(bodyText1Style, selectedLabelTextWidget.style);
nextLabelTextWidget =
tester.widget<Text>(find.text('Label ${index + 2}'));
expect(bodyText2Style, nextLabelTextWidget.style);
});
} }
class _TappableColorWidget extends StatefulWidget { class _TappableColorWidget extends StatefulWidget {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment