Unverified Commit 60de2deb authored by jslavitz's avatar jslavitz Committed by GitHub

Adds custom control builder functionality to Stepper (#23310)

* Adds test and builder
parent bc56a7a6
...@@ -140,6 +140,7 @@ class Stepper extends StatefulWidget { ...@@ -140,6 +140,7 @@ class Stepper extends StatefulWidget {
this.onStepTapped, this.onStepTapped,
this.onStepContinue, this.onStepContinue,
this.onStepCancel, this.onStepCancel,
this.controlsBuilder,
}) : assert(steps != null), }) : assert(steps != null),
assert(type != null), assert(type != null),
assert(currentStep != null), assert(currentStep != null),
...@@ -174,6 +175,53 @@ class Stepper extends StatefulWidget { ...@@ -174,6 +175,53 @@ class Stepper extends StatefulWidget {
/// If null, the 'cancel' button will be disabled. /// If null, the 'cancel' button will be disabled.
final VoidCallback onStepCancel; final VoidCallback onStepCancel;
/// The callback for creating custom controls.
///
/// If null, the default controls from the current theme will be used.
///
/// This callback which takes in a context and two functions,[onStepContinue]
/// and [onStepCancel]. These can be used to control the stepper.
///
/// ## Sample Code:
/// Creates a stepper control with custom buttons.
///
/// ```dart
/// Stepper(
/// controlsBuilder:
/// (BuildContext context, {VoidCallback onStepContinue, VoidCallback onStepCancel}) {
/// return Row(
/// children: <Widget>[
/// FlatButton(
/// onPressed: onStepContinue,
/// child: const Text('My Awesome Continue Message!'),
/// ),
/// FlatButton(
/// onPressed: onStepCancel,
/// child: const Text('My Awesome Cancel Message!'),
/// ),
/// ],
/// ),
/// },
/// steps: const <Step>[
/// Step(
/// title: Text('A'),
/// content: SizedBox(
/// width: 100.0,
/// height: 100.0,
/// ),
/// ),
/// Step(
/// title: Text('B'),
/// content: SizedBox(
/// width: 100.0,
/// height: 100.0,
/// ),
/// ),
/// ],
/// )
/// ```
final ControlsWidgetBuilder controlsBuilder;
@override @override
_StepperState createState() => _StepperState(); _StepperState createState() => _StepperState();
} }
...@@ -327,6 +375,9 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin { ...@@ -327,6 +375,9 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
} }
Widget _buildVerticalControls() { Widget _buildVerticalControls() {
if (widget.controlsBuilder != null)
return widget.controlsBuilder(context, onStepContinue: widget.onStepContinue, onStepCancel: widget.onStepCancel);
Color cancelColor; Color cancelColor;
switch (Theme.of(context).brightness) { switch (Theme.of(context).brightness) {
......
...@@ -3632,6 +3632,11 @@ typedef IndexedWidgetBuilder = Widget Function(BuildContext context, int index); ...@@ -3632,6 +3632,11 @@ typedef IndexedWidgetBuilder = Widget Function(BuildContext context, int index);
/// [MaterialApp.builder]. /// [MaterialApp.builder].
typedef TransitionBuilder = Widget Function(BuildContext context, Widget child); typedef TransitionBuilder = Widget Function(BuildContext context, Widget child);
/// A Signiture for a function that creates a widget given [onStepContinue] and [onStepCancel].
///
/// Used by [Stepper.builder].
typedef ControlsWidgetBuilder = Widget Function(BuildContext context, {VoidCallback onStepContinue, VoidCallback onStepCancel});
/// An [Element] that composes other [Element]s. /// An [Element] that composes other [Element]s.
/// ///
/// Rather than creating a [RenderObject] directly, a [ComponentElement] creates /// Rather than creating a [RenderObject] directly, a [ComponentElement] creates
......
...@@ -368,6 +368,91 @@ void main() { ...@@ -368,6 +368,91 @@ void main() {
expect(find.text('2'), findsOneWidget); expect(find.text('2'), findsOneWidget);
}); });
testWidgets('Stepper custom controls test', (WidgetTester tester) async {
bool continuePressed = false;
void setContinue() {
continuePressed = true;
}
bool canceledPressed = false;
void setCanceled() {
canceledPressed = true;
}
final ControlsWidgetBuilder builder =
(BuildContext context, {VoidCallback onStepContinue, VoidCallback onStepCancel}) {
return Container(
margin: const EdgeInsets.only(top: 16.0),
child: ConstrainedBox(
constraints: const BoxConstraints.tightFor(height: 48.0),
child: Row(
children: <Widget>[
FlatButton(
onPressed: onStepContinue,
color: Colors.blue,
textColor: Colors.white,
textTheme: ButtonTextTheme.normal,
child: const Text('Let us continue!'),
),
Container(
margin: const EdgeInsetsDirectional.only(start: 8.0),
child: FlatButton(
onPressed: onStepCancel,
textColor: Colors.red,
textTheme: ButtonTextTheme.normal,
child: const Text('Cancel This!'),
),
),
],
),
),
);
};
await tester.pumpWidget(
MaterialApp(
home: Center(
child: Material(
child: Stepper(
controlsBuilder: builder,
onStepCancel: setCanceled,
onStepContinue: setContinue,
steps: const <Step>[
Step(
title: Text('A'),
state: StepState.complete,
content: SizedBox(
width: 100.0,
height: 100.0,
),
),
Step(
title: Text('B'),
content: SizedBox(
width: 100.0,
height: 100.0,
),
),
],
),
),
),
),
);
// 2 because stepper creates a set of controls for each step
expect(find.text('Let us continue!'), findsNWidgets(2));
expect(find.text('Cancel This!'), findsNWidgets(2));
await tester.tap(find.text('Cancel This!').first);
await tester.pumpAndSettle();
await tester.tap(find.text('Let us continue!').first);
await tester.pumpAndSettle();
expect(canceledPressed, isTrue);
expect(continuePressed, isTrue);
});
testWidgets('Stepper error test', (WidgetTester tester) async { testWidgets('Stepper error test', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
......
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