Skip to content

Commit

Permalink
Implement red theme for stepper and improve coloring
Browse files Browse the repository at this point in the history
  • Loading branch information
UlrichRaab committed Jun 6, 2024
1 parent 15fbb7e commit b395f1c
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 74 deletions.
48 changes: 40 additions & 8 deletions example/lib/pages/stepper_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,53 @@ class _StepperPageState extends State<StepperPage> {
SBBStepperItem(label: 'Step 1'),
SBBStepperItem(label: 'Step 2'),
SBBStepperItem(label: 'Step 3'),
SBBStepperItem(label: 'Step 4', icon: SBBIcons.train_small),
SBBStepperItem(label: 'Step 4'),
SBBStepperItem(label: 'Step 5', icon: SBBIcons.tick_small),
];
var activeStep = 0;
var red = false;

@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsetsDirectional.all(24),
child: SBBStepper(
steps: steps,
activeStep: activeStep,
onStepPressed: _onStepPressed,
),
body: _stepper(context),
bottomNavigationBar: _footer(context),
);
}

Widget _stepper(BuildContext context) {
late final Widget stepper;
if (red) {
stepper = SBBStepper.red(
steps: steps,
activeStep: activeStep,
onStepPressed: _onStepPressed,
);
} else {
stepper = SBBStepper(
steps: steps,
activeStep: activeStep,
onStepPressed: _onStepPressed,
);
}
return Container(
padding: EdgeInsetsDirectional.all(24),
color: red ? SBBColors.red : null,
child: stepper,
);
}

Widget _footer(BuildContext context) {
return Container(
padding: EdgeInsetsDirectional.all(16),
child: Row(
children: [
Expanded(child: Text('RED')),
SBBSwitch(
value: red,
onChanged: (value) => setState(() => red = value),
),
],
),
);
}
Expand Down
125 changes: 59 additions & 66 deletions lib/src/stepper/sbb_stepper.dart
Original file line number Diff line number Diff line change
@@ -1,62 +1,90 @@
import 'package:flutter/material.dart';

import '../../design_system_flutter.dart';
import 'sbb_stepper_colors.dart';

typedef OnStepPressedCallback = void Function(SBBStepperItem item, int index);

/// SBB Stepper widget.
class SBBStepper extends StatelessWidget {
const SBBStepper({
super.key,
required this.steps,
required this.activeStep,
required this.onStepPressed,
});
required List<SBBStepperItem> steps,
required int activeStep,
required OnStepPressedCallback onStepPressed,
}) : _steps = steps,
_activeStep = activeStep,
_onStepPressed = onStepPressed,
_colors = null;

/// This variant of the SSB Stepper should be used on a red background.
const SBBStepper.red({
super.key,
required List<SBBStepperItem> steps,
required int activeStep,
required OnStepPressedCallback onStepPressed,
}) : _steps = steps,
_activeStep = activeStep,
_onStepPressed = onStepPressed,
_colors = SBBStepperColors.red;

/// The list of steps.
final List<SBBStepperItem> steps;
final List<SBBStepperItem> _steps;

/// The index of the active step.
final int activeStep;
final int _activeStep;

/// Called when the user has pressed a step.
final OnStepPressedCallback onStepPressed;
final OnStepPressedCallback _onStepPressed;

/// The colors of this stepper.
final SBBStepperColors? _colors;

