Unverified Commit fb56442d authored by Craig Labenz's avatar Craig Labenz Committed by GitHub

fix: Preserve state in horizontal stepper (#84993)

parent e3c338a8
......@@ -728,6 +728,17 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
],
];
final List<Widget> stepPanels = <Widget>[];
for (int i = 0; i < widget.steps.length; i += 1) {
stepPanels.add(
Visibility(
maintainState: true,
visible: i == widget.currentStep,
child: widget.steps[i].content,
),
);
}
return Column(
children: <Widget>[
Material(
......@@ -747,7 +758,7 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
AnimatedSize(
curve: Curves.fastOutSlowIn,
duration: kThemeAnimationDuration,
child: widget.steps[widget.currentStep].content,
child: Column(children: stepPanels, crossAxisAlignment: CrossAxisAlignment.stretch),
),
_buildVerticalControls(widget.currentStep),
],
......
......@@ -1048,6 +1048,77 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
expect(material.elevation, 2.0);
});
testWidgets('Stepper horizontal preserves state', (WidgetTester tester) async {
const Color untappedColor = Colors.blue;
const Color tappedColor = Colors.red;
int index = 0;
Widget buildFrame() {
return MaterialApp(
home: Scaffold(
body: Center(
// Must break this out into its own widget purely to be able to call `setState()`
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Stepper(
onStepTapped: (int i) => setState(() => index = i),
currentStep: index,
type: StepperType.horizontal,
steps: const <Step>[
Step(
title: Text('Step 1'),
content: _TappableColorWidget(
key: Key('tappable-color'),
tappedColor: tappedColor,
untappedColor: untappedColor,
),
),
Step(
title: Text('Step 2'),
content: Text('Step 2 Content'),
),
],
);
},
),
),
),
);
}
final Widget widget = buildFrame();
await tester.pumpWidget(widget);
// Set up a getter to examine the MacGuffin's color
Color getColor() => tester.widget<ColoredBox>(
find.descendant(of: find.byKey(const Key('tappable-color')), matching: find.byType(ColoredBox)),
).color;
// We are on step 1
expect(find.text('Step 2 Content'), findsNothing);
expect(getColor(), untappedColor);
await tester.tap(find.byKey(const Key('tap-me')));
await tester.pumpAndSettle();
expect(getColor(), tappedColor);
// Now flip to step 2
await tester.tap(find.text('Step 2'));
await tester.pumpAndSettle();
// Confirm that we did in fact flip to step 2
expect(find.text('Step 2 Content'), findsOneWidget);
// Now go back to step 1
await tester.tap(find.text('Step 1'));
await tester.pumpAndSettle();
// Confirm that we flipped back to step 1
expect(find.text('Step 2 Content'), findsNothing);
// The color should still be `tappedColor`
expect(getColor(), tappedColor);
});
testWidgets('Stepper custom margin', (WidgetTester tester) async {
const EdgeInsetsGeometry margin = EdgeInsetsDirectional.only(
......@@ -1086,3 +1157,41 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
expect(material.margin, equals(margin));
});
}
class _TappableColorWidget extends StatefulWidget {
const _TappableColorWidget({required this.tappedColor, required this.untappedColor, Key? key,}) : super(key: key);
final Color tappedColor;
final Color untappedColor;
@override
State<StatefulWidget> createState() => _TappableColorWidgetState();
}
class _TappableColorWidgetState extends State<_TappableColorWidget> {
Color? color;
@override
void initState() {
super.initState();
color = widget.untappedColor;
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState((){
color = widget.tappedColor;
});
},
child: Container(
key: const Key('tap-me'),
height: 50,
width: 50,
color: color,
),
);
}
}
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