widget appearanceI have a Flutter code snippet where I'm using CustomPaint
and Stack
widgets to create a progress bar with steps. However, I am facing an issue with the labelText
when it is too long; it doesn't fit within the SizedBox
.
Here is my code:
import 'package:flutter/material.dart';
import 'package:percent_indicator/percent_indicator.dart';
class StepperProgressBar extends StatelessWidget {
final List<Widget> stepUnits;
StepperProgressBar({
super.key,
required this.stepUnits,
});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
width: 450,
child: ListView(
scrollDirection: Axis.horizontal,
children: stepUnits,
),
);
}
}
class StepUnit extends StatelessWidget {
final double progressBarHeight;
double? progressBarWidth;
double? circularStepSize;
double? circularStepWidth;
final double progressBarValue;
final Icon? icon;
bool isFirst = false;
final Color? backgroundColor;
final Color? progressColor;
final String? labelText;
StepUnit({
super.key,
this.progressBarHeight = 5,
this.progressBarWidth,
this.circularStepSize,
this.circularStepWidth,
this.progressBarValue = 0.0,
this.icon,
this.progressColor = const Color.fromARGB(255, 90, 116, 125),
this.backgroundColor = const Color.fromARGB(255, 161, 169, 172),
this.labelText,
});
@override
Widget build(BuildContext context) {
bool isDone = progressBarValue >= 1;
bool isInProgress = progressBarValue > 0 && progressBarValue < 1;
final stepUnitBoxWidth = SizedBox(width: 11);
progressBarWidth ??= MediaQuery.of(context).size.width * 0.3;
circularStepSize ??= MediaQuery.of(context).size.width * 0.11;
circularStepWidth ??= MediaQuery.of(context).size.width * 0.1;
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (!isFirst) ...[
stepUnitBoxWidth,
LinearPercentIndicator(
width: progressBarWidth,
animation: false,
lineHeight: progressBarHeight,
animationDuration: 2500,
percent: progressBarValue,
progressColor: progressColor,
backgroundColor: backgroundColor,
barRadius: Radius.circular(10),
alignment: MainAxisAlignment.start,
),
],
stepUnitBoxWidth,
SizedBox(
height: 300,
width: 103, // Increased width to fit longer label
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: CirclePainter(
isDone: isDone,
width: circularStepWidth!,
isInProgress: isInProgress,
backgroundColor: backgroundColor!,
progressColor: progressColor!,
),
size: Size(circularStepSize!, circularStepSize!),
),
if (isDone)
icon ?? const Icon(Icons.done, size: 25, color: Colors.white)
else if (icon != null)
icon!,
if (labelText != null)
Positioned(
bottom: 30,
child: Text(
labelText!,
style: TextStyle(fontSize: 15, color: Colors.blueGrey),
),
),
],
),
),
],
);
}
}
class CirclePainter extends CustomPainter {
final bool isDone;
final double width;
final bool isInProgress;
final Color backgroundColor;
final Color progressColor;
CirclePainter({
this.isDone = false,
this.width = 4,
this.isInProgress = false,
required this.progressColor,
required this.backgroundColor,
});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = isDone ? progressColor : backgroundColor
..strokeWidth = 4.0
..style = isInProgress ? PaintingStyle.stroke : PaintingStyle.fill;
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
canvas.drawCircle(center, radius, paint);
if (isInProgress) {
paint.style = PaintingStyle.fill;
canvas.drawCircle(center, radius - radius / 3, paint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
Example usage:
List<StepUnit> stepUnits = [
StepUnit(
progressBarValue: 1,
labelText: 'STEP 1',
),
StepUnit(
progressBarValue: 1,
labelText: 'TEXTTOOLONG',
),
StepUnit(
progressBarValue: 1,
labelText: 'STEP 3',
),
StepUnit(
progressBarValue: 1,
labelText: 'STEP 4',
),
];
When the labelText
is a long string, it doesn't fit within the SizedBox
. I increased the width of the surrounding SizedBox
in the Stack
:
SizedBox(
height: 300,
width: 103,
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: CirclePainter(
isDone: isDone,
width: circularStepWidth!,
isInProgress: isInProgress,
backgroundColor: backgroundColor!,
progressColor: progressColor!,
),
size: Size(circularStepSize!, circularStepSize!),
),
if (isDone)
icon ?? const Icon(Icons.done, size: 25, color: Colors.white)
else if (icon != null)
icon!,
if (labelText != null)
Positioned(
bottom: 30,
child: Text(
labelText!,
style: TextStyle(fontSize: 15, color: Colors.blueGrey),
),
),
],
),
),
However, increasing the width of the SizedBox
affects the layout of the widgets, and I can't achieve the desired look.
I need the Text
widgets to expand to the right, regardless of the font size or length of the text, without disrupting the layout. An example of the desired appearance is shown in the second photo attached.desired widget appearance