library easy_stepper; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'components/base_step.dart'; import 'components/custom_line.dart'; class CustomStepper extends StatefulWidget { final List steps; final OnStepReached onStepReached; final Color unreachedStepBackgroundColor; final Color unreachedStepTextColor; final Color unreachedStepIconColor; final Color unreachedStepBorderColor; final BorderType unreachedStepBorderType; final Color activeStepBackgroundColor; final Color activeStepTextColor; final Color activeStepIconColor; final Color activeStepBorderColor; final BorderType activeStepBorderType; final Color finishedStepBackgroundColor; final Color finishedStepBorderColor; final Color finishedStepTextColor; final Color finishedStepIconColor; final BorderType finishedStepBorderType; final double stepRadius; final int activeStep; final int maxReachedStep; final Set reachedSteps; final AlignmentGeometry alignment; final double internalPadding; final EdgeInsetsGeometry padding; final bool titlesAreLargerThanSteps; final Curve stepAnimationCurve; final Duration stepAnimationDuration; final double borderThickness; final StepShape stepShape; final double stepBorderRadius; final bool showStepBorder; final bool showScrollbar; final bool fitWidth; final LineStyle lineStyle; const CustomStepper({ Key key, @required this.activeStep, @required this.steps, this.reachedSteps, this.maxReachedStep, this.onStepReached, this.unreachedStepBackgroundColor, this.unreachedStepTextColor, this.unreachedStepIconColor, this.unreachedStepBorderColor, this.activeStepTextColor, this.activeStepIconColor=AppColor.primary10, this.activeStepBackgroundColor=AppColor.primary10, this.activeStepBorderColor=AppColor.primary10, this.finishedStepBackgroundColor=AppColor.primary10, this.finishedStepBorderColor, this.finishedStepIconColor, this.stepRadius = 20, this.alignment = Alignment.center, this.fitWidth = true, this.showScrollbar = false, this.padding=EdgeInsetsDirectional.zero, this.titlesAreLargerThanSteps = false, this.internalPadding = 8, this.stepAnimationCurve = Curves.linear, this.stepAnimationDuration = const Duration(seconds: 1), this.borderThickness = 1, this.stepShape = StepShape.circle, this.stepBorderRadius, this.unreachedStepBorderType, this.activeStepBorderType, this.finishedStepBorderType, this.showStepBorder = false, this.lineStyle, this.finishedStepTextColor, }) : assert(maxReachedStep == null || reachedSteps == null, 'only "maxReachedStep" or "reachedSteps" allowed'), super(key: key); @override State createState() => _CustomStepperState(); } class _CustomStepperState extends State { ScrollController _scrollController; int _selectedIndex; LineStyle lineStyle; EdgeInsetsGeometry _padding; @override void initState() { lineStyle = widget.lineStyle ?? const LineStyle(); _selectedIndex = widget.activeStep; _scrollController = ScrollController(); _padding = const EdgeInsetsDirectional.all(10); if (widget.steps.any((element) => element.topTitle)) { _padding = _padding.add(const EdgeInsetsDirectional.only(top: 45)); } if (widget.titlesAreLargerThanSteps) { _padding = _padding.add(EdgeInsetsDirectional.symmetric( horizontal: lineStyle.lineLength / 2)); } if (widget.padding != null) { _padding.add(widget.padding); } super.initState(); } @override void didUpdateWidget(CustomStepper oldWidget) { super.didUpdateWidget(oldWidget); // Verify that the active step falls within a valid range. if (widget.activeStep >= 0 && widget.activeStep < widget.steps.length) { _selectedIndex = widget.activeStep; } } @override void dispose() { _scrollController.dispose(); super.dispose(); } /// Controls the step scrolling. void _afterLayout(_) { for (int i = 0; i < widget.steps.length; i++) { _scrollController.animateTo( i * ((widget.stepRadius * 2) + widget.internalPadding + lineStyle.lineLength), duration: widget.stepAnimationDuration, curve: widget.stepAnimationCurve, ); if (_selectedIndex == i) break; } } @override Widget build(BuildContext context) { lineStyle = widget.lineStyle ?? const LineStyle(); return Align( alignment: widget.alignment, child: NotificationListener( onNotification: (OverscrollIndicatorNotification overscroll) { overscroll.disallowIndicator(); return false; }, child: FittedBox( fit: widget.fitWidth ? BoxFit.fitWidth : BoxFit.none, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: _buildEasySteps(), ), ) ), ); } List _buildEasySteps() { return List.generate(widget.steps.length, (index) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildStep(index), _buildLine(index, Axis.horizontal), ], ); }); } BaseStep _buildStep(int index) { final step = widget.steps[index]; return BaseStep( step: step, radius: widget.stepRadius, isActive: index == widget.activeStep, isFinished: widget.reachedSteps != null ? index < widget.activeStep && widget.reachedSteps.contains(index) : index < widget.activeStep, isUnreached: index > widget.activeStep, isAlreadyReached: widget.reachedSteps != null ? widget.reachedSteps.contains(index) : widget.maxReachedStep != null ? index <= widget.maxReachedStep : false, activeStepBackgroundColor: widget.activeStepBackgroundColor, activeStepBorderColor: widget.activeStepBorderColor, activeTextColor: widget.activeStepTextColor, activeIconColor: widget.activeStepIconColor, unreachedBackgroundColor: widget.unreachedStepBackgroundColor, unreachedBorderColor: widget.unreachedStepBorderColor, unreachedTextColor: widget.unreachedStepTextColor, unreachedIconColor: widget.unreachedStepIconColor, padding: max(widget.internalPadding, 0), stepRadius: widget.stepBorderRadius, showStepBorder: widget.showStepBorder, lineLength: lineStyle.lineLength, enabled: widget.steps[index].enabled, ); } // BorderType _handleBorderType(int index) { // if (index == widget.activeStep) { // //Active Step // return widget.activeStepBorderType ?? widget.defaultStepBorderType; // } else if (index > widget.activeStep) { // //Unreached Step // return widget.unreachedStepBorderType ?? widget.defaultStepBorderType; // } else if (index < widget.activeStep) { // //Finished Step // return widget.finishedStepBorderType ?? widget.defaultStepBorderType; // } else { // return widget.defaultStepBorderType; // } // } Color _getLineColor(int index) { Color preferredColor; if (index == widget.activeStep) { //Active Step preferredColor = lineStyle.activeLineColor; } else if (index > widget.activeStep) { //Unreached Step preferredColor = lineStyle.unreachedLineColor; } else if (index < widget.activeStep) { //Finished Step preferredColor = lineStyle.finishedLineColor; } return preferredColor ?? lineStyle.defaultLineColor ?? Theme.of(context).colorScheme.primary; } Widget _buildLine(int index, Axis axis) { return index < widget.steps.length - 1 ? Column( children: [ Padding( padding: EdgeInsets.only( top: axis == Axis.horizontal ? (widget.stepRadius - lineStyle.lineThickness) : 0, ), child: _buildBaseLine(index, axis), ), if (axis == Axis.horizontal && widget.steps[index].lineText != null) ...[ const SizedBox(height: 5), SizedBox( width: lineStyle.lineLength, child: widget.steps[index].customLineWidget ?? Text( widget.steps[index].lineText, maxLines: 3, textAlign: TextAlign.center, style: Theme.of(context).textTheme.labelSmall, ), ), ], ], ) : const Offstage(); } EasyLine _buildBaseLine(int index, Axis axis) { return EasyLine( length: lineStyle.lineLength, color: _getLineColor(index), thickness: lineStyle.lineThickness, spacing: lineStyle.lineSpace, width: lineStyle.lineWidth, axis: axis, lineType: index > widget.activeStep - 1 && lineStyle.unreachedLineType != null ? lineStyle.unreachedLineType : lineStyle.lineType, ); } }