Skip to content

Commit

Permalink
[Stepper] adds stepIconBuilder property (flutter#122816)
Browse files Browse the repository at this point in the history
[Stepper] adds stepIconBuilder property
  • Loading branch information
AyushBherwani1998 authored Mar 29, 2023
1 parent d74a1bb commit 9131115
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
19 changes: 18 additions & 1 deletion packages/flutter/lib/src/material/stepper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ class ControlsDetails {
/// * [WidgetBuilder], which is similar but only takes a [BuildContext].
typedef ControlsWidgetBuilder = Widget Function(BuildContext context, ControlsDetails details);

/// A builder that creates the icon widget for the [Step] at [stepIndex], given
/// [stepState].
typedef StepIconBuilder = Widget? Function(int stepIndex, StepState stepState);

const TextStyle _kStepStyle = TextStyle(
fontSize: 12.0,
color: Colors.white,
Expand Down Expand Up @@ -207,6 +211,7 @@ class Stepper extends StatefulWidget {
this.controlsBuilder,
this.elevation,
this.margin,
this.stepIconBuilder,
}) : assert(0 <= currentStep && currentStep < steps.length);

/// The steps of the stepper whose titles, subtitles, icons always get shown.
Expand Down Expand Up @@ -303,9 +308,17 @@ class Stepper extends StatefulWidget {
/// The elevation of this stepper's [Material] when [type] is [StepperType.horizontal].
final double? elevation;

/// custom margin on vertical stepper.
/// Custom margin on vertical stepper.
final EdgeInsetsGeometry? margin;

/// Callback for creating custom icons for the [steps].
///
/// When overriding icon for [StepState.error], please return
/// a widget whose width and height are 14 pixels or less to avoid overflow.
///
/// If null, the default icons will be used for respective [StepState].
final StepIconBuilder? stepIconBuilder;

@override
State<Stepper> createState() => _StepperState();
}
Expand Down Expand Up @@ -373,6 +386,10 @@ class _StepperState extends State<Stepper> with TickerProviderStateMixin {
Widget _buildCircleChild(int index, bool oldState) {
final StepState state = oldState ? _oldStates[index]! : widget.steps[index].state;
final bool isDarkActive = _isDark() && widget.steps[index].isActive;
final Widget? icon = widget.stepIconBuilder?.call(index, state);
if (icon != null) {
return icon;
}
switch (state) {
case StepState.indexed:
case StepState.disabled:
Expand Down
42 changes: 42 additions & 0 deletions packages/flutter/test/material/stepper_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,48 @@ testWidgets('Stepper custom indexed controls test', (WidgetTester tester) async
tester.widget<Text>(find.text('Label ${index + 2}'));
expect(bodyMediumStyle, nextLabelTextWidget.style);
});

testWidgets('Stepper stepIconBuilder test', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Stepper(
stepIconBuilder: (int index, StepState state) {
if (state == StepState.complete) {
return const FlutterLogo(size: 18);
}
return null;
},
steps: const <Step>[
Step(
title: Text('A'),
state: StepState.complete,
content: SizedBox(width: 100.0, height: 100.0),
),
Step(
title: Text('B'),
state: StepState.editing,
content: SizedBox(width: 100.0, height: 100.0),
),
Step(
title: Text('C'),
state: StepState.error,
content: SizedBox(width: 100.0, height: 100.0),
),
],
),
),
),
);

/// Finds the overridden widget for StepState.complete
expect(find.byType(FlutterLogo), findsOneWidget);

/// StepState.editing and StepState.error should have a default icon
expect(find.byIcon(Icons.edit), findsOneWidget);
expect(find.text('!'), findsOneWidget);
});

}

class _TappableColorWidget extends StatefulWidget {
Expand Down

0 comments on commit 9131115

Please sign in to comment.