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 {
required this.content,
this.state = StepState.indexed,
this.isActive = false,
this.label,
}) : assert(title != null),
assert(content != null),
assert(state != null);
......@@ -162,6 +163,10 @@ class Step {
/// Whether or not the step is active. The flag only influences styling.
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
......@@ -353,6 +358,15 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
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) {
return Container(
width: visible ? 1.0 : 0.0,
......@@ -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) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
......@@ -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) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 24.0),
......@@ -709,9 +755,14 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
child: Row(
children: <Widget>[
SizedBox(
height: 72.0,
child: Center(
child: _buildIcon(i),
height: _isLabel() ? 104.0 : 72.0,
child: Column(
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(
......
......@@ -1144,6 +1144,90 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
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 {
......
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