├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── doc
└── screenshots
│ ├── article_thumbnail.png
│ ├── circular_step_progress_indicator
│ ├── circular_animation1.gif
│ ├── circular_bar_example1.png
│ ├── circular_bar_example2.png
│ ├── circular_bar_example3.png
│ ├── circular_bar_example4.png
│ └── circular_indicator1.png
│ ├── screen1.png
│ ├── screen2.png
│ ├── screen3.png
│ ├── screen4.png
│ └── step_progress_indicator
│ ├── linear_bar_example1.png
│ ├── linear_bar_example2.png
│ ├── linear_bar_example3.png
│ ├── linear_bar_example4.png
│ ├── linear_bar_example5.png
│ ├── linear_bar_example6.png
│ ├── linear_bar_example7.png
│ ├── linear_bar_example8.png
│ └── linear_bar_example9.png
├── example
├── app_example
│ ├── .gitignore
│ ├── .metadata
│ ├── README.md
│ ├── analysis_options.yaml
│ ├── android
│ │ ├── .gitignore
│ │ ├── app
│ │ │ ├── build.gradle
│ │ │ └── src
│ │ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ │ ├── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ │ └── example
│ │ │ │ │ │ └── app_example
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── res
│ │ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── values-night
│ │ │ │ │ └── styles.xml
│ │ │ │ │ └── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ └── gradle-wrapper.properties
│ │ └── settings.gradle
│ ├── ios
│ │ ├── .gitignore
│ │ ├── Flutter
│ │ │ ├── AppFrameworkInfo.plist
│ │ │ ├── Debug.xcconfig
│ │ │ └── Release.xcconfig
│ │ ├── Runner.xcodeproj
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcshareddata
│ │ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ │ └── WorkspaceSettings.xcsettings
│ │ │ └── xcshareddata
│ │ │ │ └── xcschemes
│ │ │ │ └── Runner.xcscheme
│ │ ├── Runner.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── Runner
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ │ └── LaunchImage.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ └── README.md
│ │ │ ├── Base.lproj
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ │ ├── Info.plist
│ │ │ └── Runner-Bridging-Header.h
│ ├── lib
│ │ └── main.dart
│ ├── pubspec.lock
│ ├── pubspec.yaml
│ ├── test
│ │ └── widget_test.dart
│ └── web
│ │ ├── favicon.png
│ │ ├── icons
│ │ ├── Icon-192.png
│ │ ├── Icon-512.png
│ │ ├── Icon-maskable-192.png
│ │ └── Icon-maskable-512.png
│ │ ├── index.html
│ │ └── manifest.json
├── circular_animation1.dart
├── circular_bar1.dart
├── circular_bar2.dart
├── horizontal_bar.dart
├── main.dart
└── vertical_bar.dart
├── lib
├── src
│ ├── circular_step_progress_indicator.dart
│ └── step_progress_indicator.dart
└── step_progress_indicator.dart
├── pubspec.lock
├── pubspec.yaml
└── test
├── circular_step_progress_indicator_test.dart
└── step_progress_indicator_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | .vscode/
19 |
20 | # The .vscode folder contains launch configuration and tasks you configure in
21 | # VS Code which you may wish to be included in version control, so this line
22 | # is commented out by default.
23 | #.vscode/
24 |
25 | # Flutter/Dart/Pub related
26 | **/doc/api/
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | build/
34 |
35 | # Android related
36 | **/android/**/gradle-wrapper.jar
37 | **/android/.gradle
38 | **/android/captures/
39 | **/android/gradlew
40 | **/android/gradlew.bat
41 | **/android/local.properties
42 | **/android/**/GeneratedPluginRegistrant.java
43 |
44 | # iOS/XCode related
45 | **/ios/**/*.mode1v3
46 | **/ios/**/*.mode2v3
47 | **/ios/**/*.moved-aside
48 | **/ios/**/*.pbxuser
49 | **/ios/**/*.perspectivev3
50 | **/ios/**/*sync/
51 | **/ios/**/.sconsign.dblite
52 | **/ios/**/.tags*
53 | **/ios/**/.vagrant/
54 | **/ios/**/DerivedData/
55 | **/ios/**/Icon?
56 | **/ios/**/Pods/
57 | **/ios/**/.symlinks/
58 | **/ios/**/profile
59 | **/ios/**/xcuserdata
60 | **/ios/.generated/
61 | **/ios/Flutter/App.framework
62 | **/ios/Flutter/Flutter.framework
63 | **/ios/Flutter/Flutter.podspec
64 | **/ios/Flutter/Generated.xcconfig
65 | **/ios/Flutter/app.flx
66 | **/ios/Flutter/app.zip
67 | **/ios/Flutter/flutter_assets/
68 | **/ios/Flutter/flutter_export_environment.sh
69 | **/ios/ServiceDefinitions.json
70 | **/ios/Runner/GeneratedPluginRegistrant.*
71 |
72 | # Exceptions to above rules.
73 | !**/ios/**/default.mode1v3
74 | !**/ios/**/default.mode2v3
75 | !**/ios/**/default.pbxuser
76 | !**/ios/**/default.perspectivev3
77 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
78 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 27321ebbad34b0a3fafe99fac037102196d655ff
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.0.2] - 2 January 2022
2 | - Fixed issue with `blendMode` and `roundedEdges` ([#32](https://github.com/SandroMaglione/step-progress-indicator/issues/32))
3 |
4 | ## [1.0.1] - 16 June 2021
5 | - `StepProgressIndicator` and `CircularStepProgressIndicator` constructors as `const`
6 | - Added `stepMainAxisAlignment` and `stepCrossAxisAlignment` to align single step in `StepProgressIndicator` ([#28](https://github.com/SandroMaglione/step-progress-indicator/issues/28))
7 |
8 | ## [1.0.0] Nullsafety - 10 May 2021
9 | - Added `removeRoundedCapExtraAngle` to `CircularStepProgressIndicator` to remove extra angle caused by `StrokeCap.butt` when `roundedCap` is applied ([#20](https://github.com/SandroMaglione/step-progress-indicator/issues/20#issue-786114745))
10 | - Added the option to specify alignment for `StepProgressIndicator` (Thanks to [@faridg18](https://github.com/faridg18) for his contribution ([#15](https://github.com/SandroMaglione/step-progress-indicator/pull/15)))
11 | - Added `blendMode` for gradient of `StepProgressIndicator` ([#16](https://github.com/SandroMaglione/step-progress-indicator/issues/16))
12 | - Fixed issue on `StepProgressIndicator` when adding `roundedEdges` with `padding == 0` and no steps selected ([#23](https://github.com/SandroMaglione/step-progress-indicator/issues/23))
13 |
14 | ## [0.2.5+8] - 01 December 2020
15 | - Fixed issue when adding `roundedEdges` with only one step ([#12](https://github.com/SandroMaglione/step-progress-indicator/issues/12))
16 | - Added more widget tests
17 |
18 | ## [0.2.4+7] - 25 August 2020
19 | - Added `roundedCap` property to `CircularStepProgressIndicator` ([#7](https://github.com/SandroMaglione/step-progress-indicator/issues/7))
20 | - Added `gradientColor` property to `CircularStepProgressIndicator`, and `gradientColor`, `selectedGradientColor` and `unselectedGradientColor` to `StepProgressIndicator` ([#8](https://github.com/SandroMaglione/step-progress-indicator/issues/8))
21 | - Fixed `customStepSize` when `circularDirection` is `CircularDirection.counterclockwise`: now the step indexes start at 0 from the left to the right as expected
22 | - Added `isSelected` bool parameter to `customStepSize`, used to change the step size based on the selected/unselected status of the current step (**breaking change**)
23 | - Updated and expanded documentation examples
24 | - Fixed documentations updates
25 |
26 | ## [0.2.3+6] - 20 May 2020
27 | - Added `arcSize` property to `CircularStepProgressIndicator` that allows you to draw semi-circle indicators ([#3](https://github.com/SandroMaglione/step-progress-indicator/issues/3))
28 | - Adjusted default `startingAngle` to 0 instead of `-math.pi / 2` (**breaking change**)
29 | - Fixed imports and small issues
30 |
31 | ## [0.2.2+5] - 26 April 2020
32 | - Added material ripple effect on step tap (Thanks to [@rodineijf](https://github.com/rodineijf) for his contribution ([#1](https://github.com/SandroMaglione/step-progress-indicator/pull/1)))
33 | - Implemented `roundedEdges` property to add rounded edge corners to first and last step of the indicator ([#2](https://github.com/SandroMaglione/step-progress-indicator/issues/2))
34 | - Updated and improved documentation and [examples](https://github.com/SandroMaglione/step-progress-indicator/tree/master/example)
35 |
36 | ## [0.2.1+4] - 25 Febraury 2020
37 | - Updated LICENSE, from GPL3 to MIT
38 |
39 | ## [0.2.0+3] - 24 Febraury 2020
40 | - Added **CircularStepProgressIndicator**
41 | - Added optimized support for **Continuous Progress Indicators** (by setting `padding` to 0 and not using custom attributes)
42 | - Added more attributes
43 | * `customSize`
44 | * `selectedSize`
45 | * `unselectedSize`
46 | * `fallbackLength`
47 | - Made indicator custom attributes (`customStep`, `customColor`, `onTap`, `customSize`) zero-based (**breaking change**)
48 | - Added more [examples](https://github.com/SandroMaglione/step-progress-indicator/tree/master/example)
49 | - Updated the documentation with more screens and detailed examples
50 | - Added CircularStepProgressIndicator [animation example](https://github.com/SandroMaglione/step-progress-indicator/blob/master/example/circular_animation1.dart)
51 | - Bug fixing
52 |
53 | ## [0.1.1+2] - 24 January 2020
54 | - Completed documentation
55 | - [New tutorial article](https://www.sandromaglione.com/blog/2020/01/24/step-progress-indicator-flutter-package-tutorial/)
56 |
57 | ## [0.1.0+1] - 23 January 2020
58 | Initial release
59 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Sandro Maglione
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/doc/screenshots/article_thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/article_thumbnail.png
--------------------------------------------------------------------------------
/doc/screenshots/circular_step_progress_indicator/circular_animation1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/circular_step_progress_indicator/circular_animation1.gif
--------------------------------------------------------------------------------
/doc/screenshots/circular_step_progress_indicator/circular_bar_example1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/circular_step_progress_indicator/circular_bar_example1.png
--------------------------------------------------------------------------------
/doc/screenshots/circular_step_progress_indicator/circular_bar_example2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/circular_step_progress_indicator/circular_bar_example2.png
--------------------------------------------------------------------------------
/doc/screenshots/circular_step_progress_indicator/circular_bar_example3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/circular_step_progress_indicator/circular_bar_example3.png
--------------------------------------------------------------------------------
/doc/screenshots/circular_step_progress_indicator/circular_bar_example4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/circular_step_progress_indicator/circular_bar_example4.png
--------------------------------------------------------------------------------
/doc/screenshots/circular_step_progress_indicator/circular_indicator1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/circular_step_progress_indicator/circular_indicator1.png
--------------------------------------------------------------------------------
/doc/screenshots/screen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/screen1.png
--------------------------------------------------------------------------------
/doc/screenshots/screen2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/screen2.png
--------------------------------------------------------------------------------
/doc/screenshots/screen3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/screen3.png
--------------------------------------------------------------------------------
/doc/screenshots/screen4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/screen4.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example1.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example2.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example3.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example4.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example5.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example6.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example7.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example8.png
--------------------------------------------------------------------------------
/doc/screenshots/step_progress_indicator/linear_bar_example9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/doc/screenshots/step_progress_indicator/linear_bar_example9.png
--------------------------------------------------------------------------------
/example/app_example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/example/app_example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: cf4400006550b70f28e4b4af815151d1e74846c6
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/app_example/README.md:
--------------------------------------------------------------------------------
1 | # app_example
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/app_example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/example/app_example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/example/app_example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion flutter.compileSdkVersion
30 |
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 |
36 | kotlinOptions {
37 | jvmTarget = '1.8'
38 | }
39 |
40 | sourceSets {
41 | main.java.srcDirs += 'src/main/kotlin'
42 | }
43 |
44 | defaultConfig {
45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
46 | applicationId "com.example.app_example"
47 | minSdkVersion flutter.minSdkVersion
48 | targetSdkVersion flutter.targetSdkVersion
49 | versionCode flutterVersionCode.toInteger()
50 | versionName flutterVersionName
51 | }
52 |
53 | buildTypes {
54 | release {
55 | // TODO: Add your own signing config for the release build.
56 | // Signing with the debug keys for now, so `flutter run --release` works.
57 | signingConfig signingConfigs.debug
58 | }
59 | }
60 | }
61 |
62 | flutter {
63 | source '../..'
64 | }
65 |
66 | dependencies {
67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
68 | }
69 |
--------------------------------------------------------------------------------
/example/app_example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/kotlin/com/example/app_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.app_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/app_example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/app_example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/app_example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/example/app_example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/app_example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
7 |
--------------------------------------------------------------------------------
/example/app_example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/example/app_example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/example/app_example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/app_example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/app_example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = "";
23 | dstSubfolderSpec = 10;
24 | files = (
25 | );
26 | name = "Embed Frameworks";
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXCopyFilesBuildPhase section */
30 |
31 | /* Begin PBXFileReference section */
32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 9740EEB11CF90186004384FC /* Flutter */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
65 | );
66 | name = Flutter;
67 | sourceTree = "";
68 | };
69 | 97C146E51CF9000F007C117D = {
70 | isa = PBXGroup;
71 | children = (
72 | 9740EEB11CF90186004384FC /* Flutter */,
73 | 97C146F01CF9000F007C117D /* Runner */,
74 | 97C146EF1CF9000F007C117D /* Products */,
75 | );
76 | sourceTree = "";
77 | };
78 | 97C146EF1CF9000F007C117D /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 97C146EE1CF9000F007C117D /* Runner.app */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 97C146F01CF9000F007C117D /* Runner */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
92 | 97C147021CF9000F007C117D /* Info.plist */,
93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
97 | );
98 | path = Runner;
99 | sourceTree = "";
100 | };
101 | /* End PBXGroup section */
102 |
103 | /* Begin PBXNativeTarget section */
104 | 97C146ED1CF9000F007C117D /* Runner */ = {
105 | isa = PBXNativeTarget;
106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
107 | buildPhases = (
108 | 9740EEB61CF901F6004384FC /* Run Script */,
109 | 97C146EA1CF9000F007C117D /* Sources */,
110 | 97C146EB1CF9000F007C117D /* Frameworks */,
111 | 97C146EC1CF9000F007C117D /* Resources */,
112 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
114 | );
115 | buildRules = (
116 | );
117 | dependencies = (
118 | );
119 | name = Runner;
120 | productName = Runner;
121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
122 | productType = "com.apple.product-type.application";
123 | };
124 | /* End PBXNativeTarget section */
125 |
126 | /* Begin PBXProject section */
127 | 97C146E61CF9000F007C117D /* Project object */ = {
128 | isa = PBXProject;
129 | attributes = {
130 | LastUpgradeCheck = 1300;
131 | ORGANIZATIONNAME = "";
132 | TargetAttributes = {
133 | 97C146ED1CF9000F007C117D = {
134 | CreatedOnToolsVersion = 7.3.1;
135 | LastSwiftMigration = 1100;
136 | };
137 | };
138 | };
139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
140 | compatibilityVersion = "Xcode 9.3";
141 | developmentRegion = en;
142 | hasScannedForEncodings = 0;
143 | knownRegions = (
144 | en,
145 | Base,
146 | );
147 | mainGroup = 97C146E51CF9000F007C117D;
148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
149 | projectDirPath = "";
150 | projectRoot = "";
151 | targets = (
152 | 97C146ED1CF9000F007C117D /* Runner */,
153 | );
154 | };
155 | /* End PBXProject section */
156 |
157 | /* Begin PBXResourcesBuildPhase section */
158 | 97C146EC1CF9000F007C117D /* Resources */ = {
159 | isa = PBXResourcesBuildPhase;
160 | buildActionMask = 2147483647;
161 | files = (
162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXResourcesBuildPhase section */
170 |
171 | /* Begin PBXShellScriptBuildPhase section */
172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
173 | isa = PBXShellScriptBuildPhase;
174 | buildActionMask = 2147483647;
175 | files = (
176 | );
177 | inputPaths = (
178 | );
179 | name = "Thin Binary";
180 | outputPaths = (
181 | );
182 | runOnlyForDeploymentPostprocessing = 0;
183 | shellPath = /bin/sh;
184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
185 | };
186 | 9740EEB61CF901F6004384FC /* Run Script */ = {
187 | isa = PBXShellScriptBuildPhase;
188 | buildActionMask = 2147483647;
189 | files = (
190 | );
191 | inputPaths = (
192 | );
193 | name = "Run Script";
194 | outputPaths = (
195 | );
196 | runOnlyForDeploymentPostprocessing = 0;
197 | shellPath = /bin/sh;
198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
199 | };
200 | /* End PBXShellScriptBuildPhase section */
201 |
202 | /* Begin PBXSourcesBuildPhase section */
203 | 97C146EA1CF9000F007C117D /* Sources */ = {
204 | isa = PBXSourcesBuildPhase;
205 | buildActionMask = 2147483647;
206 | files = (
207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
209 | );
210 | runOnlyForDeploymentPostprocessing = 0;
211 | };
212 | /* End PBXSourcesBuildPhase section */
213 |
214 | /* Begin PBXVariantGroup section */
215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
216 | isa = PBXVariantGroup;
217 | children = (
218 | 97C146FB1CF9000F007C117D /* Base */,
219 | );
220 | name = Main.storyboard;
221 | sourceTree = "";
222 | };
223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
224 | isa = PBXVariantGroup;
225 | children = (
226 | 97C147001CF9000F007C117D /* Base */,
227 | );
228 | name = LaunchScreen.storyboard;
229 | sourceTree = "";
230 | };
231 | /* End PBXVariantGroup section */
232 |
233 | /* Begin XCBuildConfiguration section */
234 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
235 | isa = XCBuildConfiguration;
236 | buildSettings = {
237 | ALWAYS_SEARCH_USER_PATHS = NO;
238 | CLANG_ANALYZER_NONNULL = YES;
239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
240 | CLANG_CXX_LIBRARY = "libc++";
241 | CLANG_ENABLE_MODULES = YES;
242 | CLANG_ENABLE_OBJC_ARC = YES;
243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
244 | CLANG_WARN_BOOL_CONVERSION = YES;
245 | CLANG_WARN_COMMA = YES;
246 | CLANG_WARN_CONSTANT_CONVERSION = YES;
247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
249 | CLANG_WARN_EMPTY_BODY = YES;
250 | CLANG_WARN_ENUM_CONVERSION = YES;
251 | CLANG_WARN_INFINITE_RECURSION = YES;
252 | CLANG_WARN_INT_CONVERSION = YES;
253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
258 | CLANG_WARN_STRICT_PROTOTYPES = YES;
259 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
260 | CLANG_WARN_UNREACHABLE_CODE = YES;
261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
263 | COPY_PHASE_STRIP = NO;
264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
265 | ENABLE_NS_ASSERTIONS = NO;
266 | ENABLE_STRICT_OBJC_MSGSEND = YES;
267 | GCC_C_LANGUAGE_STANDARD = gnu99;
268 | GCC_NO_COMMON_BLOCKS = YES;
269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
271 | GCC_WARN_UNDECLARED_SELECTOR = YES;
272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
273 | GCC_WARN_UNUSED_FUNCTION = YES;
274 | GCC_WARN_UNUSED_VARIABLE = YES;
275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
276 | MTL_ENABLE_DEBUG_INFO = NO;
277 | SDKROOT = iphoneos;
278 | SUPPORTED_PLATFORMS = iphoneos;
279 | TARGETED_DEVICE_FAMILY = "1,2";
280 | VALIDATE_PRODUCT = YES;
281 | };
282 | name = Profile;
283 | };
284 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
285 | isa = XCBuildConfiguration;
286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
287 | buildSettings = {
288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
289 | CLANG_ENABLE_MODULES = YES;
290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
291 | ENABLE_BITCODE = NO;
292 | INFOPLIST_FILE = Runner/Info.plist;
293 | LD_RUNPATH_SEARCH_PATHS = (
294 | "$(inherited)",
295 | "@executable_path/Frameworks",
296 | );
297 | PRODUCT_BUNDLE_IDENTIFIER = com.example.appExample;
298 | PRODUCT_NAME = "$(TARGET_NAME)";
299 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
300 | SWIFT_VERSION = 5.0;
301 | VERSIONING_SYSTEM = "apple-generic";
302 | };
303 | name = Profile;
304 | };
305 | 97C147031CF9000F007C117D /* Debug */ = {
306 | isa = XCBuildConfiguration;
307 | buildSettings = {
308 | ALWAYS_SEARCH_USER_PATHS = NO;
309 | CLANG_ANALYZER_NONNULL = YES;
310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
311 | CLANG_CXX_LIBRARY = "libc++";
312 | CLANG_ENABLE_MODULES = YES;
313 | CLANG_ENABLE_OBJC_ARC = YES;
314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
315 | CLANG_WARN_BOOL_CONVERSION = YES;
316 | CLANG_WARN_COMMA = YES;
317 | CLANG_WARN_CONSTANT_CONVERSION = YES;
318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
320 | CLANG_WARN_EMPTY_BODY = YES;
321 | CLANG_WARN_ENUM_CONVERSION = YES;
322 | CLANG_WARN_INFINITE_RECURSION = YES;
323 | CLANG_WARN_INT_CONVERSION = YES;
324 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
325 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
326 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
327 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
329 | CLANG_WARN_STRICT_PROTOTYPES = YES;
330 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
331 | CLANG_WARN_UNREACHABLE_CODE = YES;
332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
334 | COPY_PHASE_STRIP = NO;
335 | DEBUG_INFORMATION_FORMAT = dwarf;
336 | ENABLE_STRICT_OBJC_MSGSEND = YES;
337 | ENABLE_TESTABILITY = YES;
338 | GCC_C_LANGUAGE_STANDARD = gnu99;
339 | GCC_DYNAMIC_NO_PIC = NO;
340 | GCC_NO_COMMON_BLOCKS = YES;
341 | GCC_OPTIMIZATION_LEVEL = 0;
342 | GCC_PREPROCESSOR_DEFINITIONS = (
343 | "DEBUG=1",
344 | "$(inherited)",
345 | );
346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
348 | GCC_WARN_UNDECLARED_SELECTOR = YES;
349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
350 | GCC_WARN_UNUSED_FUNCTION = YES;
351 | GCC_WARN_UNUSED_VARIABLE = YES;
352 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
353 | MTL_ENABLE_DEBUG_INFO = YES;
354 | ONLY_ACTIVE_ARCH = YES;
355 | SDKROOT = iphoneos;
356 | TARGETED_DEVICE_FAMILY = "1,2";
357 | };
358 | name = Debug;
359 | };
360 | 97C147041CF9000F007C117D /* Release */ = {
361 | isa = XCBuildConfiguration;
362 | buildSettings = {
363 | ALWAYS_SEARCH_USER_PATHS = NO;
364 | CLANG_ANALYZER_NONNULL = YES;
365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
366 | CLANG_CXX_LIBRARY = "libc++";
367 | CLANG_ENABLE_MODULES = YES;
368 | CLANG_ENABLE_OBJC_ARC = YES;
369 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
370 | CLANG_WARN_BOOL_CONVERSION = YES;
371 | CLANG_WARN_COMMA = YES;
372 | CLANG_WARN_CONSTANT_CONVERSION = YES;
373 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
374 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
375 | CLANG_WARN_EMPTY_BODY = YES;
376 | CLANG_WARN_ENUM_CONVERSION = YES;
377 | CLANG_WARN_INFINITE_RECURSION = YES;
378 | CLANG_WARN_INT_CONVERSION = YES;
379 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
380 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
381 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
382 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
383 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
384 | CLANG_WARN_STRICT_PROTOTYPES = YES;
385 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
386 | CLANG_WARN_UNREACHABLE_CODE = YES;
387 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
388 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
389 | COPY_PHASE_STRIP = NO;
390 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
391 | ENABLE_NS_ASSERTIONS = NO;
392 | ENABLE_STRICT_OBJC_MSGSEND = YES;
393 | GCC_C_LANGUAGE_STANDARD = gnu99;
394 | GCC_NO_COMMON_BLOCKS = YES;
395 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
396 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
397 | GCC_WARN_UNDECLARED_SELECTOR = YES;
398 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
399 | GCC_WARN_UNUSED_FUNCTION = YES;
400 | GCC_WARN_UNUSED_VARIABLE = YES;
401 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
402 | MTL_ENABLE_DEBUG_INFO = NO;
403 | SDKROOT = iphoneos;
404 | SUPPORTED_PLATFORMS = iphoneos;
405 | SWIFT_COMPILATION_MODE = wholemodule;
406 | SWIFT_OPTIMIZATION_LEVEL = "-O";
407 | TARGETED_DEVICE_FAMILY = "1,2";
408 | VALIDATE_PRODUCT = YES;
409 | };
410 | name = Release;
411 | };
412 | 97C147061CF9000F007C117D /* Debug */ = {
413 | isa = XCBuildConfiguration;
414 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
415 | buildSettings = {
416 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
417 | CLANG_ENABLE_MODULES = YES;
418 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
419 | ENABLE_BITCODE = NO;
420 | INFOPLIST_FILE = Runner/Info.plist;
421 | LD_RUNPATH_SEARCH_PATHS = (
422 | "$(inherited)",
423 | "@executable_path/Frameworks",
424 | );
425 | PRODUCT_BUNDLE_IDENTIFIER = com.example.appExample;
426 | PRODUCT_NAME = "$(TARGET_NAME)";
427 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
428 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
429 | SWIFT_VERSION = 5.0;
430 | VERSIONING_SYSTEM = "apple-generic";
431 | };
432 | name = Debug;
433 | };
434 | 97C147071CF9000F007C117D /* Release */ = {
435 | isa = XCBuildConfiguration;
436 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
437 | buildSettings = {
438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
439 | CLANG_ENABLE_MODULES = YES;
440 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
441 | ENABLE_BITCODE = NO;
442 | INFOPLIST_FILE = Runner/Info.plist;
443 | LD_RUNPATH_SEARCH_PATHS = (
444 | "$(inherited)",
445 | "@executable_path/Frameworks",
446 | );
447 | PRODUCT_BUNDLE_IDENTIFIER = com.example.appExample;
448 | PRODUCT_NAME = "$(TARGET_NAME)";
449 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
450 | SWIFT_VERSION = 5.0;
451 | VERSIONING_SYSTEM = "apple-generic";
452 | };
453 | name = Release;
454 | };
455 | /* End XCBuildConfiguration section */
456 |
457 | /* Begin XCConfigurationList section */
458 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
459 | isa = XCConfigurationList;
460 | buildConfigurations = (
461 | 97C147031CF9000F007C117D /* Debug */,
462 | 97C147041CF9000F007C117D /* Release */,
463 | 249021D3217E4FDB00AE95B9 /* Profile */,
464 | );
465 | defaultConfigurationIsVisible = 0;
466 | defaultConfigurationName = Release;
467 | };
468 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
469 | isa = XCConfigurationList;
470 | buildConfigurations = (
471 | 97C147061CF9000F007C117D /* Debug */,
472 | 97C147071CF9000F007C117D /* Release */,
473 | 249021D4217E4FDB00AE95B9 /* Profile */,
474 | );
475 | defaultConfigurationIsVisible = 0;
476 | defaultConfigurationName = Release;
477 | };
478 | /* End XCConfigurationList section */
479 | };
480 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
481 | }
482 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | App Example
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | app_example
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UIViewControllerBasedStatusBarAppearance
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/app_example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/app_example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:step_progress_indicator/step_progress_indicator.dart';
3 |
4 | void main() {
5 | runApp(const MyApp());
6 | }
7 |
8 | class MyApp extends StatelessWidget {
9 | const MyApp({Key? key}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return const MaterialApp(
14 | title: "Step Progress Indicator Example",
15 | home: GradientRoundedEdge(),
16 | );
17 | }
18 | }
19 |
20 | class GradientRoundedEdge extends StatelessWidget {
21 | const GradientRoundedEdge({Key? key}) : super(key: key);
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | return Scaffold(
26 | body: SafeArea(
27 | child: Padding(
28 | padding: const EdgeInsets.all(50.0),
29 | child: Column(
30 | children: [
31 | Container(
32 | color: Colors.grey[600],
33 | padding: const EdgeInsets.all(10),
34 | child: const StepProgressIndicator(
35 | totalSteps: 100,
36 | currentStep: 40,
37 | size: 30,
38 | padding: 0,
39 | roundedEdges: Radius.circular(100), // Needed
40 | selectedGradientColor: LinearGradient(
41 | begin: Alignment.topLeft,
42 | end: Alignment.bottomRight,
43 | colors: [
44 | Color(0XFFFFE2CD),
45 | Color(0XFFFEC2E7),
46 | Color(0XFFC9E7FF),
47 | Color(0XFF86FEF4),
48 | ],
49 | ),
50 | unselectedGradientColor: LinearGradient(
51 | begin: Alignment.topLeft,
52 | end: Alignment.bottomRight,
53 | colors: [
54 | Color(0XFF171C21),
55 | Colors.black,
56 | ],
57 | ), // Needed
58 | ),
59 | ),
60 | ],
61 | ),
62 | )),
63 | );
64 | }
65 | }
66 |
67 | class WithLabelSizedIndicator extends StatelessWidget {
68 | const WithLabelSizedIndicator({Key? key}) : super(key: key);
69 |
70 | @override
71 | Widget build(BuildContext context) {
72 | return Scaffold(
73 | body: SafeArea(
74 | child: Padding(
75 | padding: const EdgeInsets.all(50.0),
76 | child: Column(
77 | children: [
78 | // With custom step
79 | StepProgressIndicator(
80 | totalSteps: 20,
81 | currentStep: 4,
82 | size: 30,
83 | customStep: (index, _, __) => Container(
84 | decoration: BoxDecoration(
85 | border: Border.all(color: Colors.grey, width: 2),
86 | ),
87 | child: Column(
88 | children: [
89 | Text('$index'),
90 | Container(
91 | height: 10,
92 | color: Colors.yellow,
93 | )
94 | ],
95 | ),
96 | ),
97 | ),
98 |
99 | // Spacing
100 | const SizedBox(height: 30),
101 |
102 | // With double indicator
103 | Column(
104 | children: [
105 | StepProgressIndicator(
106 | totalSteps: 20,
107 | currentStep: 4,
108 | size: 20,
109 | customStep: (index, _, __) => Text('$index'),
110 | ),
111 | const StepProgressIndicator(
112 | totalSteps: 20,
113 | currentStep: 4,
114 | ),
115 | ],
116 | ),
117 | ],
118 | ),
119 | )),
120 | );
121 | }
122 | }
123 |
124 | class ScrollContainerSize extends StatelessWidget {
125 | const ScrollContainerSize({Key? key}) : super(key: key);
126 |
127 | @override
128 | Widget build(BuildContext context) {
129 | return Scaffold(
130 | body: SafeArea(
131 | child: SizedBox(
132 | height: 300,
133 | child: SingleChildScrollView(
134 | scrollDirection: Axis.vertical,
135 | child: Row(
136 | crossAxisAlignment: CrossAxisAlignment.start,
137 | children: const [
138 | Box(height: 500),
139 | ],
140 | ),
141 | ),
142 | ),
143 | ),
144 | );
145 | }
146 | }
147 |
148 | class FixedContainerSize extends StatelessWidget {
149 | const FixedContainerSize({Key? key}) : super(key: key);
150 |
151 | @override
152 | Widget build(BuildContext context) {
153 | return Scaffold(
154 | body: SafeArea(
155 | child: Row(
156 | crossAxisAlignment: CrossAxisAlignment.start,
157 | children: const [
158 | Box(height: 100),
159 | Box(height: 200),
160 | Box(height: 300),
161 | Box(height: 400),
162 | Box(height: 500),
163 | ],
164 | ),
165 | ),
166 | );
167 | }
168 | }
169 |
170 | class Box extends StatelessWidget {
171 | final double height;
172 | const Box({
173 | Key? key,
174 | required this.height,
175 | }) : super(key: key);
176 |
177 | @override
178 | Widget build(BuildContext context) {
179 | return Container(
180 | decoration: BoxDecoration(
181 | border: Border.all(
182 | color: Colors.black,
183 | width: 2,
184 | ),
185 | ),
186 | width: 100,
187 | height: height,
188 | child: const Indicator(),
189 | );
190 | }
191 | }
192 |
193 | class Indicator extends StatelessWidget {
194 | const Indicator({
195 | Key? key,
196 | }) : super(key: key);
197 |
198 | @override
199 | Widget build(BuildContext context) {
200 | return const StepProgressIndicator(
201 | totalSteps: 10,
202 | currentStep: 5,
203 | size: 20,
204 | direction: Axis.vertical,
205 | );
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/example/app_example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.8.2"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.2.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.3.1"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | fake_async:
47 | dependency: transitive
48 | description:
49 | name: fake_async
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.2.0"
53 | flutter:
54 | dependency: "direct main"
55 | description: flutter
56 | source: sdk
57 | version: "0.0.0"
58 | flutter_lints:
59 | dependency: "direct dev"
60 | description:
61 | name: flutter_lints
62 | url: "https://pub.dartlang.org"
63 | source: hosted
64 | version: "1.0.4"
65 | flutter_test:
66 | dependency: "direct dev"
67 | description: flutter
68 | source: sdk
69 | version: "0.0.0"
70 | lints:
71 | dependency: transitive
72 | description:
73 | name: lints
74 | url: "https://pub.dartlang.org"
75 | source: hosted
76 | version: "1.0.1"
77 | matcher:
78 | dependency: transitive
79 | description:
80 | name: matcher
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "0.12.11"
84 | meta:
85 | dependency: transitive
86 | description:
87 | name: meta
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "1.7.0"
91 | path:
92 | dependency: transitive
93 | description:
94 | name: path
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "1.8.0"
98 | sky_engine:
99 | dependency: transitive
100 | description: flutter
101 | source: sdk
102 | version: "0.0.99"
103 | source_span:
104 | dependency: transitive
105 | description:
106 | name: source_span
107 | url: "https://pub.dartlang.org"
108 | source: hosted
109 | version: "1.8.1"
110 | stack_trace:
111 | dependency: transitive
112 | description:
113 | name: stack_trace
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "1.10.0"
117 | step_progress_indicator:
118 | dependency: "direct main"
119 | description:
120 | path: "../.."
121 | relative: true
122 | source: path
123 | version: "1.0.1"
124 | stream_channel:
125 | dependency: transitive
126 | description:
127 | name: stream_channel
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "2.1.0"
131 | string_scanner:
132 | dependency: transitive
133 | description:
134 | name: string_scanner
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "1.1.0"
138 | term_glyph:
139 | dependency: transitive
140 | description:
141 | name: term_glyph
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "1.2.0"
145 | test_api:
146 | dependency: transitive
147 | description:
148 | name: test_api
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "0.4.3"
152 | typed_data:
153 | dependency: transitive
154 | description:
155 | name: typed_data
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "1.3.0"
159 | vector_math:
160 | dependency: transitive
161 | description:
162 | name: vector_math
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "2.1.1"
166 | sdks:
167 | dart: ">=2.15.0 <3.0.0"
168 |
--------------------------------------------------------------------------------
/example/app_example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: app_example
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.0+1
19 |
20 | environment:
21 | sdk: ">=2.15.0 <3.0.0"
22 |
23 | # Dependencies specify other packages that your package needs in order to work.
24 | # To automatically upgrade your package dependencies to the latest versions
25 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
26 | # dependencies can be manually updated by changing the version numbers below to
27 | # the latest version available on pub.dev. To see which dependencies have newer
28 | # versions available, run `flutter pub outdated`.
29 | dependencies:
30 | flutter:
31 | sdk: flutter
32 | step_progress_indicator:
33 | path: ../../.
34 |
35 | dev_dependencies:
36 | flutter_test:
37 | sdk: flutter
38 |
39 | # The "flutter_lints" package below contains a set of recommended lints to
40 | # encourage good coding practices. The lint set provided by the package is
41 | # activated in the `analysis_options.yaml` file located at the root of your
42 | # package. See that file for information about deactivating specific lint
43 | # rules and activating additional ones.
44 | flutter_lints: ^1.0.0
45 |
46 | # For information on the generic Dart part of this file, see the
47 | # following page: https://dart.dev/tools/pub/pubspec
48 |
49 | # The following section is specific to Flutter.
50 | flutter:
51 | # The following line ensures that the Material Icons font is
52 | # included with your application, so that you can use the icons in
53 | # the material Icons class.
54 | uses-material-design: true
55 |
56 | # To add assets to your application, add an assets section, like this:
57 | # assets:
58 | # - images/a_dot_burr.jpeg
59 | # - images/a_dot_ham.jpeg
60 |
61 | # An image asset can refer to one or more resolution-specific "variants", see
62 | # https://flutter.dev/assets-and-images/#resolution-aware.
63 |
64 | # For details regarding adding assets from package dependencies, see
65 | # https://flutter.dev/assets-and-images/#from-packages
66 |
67 | # To add custom fonts to your application, add a fonts section here,
68 | # in this "flutter" section. Each entry in this list should have a
69 | # "family" key with the font family name, and a "fonts" key with a
70 | # list giving the asset and other descriptors for the font. For
71 | # example:
72 | # fonts:
73 | # - family: Schyler
74 | # fonts:
75 | # - asset: fonts/Schyler-Regular.ttf
76 | # - asset: fonts/Schyler-Italic.ttf
77 | # style: italic
78 | # - family: Trajan Pro
79 | # fonts:
80 | # - asset: fonts/TrajanPro.ttf
81 | # - asset: fonts/TrajanPro_Bold.ttf
82 | # weight: 700
83 | #
84 | # For details regarding fonts from package dependencies,
85 | # see https://flutter.dev/custom-fonts/#from-packages
86 |
--------------------------------------------------------------------------------
/example/app_example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:app_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(const MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/example/app_example/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/web/favicon.png
--------------------------------------------------------------------------------
/example/app_example/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/example/app_example/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/example/app_example/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/example/app_example/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SandroMaglione/step-progress-indicator/90803369404d5d78e9f2f9d50414a2a84c84356c/example/app_example/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/example/app_example/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | app_example
33 |
34 |
35 |
36 |
39 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/example/app_example/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app_example",
3 | "short_name": "app_example",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/example/circular_animation1.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:step_progress_indicator/step_progress_indicator.dart';
3 |
4 | class CircularAnimation1 extends StatefulWidget {
5 | @override
6 | _CircularAnimation1State createState() => _CircularAnimation1State();
7 | }
8 |
9 | class _CircularAnimation1State extends State
10 | with SingleTickerProviderStateMixin {
11 | late AnimationController _controller;
12 | late Animation _animation;
13 |
14 | @override
15 | void initState() {
16 | super.initState();
17 |
18 | _controller = AnimationController(
19 | vsync: this,
20 | duration: const Duration(
21 | milliseconds: 6000,
22 | ),
23 | );
24 |
25 | _animation = Tween(
26 | begin: 0,
27 | end: 10000,
28 | ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut))
29 | ..addListener(() {
30 | setState(() {});
31 | });
32 |
33 | _controller.forward();
34 | }
35 |
36 | @override
37 | Widget build(BuildContext context) {
38 | return Scaffold(
39 | body: Padding(
40 | padding: const EdgeInsets.symmetric(
41 | horizontal: 24.0,
42 | vertical: 24.0,
43 | ),
44 | child: SizedBox(
45 | width: double.infinity,
46 | child: Column(
47 | mainAxisAlignment: MainAxisAlignment.start,
48 | children: [
49 | Expanded(
50 | child: Column(
51 | mainAxisAlignment: MainAxisAlignment.center,
52 | crossAxisAlignment: CrossAxisAlignment.center,
53 | children: [
54 | CircularStepProgressIndicator(
55 | totalSteps: 10000,
56 | currentStep: _animation.value.toInt(),
57 | selectedColor: Colors.blueAccent,
58 | unselectedColor: Colors.grey[100],
59 | width: 200,
60 | height: 200,
61 | padding: 0,
62 | selectedStepSize: 10.0,
63 | ),
64 | ],
65 | ),
66 | ),
67 | Text(
68 | 'https//www.sandromaglione.com',
69 | style: TextStyle(
70 | fontSize: 15.0,
71 | fontWeight: FontWeight.w600,
72 | letterSpacing: 1.2,
73 | color: Colors.black87,
74 | ),
75 | ),
76 | ],
77 | ),
78 | ),
79 | ),
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/example/circular_bar1.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:step_progress_indicator/step_progress_indicator.dart';
3 | import 'dart:math' as math;
4 |
5 | class CircularBar1 extends StatelessWidget {
6 | @override
7 | Widget build(BuildContext context) {
8 | return Scaffold(
9 | body: Padding(
10 | padding: const EdgeInsets.symmetric(
11 | horizontal: 24.0,
12 | vertical: 24.0,
13 | ),
14 | child: Column(
15 | mainAxisAlignment: MainAxisAlignment.start,
16 | children: [
17 | Expanded(
18 | child: SizedBox(
19 | width: double.infinity,
20 | child: Column(
21 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
22 | crossAxisAlignment: CrossAxisAlignment.center,
23 | children: [
24 | Row(
25 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
26 | children: [
27 | CircularStepProgressIndicator(
28 | totalSteps: 10,
29 | currentStep: 6,
30 | width: 100,
31 | roundedCap: (_, isSelected) => isSelected,
32 | ),
33 | CircularStepProgressIndicator(
34 | totalSteps: 12,
35 | currentStep: 6,
36 | selectedColor: Colors.redAccent,
37 | unselectedColor: Colors.grey[200],
38 | selectedStepSize: 10.0,
39 | width: 100,
40 | gradientColor: LinearGradient(
41 | begin: Alignment.topLeft,
42 | end: Alignment.bottomRight,
43 | colors: [Colors.cyan, Colors.orangeAccent],
44 | ),
45 | ),
46 | CircularStepProgressIndicator(
47 | totalSteps: 20,
48 | currentStep: 6,
49 | padding: math.pi / 15,
50 | selectedColor: Colors.cyan,
51 | unselectedColor: Colors.yellowAccent,
52 | selectedStepSize: 3.0,
53 | unselectedStepSize: 9.0,
54 | width: 100,
55 | ),
56 | ],
57 | ),
58 | Row(
59 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
60 | children: [
61 | CircularStepProgressIndicator(
62 | totalSteps: 20,
63 | currentStep: 12,
64 | stepSize: 20,
65 | selectedColor: Colors.red,
66 | unselectedColor: Colors.purple[400],
67 | padding: math.pi / 80,
68 | width: 150,
69 | height: 150,
70 | startingAngle: -math.pi * 2 / 3,
71 | arcSize: math.pi * 2 / 3 * 2,
72 | gradientColor: LinearGradient(
73 | colors: [Colors.red, Colors.purple],
74 | ),
75 | ),
76 | CircularStepProgressIndicator(
77 | totalSteps: 100,
78 | currentStep: 74,
79 | stepSize: 10,
80 | selectedColor: Colors.greenAccent,
81 | unselectedColor: Colors.grey[200],
82 | padding: 0,
83 | width: 150,
84 | height: 150,
85 | selectedStepSize: 15,
86 | roundedCap: (_, __) => true,
87 | ),
88 | ],
89 | ),
90 | Row(
91 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
92 | children: [
93 | CircularStepProgressIndicator(
94 | totalSteps: 100,
95 | currentStep: 72,
96 | selectedColor: Colors.yellow,
97 | unselectedColor: Colors.lightBlue,
98 | padding: 0,
99 | width: 100,
100 | child: Icon(
101 | Icons.tag_faces,
102 | color: Colors.red,
103 | size: 84,
104 | ),
105 | ),
106 | CircularStepProgressIndicator(
107 | totalSteps: 30,
108 | currentStep: 20,
109 | selectedColor: Colors.lightGreen,
110 | unselectedColor: Colors.black,
111 | padding: math.pi / 60,
112 | width: 100,
113 | startingAngle: math.pi / 3,
114 | arcSize: math.pi,
115 | child: CircularStepProgressIndicator(
116 | totalSteps: 30,
117 | currentStep: 20,
118 | selectedColor: Colors.lightBlue,
119 | unselectedColor: Colors.black,
120 | padding: math.pi / 60,
121 | width: 100,
122 | startingAngle: math.pi,
123 | arcSize: math.pi,
124 | child: CircularStepProgressIndicator(
125 | totalSteps: 30,
126 | currentStep: 20,
127 | selectedColor: Colors.yellow,
128 | unselectedColor: Colors.black,
129 | padding: math.pi / 60,
130 | width: 100,
131 | startingAngle: -math.pi / 3,
132 | arcSize: math.pi,
133 | child: CircularStepProgressIndicator(
134 | totalSteps: 30,
135 | currentStep: 20,
136 | selectedColor: Colors.cyanAccent,
137 | unselectedColor: Colors.black,
138 | padding: math.pi / 60,
139 | width: 100,
140 | startingAngle: math.pi / 3,
141 | arcSize: math.pi,
142 | child: CircularStepProgressIndicator(
143 | totalSteps: 30,
144 | currentStep: 20,
145 | selectedColor: Colors.deepOrangeAccent,
146 | unselectedColor: Colors.black,
147 | padding: math.pi / 60,
148 | width: 100,
149 | startingAngle: math.pi,
150 | arcSize: math.pi,
151 | ),
152 | ),
153 | ),
154 | ),
155 | ),
156 | ],
157 | ),
158 | CircularStepProgressIndicator(
159 | totalSteps: 20,
160 | stepSize: 20,
161 | customColor: (index) => index % 3 == 0
162 | ? Colors.deepPurple
163 | : index % 2 == 0
164 | ? Colors.deepOrange
165 | : Colors.grey[100]!,
166 | width: 250,
167 | ),
168 | ],
169 | ),
170 | ),
171 | ),
172 | Text(
173 | 'https//www.sandromaglione.com',
174 | style: TextStyle(
175 | fontSize: 15.0,
176 | fontWeight: FontWeight.w600,
177 | letterSpacing: 1.2,
178 | color: Colors.black87,
179 | ),
180 | ),
181 | ],
182 | ),
183 | ),
184 | );
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/example/circular_bar2.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:step_progress_indicator/step_progress_indicator.dart';
3 | import 'dart:math' as math;
4 |
5 | class CircularBar2 extends StatelessWidget {
6 | @override
7 | Widget build(BuildContext context) {
8 | return Scaffold(
9 | body: Padding(
10 | padding: const EdgeInsets.symmetric(
11 | horizontal: 24.0,
12 | vertical: 24.0,
13 | ),
14 | child: Column(
15 | mainAxisAlignment: MainAxisAlignment.start,
16 | children: [
17 | Expanded(
18 | child: SizedBox(
19 | width: double.infinity,
20 | child: Column(
21 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
22 | crossAxisAlignment: CrossAxisAlignment.center,
23 | children: [
24 | Row(
25 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
26 | children: [
27 | CircularStepProgressIndicator(
28 | totalSteps: 15,
29 | currentStep: 3,
30 | stepSize: 20,
31 | width: 50,
32 | height: 50,
33 | unselectedColor: Colors.transparent,
34 | ),
35 | CircularStepProgressIndicator(
36 | totalSteps: 15,
37 | currentStep: 9,
38 | stepSize: 10,
39 | width: 50,
40 | height: 50,
41 | unselectedColor: Colors.transparent,
42 | ),
43 | CircularStepProgressIndicator(
44 | totalSteps: 15,
45 | currentStep: 12,
46 | stepSize: 4,
47 | width: 50,
48 | height: 50,
49 | unselectedColor: Colors.transparent,
50 | ),
51 | CircularStepProgressIndicator(
52 | totalSteps: 15,
53 | currentStep: 14,
54 | stepSize: 1,
55 | width: 50,
56 | height: 50,
57 | unselectedColor: Colors.transparent,
58 | ),
59 | ],
60 | ),
61 | Row(
62 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
63 | children: [
64 | CircularStepProgressIndicator(
65 | totalSteps: 40,
66 | stepSize: 10,
67 | padding: math.pi / 80,
68 | width: 80,
69 | height: 80,
70 | customColor: (index) => index < 10
71 | ? Colors.red[300]!
72 | : index < 20
73 | ? Colors.red[500]!
74 | : index < 30
75 | ? Colors.red[900]!
76 | : Colors.red[100]!,
77 | ),
78 | CircularStepProgressIndicator(
79 | totalSteps: 30,
80 | stepSize: 12,
81 | padding: math.pi / 20,
82 | width: 80,
83 | height: 80,
84 | customColor: (index) => index < 10
85 | ? Colors.red[300]!
86 | : index < 20
87 | ? Colors.red[500]!
88 | : index < 30
89 | ? Colors.red[900]!
90 | : Colors.red[100]!,
91 | ),
92 | CircularStepProgressIndicator(
93 | totalSteps: 20,
94 | stepSize: 20,
95 | padding: math.pi / 15,
96 | width: 80,
97 | height: 80,
98 | customColor: (index) => index < 10
99 | ? Colors.red[300]!
100 | : index < 15
101 | ? Colors.red[500]!
102 | : Colors.red[100]!,
103 | ),
104 | CircularStepProgressIndicator(
105 | totalSteps: 6,
106 | currentStep: 3,
107 | stepSize: 30,
108 | padding: math.pi / 18,
109 | width: 80,
110 | height: 80,
111 | selectedColor: Colors.red[800],
112 | unselectedColor: Colors.red[100],
113 | ),
114 | ],
115 | ),
116 | Row(
117 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
118 | children: [
119 | CircularStepProgressIndicator(
120 | totalSteps: 100,
121 | currentStep: 63,
122 | selectedColor: Colors.teal,
123 | unselectedColor: Colors.transparent,
124 | padding: 0,
125 | width: 100,
126 | stepSize: 27,
127 | child: CircleAvatar(
128 | backgroundColor: Colors.teal,
129 | child: Icon(
130 | Icons.thumb_up,
131 | color: Colors.white,
132 | ),
133 | ),
134 | ),
135 | CircularStepProgressIndicator(
136 | totalSteps: 100,
137 | currentStep: 90,
138 | selectedColor: Colors.lime[900],
139 | unselectedColor: Colors.transparent,
140 | padding: 0,
141 | width: 100,
142 | stepSize: 6,
143 | child: CircularStepProgressIndicator(
144 | totalSteps: 100,
145 | currentStep: 80,
146 | selectedColor: Colors.lime[800],
147 | unselectedColor: Colors.transparent,
148 | padding: 0,
149 | stepSize: 6,
150 | child: CircularStepProgressIndicator(
151 | totalSteps: 100,
152 | currentStep: 70,
153 | selectedColor: Colors.lime[700],
154 | unselectedColor: Colors.transparent,
155 | padding: 0,
156 | stepSize: 6,
157 | child: CircularStepProgressIndicator(
158 | totalSteps: 100,
159 | currentStep: 60,
160 | selectedColor: Colors.lime[600],
161 | unselectedColor: Colors.transparent,
162 | padding: 0,
163 | stepSize: 6,
164 | child: CircularStepProgressIndicator(
165 | totalSteps: 100,
166 | currentStep: 50,
167 | selectedColor: Colors.lime[500],
168 | unselectedColor: Colors.transparent,
169 | padding: 0,
170 | stepSize: 6,
171 | child: CircularStepProgressIndicator(
172 | totalSteps: 100,
173 | currentStep: 40,
174 | selectedColor: Colors.lime[400],
175 | unselectedColor: Colors.transparent,
176 | padding: 0,
177 | stepSize: 6,
178 | child: CircularStepProgressIndicator(
179 | totalSteps: 100,
180 | currentStep: 30,
181 | selectedColor: Colors.lime[300],
182 | unselectedColor: Colors.transparent,
183 | padding: 0,
184 | stepSize: 6,
185 | ),
186 | ),
187 | ),
188 | ),
189 | ),
190 | ),
191 | ),
192 | CircularStepProgressIndicator(
193 | totalSteps: 100,
194 | currentStep: 50,
195 | selectedColor: Colors.cyan,
196 | unselectedColor: Colors.transparent,
197 | padding: 0,
198 | stepSize: 6,
199 | child: CircularStepProgressIndicator(
200 | totalSteps: 100,
201 | currentStep: 50,
202 | selectedColor: Colors.cyan,
203 | unselectedColor: Colors.transparent,
204 | padding: 0,
205 | stepSize: 6,
206 | circularDirection:
207 | CircularDirection.counterclockwise,
208 | child: CircularStepProgressIndicator(
209 | totalSteps: 100,
210 | currentStep: 50,
211 | selectedColor: Colors.cyan,
212 | unselectedColor: Colors.transparent,
213 | padding: 0,
214 | stepSize: 12,
215 | child: CircularStepProgressIndicator(
216 | totalSteps: 100,
217 | currentStep: 50,
218 | selectedColor: Colors.cyan,
219 | unselectedColor: Colors.transparent,
220 | padding: 0,
221 | stepSize: 6,
222 | circularDirection:
223 | CircularDirection.counterclockwise,
224 | child: CircularStepProgressIndicator(
225 | totalSteps: 100,
226 | currentStep: 50,
227 | selectedColor: Colors.cyan,
228 | unselectedColor: Colors.transparent,
229 | padding: 0,
230 | stepSize: 8,
231 | child: CircularStepProgressIndicator(
232 | totalSteps: 100,
233 | currentStep: 50,
234 | selectedColor: Colors.cyan,
235 | unselectedColor: Colors.transparent,
236 | padding: 0,
237 | stepSize: 6,
238 | circularDirection:
239 | CircularDirection.counterclockwise,
240 | child: CircularStepProgressIndicator(
241 | totalSteps: 100,
242 | currentStep: 50,
243 | selectedColor: Colors.cyan,
244 | unselectedColor: Colors.transparent,
245 | padding: 0,
246 | stepSize: 6,
247 | child: CircularStepProgressIndicator(
248 | totalSteps: 100,
249 | currentStep: 50,
250 | selectedColor: Colors.cyan,
251 | unselectedColor: Colors.transparent,
252 | padding: 0,
253 | stepSize: 6,
254 | circularDirection:
255 | CircularDirection.counterclockwise,
256 | child: CircularStepProgressIndicator(
257 | totalSteps: 100,
258 | currentStep: 50,
259 | selectedColor: Colors.cyan,
260 | unselectedColor: Colors.transparent,
261 | padding: 0,
262 | stepSize: 6,
263 | ),
264 | ),
265 | ),
266 | ),
267 | ),
268 | ),
269 | ),
270 | ),
271 | ),
272 | ],
273 | ),
274 | Row(
275 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
276 | children: [
277 | CircularStepProgressIndicator(
278 | totalSteps: 20,
279 | stepSize: 20,
280 | customColor: (index) => index % 3 == 0
281 | ? Colors.deepPurple
282 | : index % 2 == 0
283 | ? Colors.deepOrange
284 | : Colors.grey[100]!,
285 | width: 60,
286 | height: 60,
287 | ),
288 | CircularStepProgressIndicator(
289 | totalSteps: 6,
290 | stepSize: 6,
291 | customColor: (index) => index % 3 == 0
292 | ? Colors.deepPurple
293 | : index % 2 == 0
294 | ? Colors.deepOrange
295 | : Colors.grey[100]!,
296 | width: 60,
297 | height: 60,
298 | ),
299 | CircularStepProgressIndicator(
300 | totalSteps: 20,
301 | customColor: (index) =>
302 | (index > 6 && index < 10) || index > 16
303 | ? Colors.deepPurple
304 | : Colors.deepOrange,
305 | width: 60,
306 | height: 60,
307 | ),
308 | CircularStepProgressIndicator(
309 | totalSteps: 3,
310 | currentStep: 2,
311 | selectedColor: Colors.deepOrange,
312 | unselectedColor: Colors.deepPurple,
313 | stepSize: 10,
314 | width: 60,
315 | height: 60,
316 | ),
317 | ],
318 | ),
319 | ],
320 | ),
321 | ),
322 | ),
323 | Text(
324 | 'https//www.sandromaglione.com',
325 | style: TextStyle(
326 | fontSize: 15.0,
327 | fontWeight: FontWeight.w600,
328 | letterSpacing: 1.2,
329 | color: Colors.black87,
330 | ),
331 | ),
332 | ],
333 | ),
334 | ),
335 | );
336 | }
337 | }
338 |
--------------------------------------------------------------------------------
/example/horizontal_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import '../lib/step_progress_indicator.dart';
3 |
4 | /// Examples of step_progress_indicators, direction [Axis.horizontal]
5 | class HorizontalBar extends StatelessWidget {
6 | @override
7 | Widget build(BuildContext context) {
8 | return Scaffold(
9 | body: Padding(
10 | padding: const EdgeInsets.symmetric(
11 | horizontal: 24.0,
12 | vertical: 24.0,
13 | ),
14 | child: Column(
15 | mainAxisAlignment: MainAxisAlignment.start,
16 | children: [
17 | Expanded(
18 | child: SizedBox(
19 | width: double.infinity,
20 | child: Column(
21 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
22 | crossAxisAlignment: CrossAxisAlignment.center,
23 | children: [
24 | StepProgressIndicator(
25 | totalSteps: 10,
26 | ),
27 | StepProgressIndicator(
28 | totalSteps: 10,
29 | currentStep: 6,
30 | selectedColor: Colors.red,
31 | unselectedColor: Colors.yellow,
32 | ),
33 | StepProgressIndicator(
34 | totalSteps: 20,
35 | currentStep: 6,
36 | size: 10,
37 | selectedColor: Colors.purple,
38 | unselectedColor: Colors.transparent,
39 | ),
40 | StepProgressIndicator(
41 | totalSteps: 15,
42 | currentStep: 12,
43 | size: 20,
44 | selectedColor: Colors.amber,
45 | unselectedColor: Colors.black,
46 | roundedEdges: Radius.circular(10),
47 | gradientColor: LinearGradient(
48 | begin: Alignment.topLeft,
49 | end: Alignment.bottomRight,
50 | colors: [Colors.orange, Colors.white],
51 | ),
52 | ),
53 | StepProgressIndicator(
54 | totalSteps: 100,
55 | currentStep: 32,
56 | size: 8,
57 | padding: 0,
58 | selectedColor: Colors.yellow,
59 | unselectedColor: Colors.cyan,
60 | roundedEdges: Radius.circular(10),
61 | selectedGradientColor: LinearGradient(
62 | begin: Alignment.topLeft,
63 | end: Alignment.bottomRight,
64 | colors: [Colors.yellowAccent, Colors.deepOrange],
65 | ),
66 | unselectedGradientColor: LinearGradient(
67 | begin: Alignment.topLeft,
68 | end: Alignment.bottomRight,
69 | colors: [Colors.black, Colors.blue],
70 | ),
71 | ),
72 | StepProgressIndicator(
73 | totalSteps: 12,
74 | currentStep: 4,
75 | padding: 6.0,
76 | size: 12,
77 | progressDirection: TextDirection.rtl,
78 | selectedColor: Colors.green,
79 | unselectedColor: Colors.black12,
80 | selectedGradientColor: LinearGradient(
81 | begin: Alignment.topLeft,
82 | end: Alignment.bottomRight,
83 | colors: [Colors.yellowAccent, Colors.deepOrange],
84 | ),
85 | unselectedGradientColor: LinearGradient(
86 | begin: Alignment.topLeft,
87 | end: Alignment.bottomRight,
88 | colors: [Colors.black, Colors.blue],
89 | ),
90 | ),
91 | StepProgressIndicator(
92 | totalSteps: 5,
93 | padding: 20.0,
94 | size: 20,
95 | customColor: (index) => index == 0
96 | ? Colors.redAccent
97 | : index == 4
98 | ? Colors.blueAccent
99 | : Colors.deepOrange,
100 | ),
101 | StepProgressIndicator(
102 | totalSteps: 6,
103 | currentStep: 4,
104 | size: 36,
105 | selectedColor: Colors.black,
106 | unselectedColor: Colors.lightBlue,
107 | customStep: (index, color, _) => color == Colors.black
108 | ? Container(
109 | color: color,
110 | child: Icon(
111 | Icons.check,
112 | color: Colors.white,
113 | ),
114 | )
115 | : Container(
116 | color: color,
117 | child: Icon(
118 | Icons.remove,
119 | ),
120 | ),
121 | ),
122 | StepProgressIndicator(
123 | totalSteps: 10,
124 | currentStep: 7,
125 | selectedColor: Colors.pink,
126 | unselectedColor: Colors.amber,
127 | customSize: (index, _) => (index + 1) * 10.0,
128 | ),
129 | ],
130 | ),
131 | ),
132 | ),
133 | Text(
134 | 'https//www.sandromaglione.com',
135 | style: TextStyle(
136 | fontSize: 15.0,
137 | fontWeight: FontWeight.w600,
138 | letterSpacing: 1.2,
139 | color: Colors.black87,
140 | ),
141 | ),
142 | ],
143 | ),
144 | ),
145 | );
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/example/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'horizontal_bar.dart';
4 | import 'vertical_bar.dart';
5 |
6 | void main() => runApp(MyApp());
7 |
8 | class MyApp extends StatefulWidget {
9 | @override
10 | _MyAppState createState() => _MyAppState();
11 | }
12 |
13 | class _MyAppState extends State {
14 | int _bottomBarIndex = 0;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return MaterialApp(
19 | title: 'Step Progress Bar',
20 | theme: ThemeData(
21 | primarySwatch: Colors.blue,
22 | ),
23 | debugShowCheckedModeBanner: false,
24 | home: SafeArea(
25 | child: Scaffold(
26 | body: _bottomBarIndex == 0 ? HorizontalBar() : VerticalBar(),
27 | bottomNavigationBar: BottomNavigationBar(
28 | currentIndex: _bottomBarIndex,
29 | onTap: (index) => setState(() => _bottomBarIndex = index),
30 | items: [
31 | BottomNavigationBarItem(
32 | icon: Icon(Icons.more_horiz),
33 | ),
34 | BottomNavigationBarItem(
35 | icon: Icon(Icons.more_vert),
36 | ),
37 | ],
38 | ),
39 | ),
40 | ),
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/example/vertical_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import '../lib/step_progress_indicator.dart';
3 |
4 | class VerticalBar extends StatelessWidget {
5 | @override
6 | Widget build(BuildContext context) {
7 | return Scaffold(
8 | body: Padding(
9 | padding: const EdgeInsets.only(
10 | bottom: 24.0,
11 | top: 60.0,
12 | ),
13 | child: Column(
14 | children: [
15 | Expanded(
16 | child: Row(
17 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
18 | children: [
19 | StepProgressIndicator(
20 | totalSteps: 10,
21 | direction: Axis.vertical,
22 | currentStep: 2,
23 | ),
24 | StepProgressIndicator(
25 | totalSteps: 20,
26 | direction: Axis.vertical,
27 | currentStep: 2,
28 | selectedColor: Colors.amber,
29 | unselectedColor: Colors.black12,
30 | size: 10.0,
31 | ),
32 | StepProgressIndicator(
33 | totalSteps: 8,
34 | direction: Axis.vertical,
35 | currentStep: 2,
36 | roundedEdges: Radius.circular(12),
37 | size: 10.0,
38 | gradientColor: LinearGradient(
39 | begin: Alignment.topCenter,
40 | end: Alignment.bottomCenter,
41 | colors: [Colors.red, Colors.blueAccent],
42 | ),
43 | ),
44 | StepProgressIndicator(
45 | totalSteps: 25,
46 | direction: Axis.vertical,
47 | customColor: (index) => Colors.deepOrange,
48 | customStep: (index, color, _) => Icon(
49 | Icons.tag_faces,
50 | color: color,
51 | ),
52 | ),
53 | StepProgressIndicator(
54 | totalSteps: 10,
55 | direction: Axis.vertical,
56 | padding: 6.0,
57 | size: 30.0,
58 | customColor: (index) => Colors.lightBlue,
59 | customStep: (index, color, _) => Container(
60 | color: color,
61 | alignment: Alignment.center,
62 | child: Text('$index'),
63 | ),
64 | ),
65 | StepProgressIndicator(
66 | totalSteps: 20,
67 | direction: Axis.vertical,
68 | padding: 3.0,
69 | size: 40.0,
70 | progressDirection: TextDirection.rtl,
71 | customColor: (index) => Colors.lightGreen,
72 | customStep: (index, color, _) => Container(
73 | color: color,
74 | alignment: Alignment.center,
75 | child: Text('$index'),
76 | ),
77 | ),
78 | StepProgressIndicator(
79 | totalSteps: 100,
80 | direction: Axis.vertical,
81 | currentStep: 87,
82 | padding: 0,
83 | selectedColor: Colors.redAccent,
84 | unselectedColor: Colors.black12,
85 | progressDirection: TextDirection.rtl,
86 | selectedSize: 10.0,
87 | roundedEdges: Radius.elliptical(6, 30),
88 | ),
89 | ],
90 | ),
91 | ),
92 | SizedBox(
93 | height: 24.0,
94 | ),
95 | Text(
96 | 'https//www.sandromaglione.com',
97 | style: TextStyle(
98 | fontSize: 15.0,
99 | fontWeight: FontWeight.w600,
100 | letterSpacing: 1.2,
101 | color: Colors.black87,
102 | ),
103 | ),
104 | ],
105 | ),
106 | ),
107 | );
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/lib/src/circular_step_progress_indicator.dart:
--------------------------------------------------------------------------------
1 | library circular_step_progress_indicator;
2 |
3 | import 'package:flutter/material.dart';
4 | import 'dart:math' as math;
5 |
6 | /// Circular Progress indicator made of a series of steps.
7 | /// It can contain a child [Widget] inside.
8 | ///
9 | /// Developed and published by Sandro Maglione
10 | /// https://www.sandromaglione.com
11 | ///
12 | /// Check out the official tutorial on
13 | /// https://www.sandromaglione.com/blog
14 | class CircularStepProgressIndicator extends StatelessWidget {
15 | /// Defines if steps grow from
16 | /// clockwise [CircularDirection.clockwise] or
17 | /// counterclockwise [CircularDirection.counterclockwise]
18 | final CircularDirection circularDirection;
19 |
20 | /// Number of steps to underline, all the steps with
21 | /// index <= [currentStep] will have [Color] equal to
22 | /// [selectedColor]
23 | ///
24 | /// Only used when [customColor] is [null]
25 | ///
26 | /// Default value: 0
27 | final int currentStep;
28 |
29 | /// Total number of step of the complete indicator
30 | final int totalSteps;
31 |
32 | /// Radial spacing between each step. Remember to
33 | /// define the value in radiant units
34 | ///
35 | /// Default value: math.pi / 20
36 | final double padding;
37 |
38 | /// Height of the indicator's box container
39 | final double? height;
40 |
41 | /// Width of the indicator's box container
42 | final double? width;
43 |
44 | /// Assign a custom [Color] for each step
45 | ///
46 | /// Takes a [int], index of the current step starting from 0, and
47 | /// must return a [Color]
48 | ///
49 | /// **NOTE**: If provided, it overrides
50 | /// [selectedColor] and [unselectedColor]
51 | final Color Function(int)? customColor;
52 |
53 | /// [Color] of the selected steps
54 | ///
55 | /// All the steps with index <= [currentStep]
56 | ///
57 | /// Default value: [Colors.blue]
58 | final Color? selectedColor;
59 |
60 | /// [Color] of the unselected steps
61 | ///
62 | /// All the steps with index between
63 | /// [currentStep] and [totalSteps]
64 | ///
65 | /// Default value: [Colors.grey]
66 | final Color? unselectedColor;
67 |
68 | /// The size of a single step in the indicator
69 | ///
70 | /// Default value: 6.0
71 | final double stepSize;
72 |
73 | /// Specify a custom size for selected steps
74 | ///
75 | /// Only applicable when not custom setting (customColor, customStepSize) is defined
76 | ///
77 | /// This value will replace the [stepSize] only for selected steps
78 | final double? selectedStepSize;
79 |
80 | /// Specify a custom size for unselected steps
81 | ///
82 | /// Only applicable when not custom setting (customColor, customStepSize) is defined
83 | ///
84 | /// This value will replace the [stepSize] only for unselected steps
85 | final double? unselectedStepSize;
86 |
87 | /// Assign a custom size [double] for each step
88 | ///
89 | /// Function takes a [int], index of the current step starting from 0, and
90 | /// a [bool], which tells if the step is selected based on [currentStep],
91 | /// and must return a [double] size of the step
92 | ///
93 | /// **NOTE**: If provided, it overrides [stepSize]
94 | final double Function(int, bool)? customStepSize;
95 |
96 | /// [Widget] contained inside the circular indicator
97 | final Widget? child;
98 |
99 | /// Height of the indicator container in case no [height] parameter
100 | /// given and parent height is [double.infinity]
101 | ///
102 | /// Default value: 100.0
103 | final double fallbackHeight;
104 |
105 | /// Height of the indicator container in case no [width] parameter
106 | /// given and parent height is [double.infinity]
107 | ///
108 | /// Default value: 100.0
109 | final double fallbackWidth;
110 |
111 | /// Angle in radiants in which the first step of the indicator is placed.
112 | /// The initial value is on the top of the indicator (- math.pi / 2)
113 | /// - 0 => TOP
114 | /// - math.pi / 2 => LEFT
115 | /// - math.pi => BOTTOM
116 | /// - math.pi / 2 * 3 => RIGHT
117 | /// - math.pi / 2 => TOP (again)
118 | final double startingAngle;
119 |
120 | /// Angle in radiants which represents the size of the arc used to display the indicator.
121 | /// It allows you to draw a semi-circle instead of a full 360° (math.pi * 2) circle.
122 | final double arcSize;
123 |
124 | /// Adds rounded edges at the beginning and at the end of the circular indicator
125 | /// given a [int], index of each step, and a [bool],
126 | /// which tells if the step is selected based on [currentStep], and must return a
127 | /// [bool] that tells if the edges are rounded or not
128 | ///
129 | /// **NOTE**: For continuous circular indicators (`padding: 0`), to check if to apply
130 | /// the rounded edges the packages uses index 0 (for the first arc painted) and
131 | /// 1 (for the second arc painted)
132 | ///
133 | /// ```dart
134 | /// // Example: Add rounded edges for all the steps
135 | /// roundedCap: (index, _) => true
136 | /// ```
137 | ///
138 | /// ```dart
139 | /// // Example: Add rounded edges for the selected arc of the indicator
140 | /// roundedCap: (index, _) => index == 0,
141 | /// padding: 0
142 | /// ```
143 | final bool Function(int, bool)? roundedCap;
144 |
145 | /// Adds a gradient color to the circular indicator
146 | ///
147 | /// **NOTE**: If provided, it overrides [selectedColor], [unselectedColor], and [customColor]
148 | final Gradient? gradientColor;
149 |
150 | /// Removes the extra angle caused by [StrokeCap.round] when [roundedCap] is applied
151 | final bool removeRoundedCapExtraAngle;
152 |
153 | const CircularStepProgressIndicator({
154 | required this.totalSteps,
155 | this.child,
156 | this.height,
157 | this.width,
158 | this.customColor,
159 | this.customStepSize,
160 | this.selectedStepSize,
161 | this.unselectedStepSize,
162 | this.roundedCap,
163 | this.gradientColor,
164 | this.circularDirection = CircularDirection.clockwise,
165 | this.fallbackHeight = 100.0,
166 | this.fallbackWidth = 100.0,
167 | this.currentStep = 0,
168 | this.selectedColor = Colors.blue,
169 | this.unselectedColor = Colors.grey,
170 | this.padding = math.pi / 20,
171 | this.stepSize = 6.0,
172 | this.startingAngle = 0,
173 | this.arcSize = math.pi * 2,
174 | this.removeRoundedCapExtraAngle = false,
175 | Key? key,
176 | }) : assert(totalSteps > 0,
177 | "Number of total steps (totalSteps) of the CircularStepProgressIndicator must be greater than 0"),
178 | assert(currentStep >= 0,
179 | "Current step (currentStep) of the CircularStepProgressIndicator must be greater than or equal to 0"),
180 | assert(padding >= 0.0,
181 | "Padding (padding) of the CircularStepProgressIndicator must be greater or equal to 0"),
182 | super(key: key);
183 |
184 | @override
185 | Widget build(BuildContext context) {
186 | // Print warning when arcSize greater than math.pi * 2 which causes steps to overlap
187 | if (arcSize > math.pi * 2)
188 | print(
189 | "WARNING (step_progress_indicator): arcSize of CircularStepProgressIndicator is greater than 360° (math.pi * 2), this will cause some steps to overlap!");
190 | final TextDirection textDirection = Directionality.of(context);
191 |
192 | return LayoutBuilder(
193 | builder: (context, constraints) => SizedBox(
194 | // Apply fallback for both height and width
195 | // if their value is null and no parent size limit
196 | height: height != null
197 | ? height
198 | : constraints.maxHeight != double.infinity
199 | ? constraints.maxHeight
200 | : fallbackHeight,
201 | width: width != null
202 | ? width
203 | : constraints.maxWidth != double.infinity
204 | ? constraints.maxWidth
205 | : fallbackWidth,
206 | child: CustomPaint(
207 | painter: _CircularIndicatorPainter(
208 | totalSteps: totalSteps,
209 | currentStep: currentStep,
210 | customColor: customColor,
211 | padding: padding,
212 | circularDirection: circularDirection,
213 | selectedColor: selectedColor,
214 | unselectedColor: unselectedColor,
215 | arcSize: arcSize,
216 | stepSize: stepSize,
217 | customStepSize: customStepSize,
218 | maxDefinedSize: maxDefinedSize,
219 | selectedStepSize: selectedStepSize,
220 | unselectedStepSize: unselectedStepSize,
221 | startingAngle: startingAngleTopOfIndicator,
222 | roundedCap: roundedCap,
223 | gradientColor: gradientColor,
224 | textDirection: textDirection,
225 | removeRoundedCapExtraAngle: removeRoundedCapExtraAngle,
226 | ),
227 | // Padding needed to show the indicator when child is placed on top of it
228 | child: Padding(
229 | padding: EdgeInsets.all(maxDefinedSize),
230 | child: child,
231 | ),
232 | ),
233 | ),
234 | );
235 | }
236 |
237 | /// Compute the maximum possible size of the indicator between
238 | /// [stepSize] and [customStepSize]
239 | double get maxDefinedSize {
240 | if (customStepSize == null) {
241 | return math.max(
242 | stepSize, math.max(selectedStepSize ?? 0, unselectedStepSize ?? 0));
243 | }
244 |
245 | // When customSize defined, compute and return max possible size
246 | double currentMaxSize = 0;
247 |
248 | for (int step = 0; step < totalSteps; ++step) {
249 | // Consider max between selected and unselected case
250 | final customSizeValue =
251 | math.max(customStepSize!(step, false), customStepSize!(step, true));
252 | if (customSizeValue > currentMaxSize) {
253 | currentMaxSize = customSizeValue;
254 | }
255 | }
256 |
257 | return currentMaxSize;
258 | }
259 |
260 | /// Make [startingAngle] to top-center of indicator (0°) by default
261 | double get startingAngleTopOfIndicator => startingAngle - math.pi / 2;
262 | }
263 |
264 | class _CircularIndicatorPainter implements CustomPainter {
265 | final int totalSteps;
266 | final int currentStep;
267 | final double padding;
268 | final Color? selectedColor;
269 | final Color? unselectedColor;
270 | final double stepSize;
271 | final double? selectedStepSize;
272 | final double? unselectedStepSize;
273 | final double Function(int, bool)? customStepSize;
274 | final double maxDefinedSize;
275 | final Color Function(int)? customColor;
276 | final CircularDirection circularDirection;
277 | final double startingAngle;
278 | final double arcSize;
279 | final bool Function(int, bool)? roundedCap;
280 | final Gradient? gradientColor;
281 | final TextDirection textDirection;
282 | final bool removeRoundedCapExtraAngle;
283 |
284 | _CircularIndicatorPainter({
285 | required this.totalSteps,
286 | required this.circularDirection,
287 | required this.customColor,
288 | required this.currentStep,
289 | required this.selectedColor,
290 | required this.unselectedColor,
291 | required this.padding,
292 | required this.stepSize,
293 | required this.selectedStepSize,
294 | required this.unselectedStepSize,
295 | required this.customStepSize,
296 | required this.startingAngle,
297 | required this.arcSize,
298 | required this.maxDefinedSize,
299 | required this.roundedCap,
300 | required this.gradientColor,
301 | required this.textDirection,
302 | required this.removeRoundedCapExtraAngle,
303 | });
304 |
305 | @override
306 | void paint(Canvas canvas, Size size) {
307 | final w = size.width;
308 | final h = size.height;
309 |
310 | // Step length is user-defined arcSize
311 | // divided by the total number of steps (each step same size)
312 | final stepLength = arcSize / totalSteps;
313 |
314 | // Define general arc paint
315 | Paint paint = Paint()
316 | ..style = PaintingStyle.stroke
317 | ..strokeWidth = maxDefinedSize;
318 |
319 | final rect = Rect.fromCenter(
320 | // Rect created from the center of the widget
321 | center: Offset(w / 2, h / 2),
322 | // For both height and width, subtract maxDefinedSize to fit indicator inside the parent container
323 | height: h - maxDefinedSize,
324 | width: w - maxDefinedSize,
325 | );
326 |
327 | if (gradientColor != null) {
328 | paint.shader =
329 | gradientColor!.createShader(rect, textDirection: textDirection);
330 | }
331 |
332 | // Change color selected or unselected based on the circularDirection
333 | final isClockwise = circularDirection == CircularDirection.clockwise;
334 |
335 | // Make a continuous arc without rendering all the steps when possible
336 | if (padding == 0 &&
337 | customColor == null &&
338 | customStepSize == null &&
339 | roundedCap == null) {
340 | _drawContinuousArc(canvas, paint, rect, isClockwise);
341 | } else {
342 | _drawStepArc(canvas, paint, rect, isClockwise, stepLength);
343 | }
344 | }
345 |
346 | /// Draw a series of arcs, each composing the full steps of the indicator
347 | void _drawStepArc(Canvas canvas, Paint paint, Rect rect, bool isClockwise,
348 | double stepLength) {
349 | // Draw a series of circular arcs to compose the indicator
350 | // Starting based on startingAngle attribute
351 | //
352 | // When clockwise:
353 | // - Start drawing counterclockwise so to have the selected steps on top of the unselected
354 | int step = isClockwise ? totalSteps - 1 : 0;
355 | double stepAngle = isClockwise ? startingAngle - stepLength : startingAngle;
356 | for (;
357 | isClockwise ? step >= 0 : step < totalSteps;
358 | isClockwise ? stepAngle -= stepLength : stepAngle += stepLength,
359 | isClockwise ? --step : ++step) {
360 | // Check if the current step is selected or unselected
361 | final isSelectedColor = _isSelectedColor(step, isClockwise);
362 |
363 | // Size of the step
364 | final indexStepSize = customStepSize != null
365 | // Consider step index inverted when counterclockwise
366 | ? customStepSize!(_indexOfStep(step, isClockwise), isSelectedColor)
367 | : isSelectedColor
368 | ? selectedStepSize ?? stepSize
369 | : unselectedStepSize ?? stepSize;
370 |
371 | // Use customColor if defined
372 | final stepColor = customColor != null
373 | // Consider step index inverted when counterclockwise
374 | ? customColor!(_indexOfStep(step, isClockwise))
375 | : isSelectedColor
376 | ? selectedColor!
377 | : unselectedColor!;
378 |
379 | // Apply stroke cap to each step
380 | final hasStrokeCap = roundedCap != null
381 | ? roundedCap!(_indexOfStep(step, isClockwise), isSelectedColor)
382 | : false;
383 | final strokeCap = hasStrokeCap ? StrokeCap.round : StrokeCap.butt;
384 |
385 | // Remove extra size caused by rounded stroke cap
386 | // https://github.com/SandroMaglione/step-progress-indicator/issues/20#issue-786114745
387 | final extraCapSize = indexStepSize / 2;
388 | final extraCapAngle = extraCapSize / (rect.width / 2);
389 | final extraCapRemove = hasStrokeCap && removeRoundedCapExtraAngle;
390 |
391 | // Draw arc steps of the indicator
392 | _drawArcOnCanvas(
393 | canvas: canvas,
394 | rect: rect,
395 | startingAngle: stepAngle + (extraCapRemove ? extraCapAngle : 0),
396 | sweepAngle:
397 | stepLength - padding - (extraCapRemove ? extraCapAngle * 2 : 0),
398 | paint: paint,
399 | color: stepColor,
400 | strokeWidth: indexStepSize,
401 | strokeCap: strokeCap,
402 | );
403 | }
404 | }
405 |
406 | /// Draw optimized continuous indicator instead of multiple steps
407 | void _drawContinuousArc(
408 | Canvas canvas, Paint paint, Rect rect, bool isClockwise) {
409 | // Compute color of the selected and unselected bars
410 | final firstStepColor = isClockwise ? selectedColor : unselectedColor;
411 | final secondStepColor = !isClockwise ? selectedColor : unselectedColor;
412 |
413 | // Selected and unselected step sizes if defined, otherwise use stepSize
414 | final firstStepSize = isClockwise
415 | ? selectedStepSize ?? stepSize
416 | : unselectedStepSize ?? stepSize;
417 | final secondStepSize = !isClockwise
418 | ? selectedStepSize ?? stepSize
419 | : unselectedStepSize ?? stepSize;
420 |
421 | // Compute length and starting angle of the selected and unselected bars
422 | final firstArcLength = arcSize * (currentStep / totalSteps);
423 | final secondArcLength = arcSize - firstArcLength;
424 |
425 | // firstArcStartingAngle = startingAngle
426 | final secondArcStartingAngle = startingAngle + firstArcLength;
427 |
428 | // Apply stroke cap to both arcs
429 | // NOTE: For continuous circular indicator, it uses 0 and 1 as index to
430 | // apply the rounded cap
431 | final firstArcStrokeCap = roundedCap != null
432 | ? isClockwise
433 | ? roundedCap!(0, true)
434 | : roundedCap!(1, false)
435 | : false;
436 | final secondArcStrokeCap = roundedCap != null
437 | ? isClockwise
438 | ? roundedCap!(1, false)
439 | : roundedCap!(0, true)
440 | : false;
441 | final firstCap = firstArcStrokeCap ? StrokeCap.round : StrokeCap.butt;
442 | final secondCap = secondArcStrokeCap ? StrokeCap.round : StrokeCap.butt;
443 |
444 | // When clockwise, draw the second arc first and the first on top of it
445 | // Required when stroke cap is rounded to make the selected step on top of the unselected
446 | if (circularDirection == CircularDirection.clockwise) {
447 | // Second arc, selected when counterclockwise, unselected otherwise
448 | _drawArcOnCanvas(
449 | canvas: canvas,
450 | rect: rect,
451 | paint: paint,
452 | startingAngle: secondArcStartingAngle,
453 | sweepAngle: secondArcLength,
454 | strokeWidth: secondStepSize,
455 | color: secondStepColor!,
456 | strokeCap: secondCap,
457 | );
458 |
459 | // First arc, selected when clockwise, unselected otherwise
460 | _drawArcOnCanvas(
461 | canvas: canvas,
462 | rect: rect,
463 | paint: paint,
464 | startingAngle: startingAngle,
465 | sweepAngle: firstArcLength,
466 | strokeWidth: firstStepSize,
467 | color: firstStepColor!,
468 | strokeCap: firstCap,
469 | );
470 | } else {
471 | // First arc, selected when clockwise, unselected otherwise
472 | _drawArcOnCanvas(
473 | canvas: canvas,
474 | rect: rect,
475 | paint: paint,
476 | startingAngle: startingAngle,
477 | sweepAngle: firstArcLength,
478 | strokeWidth: firstStepSize,
479 | color: firstStepColor!,
480 | strokeCap: firstCap,
481 | );
482 |
483 | // Second arc, selected when counterclockwise, unselected otherwise
484 | _drawArcOnCanvas(
485 | canvas: canvas,
486 | rect: rect,
487 | paint: paint,
488 | startingAngle: secondArcStartingAngle,
489 | sweepAngle: secondArcLength,
490 | strokeWidth: secondStepSize,
491 | color: secondStepColor!,
492 | strokeCap: secondCap,
493 | );
494 | }
495 | }
496 |
497 | /// Draw the actual arc for a continuous indicator
498 | void _drawArcOnCanvas({
499 | required Canvas canvas,
500 | required Rect rect,
501 | required double startingAngle,
502 | required double sweepAngle,
503 | required Paint paint,
504 | required Color color,
505 | required double strokeWidth,
506 | required StrokeCap strokeCap,
507 | }) =>
508 | canvas.drawArc(
509 | rect,
510 | startingAngle,
511 | sweepAngle,
512 | false /*isRadial*/,
513 | paint
514 | ..color = color
515 | ..strokeWidth = strokeWidth
516 | ..strokeCap = strokeCap,
517 | );
518 |
519 | bool _isSelectedColor(int step, bool isClockwise) => isClockwise
520 | ? step < currentStep
521 | : (step + 1) > (totalSteps - currentStep);
522 |
523 | /// Start counting indexes from the right if clockwise and on the left if counterclockwise
524 | int _indexOfStep(int step, bool isClockwise) =>
525 | isClockwise ? step : totalSteps - step - 1;
526 |
527 | @override
528 | bool shouldRepaint(CustomPainter oldDelegate) => oldDelegate != this;
529 |
530 | @override
531 | bool hitTest(Offset position) => false;
532 |
533 | @override
534 | void addListener(listener) {}
535 |
536 | @override
537 | void removeListener(listener) {}
538 |
539 | @override
540 | get semanticsBuilder => null;
541 |
542 | @override
543 | bool shouldRebuildSemantics(CustomPainter oldDelegate) => false;
544 | }
545 |
546 | /// Used to define the [circularDirection] attribute of the [CircularStepProgressIndicator]
547 | enum CircularDirection {
548 | clockwise,
549 | counterclockwise,
550 | }
551 |
--------------------------------------------------------------------------------
/lib/src/step_progress_indicator.dart:
--------------------------------------------------------------------------------
1 | library step_progress_indicator;
2 |
3 | import 'dart:math';
4 |
5 | import 'package:flutter/material.dart';
6 |
7 | /// (Linear) Progress indicator made of a series of steps
8 | ///
9 | /// Developed and published by Sandro Maglione
10 | /// https://www.sandromaglione.com
11 | ///
12 | /// Check out the official tutorial on
13 | /// https://www.sandromaglione.com/blog
14 | class StepProgressIndicator extends StatelessWidget {
15 | /// Defines a custom [Widget] to display at each step instead of a simple container,
16 | /// given the current step index, the [Color] of the step, which
17 | /// could be defined with [selectedColor] and [unselectedColor] or
18 | /// using [customColor], and its size [double], which could be defined
19 | /// using [size], [selectedSize], [unselectedSize], or [customSize].
20 | /// When [progressDirection] is [TextDirection.rtl], the index
21 | /// count starts from the last step i.e. the right-most step in the indicator has index 0
22 | ///
23 | /// ```dart
24 | /// customStep: (index, color, size) {
25 | /// return Container(
26 | /// color: color,
27 | /// child: Text('$index $size'),
28 | /// );
29 | /// }
30 | /// ```
31 | ///
32 | /// If you are not interested in the color and the size:
33 | /// ```dart
34 | /// customStep: (index, _, __) {
35 | /// return Text('$index');
36 | /// }
37 | /// ```
38 | final Widget Function(int, Color, double)? customStep;
39 |
40 | /// Defines if indicator is
41 | /// horizontal [Axis.horizontal] or
42 | /// vertical [Axis.vertical]
43 | ///
44 | /// Default value: [Axis.horizontal]
45 | final Axis direction;
46 |
47 | /// Defines if steps grow from
48 | /// left-to-right / top-to-bottom [TextDirection.ltr] or
49 | /// right-to-left / bottom-to-top [TextDirection.rtl]
50 | ///
51 | /// Default value: [TextDirection.ltr]
52 | final TextDirection progressDirection;
53 |
54 | /// Defines onTap function given index of the pressed step.
55 | ///
56 | /// Returns the function to execute when the step with given index is pressed.
57 | ///
58 | /// For example, if you want to print the index of the pressed step:
59 | /// ```dart
60 | /// onTap: (index) => () => print('$index pressed')
61 | /// ```
62 | /// or
63 | /// ```dart
64 | /// onTap: (index) {
65 | /// return () {
66 | /// print('$index pressed');
67 | /// };
68 | /// },
69 | /// ```
70 | final void Function() Function(int)? onTap;
71 |
72 | /// Number of steps to underline, all the steps with
73 | /// index <= [currentStep] will have [Color] equal to
74 | /// [selectedColor]
75 | ///
76 | /// Only used when [customColor] is [null]
77 | ///
78 | /// Default value: 0
79 | final int currentStep;
80 |
81 | /// Total number of step of the complete indicator
82 | final int totalSteps;
83 |
84 | /// Spacing between each step
85 | ///
86 | /// Default value: 2.0
87 | final double padding;
88 |
89 | /// Height (when [direction] is [Axis.horizontal]) or
90 | /// width (when [direction] is [Axis.vertical]) of a single indicator step
91 | ///
92 | /// **NOTE**: Overrided by selectedSize and unselected size when those values are applicable
93 | /// i.e. when not custom setting (customColor, customStep, customSize, onTap) is defined
94 | ///
95 | /// Default value: 4.0
96 | final double size;
97 |
98 | /// Specify a custom size for selected steps
99 | ///
100 | /// Only applicable when not custom setting (customColor, customStep, customSize, onTap) is defined
101 | ///
102 | /// This value will replace the [size] only for selected steps
103 | final double? selectedSize;
104 |
105 | /// Specify a custom size for unselected steps
106 | ///
107 | /// Only applicable when not custom setting (customColor, customStep, customSize, onTap) is defined
108 | ///
109 | /// This value will replace the [size] only for unselected steps
110 | final double? unselectedSize;
111 |
112 | /// Assign a custom size [double] for each step
113 | ///
114 | /// Function takes a [int], index of the current step starting from 0, and
115 | /// a [bool], which tells if the step is selected based on [currentStep], and
116 | /// must return a [double] size of the step
117 | ///
118 | /// **NOTE**: If provided, it overrides
119 | /// [size], [selectedSize], and [unselectedSize]
120 | final double Function(int, bool)? customSize;
121 |
122 | /// Assign a custom [Color] for each step
123 | ///
124 | /// Function takes a [int], index of the current step starting from 0, and
125 | /// must return a [Color]
126 | ///
127 | /// **NOTE**: If provided, it overrides
128 | /// [selectedColor] and [unselectedColor]
129 | /// ```
130 | /// customColor: (index) => index == 0 ? Colors.red : Colors.blue,
131 | /// ```
132 | final Color Function(int)? customColor;
133 |
134 | /// [Color] of the selected steps
135 | ///
136 | /// All the steps with index <= [currentStep]
137 | ///
138 | /// Default value: [Colors.blue]
139 | final Color selectedColor;
140 |
141 | /// [Color] of the unselected steps
142 | ///
143 | /// All the steps with index between
144 | /// [currentStep] and [totalSteps]
145 | ///
146 | /// Default value: [Colors.grey]
147 | final Color unselectedColor;
148 |
149 | /// Length of the progress indicator in case the main axis
150 | /// (based on [direction] attribute) has no size limit i.e. [double.infinity]
151 | ///
152 | /// Default value: 100.0
153 | final double fallbackLength;
154 |
155 | /// Added rounded corners to the first and last step of the indicator
156 | final Radius? roundedEdges;
157 |
158 | /// Adds a gradient color to the indicator
159 | ///
160 | /// **NOTE**: If provided, it overrides [selectedColor], [unselectedColor], and [customColor]
161 | final Gradient? gradientColor;
162 |
163 | /// Adds a gradient color to the selected steps of the indicator
164 | ///
165 | /// **NOTE**: If provided, it overrides [selectedColor], [unselectedColor], and [customColor]
166 | final Gradient? selectedGradientColor;
167 |
168 | /// Adds a gradient color to the unselected steps of the indicator
169 | ///
170 | /// **NOTE**: If provided, it overrides [selectedColor], [unselectedColor], and [customColor]
171 | final Gradient? unselectedGradientColor;
172 |
173 | /// Apply [BlendMode] to [ShaderMask] when [gradientColor], [selectedGradientColor], or [unselectedGradientColor] defined
174 | final BlendMode? blendMode;
175 |
176 | /// Assign alignment [MainAxisAlignment] for indicator's container
177 | ///
178 | /// **NOTE**: if not provided it defaults to [MainAxisAlignment.center]
179 | final MainAxisAlignment mainAxisAlignment;
180 |
181 | /// Assign alignment [CrossAxisAlignment] for indicator's container
182 | ///
183 | /// **NOTE**: if not provided it defaults to [CrossAxisAlignment.center]
184 | final CrossAxisAlignment crossAxisAlignment;
185 |
186 | /// Assign alignment [MainAxisAlignment] for a single step
187 | ///
188 | /// **NOTE**: if not provided it defaults to [MainAxisAlignment.center]
189 | final MainAxisAlignment stepMainAxisAlignment;
190 |
191 | /// Assign alignment [CrossAxisAlignment] for a single step
192 | ///
193 | /// **NOTE**: if not provided it defaults to [CrossAxisAlignment.center]
194 | final CrossAxisAlignment stepCrossAxisAlignment;
195 |
196 | const StepProgressIndicator({
197 | required this.totalSteps,
198 | this.customStep,
199 | this.onTap,
200 | this.customColor,
201 | this.customSize,
202 | this.selectedSize,
203 | this.unselectedSize,
204 | this.roundedEdges,
205 | this.gradientColor,
206 | this.selectedGradientColor,
207 | this.unselectedGradientColor,
208 | this.blendMode,
209 | this.direction = Axis.horizontal,
210 | this.progressDirection = TextDirection.ltr,
211 | this.size = 4.0,
212 | this.currentStep = 0,
213 | this.selectedColor = Colors.blue,
214 | this.unselectedColor = Colors.grey,
215 | this.padding = 2.0,
216 | this.fallbackLength = 100.0,
217 | this.mainAxisAlignment = MainAxisAlignment.center,
218 | this.crossAxisAlignment = CrossAxisAlignment.center,
219 | this.stepMainAxisAlignment = MainAxisAlignment.center,
220 | this.stepCrossAxisAlignment = CrossAxisAlignment.center,
221 | Key? key,
222 | }) : assert(totalSteps > 0,
223 | "Number of total steps (totalSteps) of the StepProgressIndicator must be greater than 0"),
224 | assert(currentStep >= 0,
225 | "Current step (currentStep) of the StepProgressIndicator must be greater than or equal to 0"),
226 | assert(padding >= 0.0,
227 | "Padding (padding) of the StepProgressIndicator must be greater or equal to 0"),
228 | super(key: key);
229 |
230 | @override
231 | Widget build(BuildContext context) {
232 | return LayoutBuilder(
233 | builder: (ctx, constraits) => SizedBox(
234 | width: _sizeOrMaxLength(
235 | direction == Axis.horizontal,
236 | constraits.maxWidth,
237 | ),
238 | height: _sizeOrMaxLength(
239 | direction == Axis.vertical,
240 | constraits.maxHeight,
241 | ),
242 | child: LayoutBuilder(
243 | builder: (ctx, constraits) => _applyShaderMask(
244 | gradientColor,
245 | _applyWidgetDirection(
246 | (maxSize) => !_isOptimizable
247 | ? _buildSteps(
248 | _stepHeightOrWidthValue(maxSize),
249 | )
250 | : _buildOptimizedSteps(
251 | _maxHeightOrWidthValue(maxSize),
252 | ),
253 | constraits,
254 | ),
255 | ),
256 | ),
257 | ),
258 | );
259 | }
260 |
261 | /// Apply both left and right rounded edges when only one step
262 | /// - Only 1 total steps
263 | /// - Two steps (padding == 0) and only one is visible (currentStep == 0)
264 | bool get _isOnlyOneStep =>
265 | totalSteps == 1 || (currentStep == 0 && padding == 0);
266 |
267 | /// Apply a [Row] when the [direction] of the indicator is [Axis.horizontal],
268 | /// or a [Column] otherwise ([Axis.vertical])
269 | Widget _applyWidgetDirection(
270 | List Function(double) children, BoxConstraints constraits) {
271 | if (direction == Axis.horizontal) {
272 | // If horizontal indicator, then use a Row
273 | return Row(
274 | crossAxisAlignment: crossAxisAlignment,
275 | mainAxisAlignment: mainAxisAlignment,
276 | children: children(constraits.maxWidth),
277 | );
278 | } else {
279 | // If vertical indicator, then use a Column
280 | return Column(
281 | crossAxisAlignment: crossAxisAlignment,
282 | mainAxisAlignment: mainAxisAlignment,
283 | children: children(constraits.maxHeight),
284 | );
285 | }
286 | }
287 |
288 | /// If the gradient given is defined, then wrap the given [child] in a gradient [ShaderMask]
289 | Widget _applyShaderMask(Gradient? gradient, Widget child) {
290 | if (gradient != null) {
291 | return ShaderMask(
292 | shaderCallback: (rect) => gradient.createShader(rect),
293 | // Apply user defined blendMode if defined, default otherwise
294 | blendMode: blendMode != null ? blendMode! : BlendMode.modulate,
295 | child: child,
296 | );
297 | } else {
298 | return child;
299 | }
300 | }
301 |
302 | /// Compute the maximum possible size of the indicator between
303 | /// [size], [selectedSize], [unselectedSize], and [customSize]
304 | double get maxDefinedSize {
305 | // If customSize not defined, use size, selectedSize, unselectedSize
306 | if (customSize == null) {
307 | return max(size, max(selectedSize ?? 0, unselectedSize ?? 0));
308 | }
309 |
310 | // When customSize defined, compute max possible size
311 | double currentMaxSize = 0;
312 |
313 | for (int step = 0; step < totalSteps; ++step) {
314 | final customSizeValue = customSize!(step, _isSelectedColor(step));
315 | if (customSizeValue > currentMaxSize) {
316 | currentMaxSize = customSizeValue;
317 | }
318 | }
319 |
320 | return currentMaxSize;
321 | }
322 |
323 | /// As much space as possible when size not unbounded, otherwise use fallbackLength.
324 | /// If indicator is in the opposite direction, then use size
325 | double _sizeOrMaxLength(bool isCorrectDirection, double maxLength) =>
326 | isCorrectDirection
327 | // If space is not unbounded, then fill it with the indicator
328 | ? maxLength != double.infinity
329 | ? double.infinity
330 | : fallbackLength
331 | : maxDefinedSize;
332 |
333 | /// Draw just two containers in case no specific step setting is required
334 | /// i.e. it becomes a linear progress indicator with two steps: selected and unselected
335 | bool get _isOptimizable =>
336 | padding == 0 &&
337 | customColor == null &&
338 | customStep == null &&
339 | customSize == null &&
340 | onTap == null;
341 |
342 | /// Compute single step length, based on total length available
343 | double _stepHeightOrWidthValue(double maxSize) =>
344 | (_maxHeightOrWidthValue(maxSize) - (padding * 2 * totalSteps)) /
345 | totalSteps;
346 |
347 | /// Total length (horizontal or vertical) available for the indicator
348 | double _maxHeightOrWidthValue(double maxSize) =>
349 | maxSize != double.infinity ? maxSize : fallbackLength;
350 |
351 | /// Choose what [Color] to assign
352 | /// given current [step] index (zero-based)
353 | Color _chooseStepColor(int step, int stepIndex) {
354 | // Compute id given step is unselected or not
355 | final isUnselectedStepColor = progressDirection == TextDirection.ltr
356 | ? step > currentStep
357 | : step < totalSteps - currentStep;
358 |
359 | // Override all the other color options when gradient is defined
360 | if (gradientColor != null ||
361 | (isUnselectedStepColor && unselectedGradientColor != null) ||
362 | (!isUnselectedStepColor && selectedGradientColor != null)) {
363 | return Colors.white;
364 | }
365 |
366 | // Assign customColor if not null
367 | if (customColor != null) {
368 | return customColor!(stepIndex);
369 | }
370 |
371 | // Selected or Unselected color based on the progressDirection
372 | if (isUnselectedStepColor) {
373 | return unselectedColor;
374 | } else {
375 | return selectedColor;
376 | }
377 | }
378 |
379 | /// `true` if color of the step given index is [selectedColor]
380 | bool _isSelectedColor(int step) =>
381 | customColor == null &&
382 | !(progressDirection == TextDirection.ltr
383 | ? step > currentStep
384 | : step < totalSteps - currentStep);
385 |
386 | /// Build only two steps when the condition of [_isOptimizable] is verified
387 | List _buildOptimizedSteps(double indicatorLength) {
388 | List stepList = [];
389 | final isLtr = progressDirection == TextDirection.ltr;
390 | final isHorizontal = direction == Axis.horizontal;
391 |
392 | // Choose gradient based on direction defined
393 | final firstStepGradient =
394 | isLtr ? selectedGradientColor : unselectedGradientColor;
395 | final secondStepGradient =
396 | !isLtr ? selectedGradientColor : unselectedGradientColor;
397 |
398 | final firstStepLength = indicatorLength * (currentStep / totalSteps);
399 | final secondStepLength = indicatorLength - firstStepLength;
400 |
401 | // Add first step
402 | stepList.add(
403 | _applyShaderMask(
404 | firstStepGradient,
405 | _ProgressStep(
406 | direction: direction,
407 | padding: padding,
408 | color: firstStepGradient != null
409 | ? Colors.white
410 | : isLtr
411 | ? selectedColor
412 | : unselectedColor,
413 | width: isHorizontal
414 | ? isLtr
415 | ? firstStepLength
416 | : secondStepLength
417 | : isLtr
418 | ? selectedSize ?? size
419 | : unselectedSize ?? size,
420 | height: !isHorizontal
421 | ? isLtr
422 | ? firstStepLength
423 | : secondStepLength
424 | : isLtr
425 | ? selectedSize ?? size
426 | : unselectedSize ?? size,
427 | roundedEdges: roundedEdges,
428 | isOnlyOneStep: _isOnlyOneStep,
429 | isFirstStep: true,
430 | mainAxisAlignment: stepMainAxisAlignment,
431 | crossAxisAlignment: stepCrossAxisAlignment,
432 | ),
433 | ),
434 | );
435 |
436 | // Add second step
437 | stepList.add(
438 | _applyShaderMask(
439 | secondStepGradient,
440 | _ProgressStep(
441 | direction: direction,
442 | padding: padding,
443 | color: secondStepGradient != null
444 | ? Colors.white
445 | : !isLtr
446 | ? selectedColor
447 | : unselectedColor,
448 | width: isHorizontal
449 | ? isLtr
450 | ? secondStepLength
451 | : firstStepLength
452 | : !isLtr
453 | ? selectedSize ?? size
454 | : unselectedSize ?? size,
455 | height: !isHorizontal
456 | ? isLtr
457 | ? secondStepLength
458 | : firstStepLength
459 | : !isLtr
460 | ? selectedSize ?? size
461 | : unselectedSize ?? size,
462 | roundedEdges: roundedEdges,
463 | isOnlyOneStep: _isOnlyOneStep,
464 | isLastStep: true,
465 | mainAxisAlignment: stepMainAxisAlignment,
466 | crossAxisAlignment: stepCrossAxisAlignment,
467 | ),
468 | ),
469 | );
470 |
471 | return stepList;
472 | }
473 |
474 | /// Build the list of [_ProgressStep],
475 | /// based on number of [totalSteps]
476 | List _buildSteps(double stepLength) {
477 | // Build list of selected and unselected steps
478 | List selectedStepList = [];
479 | List unselectedStepList = [];
480 |
481 | // Define directions parameters
482 | final isLtr = progressDirection == TextDirection.ltr;
483 | final isHorizontal = direction == Axis.horizontal;
484 |
485 | // From 0 to totalStep if TextDirection.ltr, from (totalSteps - 1) to 0 otherwise
486 | int step = isLtr ? 0 : totalSteps - 1;
487 |
488 | // Add steps to the list, based on the progressDirection attribute
489 | for (; isLtr ? step < totalSteps : step >= 0; isLtr ? ++step : --step) {
490 | // currentStep = 6, then 6 selected and 4 not selected
491 | final loopStep = isLtr ? step + 1 : totalSteps - step - 1;
492 | final isSelectedStepColor = _isSelectedColor(loopStep);
493 |
494 | // customColor if not null, otherwise selected or unselected color
495 | final stepColor = _chooseStepColor(loopStep, step);
496 |
497 | // If defined and applicable, apply customSize or
498 | // different sizes for selected and unselected
499 | final stepSize = customSize != null
500 | ? customSize!(step, isSelectedStepColor)
501 | : isSelectedStepColor
502 | ? selectedSize ?? size
503 | : unselectedSize ?? size;
504 |
505 | final progressStep = _ProgressStep(
506 | direction: direction,
507 | padding: padding,
508 | color: stepColor,
509 | width: isHorizontal ? stepLength : stepSize,
510 | height: !isHorizontal ? stepLength : stepSize,
511 | customStep:
512 | customStep != null ? customStep!(step, stepColor, stepSize) : null,
513 | onTap: onTap != null ? onTap!(step) : null,
514 | isFirstStep: step == 0,
515 | isLastStep: step == totalSteps - 1,
516 | roundedEdges: roundedEdges,
517 | isOnlyOneStep: _isOnlyOneStep,
518 | mainAxisAlignment: stepMainAxisAlignment,
519 | crossAxisAlignment: stepCrossAxisAlignment,
520 | );
521 |
522 | // Add to list of selected or unselected steps based on selection state
523 | if (isSelectedStepColor) {
524 | selectedStepList.add(progressStep);
525 | } else {
526 | unselectedStepList.add(progressStep);
527 | }
528 | }
529 |
530 | // Apply shader if gradient is not null and build a row or column based on the direction
531 | return [
532 | _applyShaderMask(
533 | isLtr ? selectedGradientColor : unselectedGradientColor,
534 | direction == Axis.horizontal
535 | ? Row(
536 | children: isLtr ? selectedStepList : unselectedStepList,
537 | )
538 | : Column(
539 | children: isLtr ? selectedStepList : unselectedStepList,
540 | ),
541 | ),
542 | _applyShaderMask(
543 | !isLtr ? selectedGradientColor : unselectedGradientColor,
544 | direction == Axis.horizontal
545 | ? Row(
546 | children: !isLtr ? selectedStepList : unselectedStepList,
547 | )
548 | : Column(
549 | children: !isLtr ? selectedStepList : unselectedStepList,
550 | ),
551 | ),
552 | ];
553 | }
554 | }
555 |
556 | /// Single step of the indicator
557 | class _ProgressStep extends StatelessWidget {
558 | final Axis direction;
559 | final double width;
560 | final double height;
561 | final Color color;
562 | final double padding;
563 | final Widget? customStep;
564 | final void Function()? onTap;
565 | final bool isFirstStep;
566 | final bool isLastStep;
567 | final bool isOnlyOneStep;
568 | final Radius? roundedEdges;
569 | final MainAxisAlignment mainAxisAlignment;
570 | final CrossAxisAlignment crossAxisAlignment;
571 |
572 | const _ProgressStep({
573 | required this.direction,
574 | required this.color,
575 | required this.padding,
576 | required this.width,
577 | required this.height,
578 | required this.mainAxisAlignment,
579 | required this.crossAxisAlignment,
580 | this.customStep,
581 | this.onTap,
582 | this.isFirstStep = false,
583 | this.isLastStep = false,
584 | this.isOnlyOneStep = false,
585 | this.roundedEdges,
586 | Key? key,
587 | }) : super(key: key);
588 |
589 | @override
590 | Widget build(BuildContext context) {
591 | // Assign given padding
592 | return Column(
593 | // Single step alignment
594 | mainAxisAlignment: mainAxisAlignment,
595 | crossAxisAlignment: crossAxisAlignment,
596 | children: [
597 | Padding(
598 | padding: EdgeInsets.symmetric(
599 | horizontal: direction == Axis.horizontal ? padding : 0.0,
600 | vertical: direction == Axis.vertical ? padding : 0.0,
601 | ),
602 | // If first or last step and rounded edges enabled, apply
603 | // rounded edges using ClipRRect
604 | // Different corners based on first/last step and indicator's direction
605 | // - First step + horizontal: top-left, bottom-left
606 | // - First step + vertical: top-left, top-right
607 | // - Last step + horizontal: top-right, bottom-right
608 | // - Last step + vertical: bottom-left, bottom-right
609 | child: (isFirstStep || isLastStep || isOnlyOneStep) &&
610 | roundedEdges != null
611 | ? ClipRRect(
612 | borderRadius: BorderRadius.only(
613 | topLeft: _radiusTopLeft ? roundedEdges! : Radius.zero,
614 | bottomRight:
615 | _radiusBottomRight ? roundedEdges! : Radius.zero,
616 | bottomLeft: _radiusBottomLeft ? roundedEdges! : Radius.zero,
617 | topRight: _radiusTopRight ? roundedEdges! : Radius.zero,
618 | ),
619 | child: _buildStep,
620 | )
621 | : _buildStep,
622 | ),
623 | ],
624 | );
625 | }
626 |
627 | /// Check if to apply rounded edges to top left border
628 | bool get _radiusTopLeft => (isFirstStep || isOnlyOneStep);
629 |
630 | /// Check if to apply rounded edges to bottom right border
631 | bool get _radiusBottomRight => (isLastStep || isOnlyOneStep);
632 |
633 | /// Check if to apply rounded edges to bottom left border
634 | bool get _radiusBottomLeft =>
635 | ((isFirstStep || isOnlyOneStep) && direction == Axis.horizontal) ||
636 | ((isLastStep || isOnlyOneStep) && direction == Axis.vertical);
637 |
638 | /// Check if to apply rounded edges to top right border
639 | bool get _radiusTopRight =>
640 | ((isFirstStep || isOnlyOneStep) && direction == Axis.vertical) ||
641 | ((isLastStep || isOnlyOneStep) && direction == Axis.horizontal);
642 |
643 | /// Build the actual single step [Widget]
644 | Widget get _buildStep => onTap != null && customStep == null
645 | ? Material(
646 | color: color,
647 | child: InkWell(
648 | onTap: onTap,
649 | // Container (simple rectangle) when no customStep defined
650 | // SizedBox containing the customStep otherwise
651 | child: _stepContainer(),
652 | ),
653 | )
654 | : onTap != null && customStep != null
655 | ? SizedBox(
656 | width: width,
657 | height: height,
658 | child: GestureDetector(
659 | onTap: onTap,
660 | child: customStep,
661 | ),
662 | )
663 | : _buildStepContainer;
664 |
665 | /// Build [_stepContainer] based on input parameters:
666 | /// - [Container] with background color when [customStep] is not defined
667 | /// - [SizedBox] with [customStep] when defined
668 | Widget get _buildStepContainer => customStep == null
669 | ? _stepContainer(color)
670 | : SizedBox(
671 | width: width,
672 | height: height,
673 | child: customStep,
674 | );
675 |
676 | /// Single step [Container]
677 | Widget _stepContainer([Color? color]) => Container(
678 | width: width,
679 | height: height,
680 | color: color,
681 | );
682 | }
683 |
--------------------------------------------------------------------------------
/lib/step_progress_indicator.dart:
--------------------------------------------------------------------------------
1 | export './src/step_progress_indicator.dart';
2 | export './src/circular_step_progress_indicator.dart';
3 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.8.2"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.2.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.3.1"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | fake_async:
47 | dependency: transitive
48 | description:
49 | name: fake_async
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.2.0"
53 | flutter:
54 | dependency: "direct main"
55 | description: flutter
56 | source: sdk
57 | version: "0.0.0"
58 | flutter_test:
59 | dependency: "direct dev"
60 | description: flutter
61 | source: sdk
62 | version: "0.0.0"
63 | matcher:
64 | dependency: transitive
65 | description:
66 | name: matcher
67 | url: "https://pub.dartlang.org"
68 | source: hosted
69 | version: "0.12.11"
70 | meta:
71 | dependency: transitive
72 | description:
73 | name: meta
74 | url: "https://pub.dartlang.org"
75 | source: hosted
76 | version: "1.7.0"
77 | path:
78 | dependency: transitive
79 | description:
80 | name: path
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "1.8.0"
84 | sky_engine:
85 | dependency: transitive
86 | description: flutter
87 | source: sdk
88 | version: "0.0.99"
89 | source_span:
90 | dependency: transitive
91 | description:
92 | name: source_span
93 | url: "https://pub.dartlang.org"
94 | source: hosted
95 | version: "1.8.1"
96 | stack_trace:
97 | dependency: transitive
98 | description:
99 | name: stack_trace
100 | url: "https://pub.dartlang.org"
101 | source: hosted
102 | version: "1.10.0"
103 | stream_channel:
104 | dependency: transitive
105 | description:
106 | name: stream_channel
107 | url: "https://pub.dartlang.org"
108 | source: hosted
109 | version: "2.1.0"
110 | string_scanner:
111 | dependency: transitive
112 | description:
113 | name: string_scanner
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "1.1.0"
117 | term_glyph:
118 | dependency: transitive
119 | description:
120 | name: term_glyph
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "1.2.0"
124 | test_api:
125 | dependency: transitive
126 | description:
127 | name: test_api
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "0.4.3"
131 | typed_data:
132 | dependency: transitive
133 | description:
134 | name: typed_data
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "1.3.0"
138 | vector_math:
139 | dependency: transitive
140 | description:
141 | name: vector_math
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "2.1.1"
145 | sdks:
146 | dart: ">=2.14.0 <3.0.0"
147 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: step_progress_indicator
2 | description: Bar indicator made of a series of selected and unselected steps
3 | version: 1.0.2
4 | homepage: https://github.com/SandroMaglione/step-progress-indicator
5 |
6 | environment:
7 | sdk: ">=2.12.1 <3.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 |
13 | dev_dependencies:
14 | flutter_test:
15 | sdk: flutter
16 |
17 | flutter:
18 |
--------------------------------------------------------------------------------
/test/circular_step_progress_indicator_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:step_progress_indicator/step_progress_indicator.dart';
4 |
5 | void main() {
6 | final int tTotalSteps = 10;
7 |
8 | final double tWidth = 200;
9 | final double tHeight = 200;
10 |
11 | testWidgets('should build the Container containing the circular indicator',
12 | (WidgetTester tester) async {
13 | await tester.pumpWidget(
14 | MaterialApp(
15 | home: CircularStepProgressIndicator(
16 | totalSteps: tTotalSteps,
17 | ),
18 | ),
19 | );
20 |
21 | final container = find.byType(SizedBox);
22 |
23 | expect(
24 | container,
25 | findsNWidgets(1),
26 | );
27 | });
28 |
29 | testWidgets(
30 | 'should build a Container of the correct specified size (height and width)',
31 | (WidgetTester tester) async {
32 | await tester.pumpWidget(
33 | MaterialApp(
34 | home: Column(
35 | children: [
36 | CircularStepProgressIndicator(
37 | totalSteps: tTotalSteps,
38 | height: tHeight,
39 | width: tWidth,
40 | ),
41 | ],
42 | ),
43 | ),
44 | );
45 |
46 | final container = find.byType(SizedBox);
47 |
48 | expect(
49 | container.evaluate().first.size,
50 | Size(tWidth, tHeight),
51 | );
52 | });
53 |
54 | testWidgets(
55 | 'should build a Container of the correct specified size (fallbackHeight)',
56 | (WidgetTester tester) async {
57 | await tester.pumpWidget(
58 | MaterialApp(
59 | home: Column(
60 | children: [
61 | CircularStepProgressIndicator(
62 | totalSteps: tTotalSteps,
63 | width: tWidth,
64 | fallbackHeight: tHeight,
65 | ),
66 | ],
67 | ),
68 | ),
69 | );
70 |
71 | final container = find.byType(SizedBox);
72 |
73 | expect(
74 | container.evaluate().first.size!.height,
75 | tHeight,
76 | );
77 | });
78 |
79 | testWidgets(
80 | 'should build a Container of the correct specified size (fallBackWidth)',
81 | (WidgetTester tester) async {
82 | await tester.pumpWidget(
83 | MaterialApp(
84 | home: Row(
85 | children: [
86 | CircularStepProgressIndicator(
87 | totalSteps: tTotalSteps,
88 | height: tHeight,
89 | fallbackWidth: tWidth,
90 | ),
91 | ],
92 | ),
93 | ),
94 | );
95 |
96 | final container = find.byType(SizedBox);
97 |
98 | expect(
99 | container.evaluate().first.size!.width,
100 | tWidth,
101 | );
102 | });
103 |
104 | testWidgets('should build the child Widget given inside the indicator',
105 | (WidgetTester tester) async {
106 | await tester.pumpWidget(
107 | MaterialApp(
108 | home: Row(
109 | children: [
110 | CircularStepProgressIndicator(
111 | totalSteps: tTotalSteps,
112 | height: tHeight,
113 | width: tWidth,
114 | child: Text('text'),
115 | ),
116 | ],
117 | ),
118 | ),
119 | );
120 |
121 | final text = find.byType(Text);
122 |
123 | expect(
124 | text,
125 | findsNWidgets(1),
126 | );
127 | });
128 | }
129 |
--------------------------------------------------------------------------------
/test/step_progress_indicator_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:step_progress_indicator/step_progress_indicator.dart';
4 |
5 | void main() {
6 | final int tTotalSteps = 10;
7 | final int tTotalStepsCustomStep = 3;
8 | final int tCurrentStep = 6;
9 |
10 | final double tWidth = 100;
11 | final double tHeight = 100;
12 |
13 | testWidgets('should build all the steps of the indicator',
14 | (WidgetTester tester) async {
15 | await tester.pumpWidget(
16 | MaterialApp(
17 | home: Container(
18 | width: tWidth,
19 | child: StepProgressIndicator(
20 | totalSteps: tTotalSteps,
21 | onTap: (_) => () {},
22 | ),
23 | ),
24 | ),
25 | );
26 |
27 | // Build all the step
28 | final steps = find.byType(Container);
29 |
30 | // Find all the steps (plus the indicator container)
31 | expect(
32 | steps,
33 | findsNWidgets(tTotalSteps + 1),
34 | );
35 | });
36 |
37 | testWidgets('should build all the custom steps of the indicator',
38 | (WidgetTester tester) async {
39 | await tester.pumpWidget(
40 | MaterialApp(
41 | home: Container(
42 | width: tWidth,
43 | child: StepProgressIndicator(
44 | totalSteps: tTotalSteps,
45 | customStep: (index, _, __) => Text('$index'),
46 | ),
47 | ),
48 | ),
49 | );
50 |
51 | // Build all the step
52 | final steps = find.byType(Text);
53 |
54 | // Find all the steps
55 | expect(
56 | steps,
57 | findsNWidgets(tTotalSteps),
58 | );
59 | });
60 |
61 | testWidgets('should build the correct custom steps content',
62 | (WidgetTester tester) async {
63 | await tester.pumpWidget(
64 | MaterialApp(
65 | home: Container(
66 | width: tWidth,
67 | child: StepProgressIndicator(
68 | totalSteps: tTotalStepsCustomStep,
69 | customStep: (index, _, __) => Text('$index'),
70 | ),
71 | ),
72 | ),
73 | );
74 |
75 | // Build all the step
76 | final text1 = find.text('0');
77 | final text2 = find.text('1');
78 | final text3 = find.text('2');
79 |
80 | // Find all the steps
81 | expect(
82 | text1,
83 | findsOneWidget,
84 | );
85 | expect(
86 | text2,
87 | findsOneWidget,
88 | );
89 | expect(
90 | text3,
91 | findsOneWidget,
92 | );
93 | });
94 |
95 | testWidgets('should build the correct selected and unselected colors',
96 | (WidgetTester tester) async {
97 | await tester.pumpWidget(
98 | MaterialApp(
99 | home: Container(
100 | width: tWidth,
101 | child: StepProgressIndicator(
102 | totalSteps: tTotalSteps,
103 | currentStep: tCurrentStep,
104 | selectedColor: Colors.red,
105 | customStep: (index, color, _) {
106 | if (color == Colors.red) {
107 | return Text('selected');
108 | } else {
109 | return Text('unselected');
110 | }
111 | },
112 | ),
113 | ),
114 | ),
115 | );
116 |
117 | // Build selected and unselected steps
118 | final textSelected = find.text('selected');
119 | final textUnselected = find.text('unselected');
120 |
121 | expect(
122 | textSelected,
123 | findsNWidgets(tCurrentStep),
124 | );
125 | expect(
126 | textUnselected,
127 | findsNWidgets(tTotalSteps - tCurrentStep),
128 | );
129 | });
130 |
131 | testWidgets('should build the step right-to-left correctly',
132 | (WidgetTester tester) async {
133 | int creationIndex = -1;
134 | await tester.pumpWidget(
135 | MaterialApp(
136 | home: Container(
137 | width: tWidth,
138 | child: StepProgressIndicator(
139 | totalSteps: tTotalSteps,
140 | currentStep: tCurrentStep,
141 | progressDirection: TextDirection.rtl,
142 | customStep: (index, color, _) {
143 | ++creationIndex;
144 | return Text('$creationIndex-$index');
145 | },
146 | ),
147 | ),
148 | ),
149 | );
150 |
151 | // Build all the steps
152 | final textSelected = find.byType(Text);
153 |
154 | expect(
155 | textSelected.evaluate().map((element) => (element.widget as Text).data),
156 | List.generate(
157 | tTotalSteps, (index) => '$index-${tTotalSteps - index - 1}'),
158 | );
159 | });
160 |
161 | testWidgets('should assign the correct width to the step',
162 | (WidgetTester tester) async {
163 | await tester.pumpWidget(
164 | MaterialApp(
165 | home: Scaffold(
166 | body: Container(
167 | height: tHeight,
168 | child: StepProgressIndicator(
169 | totalSteps: tTotalSteps,
170 | direction: Axis.vertical,
171 | size: tWidth,
172 | ),
173 | ),
174 | ),
175 | ),
176 | );
177 |
178 | // Build all the step
179 | final steps = find.byType(Container);
180 |
181 | // Find all the steps (skip the indicator container)
182 | expect(
183 | steps.evaluate().skip(1).map((element) => element.size!.width),
184 | List.filled(tTotalSteps, tWidth),
185 | );
186 | });
187 |
188 | testWidgets('should assign the correct height to the step',
189 | (WidgetTester tester) async {
190 | await tester.pumpWidget(
191 | MaterialApp(
192 | home: Scaffold(
193 | body: Container(
194 | width: tWidth,
195 | child: StepProgressIndicator(
196 | totalSteps: tTotalSteps,
197 | direction: Axis.horizontal,
198 | size: tHeight,
199 | ),
200 | ),
201 | ),
202 | ),
203 | );
204 |
205 | // Build all the step
206 | final steps = find.byType(Container);
207 |
208 | // Find all the steps (skip the indicator container)
209 | expect(
210 | steps.evaluate().skip(1).map((element) => element.size!.height),
211 | List.filled(tTotalSteps, tHeight),
212 | );
213 | });
214 |
215 | testWidgets(
216 | 'should build only two steps (selected and unselected) when no custom setting and padding is 0.0',
217 | (WidgetTester tester) async {
218 | await tester.pumpWidget(
219 | MaterialApp(
220 | home: Scaffold(
221 | body: Container(
222 | width: tWidth,
223 | child: StepProgressIndicator(
224 | totalSteps: tTotalSteps,
225 | currentStep: tCurrentStep,
226 | padding: 0.0,
227 | ),
228 | ),
229 | ),
230 | ),
231 | );
232 |
233 | // Build all the step
234 | final steps = find.byType(Container);
235 |
236 | // Find all the steps (plus the container of the indicator)
237 | expect(
238 | steps,
239 | findsNWidgets(3),
240 | );
241 | });
242 |
243 | testWidgets(
244 | 'should all the steps have the same width (considered the extra padding)',
245 | (WidgetTester tester) async {
246 | await tester.pumpWidget(
247 | MaterialApp(
248 | home: Scaffold(
249 | body: Container(
250 | width: tWidth,
251 | child: StepProgressIndicator(
252 | totalSteps: tTotalSteps,
253 | direction: Axis.horizontal,
254 | padding: 2.0,
255 | ),
256 | ),
257 | ),
258 | ),
259 | );
260 |
261 | // Build all the step
262 | final steps = find.byType(Container);
263 |
264 | // Find all the steps (skip the indicator container)
265 | expect(
266 | steps.evaluate().skip(1).map((element) => element.size!.width),
267 | List.filled(
268 | tTotalSteps, (tWidth - (tTotalSteps * 2.0 * 2)) / tTotalSteps),
269 | );
270 | });
271 |
272 | testWidgets('should use fallbackLength when size is unbounded',
273 | (WidgetTester tester) async {
274 | await tester.pumpWidget(
275 | MaterialApp(
276 | home: Scaffold(
277 | body: Row(
278 | children: [
279 | StepProgressIndicator(
280 | totalSteps: tTotalSteps,
281 | fallbackLength: 150,
282 | ),
283 | ],
284 | ),
285 | ),
286 | ),
287 | );
288 |
289 | final steps = find.byType(StepProgressIndicator);
290 |
291 | expect(
292 | steps.evaluate().first.size!.width,
293 | 150,
294 | );
295 | });
296 |
297 | testWidgets('should build a Column when the indicator direction is vertical',
298 | (WidgetTester tester) async {
299 | await tester.pumpWidget(
300 | MaterialApp(
301 | home: Scaffold(
302 | body: Row(
303 | children: [
304 | StepProgressIndicator(
305 | totalSteps: tTotalSteps,
306 | direction: Axis.vertical,
307 | ),
308 | ],
309 | ),
310 | ),
311 | ),
312 | );
313 |
314 | final steps = find.byType(Column);
315 | final stepsRow = find.byType(Row);
316 |
317 | expect(
318 | steps,
319 | findsWidgets,
320 | );
321 | expect(
322 | stepsRow,
323 | findsNWidgets(1),
324 | );
325 | });
326 |
327 | testWidgets(
328 | 'should apply the correct defined size to the height of the step when horizontal',
329 | (WidgetTester tester) async {
330 | await tester.pumpWidget(
331 | MaterialApp(
332 | home: Scaffold(
333 | body: Row(
334 | children: [
335 | StepProgressIndicator(
336 | totalSteps: tTotalSteps,
337 | size: 30.0,
338 | ),
339 | ],
340 | ),
341 | ),
342 | ),
343 | );
344 |
345 | final steps = find.byType(Container);
346 |
347 | expect(steps.evaluate().map((element) => element.size!.height),
348 | List.filled(tTotalSteps, 30.0));
349 | });
350 |
351 | testWidgets(
352 | 'should apply the correct defined size to the width of the step when vertical',
353 | (WidgetTester tester) async {
354 | await tester.pumpWidget(
355 | MaterialApp(
356 | home: Scaffold(
357 | body: Row(
358 | children: [
359 | StepProgressIndicator(
360 | totalSteps: tTotalSteps,
361 | direction: Axis.vertical,
362 | size: 30.0,
363 | ),
364 | ],
365 | ),
366 | ),
367 | ),
368 | );
369 |
370 | final steps = find.byType(Container);
371 |
372 | expect(steps.evaluate().map((element) => element.size!.width),
373 | List.filled(tTotalSteps, 30.0));
374 | });
375 |
376 | testWidgets(
377 | 'should apply selected and unselected specific sizes when specified',
378 | (WidgetTester tester) async {
379 | await tester.pumpWidget(
380 | MaterialApp(
381 | home: Scaffold(
382 | body: Row(
383 | children: [
384 | StepProgressIndicator(
385 | totalSteps: 10,
386 | currentStep: 6,
387 | selectedSize: 20,
388 | unselectedSize: 10,
389 | ),
390 | ],
391 | ),
392 | ),
393 | ),
394 | );
395 |
396 | final steps = find.byType(Container);
397 |
398 | expect(
399 | steps.evaluate().where((element) => element.size!.height == 20).length,
400 | 6,
401 | );
402 |
403 | expect(
404 | steps.evaluate().where((element) => element.size!.height == 10).length,
405 | 4,
406 | );
407 | });
408 |
409 | testWidgets(
410 | 'should apply customColor(s) and customStep(s) correctly (zero-based indexing)',
411 | (WidgetTester tester) async {
412 | await tester.pumpWidget(
413 | MaterialApp(
414 | home: Scaffold(
415 | body: Row(
416 | children: [
417 | StepProgressIndicator(
418 | totalSteps: tTotalSteps,
419 | customColor: (index) => index == 0
420 | ? Colors.red
421 | : index == 9
422 | ? Colors.black
423 | : Colors.blue,
424 | customStep: (index, color, _) {
425 | if ((index == 0 && color == Colors.red) ||
426 | (index == 9 && color == Colors.black) ||
427 | (index != 0 && index != 9 && color == Colors.blue)) {
428 | return Text('correct');
429 | } else {
430 | return Text('incorrect');
431 | }
432 | },
433 | ),
434 | ],
435 | ),
436 | ),
437 | ),
438 | );
439 |
440 | final textCorrect = find.text('correct');
441 | final textIncorrect = find.text('incorrect');
442 |
443 | expect(
444 | textCorrect,
445 | findsNWidgets(tTotalSteps),
446 | );
447 | expect(
448 | textIncorrect,
449 | findsNWidgets(0),
450 | );
451 | });
452 |
453 | testWidgets('should apply customSize(s) correctly (zero-based indexing)',
454 | (WidgetTester tester) async {
455 | await tester.pumpWidget(
456 | MaterialApp(
457 | home: Scaffold(
458 | body: Row(
459 | children: [
460 | StepProgressIndicator(
461 | totalSteps: tTotalSteps,
462 | customSize: (index, _) => index == 0
463 | ? 20
464 | : index == 9
465 | ? 2
466 | : 10,
467 | ),
468 | ],
469 | ),
470 | ),
471 | ),
472 | );
473 |
474 | final steps = find.byType(Container);
475 |
476 | expect(
477 | steps.evaluate().where((element) => element.size!.height == 20).length,
478 | 1,
479 | );
480 |
481 | expect(
482 | steps.evaluate().where((element) => element.size!.height == 2).length,
483 | 1,
484 | );
485 |
486 | expect(
487 | steps.evaluate().where((element) => element.size!.height == 10).length,
488 | tTotalSteps - 2,
489 | );
490 | });
491 |
492 | group('roundedEdges', () {
493 | final double tEdgesValue = 20;
494 | final Radius tRoundedEdges = Radius.circular(tEdgesValue);
495 | final StepProgressIndicator tStepProgressIndicatorDefault =
496 | StepProgressIndicator(
497 | totalSteps: tTotalSteps,
498 | roundedEdges: tRoundedEdges,
499 | );
500 |
501 | Future init(WidgetTester tester,
502 | [StepProgressIndicator? stepProgressIndicator]) =>
503 | tester.pumpWidget(
504 | MaterialApp(
505 | home: Scaffold(
506 | body: Row(
507 | children: [
508 | stepProgressIndicator ?? tStepProgressIndicatorDefault,
509 | ],
510 | ),
511 | ),
512 | ),
513 | );
514 |
515 | testWidgets('should apply roundedEdges when defined',
516 | (WidgetTester tester) async {
517 | await init(tester);
518 | final steps = find.byType(ClipRRect);
519 | expect(steps.evaluate().length, 2);
520 | });
521 |
522 | testWidgets('should not apply roundedEdges when not defined',
523 | (WidgetTester tester) async {
524 | await init(tester, StepProgressIndicator(totalSteps: 10));
525 | final steps = find.byType(ClipRRect);
526 | expect(steps.evaluate().isEmpty, true);
527 | });
528 |
529 | testWidgets('should have correct radius', (WidgetTester tester) async {
530 | await init(tester);
531 |
532 | final steps = find.byType(ClipRRect);
533 | final firstClipRRect = steps.evaluate().first.widget as ClipRRect;
534 | final lastClipRRect = steps.evaluate().last.widget as ClipRRect;
535 |
536 | expect(
537 | steps.evaluate().length,
538 | 2,
539 | );
540 |
541 | expect(
542 | firstClipRRect.borderRadius!.topLeft.x == tEdgesValue &&
543 | firstClipRRect.borderRadius!.bottomLeft.x == tEdgesValue &&
544 | firstClipRRect.borderRadius!.topLeft.y == tEdgesValue &&
545 | firstClipRRect.borderRadius!.bottomLeft.y == tEdgesValue,
546 | true,
547 | );
548 |
549 | expect(
550 | lastClipRRect.borderRadius!.topRight.x == tEdgesValue &&
551 | lastClipRRect.borderRadius!.bottomRight.x == tEdgesValue &&
552 | lastClipRRect.borderRadius!.topRight.y == tEdgesValue &&
553 | lastClipRRect.borderRadius!.bottomRight.y == tEdgesValue,
554 | true,
555 | );
556 | });
557 |
558 | testWidgets('should apply correct radius when only one step',
559 | (WidgetTester tester) async {
560 | await init(
561 | tester,
562 | StepProgressIndicator(
563 | totalSteps: 1,
564 | roundedEdges: tRoundedEdges,
565 | ),
566 | );
567 |
568 | final steps = find.byType(ClipRRect);
569 | final firstClipRRect = steps.evaluate().first.widget as ClipRRect;
570 |
571 | expect(
572 | steps.evaluate().length,
573 | 1,
574 | );
575 |
576 | expect(
577 | firstClipRRect.borderRadius!.topLeft.x == tEdgesValue &&
578 | firstClipRRect.borderRadius!.bottomLeft.x == tEdgesValue &&
579 | firstClipRRect.borderRadius!.topLeft.y == tEdgesValue &&
580 | firstClipRRect.borderRadius!.bottomLeft.y == tEdgesValue &&
581 | firstClipRRect.borderRadius!.topRight.x == tEdgesValue &&
582 | firstClipRRect.borderRadius!.bottomRight.x == tEdgesValue &&
583 | firstClipRRect.borderRadius!.topRight.y == tEdgesValue &&
584 | firstClipRRect.borderRadius!.bottomRight.y == tEdgesValue,
585 | true,
586 | );
587 | });
588 | });
589 |
590 | testWidgets('should apply gradient when defined',
591 | (WidgetTester tester) async {
592 | await tester.pumpWidget(
593 | MaterialApp(
594 | home: Scaffold(
595 | body: Row(
596 | children: [
597 | StepProgressIndicator(
598 | totalSteps: tTotalSteps,
599 | gradientColor: LinearGradient(
600 | colors: [Colors.orange, Colors.white],
601 | ),
602 | ),
603 | ],
604 | ),
605 | ),
606 | ),
607 | );
608 |
609 | final steps = find.byType(ShaderMask);
610 |
611 | expect(
612 | steps.evaluate().length,
613 | 1,
614 | );
615 | });
616 | }
617 |
--------------------------------------------------------------------------------