126 lines
3.3 KiB
Dart
126 lines
3.3 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
||
|
|
|
||
|
|
import '../../helpers/theme.dart';
|
||
|
|
|
||
|
|
class StepperWidget extends StatefulWidget {
|
||
|
|
final int currentStep;
|
||
|
|
final List<String> labels;
|
||
|
|
final double eachStepWidth;
|
||
|
|
final double height;
|
||
|
|
final ValueChanged<int>? onChange;
|
||
|
|
|
||
|
|
const StepperWidget(
|
||
|
|
{Key? key,
|
||
|
|
this.currentStep = 0,
|
||
|
|
required this.labels,
|
||
|
|
this.eachStepWidth = 100,
|
||
|
|
this.height = 80,
|
||
|
|
this.onChange})
|
||
|
|
: super(key: key);
|
||
|
|
@override
|
||
|
|
State<StepperWidget> createState() => _StepperWidgetState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _StepperWidgetState extends State<StepperWidget> {
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
var body = _body();
|
||
|
|
return Center(
|
||
|
|
child: SingleChildScrollView(
|
||
|
|
scrollDirection: Axis.horizontal, child: body));
|
||
|
|
}
|
||
|
|
|
||
|
|
Widget _body() {
|
||
|
|
return SizedBox(
|
||
|
|
width: widget.eachStepWidth * widget.labels.length,
|
||
|
|
height: widget.height,
|
||
|
|
child: Stack(
|
||
|
|
children: [
|
||
|
|
Positioned(
|
||
|
|
bottom: 13,
|
||
|
|
left: widget.eachStepWidth / 2,
|
||
|
|
child: Container(
|
||
|
|
margin: const EdgeInsets.symmetric(horizontal: 0.0),
|
||
|
|
height: 1.0,
|
||
|
|
width: widget.eachStepWidth * (widget.labels.length - 1),
|
||
|
|
color: Colors.grey.shade400,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Row(
|
||
|
|
children: widget.labels.asMap().entries.map((e) {
|
||
|
|
return StepWidget(
|
||
|
|
label: e.value,
|
||
|
|
step: e.key,
|
||
|
|
stepWidth: widget.eachStepWidth,
|
||
|
|
currentStep: widget.currentStep,
|
||
|
|
onTap: () => _onTap(e.key),
|
||
|
|
end: e.key == widget.labels.length - 1,
|
||
|
|
);
|
||
|
|
}).toList()),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
_onTap(int index) {
|
||
|
|
if (widget.onChange != null) {
|
||
|
|
widget.onChange!(index);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class StepWidget extends StatelessWidget {
|
||
|
|
final String label;
|
||
|
|
final int step;
|
||
|
|
final double stepWidth;
|
||
|
|
final int currentStep;
|
||
|
|
final GestureTapCallback? onTap;
|
||
|
|
final bool end;
|
||
|
|
|
||
|
|
const StepWidget(
|
||
|
|
{Key? key,
|
||
|
|
required this.label,
|
||
|
|
required this.step,
|
||
|
|
this.stepWidth = 100,
|
||
|
|
required this.currentStep,
|
||
|
|
this.onTap,
|
||
|
|
this.end = false})
|
||
|
|
: super(key: key);
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
bool active = step == currentStep;
|
||
|
|
bool past = step < currentStep;
|
||
|
|
return InkWell(
|
||
|
|
onTap: onTap,
|
||
|
|
child: SizedBox(
|
||
|
|
width: stepWidth,
|
||
|
|
child: Column(
|
||
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
||
|
|
children: [
|
||
|
|
Text(label,
|
||
|
|
textAlign: TextAlign.center,
|
||
|
|
style: TextStyle(
|
||
|
|
color: active ? primaryColor : Colors.grey,
|
||
|
|
fontFamily: "Roboto")),
|
||
|
|
const SizedBox(height: 5),
|
||
|
|
CircleAvatar(
|
||
|
|
backgroundColor: end && active
|
||
|
|
? primaryColor
|
||
|
|
: active || past
|
||
|
|
? primaryColor
|
||
|
|
: Colors.grey,
|
||
|
|
radius: 13,
|
||
|
|
child: end
|
||
|
|
? const Icon(Icons.check, color: Colors.white, size: 20)
|
||
|
|
: Text(
|
||
|
|
(step + 1).toString(),
|
||
|
|
style: const TextStyle(color: Colors.white),
|
||
|
|
),
|
||
|
|
)
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|