diff --git a/CHANGELOG.md b/CHANGELOG.md index 88c8ddf..96c7bbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [2.3.0] + +- Added lastBuilder to the StepIndicatorTheme to allow for custom last step indicator. +- Added useDashedLine to always use a dashed line for the stepper. +- Added activeLineColor to the StepIndicatorTheme to allow for custom active line color. +- Added hideStepWhenDone to the StepIndicatorTheme to scroll the stepper when the step is done. + ## [2.2.0] - 4 March 2024 - Added optional `lineHeight` to the `StepperTheme` to allow for custom line height. diff --git a/example/pubspec.lock b/example/pubspec.lock index 1be0ebf..7a89439 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: @@ -53,17 +53,26 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_lints: + flutter_iconica_analysis: dependency: "direct dev" + description: + path: "." + ref: "6.0.0" + resolved-ref: "5f0a02791db6785add4e445d383a1ac012fa8f55" + url: "https://github.com/Iconica-Development/flutter_iconica_analysis" + source: git + version: "6.0.0" + flutter_lints: + dependency: transitive description: name: flutter_lints sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c @@ -76,12 +85,36 @@ packages: path: ".." relative: true source: path - version: "2.1.0" + version: "2.3.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -94,87 +127,87 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.4" vector_math: dependency: transitive description: @@ -183,14 +216,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "14.3.1" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=1.17.0" + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/src/models/step_indicator_theme.dart b/lib/src/models/step_indicator_theme.dart index cf88969..e65c27d 100644 --- a/lib/src/models/step_indicator_theme.dart +++ b/lib/src/models/step_indicator_theme.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; class StepIndicatorTheme { const StepIndicatorTheme({ this.builder, + this.lastBuilder, this.completedBackgroundColor, this.completedBorder, this.completedTextStyle, @@ -22,6 +23,10 @@ class StepIndicatorTheme { /// and current index. final Widget Function(int stepIndex, int? currentIndex)? builder; + /// A function that defines custom widget builders based on the step index + /// and current index for the last step. + final Widget Function(int stepIndex, int? currentIndex)? lastBuilder; + /// The background color for active steps. final Color? activeBackgroundColor; diff --git a/lib/src/models/stepper_theme.dart b/lib/src/models/stepper_theme.dart index 7d696a4..2f560e8 100644 --- a/lib/src/models/stepper_theme.dart +++ b/lib/src/models/stepper_theme.dart @@ -22,6 +22,9 @@ class StepperTheme { this.paddingTopForCenterContent = 10, this.useIndicator = false, this.lineHeight = 0, + this.useDashedLine = false, + this.activeLineColor, + this.hideStepWhenDone = false, }); /// The padding between the start of the line and the stepper indicator. @@ -66,4 +69,13 @@ class StepperTheme { /// The height of the line between the steps when all steps are shown final double? lineHeight; + + /// Always use a dashedline + final bool useDashedLine; + + /// The color of the line coming from the current step + final Color? activeLineColor; + + /// scroll the completed steps up + final bool hideStepWhenDone; } diff --git a/lib/src/stepper.dart b/lib/src/stepper.dart index 8240bac..019c5f6 100644 --- a/lib/src/stepper.dart +++ b/lib/src/stepper.dart @@ -106,27 +106,34 @@ class _StepContent extends StatelessWidget { required this.steps, required this.index, required this.showOnlyCurrentStep, + required this.lineHeight, + required this.currentStep, }); final StepperTheme stepperTheme; final List steps; final bool showOnlyCurrentStep; final int index; + final double lineHeight; + final int currentStep; @override Widget build(BuildContext context) { var step = steps[index]; + if (currentStep - 1 > index && stepperTheme.hideStepWhenDone) { + return SizedBox(height: stepperTheme.linePadding); + } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: stepperTheme.linePadding), if (showOnlyCurrentStep) ...[ SizedBox( - height: index == steps.length - 1 ? 0 : stepperTheme.emptyHeight, + height: index == steps.length - 1 ? 0 : lineHeight, ), ] else if (step.hidden) ...[ SizedBox( - height: index == steps.length - 1 ? 0 : stepperTheme.emptyHeight, + height: index == steps.length - 1 ? 0 : lineHeight, width: stepperTheme.emptyWidth, ), ] else if (step.size != null) ...[ @@ -228,7 +235,24 @@ class _SinglePageStepper extends StatelessWidget { Widget build(BuildContext context) { Widget expandIndicators(Widget child) => !showOnlyCurrentStep ? Expanded(child: child) : child; + Widget dashedLine(Color lineColor) => Expanded( + child: CustomPaint( + painter: VerticalDashedLinePainter( + dashColor: lineColor, + dashHeight: stepperTheme.lineDashLength, + dashSpace: stepperTheme.lineDashGapLength, + strokeWidth: stepperTheme.lineWidth, + ), + ), + ); + Widget solidLine(Color lineColor) => Expanded( + child: Container( + height: stepperTheme.lineHeight, + width: stepperTheme.lineWidth, + color: lineColor, + ), + ); return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -236,60 +260,84 @@ class _SinglePageStepper extends StatelessWidget { Column( children: List.generate( steps.length, - (index) => IntrinsicHeight( - child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( - padding: linePadding, - child: Column( - children: [ - GestureDetector( - onTap: () {}, - child: stepperTheme.stepIndicatorTheme.builder - ?.call(index, currentIndex) ?? - _StepIndicator( - step: index, - isZeroIndexed: isZeroIndexed, - stepperTheme: stepperTheme, - currentIndex: currentIndex, - indicator: steps[index].indicator, - ), - ), - if (index < currentIndex) ...[ - Expanded( - child: Container( - height: stepperTheme.lineHeight, - width: stepperTheme.lineWidth, - color: stepperTheme.lineColor, - ), - ), - ] else ...[ - Expanded( - child: CustomPaint( - painter: VerticalDashedLinePainter( - dashColor: stepperTheme.lineColor, - dashHeight: stepperTheme.lineDashLength, - dashSpace: stepperTheme.lineDashGapLength, - strokeWidth: stepperTheme.lineWidth, - ), - ), + (index) { + var lineColor = stepperTheme.activeLineColor != null + ? currentIndex == index + ? stepperTheme.activeLineColor! + : stepperTheme.lineColor + : stepperTheme.lineColor; + Widget stepIndicator; + stepIndicator = stepperTheme.stepIndicatorTheme.builder + ?.call(index, currentIndex) ?? + _StepIndicator( + step: index, + isZeroIndexed: isZeroIndexed, + stepperTheme: stepperTheme, + currentIndex: currentIndex, + indicator: steps[index].indicator, + ); + if (currentIndex > index && stepperTheme.hideStepWhenDone) { + stepIndicator = const SizedBox.shrink(); + } + + if (steps.length - 1 == index) { + stepIndicator = stepperTheme.stepIndicatorTheme.lastBuilder + ?.call(index, currentIndex) ?? + _StepIndicator( + step: index, + isZeroIndexed: isZeroIndexed, + stepperTheme: stepperTheme, + currentIndex: currentIndex, + indicator: steps[index].indicator, + ); + } + Widget line; + + if (currentIndex < index) { + line = stepperTheme.useDashedLine + ? dashedLine(lineColor) + : solidLine(lineColor); + } else { + line = dashedLine(lineColor); + } + + if (currentIndex - 1 > index && stepperTheme.hideStepWhenDone) { + line = const SizedBox.shrink(); + } + + return IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: linePadding, + child: Column( + children: [ + GestureDetector( + onTap: () {}, + child: stepIndicator, ), + line, ], - ], + ), ), - ), - expandIndicators( - _StepContent( - stepperTheme: stepperTheme, - showOnlyCurrentStep: showOnlyCurrentStep, - steps: steps, - index: index, + expandIndicators( + _StepContent( + stepperTheme: stepperTheme, + showOnlyCurrentStep: showOnlyCurrentStep, + steps: steps, + index: index, + currentStep: currentIndex, + lineHeight: currentIndex > index && + stepperTheme.hideStepWhenDone + ? 40 + : stepperTheme.emptyHeight, + ), ), - ), - ], - ), - ), + ], + ), + ); + }, ), ), ), @@ -344,7 +392,6 @@ class _StepIndicator extends StatelessWidget { color = indicatorTheme.inactiveBackgroundColor ?? color; textStyle = indicatorTheme.inactiveTextStyle ?? textStyle; } - var indicator = this.indicator ?? Text( isZeroIndexed ? '$step' : '${step + 1}', diff --git a/pubspec.yaml b/pubspec.yaml index c0a3244..20ecd1e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_stepper description: Flutter package to create a Stepper widget that can be used to display a sequence of steps with a line between each step. -version: 2.2.0 +version: 2.3.0 homepage: https://github.com/Iconica-Development/stepper environment: diff --git a/test/stepper_test.dart b/test/stepper_test.dart index aa01283..1e30066 100644 --- a/test/stepper_test.dart +++ b/test/stepper_test.dart @@ -78,26 +78,25 @@ void main() { await tester.pumpWidget( const MaterialApp( home: MultiStepperView( + theme: StepperTheme(useDashedLine: true), showAllSteps: true, currentStep: 0, + showOnlyCurrentStep: true, steps: [ MultiViewStep( content: Text( 'First page', ), - size: 150, ), MultiViewStep( content: Text( 'Second page', ), - size: 150, ), MultiViewStep( content: Text( - 'Third page', + 'Second page', ), - size: 150, ), ], ),