@override
Widget build(BuildContext context) {
var colors = SBBStepperColors.light;
if (_colors != null) {
colors = _colors;
} else if (Theme.of(context).brightness == Brightness.dark) {
colors = SBBStepperColors.dark;
}
return LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth;
final lineWidth = (width - steps.length * 32) / (steps.length - 1);
final lineWidth = (width - _steps.length * 32) / (_steps.length - 1);
final widgets = <Widget>[];
for (int i = 0; i < steps.length; i++) {
final step = steps[i];
for (int i = 0; i < _steps.length; i++) {
final step = _steps[i];
final x = (i * 32) + (i * lineWidth);
// Create step circle widget.
final circle = PositionedDirectional(
start: x,
top: 0,
child: _Circle(
colors: colors,
index: i,
active: i == activeStep,
active: i == _activeStep,
icon: step.icon,
onPressed: () => onStepPressed(step, i),
onPressed: () => _onStepPressed(step, i),
),
);
widgets.add(circle);
// If the current step is not the last step create a connector line.
if (i <= steps.length - 1) {
if (i <= _steps.length - 1) {
final line = PositionedDirectional(
start: x + 32,
top: 16,
child: _Line(width: lineWidth),
child: _Line(
colors: colors,
width: lineWidth,
),
);
widgets.add(line);
}
// If this is the active step add the label below the circle. If the
// active step is the first or last step make sure that the label
// does not exceed the edge of the stepper.
if (i == activeStep) {
if (i == _activeStep) {
final size = _measureLabel(context, step.label, width);
var start = (x + 16) - (size.width / 2);
var end = start + size.width;
Expand All @@ -72,7 +100,7 @@ class SBBStepper extends StatelessWidget {
var textAlign = TextAlign.center;
if (i == 0) {
textAlign = TextAlign.start;
} else if (i >= steps.length - 1) {
} else if (i >= _steps.length - 1) {
textAlign = TextAlign.end;
}
final label = PositionedDirectional(
Expand All @@ -84,6 +112,7 @@ class SBBStepper extends StatelessWidget {
step.label,
style: baseStyle.themedTextStyle(
textStyle: SBBTextStyles.smallLight,
color: colors.label,
),
textAlign: textAlign,
overflow: TextOverflow.ellipsis,
Expand Down Expand Up @@ -116,12 +145,14 @@ class SBBStepper extends StatelessWidget {

class _Circle extends StatelessWidget {
const _Circle({
required this.colors,
required this.index,
required this.active,
this.icon,
this.onPressed,
});

final SBBStepperColors colors;
final int index;
final bool active;
final IconData? icon;
Expand All @@ -133,7 +164,7 @@ class _Circle extends StatelessWidget {
dimension: 32,
child: Material(
clipBehavior: Clip.antiAlias,
color: _backgroundColor(context),
color: colors.circleBackground(active),
shape: _shape(context),
child: InkWell(
onTap: onPressed,
Expand All @@ -145,37 +176,24 @@ class _Circle extends StatelessWidget {
);
}

Color _backgroundColor(BuildContext context) {
final theme = Theme.of(context);
if (theme.brightness == Brightness.dark) {
return active ? SBBColors.white : SBBColors.charcoal;
} else {
return active ? SBBColors.red : SBBColors.white;
}
}

ShapeBorder _shape(BuildContext context) {
if (active) {
return const CircleBorder();
}
final theme = Theme.of(context);
if (theme.brightness == Brightness.dark) {
return const CircleBorder(
side: BorderSide(color: SBBColors.white, width: 1),
);
} else {
return const CircleBorder(
side: BorderSide(color: SBBColors.smoke, width: 1),
);
}
return CircleBorder(
side: BorderSide(
color: colors.circleBorder(active),
width: 1,
),
);
}

Widget _iconOrNumber(BuildContext context) {
if (icon != null) {
return Icon(
icon,
size: 24,
color: _iconColor(context),
color: colors.circleContent(active),
);
} else {
final baseStyle = SBBBaseStyle.of(context);
Expand All @@ -184,36 +202,20 @@ class _Circle extends StatelessWidget {
number.toString(),
style: baseStyle.themedTextStyle(
textStyle: SBBTextStyles.mediumLight,
color: _numberColor(context),
color: colors.circleContent(active),
),
);
}
}

Color _iconColor(BuildContext context) {
final theme = Theme.of(context);
if (theme.brightness == Brightness.dark) {
return active ? SBBColors.black : SBBColors.white;
} else {
return active ? SBBColors.black : SBBColors.red;
}
}

Color _numberColor(BuildContext context) {
final theme = Theme.of(context);
if (theme.brightness == Brightness.dark) {
return active ? SBBColors.black : SBBColors.white;
} else {
return active ? SBBColors.white : SBBColors.black;
}
}
}

class _Line extends StatelessWidget {
const _Line({
required this.colors,
required this.width,
});

final SBBStepperColors colors;
final double width;

@override
Expand All @@ -225,16 +227,7 @@ class _Line extends StatelessWidget {
width: width - 4,
height: 1,
margin: const EdgeInsetsDirectional.fromSTEB(2, 0, 2, 0),
color: _color(context),
color: colors.line,
);
}

Color _color(BuildContext context) {
final theme = Theme.of(context);
if (theme.brightness == Brightness.dark) {
return SBBColors.white;
} else {
return SBBColors.smoke;
}
}
}
75 changes: 75 additions & 0 deletions lib/src/stepper/sbb_stepper_colors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import 'package:flutter/widgets.dart';

import '../theme/sbb_colors.dart';

enum SBBStepperColors {
light(
activeCircleBackground: SBBColors.red,
activeCircleContent: SBBColors.white,
inactiveCircleBackground: SBBColors.white,
inactiveCircleBorder: SBBColors.smoke,
inactiveCircleContent: SBBColors.black,
label: SBBColors.black,
line: SBBColors.smoke,
),
dark(
activeCircleBackground: SBBColors.white,
activeCircleContent: SBBColors.black,
inactiveCircleBackground: SBBColors.charcoal,
inactiveCircleBorder: SBBColors.white,
inactiveCircleContent: SBBColors.white,
label: SBBColors.white,
line: Color(0xB3FFFFFF),
),
red(
activeCircleBackground: SBBColors.white,
activeCircleContent: SBBColors.red,
inactiveCircleBackground: SBBColors.transparent,
inactiveCircleBorder: SBBColors.white,
inactiveCircleContent: SBBColors.white,
label: SBBColors.white,
line: Color(0xB3FFFFFF),
);

const SBBStepperColors({
required this.activeCircleBackground,
required this.activeCircleContent,
required this.inactiveCircleBackground,
required this.inactiveCircleBorder,
required this.inactiveCircleContent,
required this.label,
required this.line,
});

final Color activeCircleBackground;
final Color activeCircleContent;
final Color inactiveCircleBackground;
final Color inactiveCircleBorder;
final Color inactiveCircleContent;
final Color label;
final Color line;

Color circleBackground(bool active) {
if (active) {
return activeCircleBackground;
} else {
return inactiveCircleBackground;
}
}

Color circleBorder(bool active) {
if (active) {
return SBBColors.transparent;
} else {
return inactiveCircleBorder;
}
}

Color circleContent(bool active) {
if (active) {
return activeCircleContent;
} else {
return inactiveCircleContent;
}
}
}

0 comments on commit b395f1c

Please sign in to comment.