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(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Material(
|
Material(
|
||||||
@ -747,7 +758,7 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
|
|||||||
AnimatedSize(
|
AnimatedSize(
|
||||||
curve: Curves.fastOutSlowIn,
|
curve: Curves.fastOutSlowIn,
|
||||||
duration: kThemeAnimationDuration,
|
duration: kThemeAnimationDuration,
|
||||||
child: widget.steps[widget.currentStep].content,
|
child: Column(children: stepPanels, crossAxisAlignment: CrossAxisAlignment.stretch),
|
||||||
),
|
),
|
||||||
_buildVerticalControls(widget.currentStep),
|
_buildVerticalControls(widget.currentStep),
|
||||||
],
|
],
|
||||||
|
@ -1048,6 +1048,77 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
|
|||||||
expect(material.elevation, 2.0);
|
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 {
|
testWidgets('Stepper custom margin', (WidgetTester tester) async {
|
||||||
|
|
||||||
const EdgeInsetsGeometry margin = EdgeInsetsDirectional.only(
|
const EdgeInsetsGeometry margin = EdgeInsetsDirectional.only(
|
||||||
@ -1086,3 +1157,41 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
|
|||||||
expect(material.margin, equals(margin));
|
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