Skip to content

Commit

Permalink
[Stepper] Add Alternative Label on horizontal-type Stepper (#91496)
Browse files Browse the repository at this point in the history
* 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`
  • Loading branch information
KKimj authored Jun 6, 2022
1 parent fd294fa commit a939d9d
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 3 deletions.
57 changes: 54 additions & 3 deletions packages/flutter/lib/src/material/stepper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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),
Expand Down Expand Up @@ -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(
Expand Down
84 changes: 84 additions & 0 deletions packages/flutter/test/material/stepper_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit a939d9d

Please sign in to comment.