fix: Preserve state in horizontal stepper (#84993)
This commit is contained in:
parent
e3c338a8e2
commit
fb56442de7
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user