├── .github
└── workflows
│ ├── master.yml
│ └── release.yml
├── .gitignore
├── .pubignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── art
├── screen1.png
└── screen2.png
├── example
├── .gitignore
├── .metadata
├── .pubignore
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── percentindicatorexample
│ │ │ │ └── MainActivity.java
│ │ │ └── res
│ │ │ ├── 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
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── Runner
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── 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
│ │ └── main.m
├── lib
│ ├── main.dart
│ ├── multi_segment_page.dart
│ ├── sample_circular_page.dart
│ ├── sample_linear_page.dart
│ └── segment_page.dart
├── percent_indicator_example.iml
├── percent_indicator_example_android.iml
├── pubspec.lock
└── pubspec.yaml
├── lib
├── circular_percent_indicator.dart
├── flutter_percent_indicator.dart
├── linear_percent_indicator.dart
├── multi_segment_linear_indicator.dart
└── percent_indicator.dart
├── percent_indicator.iml
├── pubspec.lock
├── pubspec.yaml
├── screenshots
├── circular_percent_indicator.gif
├── circular_percent_indicator.png
├── linear_percent_indicator.gif
└── linear_percent_indicator.png
└── test
└── percent_indicator_test.dart
/.github/workflows/master.yml:
--------------------------------------------------------------------------------
1 | name: Package Publish dry-run
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 |
7 | jobs:
8 |
9 | dry-run:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: 'Checkout'
13 | uses: actions/checkout@v1
14 | - name: 'Dry-run'
15 | uses: Omega365/actions-flutter-pub-publisher@master
16 | with:
17 | credential: ${{secrets.CREDENTIAL_JSON}}
18 | flutter_package: true
19 | skip_test: true
20 | dry_run: true
21 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Package Publish
2 |
3 | on:
4 | push:
5 | branches: [release ]
6 |
7 | jobs:
8 | check_version:
9 | name: "Check Version Tag"
10 | runs-on: ubuntu-20.04
11 | steps:
12 | - name: Checkout Repository
13 | uses: actions/checkout@v3
14 |
15 | - name: Get Latest Version Tag
16 | id: previoustag
17 | uses: WyriHaximus/github-action-get-previous-tag@master
18 |
19 | - name: Print Latest Version
20 | run: "echo Latest tag: ${{ steps.previoustag.outputs.tag }}"
21 |
22 | - name: Get Version from pubspec.yaml
23 | id: config
24 | uses: CumulusDS/get-yaml-paths-action@v0.1.0
25 | with:
26 | file: pubspec.yaml
27 | version_name: version
28 |
29 | - name: Print New Version
30 | run: "echo New version from pubspec.yaml: ${{ steps.config.outputs.version_name }}"
31 |
32 | - name: Compare Version
33 | if: ${{ steps.config.outputs.version_name == steps.previoustag.outputs.tag }}
34 | run: |
35 | echo "The version from pubspec.yaml is the same as the previous tag, please update the version"
36 | exit 1
37 |
38 | - name: Save New Version
39 | run: "echo ${{ steps.config.outputs.version_name }} > version.txt"
40 |
41 | - name: Upload New Version
42 | uses: actions/upload-artifact@v4
43 | with:
44 | name: home
45 | path: version.txt
46 |
47 | publish:
48 | needs:
49 | - check_version
50 | runs-on: ubuntu-20.04
51 | steps:
52 | - name: Checkout Repository
53 | uses: actions/checkout@v3
54 |
55 | - name: Publish Package
56 | uses: Omega365/actions-flutter-pub-publisher@master
57 | with:
58 | credential: "${{ secrets.CREDENTIAL_JSON }}"
59 | dry_run: false
60 | flutter_package: true
61 | skip_test: true
62 |
63 | tag:
64 | name: "Tag Version"
65 | needs:
66 | - publish
67 | runs-on: ubuntu-20.04
68 | steps:
69 | - name: Download New Version
70 | uses: actions/download-artifact@v4
71 | with:
72 | name: home
73 |
74 | - name: Set and Tag the New Version
75 | run: |
76 | echo "RELEASE_VERSION=$(cat home/version.txt)" >> $GITHUB_ENV
77 | shell: bash
78 |
79 | - name: Create New Tag
80 | uses: tvdias/github-tagger@v0.0.2
81 | with:
82 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
83 | tag: "${{ env.RELEASE_VERSION }}"
84 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 | .idea/
7 |
8 | build/
9 | ios/.generated/
10 | ios/Flutter/Generated.xcconfig
11 | ios/Runner/GeneratedPluginRegistrant.*
12 | example/ios/Flutter/flutter_export_environment.sh
13 | example/ios/Flutter/.last_build_id
14 | example/.idea/**
15 |
16 | example/.idea/libraries/Dart_SDK.xml
17 | example/.idea/libraries/Flutter_for_Android.xml
18 | example/.idea/modules.xml
19 | example/.idea/runConfigurations/main_dart.xml
20 | example/.idea/workspace.xml
--------------------------------------------------------------------------------
/.pubignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 | .idea/
7 |
8 | build/
9 | ios/.generated/
10 | ios/Flutter/Generated.xcconfig
11 | ios/Runner/GeneratedPluginRegistrant.*
12 | example/ios/Flutter/flutter_export_environment.sh
13 | example/ios/Flutter/.last_build_id
14 | example/.idea/**
15 |
16 | example/.idea/libraries/Dart_SDK.xml
17 | example/.idea/libraries/Flutter_for_Android.xml
18 | example/.idea/modules.xml
19 | example/.idea/runConfigurations/main_dart.xml
20 | example/.idea/workspace.xml
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [4.2.5]
2 |
3 | - Added clipRotatedLinearGradient param, thanks kamil-matula
4 | - Enabling multi-segment progress bar with animations and stripes, thanks naseerahmedaziz
5 | - A lot of fixes.
6 |
7 | # [4.2.4]
8 |
9 | - A lot of fixes.
10 |
11 | # [4.2.3]
12 |
13 | - Screenshots added. Thanks FMorschel.
14 |
15 | # [4.2.2]
16 |
17 | - This version is for flutter 3.0 >=.
18 |
19 | # [4.2.1]
20 |
21 | - Add compatibility with lower versions of flutter.
22 |
23 | # [4.2.0]
24 |
25 | - Flutter 3.0 updated.
26 |
27 | # [4.0.1]
28 |
29 | - Fixed a bug: 1% progress should be inside. Thanks Gábor.
30 |
31 | # [4.0.0]
32 |
33 | - [BREAKING CHANGE] now `radius` is a real radius and not diameter. Thanks to `Nipun Shah`.
34 |
35 | # [3.5.0]
36 |
37 | - `linearStrokeCap` was deprecated. Use `barRadius` instead. Thanks to `martinkong0806`.
38 |
39 | # [3.4.0]
40 |
41 | - Null safety migration completed.
42 | - Widget Indicator works when using Arc mode on `CircularPercentIndicator`.
43 |
44 | # [3.0.1]
45 |
46 | - `linearGradientBackgroundColor` was added for `LinearPercentIndicator`. Thanks Jeremiah Parrack.
47 |
48 | # [3.3.0-nullsafety.1 - 3.0.0]
49 |
50 | - Null safety migration.
51 |
52 | # 2.1.9 - 2.1.9+1
53 |
54 | - Users can stop `animation` after this was initialize.
55 | - Added Half Arc for `CircularPercentIndicator` , added by Vivek
56 | - Extra height was removed. Thanks Brayan Cantos.
57 |
58 | # 2.1.8
59 |
60 | - New field was added : `rotateLinearGradient` -> Enable rotation of linear gradient in circular percent indicator. Added by `ecokeco`.
61 |
62 | # 2.1.7 - 2.1.7+4
63 |
64 | - Added optional `widgetIndicator` for `CircularPercentIndicator` and `LinearPercentIndicator`, it's an indicator displayed at the end of the progress, it only works when the `animation` is `true`. Thanks to Brayan Cantos for the contribution
65 |
66 | # 2.1.6
67 |
68 | - Added optional `onAnimationEnd` for `CircularPercentIndicator` and `LinearPercentIndicator`, it's a callback when the animation ends. Thanks to Brayan Cantos for the contribution
69 |
70 | # 2.1.5
71 |
72 | - Added optional `backgroundWidth` for `CircularPercentIndicator`. Thanks to CircuitGuy for the contribution
73 |
74 | # 2.1.4
75 |
76 | - `restartAnimation` was added to restart the animation when reached the end. Thanks to superciccio for the contribution
77 |
78 | # 2.1.3
79 |
80 | - Added `StrokeCap` on background. Thanks @mifama
81 |
82 | # 2.1.2
83 |
84 | - `curve` was added in both indicators.
85 |
86 | # 2.1.1
87 |
88 | - `LinearPercentIndicator` now can display only part of linear gradient using `clipLinearGradient`.
89 |
90 | # 2.1.0
91 |
92 | - vertical padding removed from `LinearPercentIndicator`.
93 |
94 | # 2.0.1
95 |
96 | - `maskFilter` was added for `LinearPercentIndicator` and `CircularPercentIndicator`. Thanks to akdu12 for the contribution
97 |
98 | # 2.0.0
99 |
100 | - `linearGradient` was added for `LinearPercentIndicator` and `CircularPercentIndicator`.
101 | - `reverse`, `arcType` and `arcBackgroundColor` were added to the `CircularPercentIndicator`.
102 |
103 | # 1.0.16
104 |
105 | - `width` is optional for `LinearPercentIndicator` widget,
106 |
107 | # 1.0.15
108 |
109 | - Added `addAutomaticKeepAlive` property to preserve the state of the widget.
110 | - Added `isRTL` for `LinearPercentIndicator` widget.
111 |
112 | # 1.0.14
113 |
114 | - Fixed bug using animateFromLastPercent. Thanks joelbrostrom
115 |
116 | # 1.0.13
117 |
118 | - Padding removed from leading and trailing on LinearPercentIndicator, now you can use Expanded or Flexible.
119 | - Fixed animation when refresh the widget with different duration
120 |
121 | # 1.0.12
122 |
123 | - animateFromLastPercent property was added for LinearPercentIndicator and CircularPercentIndicator
124 |
125 | # 1.0.11
126 |
127 | - startAngle for CircularPercentIndicator was added
128 |
129 | # 1.0.10
130 |
131 | - animation update bug fixed. Thanks @Tiagosito
132 |
133 | # 1.0.9
134 |
135 | - padding property was added for LinearPercentIndicator
136 |
137 | # 1.0.7, 1.0.8
138 |
139 | - alignment property was added for LinearPercentIndicator
140 |
141 | # 1.0.5, 1.0.6
142 |
143 | - fillColor property was added to LinearPercentIndicator
144 |
145 | # 1.0.1 - 1.0.4
146 |
147 | - Readme updated
148 |
149 | # 1.0.0
150 |
151 | Initial release of the multi_segment_linear_indicator package.
152 |
153 | ### Features
154 |
155 | - Three independently animated segments
156 | - Customizable colors for each segment
157 | - Optional striped pattern for the middle segment
158 | - Smooth animations with customizable duration and curve
159 | - Configurable border radius
160 | - RTL support
161 | - Flexible sizing and padding options
162 |
163 | ### Documentation
164 |
165 | - Added comprehensive README with usage examples
166 | - Added API documentation
167 | - Added example application
168 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2018, diegoveloper@gmail.com
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Percent Indicator
2 |
3 | [](https://pub.dev/packages/percent_indicator)
4 |
5 | Circular, Linear and Multi-segment linear percent indicators
6 |
7 |
8 |
9 |
10 |
11 | ## Features
12 |
13 | - Circle percent indicator
14 | - Linear percent indicator
15 | - Multi-segment linear indicator
16 | - Toggle animation
17 | - Custom duration of the animation
18 | - Progress based on a percentage value
19 | - Progress and background color
20 | - Custom size
21 | - Left , right or center child for Linear percent indicator
22 | - Top, bottom or center child for Circular percent indicator
23 | - Progress Color using gradients
24 |
25 | ## Getting started
26 |
27 | You should ensure that you add the router as a dependency in your flutter project.
28 |
29 | ```yaml
30 | dependencies:
31 | percent_indicator: ^4.2.5
32 | ```
33 |
34 | You should then run `flutter packages upgrade` or update your packages in IntelliJ.
35 |
36 | ## Example Project
37 |
38 | There is a example project in the `example` folder. Check it out. Otherwise, keep reading to get up and running.
39 |
40 | ## Usage
41 |
42 | Need to include the import the package to the dart file where it will be used, use the below command,
43 |
44 | ```dart
45 | import 'package:percent_indicator/percent_indicator.dart';
46 | ```
47 |
48 | **Circular percent indicator**
49 |
50 | Basic Widget
51 |
52 | ```dart
53 | new CircularPercentIndicator(
54 | radius: 60.0,
55 | lineWidth: 5.0,
56 | percent: 1.0,
57 | center: new Text("100%"),
58 | progressColor: Colors.green,
59 | )
60 | ```
61 |
62 | Complete example
63 |
64 | ```dart
65 | @override
66 | Widget build(BuildContext context) {
67 | return Scaffold(
68 | appBar: new AppBar(
69 | title: new Text("Circular Percent Indicators"),
70 | ),
71 | body: Center(
72 | child: ListView(
73 | children: [
74 | new CircularPercentIndicator(
75 | radius: 100.0,
76 | lineWidth: 10.0,
77 | percent: 0.8,
78 | header: new Text("Icon header"),
79 | center: new Icon(
80 | Icons.person_pin,
81 | size: 50.0,
82 | color: Colors.blue,
83 | ),
84 | backgroundColor: Colors.grey,
85 | progressColor: Colors.blue,
86 | ),
87 | new CircularPercentIndicator(
88 | radius: 130.0,
89 | animation: true,
90 | animationDuration: 1200,
91 | lineWidth: 15.0,
92 | percent: 0.4,
93 | center: new Text(
94 | "40 hours",
95 | style:
96 | new TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
97 | ),
98 | circularStrokeCap: CircularStrokeCap.butt,
99 | backgroundColor: Colors.yellow,
100 | progressColor: Colors.red,
101 | ),
102 | new CircularPercentIndicator(
103 | radius: 120.0,
104 | lineWidth: 13.0,
105 | animation: true,
106 | percent: 0.7,
107 | center: new Text(
108 | "70.0%",
109 | style:
110 | new TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
111 | ),
112 | footer: new Text(
113 | "Sales this week",
114 | style:
115 | new TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0),
116 | ),
117 | circularStrokeCap: CircularStrokeCap.round,
118 | progressColor: Colors.purple,
119 | ),
120 | Padding(
121 | padding: EdgeInsets.all(15.0),
122 | child: new CircularPercentIndicator(
123 | radius: 60.0,
124 | lineWidth: 5.0,
125 | percent: 1.0,
126 | center: new Text("100%"),
127 | progressColor: Colors.green,
128 | ),
129 | ),
130 | Container(
131 | padding: EdgeInsets.all(15.0),
132 | child: new Row(
133 | mainAxisAlignment: MainAxisAlignment.center,
134 | children: [
135 | new CircularPercentIndicator(
136 | radius: 45.0,
137 | lineWidth: 4.0,
138 | percent: 0.10,
139 | center: new Text("10%"),
140 | progressColor: Colors.red,
141 | ),
142 | new Padding(
143 | padding: EdgeInsets.symmetric(horizontal: 10.0),
144 | ),
145 | new CircularPercentIndicator(
146 | radius: 45.0,
147 | lineWidth: 4.0,
148 | percent: 0.30,
149 | center: new Text("30%"),
150 | progressColor: Colors.orange,
151 | ),
152 | new Padding(
153 | padding: EdgeInsets.symmetric(horizontal: 10.0),
154 | ),
155 | new CircularPercentIndicator(
156 | radius: 45.0,
157 | lineWidth: 4.0,
158 | percent: 0.60,
159 | center: new Text("60%"),
160 | progressColor: Colors.yellow,
161 | ),
162 | new Padding(
163 | padding: EdgeInsets.symmetric(horizontal: 10.0),
164 | ),
165 | new CircularPercentIndicator(
166 | radius: 45.0,
167 | lineWidth: 4.0,
168 | percent: 0.90,
169 | center: new Text("90%"),
170 | progressColor: Colors.green,
171 | )
172 | ],
173 | ),
174 | )
175 | ]),
176 | ),
177 | );
178 | }
179 | ```
180 |
181 |
182 |
183 |
184 |
185 | **Linear percent indicator**
186 |
187 | Basic Widget
188 |
189 | ```dart
190 | new LinearPercentIndicator(
191 | width: 140.0,
192 | lineHeight: 14.0,
193 | percent: 0.5,
194 | backgroundColor: Colors.grey,
195 | progressColor: Colors.blue,
196 | ),
197 |
198 | ```
199 |
200 | Complete example
201 |
202 | ```dart
203 | @override
204 | Widget build(BuildContext context) {
205 | return Scaffold(
206 | appBar: new AppBar(
207 | title: new Text("Linear Percent Indicators"),
208 | ),
209 | body: Center(
210 | child: Column(
211 | mainAxisAlignment: MainAxisAlignment.center,
212 | children: [
213 | Padding(
214 | padding: EdgeInsets.all(15.0),
215 | child: new LinearPercentIndicator(
216 | width: 140.0,
217 | lineHeight: 14.0,
218 | percent: 0.5,
219 | center: Text(
220 | "50.0%",
221 | style: new TextStyle(fontSize: 12.0),
222 | ),
223 | trailing: Icon(Icons.mood),
224 | linearStrokeCap: LinearStrokeCap.roundAll,
225 | backgroundColor: Colors.grey,
226 | progressColor: Colors.blue,
227 | ),
228 | ),
229 | Padding(
230 | padding: EdgeInsets.all(15.0),
231 | child: new LinearPercentIndicator(
232 | width: 170.0,
233 | animation: true,
234 | animationDuration: 1000,
235 | lineHeight: 20.0,
236 | leading: new Text("left content"),
237 | trailing: new Text("right content"),
238 | percent: 0.2,
239 | center: Text("20.0%"),
240 | linearStrokeCap: LinearStrokeCap.butt,
241 | progressColor: Colors.red,
242 | ),
243 | ),
244 | Padding(
245 | padding: EdgeInsets.all(15.0),
246 | child: new LinearPercentIndicator(
247 | width: MediaQuery.of(context).size.width - 50,
248 | animation: true,
249 | lineHeight: 20.0,
250 | animationDuration: 2000,
251 | percent: 0.9,
252 | center: Text("90.0%"),
253 | linearStrokeCap: LinearStrokeCap.roundAll,
254 | progressColor: Colors.greenAccent,
255 | ),
256 | ),
257 | Padding(
258 | padding: EdgeInsets.all(15.0),
259 | child: new LinearPercentIndicator(
260 | width: MediaQuery.of(context).size.width - 50,
261 | animation: true,
262 | lineHeight: 20.0,
263 | animationDuration: 2500,
264 | percent: 0.8,
265 | center: Text("80.0%"),
266 | linearStrokeCap: LinearStrokeCap.roundAll,
267 | progressColor: Colors.green,
268 | ),
269 | ),
270 | Padding(
271 | padding: EdgeInsets.all(15.0),
272 | child: Column(
273 | children: [
274 | new LinearPercentIndicator(
275 | width: 100.0,
276 | lineHeight: 8.0,
277 | percent: 0.2,
278 | progressColor: Colors.red,
279 | ),
280 | new LinearPercentIndicator(
281 | width: 100.0,
282 | lineHeight: 8.0,
283 | percent: 0.5,
284 | progressColor: Colors.orange,
285 | ),
286 | new LinearPercentIndicator(
287 | width: 100.0,
288 | lineHeight: 8.0,
289 | percent: 0.9,
290 | progressColor: Colors.blue,
291 | )
292 | ],
293 | ),
294 | ),
295 | ],
296 | ),
297 | ),
298 | );
299 | }
300 | ```
301 |
302 | **Multi-segment linear indicator**
303 |
304 | Basic Widget
305 |
306 | ```dart
307 | new MultiSegmentLinearIndicator(
308 | width: MediaQuery.of(context).size.width - 64,
309 | lineHeight: 30.0,
310 | firstSegmentPercent: 0.25,
311 | secondSegmentPercent: 0.4,
312 | thirdSegmentPercent: 0.35,
313 | firstSegmentColor: Color(0xFF4285F4),
314 | secondSegmentColor: Color(0xFF6DD5F6),
315 | thirdSegmentColor: Color(0xFFEFEFEF),
316 | enableStripes: [1],
317 | barRadius: Radius.circular(10.0),
318 | animation: true,
319 | animationDuration: 1000,
320 | curve: Curves.easeInOut,
321 | animateFromLastPercent: true,
322 | onAnimationEnd: () {
323 | ScaffoldMessenger.of(context).showSnackBar(
324 | const SnackBar(
325 | content: Text('Animation completed!'),
326 | duration: Duration(seconds: 1),
327 | ),
328 | );
329 | },
330 | ),
331 |
332 | ```
333 |
334 | Complete example
335 |
336 | ```dart
337 | @override
338 | Widget build(BuildContext context) {
339 | return Scaffold(
340 | appBar: AppBar(
341 | title: Text('Multi Segment Progress'),
342 | ),
343 | body: Center(
344 | child: Padding(
345 | padding: const EdgeInsets.all(20.0),
346 | child: Column(
347 | mainAxisAlignment: MainAxisAlignment.center,
348 | children: [
349 | MultiSegmentLinearIndicator(
350 | width: MediaQuery.of(context).size.width - 64,
351 | lineHeight: 30.0,
352 | firstSegmentPercent: 0.25,
353 | secondSegmentPercent: 0.4,
354 | thirdSegmentPercent: 0.35,
355 | firstSegmentColor: Color(0xFF4285F4),
356 | secondSegmentColor: Color(0xFF6DD5F6),
357 | thirdSegmentColor: Color(0xFFEFEFEF),
358 | enableStripes: [1],
359 | barRadius: Radius.circular(10.0),
360 | animation: true,
361 | animationDuration: 1000,
362 | curve: Curves.easeInOut,
363 | animateFromLastPercent: true,
364 | onAnimationEnd: () {
365 | ScaffoldMessenger.of(context).showSnackBar(
366 | const SnackBar(
367 | content: Text('Animation completed!'),
368 | duration: Duration(seconds: 1),
369 | ),
370 | );
371 | },
372 | ),
373 | SizedBox(height: 10),
374 | Text(
375 | 'Static with easeInOut: 25% - 40% - 35%',
376 | style: TextStyle(fontSize: 16),
377 | ),
378 | SizedBox(height: 30),
379 | MultiSegmentLinearIndicator(
380 | width: MediaQuery.of(context).size.width - 40,
381 | lineHeight: 20.0,
382 | firstSegmentPercent: 0.3,
383 | secondSegmentPercent: 0.4,
384 | thirdSegmentPercent: 0.3,
385 | firstSegmentColor: Color(0xFFBA0521),
386 | secondSegmentColor: Color(0xFF071437),
387 | thirdSegmentColor: Color(0xFFFF9205),
388 | enableStripes: [2],
389 | barRadius: Radius.circular(20),
390 | ),
391 | SizedBox(height: 10),
392 | Text(
393 | 'Static: 30% - 40% - 30%',
394 | style: TextStyle(fontSize: 16),
395 | ),
396 | SizedBox(height: 30),
397 | MultiSegmentLinearIndicator(
398 | width: MediaQuery.of(context).size.width - 40,
399 | lineHeight: 20.0,
400 | firstSegmentPercent: firstSegment,
401 | secondSegmentPercent: secondSegment,
402 | thirdSegmentPercent: thirdSegment,
403 | firstSegmentColor: Colors.green,
404 | secondSegmentColor: Colors.blue,
405 | thirdSegmentColor: Colors.orange,
406 | enableStripes: [1, 3],
407 | animation: true,
408 | animateFromLastPercent: true,
409 | animationDuration: 1000,
410 | curve: Curves.easeInOut,
411 | barRadius: Radius.circular(10),
412 | ),
413 | SizedBox(height: 10),
414 | Text(
415 | 'Progress: ${(firstSegment * 100).toInt()}% - ${(secondSegment * 100).toInt()}% - ${(thirdSegment * 100).toInt()}%',
416 | style: TextStyle(fontSize: 16),
417 | ),
418 | SizedBox(height: 30),
419 | MultiSegmentLinearIndicator(
420 | width: MediaQuery.of(context).size.width - 64,
421 | lineHeight: 30.0,
422 | firstSegmentPercent: 0.15,
423 | secondSegmentPercent: 0.4,
424 | thirdSegmentPercent: 0.45,
425 | firstSegmentColor: Color(0xFFBA0521),
426 | secondSegmentColor: Color(0xFFAEFAB00),
427 | thirdSegmentColor: Color(0xFFEFEFEF),
428 | enableStripes: [1],
429 | animation: true,
430 | animationDuration: 1000,
431 | curve: Curves.decelerate,
432 | animateFromLastPercent: true,
433 | ),
434 | SizedBox(height: 10),
435 | Text(
436 | 'Static with decelerate: 25% - 40% - 35%',
437 | style: TextStyle(fontSize: 16),
438 | ),
439 | SizedBox(height: 30),
440 | ],
441 | ),
442 | ),
443 | ),
444 | );
445 | }
446 |
447 | ```
448 |
449 |
450 |
451 |
452 |
453 | You can follow me on twitter [@diegoveloper](https://www.twitter.com/diegoveloper)
454 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # https://www.dartlang.org/guides/language/analysis-options
2 | analyzer:
3 |
4 | language:
5 |
6 |
7 | # Source of linter options:
8 | # http://dart-lang.github.io/linter/lints/options/options.html
9 | linter:
10 | rules:
11 | - camel_case_types
12 | - hash_and_equals
13 | - unrelated_type_equality_checks
14 | - valid_regexps
15 |
16 |
--------------------------------------------------------------------------------
/art/screen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/art/screen1.png
--------------------------------------------------------------------------------
/art/screen2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/art/screen2.png
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | .flutter-plugins
10 |
11 | .idea/
12 |
--------------------------------------------------------------------------------
/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: c7ea3ca377e909469c68f2ab878a5bc53d3cf66b
8 | channel: beta
9 |
--------------------------------------------------------------------------------
/example/.pubignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | .flutter-plugins
10 |
11 | .idea/
12 |
13 | .idea/**/*.xml
14 | .idea/sonarlint
15 | !.idea/libraries/Dart_SDK.xml
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.class
3 | .gradle
4 | /local.properties
5 | /.idea/workspace.xml
6 | /.idea/libraries
7 | .DS_Store
8 | /build
9 | /captures
10 | GeneratedPluginRegistrant.java
11 |
--------------------------------------------------------------------------------
/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 | apply plugin: 'com.android.application'
15 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
16 |
17 | android {
18 | compileSdkVersion 31
19 |
20 | lintOptions {
21 | disable 'InvalidPackage'
22 | }
23 |
24 | defaultConfig {
25 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
26 | applicationId "com.example.percentindicatorexample"
27 | minSdkVersion 16
28 | targetSdkVersion 31
29 | versionCode 1
30 | versionName "1.0"
31 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
32 | }
33 |
34 | buildTypes {
35 | release {
36 | // TODO: Add your own signing config for the release build.
37 | // Signing with the debug keys for now, so `flutter run --release` works.
38 | signingConfig signingConfigs.debug
39 | }
40 | }
41 | }
42 |
43 | flutter {
44 | source '../..'
45 | }
46 |
47 | dependencies {
48 | testImplementation 'junit:junit:4.13.1'
49 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
50 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
51 | }
52 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
15 |
18 |
26 |
30 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/percentindicatorexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.percentindicatorexample;
2 |
3 | import io.flutter.embedding.android.FlutterActivity;
4 |
5 | public class MainActivity extends FlutterActivity {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:7.0.4'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | mavenCentral()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
3 | org.gradle.jvmargs=-Xmx1536M
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Oct 04 01:29:47 IST 2019
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-7.2-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/app.flx
37 | /Flutter/app.zip
38 | /Flutter/flutter_assets/
39 | /Flutter/App.framework
40 | /Flutter/Flutter.framework
41 | /Flutter/Generated.xcconfig
42 | /ServiceDefinitions.json
43 |
44 | Pods/
45 | .symlinks/
46 |
--------------------------------------------------------------------------------
/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 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
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 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
13 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; };
14 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
15 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
16 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
17 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
18 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXCopyFilesBuildPhase section */
22 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
23 | isa = PBXCopyFilesBuildPhase;
24 | buildActionMask = 2147483647;
25 | dstPath = "";
26 | dstSubfolderSpec = 10;
27 | files = (
28 | );
29 | name = "Embed Frameworks";
30 | runOnlyForDeploymentPostprocessing = 0;
31 | };
32 | /* End PBXCopyFilesBuildPhase section */
33 |
34 | /* Begin PBXFileReference section */
35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
37 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
38 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
39 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
40 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
41 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
42 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
43 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
45 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
46 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
47 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
48 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
49 | /* End PBXFileReference section */
50 |
51 | /* Begin PBXFrameworksBuildPhase section */
52 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
53 | isa = PBXFrameworksBuildPhase;
54 | buildActionMask = 2147483647;
55 | files = (
56 | );
57 | runOnlyForDeploymentPostprocessing = 0;
58 | };
59 | /* End PBXFrameworksBuildPhase section */
60 |
61 | /* Begin PBXGroup section */
62 | 9740EEB11CF90186004384FC /* Flutter */ = {
63 | isa = PBXGroup;
64 | children = (
65 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
66 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
67 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
68 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
69 | );
70 | name = Flutter;
71 | sourceTree = "";
72 | };
73 | 97C146E51CF9000F007C117D = {
74 | isa = PBXGroup;
75 | children = (
76 | 9740EEB11CF90186004384FC /* Flutter */,
77 | 97C146F01CF9000F007C117D /* Runner */,
78 | 97C146EF1CF9000F007C117D /* Products */,
79 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
80 | );
81 | sourceTree = "";
82 | };
83 | 97C146EF1CF9000F007C117D /* Products */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 97C146EE1CF9000F007C117D /* Runner.app */,
87 | );
88 | name = Products;
89 | sourceTree = "";
90 | };
91 | 97C146F01CF9000F007C117D /* Runner */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
95 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
96 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
97 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
98 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
99 | 97C147021CF9000F007C117D /* Info.plist */,
100 | 97C146F11CF9000F007C117D /* Supporting Files */,
101 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
102 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
103 | );
104 | path = Runner;
105 | sourceTree = "";
106 | };
107 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 97C146F21CF9000F007C117D /* main.m */,
111 | );
112 | name = "Supporting Files";
113 | sourceTree = "";
114 | };
115 | /* End PBXGroup section */
116 |
117 | /* Begin PBXNativeTarget section */
118 | 97C146ED1CF9000F007C117D /* Runner */ = {
119 | isa = PBXNativeTarget;
120 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
121 | buildPhases = (
122 | 9740EEB61CF901F6004384FC /* Run Script */,
123 | 97C146EA1CF9000F007C117D /* Sources */,
124 | 97C146EB1CF9000F007C117D /* Frameworks */,
125 | 97C146EC1CF9000F007C117D /* Resources */,
126 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
127 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
128 | );
129 | buildRules = (
130 | );
131 | dependencies = (
132 | );
133 | name = Runner;
134 | productName = Runner;
135 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
136 | productType = "com.apple.product-type.application";
137 | };
138 | /* End PBXNativeTarget section */
139 |
140 | /* Begin PBXProject section */
141 | 97C146E61CF9000F007C117D /* Project object */ = {
142 | isa = PBXProject;
143 | attributes = {
144 | LastUpgradeCheck = 1510;
145 | ORGANIZATIONNAME = "The Chromium Authors";
146 | TargetAttributes = {
147 | 97C146ED1CF9000F007C117D = {
148 | CreatedOnToolsVersion = 7.3.1;
149 | };
150 | };
151 | };
152 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
153 | compatibilityVersion = "Xcode 3.2";
154 | developmentRegion = English;
155 | hasScannedForEncodings = 0;
156 | knownRegions = (
157 | en,
158 | Base,
159 | );
160 | mainGroup = 97C146E51CF9000F007C117D;
161 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
162 | projectDirPath = "";
163 | projectRoot = "";
164 | targets = (
165 | 97C146ED1CF9000F007C117D /* Runner */,
166 | );
167 | };
168 | /* End PBXProject section */
169 |
170 | /* Begin PBXResourcesBuildPhase section */
171 | 97C146EC1CF9000F007C117D /* Resources */ = {
172 | isa = PBXResourcesBuildPhase;
173 | buildActionMask = 2147483647;
174 | files = (
175 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
176 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
177 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
178 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
179 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
180 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
181 | );
182 | runOnlyForDeploymentPostprocessing = 0;
183 | };
184 | /* End PBXResourcesBuildPhase section */
185 |
186 | /* Begin PBXShellScriptBuildPhase section */
187 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
188 | isa = PBXShellScriptBuildPhase;
189 | alwaysOutOfDate = 1;
190 | buildActionMask = 2147483647;
191 | files = (
192 | );
193 | inputPaths = (
194 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
195 | );
196 | name = "Thin Binary";
197 | outputPaths = (
198 | );
199 | runOnlyForDeploymentPostprocessing = 0;
200 | shellPath = /bin/sh;
201 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
202 | };
203 | 9740EEB61CF901F6004384FC /* Run Script */ = {
204 | isa = PBXShellScriptBuildPhase;
205 | alwaysOutOfDate = 1;
206 | buildActionMask = 2147483647;
207 | files = (
208 | );
209 | inputPaths = (
210 | );
211 | name = "Run Script";
212 | outputPaths = (
213 | );
214 | runOnlyForDeploymentPostprocessing = 0;
215 | shellPath = /bin/sh;
216 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
217 | };
218 | /* End PBXShellScriptBuildPhase section */
219 |
220 | /* Begin PBXSourcesBuildPhase section */
221 | 97C146EA1CF9000F007C117D /* Sources */ = {
222 | isa = PBXSourcesBuildPhase;
223 | buildActionMask = 2147483647;
224 | files = (
225 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
226 | 97C146F31CF9000F007C117D /* main.m in Sources */,
227 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
228 | );
229 | runOnlyForDeploymentPostprocessing = 0;
230 | };
231 | /* End PBXSourcesBuildPhase section */
232 |
233 | /* Begin PBXVariantGroup section */
234 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
235 | isa = PBXVariantGroup;
236 | children = (
237 | 97C146FB1CF9000F007C117D /* Base */,
238 | );
239 | name = Main.storyboard;
240 | sourceTree = "";
241 | };
242 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
243 | isa = PBXVariantGroup;
244 | children = (
245 | 97C147001CF9000F007C117D /* Base */,
246 | );
247 | name = LaunchScreen.storyboard;
248 | sourceTree = "";
249 | };
250 | /* End PBXVariantGroup section */
251 |
252 | /* Begin XCBuildConfiguration section */
253 | 97C147031CF9000F007C117D /* Debug */ = {
254 | isa = XCBuildConfiguration;
255 | buildSettings = {
256 | ALWAYS_SEARCH_USER_PATHS = NO;
257 | CLANG_ANALYZER_NONNULL = YES;
258 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
259 | CLANG_CXX_LIBRARY = "libc++";
260 | CLANG_ENABLE_MODULES = YES;
261 | CLANG_ENABLE_OBJC_ARC = YES;
262 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
263 | CLANG_WARN_BOOL_CONVERSION = YES;
264 | CLANG_WARN_COMMA = YES;
265 | CLANG_WARN_CONSTANT_CONVERSION = YES;
266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
267 | CLANG_WARN_EMPTY_BODY = YES;
268 | CLANG_WARN_ENUM_CONVERSION = YES;
269 | CLANG_WARN_INFINITE_RECURSION = YES;
270 | CLANG_WARN_INT_CONVERSION = YES;
271 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
272 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
273 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
274 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
275 | CLANG_WARN_STRICT_PROTOTYPES = YES;
276 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
277 | CLANG_WARN_UNREACHABLE_CODE = YES;
278 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
279 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
280 | COPY_PHASE_STRIP = NO;
281 | DEBUG_INFORMATION_FORMAT = dwarf;
282 | ENABLE_STRICT_OBJC_MSGSEND = YES;
283 | ENABLE_TESTABILITY = YES;
284 | GCC_C_LANGUAGE_STANDARD = gnu99;
285 | GCC_DYNAMIC_NO_PIC = NO;
286 | GCC_NO_COMMON_BLOCKS = YES;
287 | GCC_OPTIMIZATION_LEVEL = 0;
288 | GCC_PREPROCESSOR_DEFINITIONS = (
289 | "DEBUG=1",
290 | "$(inherited)",
291 | );
292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
294 | GCC_WARN_UNDECLARED_SELECTOR = YES;
295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
296 | GCC_WARN_UNUSED_FUNCTION = YES;
297 | GCC_WARN_UNUSED_VARIABLE = YES;
298 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
299 | MTL_ENABLE_DEBUG_INFO = YES;
300 | ONLY_ACTIVE_ARCH = YES;
301 | SDKROOT = iphoneos;
302 | TARGETED_DEVICE_FAMILY = "1,2";
303 | };
304 | name = Debug;
305 | };
306 | 97C147041CF9000F007C117D /* Release */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ALWAYS_SEARCH_USER_PATHS = NO;
310 | CLANG_ANALYZER_NONNULL = YES;
311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
312 | CLANG_CXX_LIBRARY = "libc++";
313 | CLANG_ENABLE_MODULES = YES;
314 | CLANG_ENABLE_OBJC_ARC = YES;
315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
316 | CLANG_WARN_BOOL_CONVERSION = YES;
317 | CLANG_WARN_COMMA = YES;
318 | CLANG_WARN_CONSTANT_CONVERSION = 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_LITERAL_CONVERSION = YES;
326 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
327 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
328 | CLANG_WARN_STRICT_PROTOTYPES = YES;
329 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
330 | CLANG_WARN_UNREACHABLE_CODE = YES;
331 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
332 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
333 | COPY_PHASE_STRIP = NO;
334 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
335 | ENABLE_NS_ASSERTIONS = NO;
336 | ENABLE_STRICT_OBJC_MSGSEND = YES;
337 | GCC_C_LANGUAGE_STANDARD = gnu99;
338 | GCC_NO_COMMON_BLOCKS = YES;
339 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
340 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
341 | GCC_WARN_UNDECLARED_SELECTOR = YES;
342 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
343 | GCC_WARN_UNUSED_FUNCTION = YES;
344 | GCC_WARN_UNUSED_VARIABLE = YES;
345 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
346 | MTL_ENABLE_DEBUG_INFO = NO;
347 | SDKROOT = iphoneos;
348 | TARGETED_DEVICE_FAMILY = "1,2";
349 | VALIDATE_PRODUCT = YES;
350 | };
351 | name = Release;
352 | };
353 | 97C147061CF9000F007C117D /* Debug */ = {
354 | isa = XCBuildConfiguration;
355 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
356 | buildSettings = {
357 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
358 | CURRENT_PROJECT_VERSION = 1;
359 | ENABLE_BITCODE = NO;
360 | FRAMEWORK_SEARCH_PATHS = (
361 | "$(inherited)",
362 | "$(PROJECT_DIR)/Flutter",
363 | );
364 | INFOPLIST_FILE = Runner/Info.plist;
365 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
366 | LIBRARY_SEARCH_PATHS = (
367 | "$(inherited)",
368 | "$(PROJECT_DIR)/Flutter",
369 | );
370 | PRODUCT_BUNDLE_IDENTIFIER = com.example.percentIndicatorExample;
371 | PRODUCT_NAME = "$(TARGET_NAME)";
372 | VERSIONING_SYSTEM = "apple-generic";
373 | };
374 | name = Debug;
375 | };
376 | 97C147071CF9000F007C117D /* Release */ = {
377 | isa = XCBuildConfiguration;
378 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
379 | buildSettings = {
380 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
381 | CURRENT_PROJECT_VERSION = 1;
382 | ENABLE_BITCODE = NO;
383 | FRAMEWORK_SEARCH_PATHS = (
384 | "$(inherited)",
385 | "$(PROJECT_DIR)/Flutter",
386 | );
387 | INFOPLIST_FILE = Runner/Info.plist;
388 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
389 | LIBRARY_SEARCH_PATHS = (
390 | "$(inherited)",
391 | "$(PROJECT_DIR)/Flutter",
392 | );
393 | PRODUCT_BUNDLE_IDENTIFIER = com.example.percentIndicatorExample;
394 | PRODUCT_NAME = "$(TARGET_NAME)";
395 | VERSIONING_SYSTEM = "apple-generic";
396 | };
397 | name = Release;
398 | };
399 | /* End XCBuildConfiguration section */
400 |
401 | /* Begin XCConfigurationList section */
402 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
403 | isa = XCConfigurationList;
404 | buildConfigurations = (
405 | 97C147031CF9000F007C117D /* Debug */,
406 | 97C147041CF9000F007C117D /* Release */,
407 | );
408 | defaultConfigurationIsVisible = 0;
409 | defaultConfigurationName = Release;
410 | };
411 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
412 | isa = XCConfigurationList;
413 | buildConfigurations = (
414 | 97C147061CF9000F007C117D /* Debug */,
415 | 97C147071CF9000F007C117D /* Release */,
416 | );
417 | defaultConfigurationIsVisible = 0;
418 | defaultConfigurationName = Release;
419 | };
420 | /* End XCConfigurationList section */
421 | };
422 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
423 | }
424 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/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/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/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/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/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/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/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/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | percent_indicator_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | CADisableMinimumFrameDurationOnPhone
45 |
46 | UIApplicationSupportsIndirectInputEvents
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/example/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:percent_indicator_example/sample_circular_page.dart';
3 | import 'package:percent_indicator_example/sample_linear_page.dart';
4 | import 'package:percent_indicator_example/multi_segment_page.dart';
5 |
6 | void main() {
7 | runApp(MaterialApp(home: Scaffold(body: SamplePage())));
8 | }
9 |
10 | class SamplePage extends StatefulWidget {
11 | @override
12 | _SamplePageState createState() => _SamplePageState();
13 | }
14 |
15 | class _SamplePageState extends State {
16 | void _openPage(Widget page) {
17 | Navigator.push(
18 | context,
19 | MaterialPageRoute(
20 | builder: (BuildContext context) => page,
21 | ),
22 | );
23 | }
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | return Container(
28 | child: Center(
29 | child: Column(
30 | crossAxisAlignment: CrossAxisAlignment.center,
31 | mainAxisAlignment: MainAxisAlignment.center,
32 | children: [
33 | MaterialButton(
34 | color: Colors.blueAccent,
35 | child: Text("Circular Library"),
36 | onPressed: () => _openPage(SampleCircularPage()),
37 | ),
38 | Padding(
39 | padding: EdgeInsets.all(20.0),
40 | ),
41 | MaterialButton(
42 | color: Colors.blueAccent,
43 | child: Text("Linear Library"),
44 | onPressed: () => _openPage(SampleLinearPage()),
45 | ),
46 | Padding(
47 | padding: EdgeInsets.all(20.0),
48 | ),
49 | MaterialButton(
50 | color: Colors.blueAccent,
51 | child: Text("Multi Segment Linear Library"),
52 | onPressed: () => _openPage(MultiSegmentPage()),
53 | ),
54 | ],
55 | ),
56 | ),
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/example/lib/multi_segment_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:percent_indicator/flutter_percent_indicator.dart';
3 |
4 | class MultiSegmentPage extends StatefulWidget {
5 | @override
6 | _MultiSegmentPageState createState() => _MultiSegmentPageState();
7 | }
8 |
9 | class _MultiSegmentPageState extends State {
10 | double firstSegment = 0.3;
11 | double secondSegment = 0.4;
12 | double thirdSegment = 0.3;
13 |
14 | @override
15 | void initState() {
16 | super.initState();
17 |
18 | Future.delayed(Duration(seconds: 2), () {
19 | setState(() {
20 | secondSegment = 0.5;
21 | thirdSegment = 0.2;
22 | });
23 | });
24 | }
25 |
26 | @override
27 | Widget build(BuildContext context) {
28 | return Scaffold(
29 | appBar: AppBar(
30 | title: Text('Multi Segment Progress'),
31 | ),
32 | body: Center(
33 | child: Padding(
34 | padding: const EdgeInsets.all(20.0),
35 | child: Column(
36 | mainAxisAlignment: MainAxisAlignment.center,
37 | children: [
38 | MultiSegmentLinearIndicator(
39 | width: MediaQuery.of(context).size.width - 64,
40 | lineHeight: 30.0,
41 | segments: [
42 | SegmentLinearIndicator(
43 | percent: 0.25,
44 | color: Color(0xFF4285F4),
45 | enableStripes: true,
46 | ),
47 | SegmentLinearIndicator(
48 | percent: 0.4,
49 | color: Color(0xFF6DD5F6),
50 | ),
51 | SegmentLinearIndicator(
52 | percent: 0.35,
53 | color: Color(0xFFEFEFEF),
54 | ),
55 | ],
56 | barRadius: Radius.circular(10.0),
57 | animation: true,
58 | animationDuration: 1000,
59 | curve: Curves.easeInOut,
60 | animateFromLastPercent: true,
61 | onAnimationEnd: () {
62 | ScaffoldMessenger.of(context).showSnackBar(
63 | const SnackBar(
64 | content: Text('Animation completed!'),
65 | duration: Duration(seconds: 1),
66 | ),
67 | );
68 | },
69 | ),
70 | SizedBox(height: 10),
71 | Text(
72 | 'Static with easeInOut: 25% - 40% - 35%',
73 | style: TextStyle(fontSize: 16),
74 | ),
75 | SizedBox(height: 30),
76 | MultiSegmentLinearIndicator(
77 | width: MediaQuery.of(context).size.width - 40,
78 | lineHeight: 20.0,
79 | segments: [
80 | SegmentLinearIndicator(
81 | percent: 0.3,
82 | color: Color(0xFFBA0521),
83 | ),
84 | SegmentLinearIndicator(
85 | percent: 0.4,
86 | color: Color(0xFF071437),
87 | enableStripes: true,
88 | ),
89 | SegmentLinearIndicator(
90 | percent: 0.3,
91 | color: Color(0xFFFF9205),
92 | ),
93 | ],
94 | barRadius: Radius.circular(20),
95 | ),
96 | SizedBox(height: 10),
97 | Text(
98 | 'Static: 30% - 40% - 30%',
99 | style: TextStyle(fontSize: 16),
100 | ),
101 | SizedBox(height: 30),
102 | MultiSegmentLinearIndicator(
103 | width: MediaQuery.of(context).size.width - 40,
104 | lineHeight: 20.0,
105 | segments: [
106 | SegmentLinearIndicator(
107 | percent: firstSegment,
108 | color: Colors.green,
109 | enableStripes: true,
110 | ),
111 | SegmentLinearIndicator(
112 | percent: secondSegment,
113 | color: Colors.blue,
114 | enableStripes: true,
115 | ),
116 | SegmentLinearIndicator(
117 | percent: thirdSegment,
118 | color: Colors.orange,
119 | ),
120 | ],
121 | animation: true,
122 | animateFromLastPercent: true,
123 | animationDuration: 1000,
124 | curve: Curves.easeInOut,
125 | barRadius: Radius.circular(10),
126 | ),
127 | SizedBox(height: 10),
128 | Text(
129 | 'Progress: ${(firstSegment * 100).toInt()}% - ${(secondSegment * 100).toInt()}% - ${(thirdSegment * 100).toInt()}%',
130 | style: TextStyle(fontSize: 16),
131 | ),
132 | SizedBox(height: 30),
133 | MultiSegmentLinearIndicator(
134 | width: MediaQuery.of(context).size.width - 64,
135 | lineHeight: 30.0,
136 | segments: [
137 | SegmentLinearIndicator(
138 | percent: 0.15,
139 | color: Color(0xFFBA0521),
140 | enableStripes: true,
141 | ),
142 | SegmentLinearIndicator(
143 | percent: 0.4, color: Color(0xFFAEFAB00)),
144 | SegmentLinearIndicator(
145 | percent: 0.45, color: Color(0xFFEFEFEF)),
146 | ],
147 | animation: true,
148 | animationDuration: 1000,
149 | curve: Curves.decelerate,
150 | animateFromLastPercent: true,
151 | ),
152 | SizedBox(height: 10),
153 | Text(
154 | 'Static with decelerate: 25% - 40% - 35%',
155 | style: TextStyle(fontSize: 16),
156 | ),
157 | SizedBox(height: 30),
158 | ],
159 | ),
160 | ),
161 | ),
162 | );
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/example/lib/sample_circular_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:percent_indicator/percent_indicator.dart';
3 |
4 | class SampleCircularPage extends StatefulWidget {
5 | @override
6 | _SampleCircularPageState createState() => _SampleCircularPageState();
7 | }
8 |
9 | class _SampleCircularPageState extends State {
10 | String state = 'Animation start';
11 | @override
12 | Widget build(BuildContext context) {
13 | return Scaffold(
14 | appBar: AppBar(
15 | title: Text("Circular Percent Indicators"),
16 | ),
17 | body: Center(
18 | child: ListView(
19 | children: [
20 | CircularPercentIndicator(
21 | radius: 60.0,
22 | animation: true,
23 | animationDuration: 1000,
24 | lineWidth: 10.0,
25 | percent: 1.0,
26 | reverse: false,
27 | arcType: ArcType.FULL_REVERSED,
28 | startAngle: 0.0,
29 | animateFromLastPercent: true,
30 | circularStrokeCap: CircularStrokeCap.round,
31 | backgroundColor: Colors.green,
32 | linearGradient: const LinearGradient(
33 | begin: Alignment.centerLeft,
34 | end: Alignment.centerRight,
35 | tileMode: TileMode.clamp,
36 | stops: [0.0, 1.0],
37 | colors: [
38 | Colors.yellow,
39 | Colors.red,
40 | ],
41 | ),
42 | widgetIndicator: Center(
43 | child: Container(
44 | height: 20,
45 | width: 20,
46 | decoration: BoxDecoration(
47 | color: Colors.yellow,
48 | borderRadius: BorderRadius.circular(50),
49 | ),
50 | padding: const EdgeInsets.all(5),
51 | child: Container(
52 | decoration: BoxDecoration(
53 | color: Colors.yellow,
54 | borderRadius: BorderRadius.circular(50),
55 | ),
56 | ),
57 | ),
58 | ),
59 | arcBackgroundColor: Colors.grey,
60 | ),
61 | CircularPercentIndicator(
62 | radius: 60.0,
63 | animation: true,
64 | animationDuration: 1000,
65 | lineWidth: 10.0,
66 | percent: 0.9,
67 | reverse: false,
68 | arcType: ArcType.FULL,
69 | startAngle: 0.0,
70 | animateFromLastPercent: true,
71 | circularStrokeCap: CircularStrokeCap.round,
72 | backgroundColor: Colors.green,
73 | linearGradient: const LinearGradient(
74 | begin: Alignment.centerLeft,
75 | end: Alignment.centerRight,
76 | tileMode: TileMode.clamp,
77 | stops: [0.0, 1.0],
78 | colors: [
79 | Colors.yellow,
80 | Colors.red,
81 | ],
82 | ),
83 | widgetIndicator: Center(
84 | child: Container(
85 | height: 30,
86 | width: 30,
87 | decoration: BoxDecoration(
88 | color: Colors.yellow,
89 | borderRadius: BorderRadius.circular(50),
90 | ),
91 | padding: const EdgeInsets.all(5),
92 | child: Container(
93 | decoration: BoxDecoration(
94 | color: Colors.yellow,
95 | borderRadius: BorderRadius.circular(50),
96 | ),
97 | ),
98 | ),
99 | ),
100 | arcBackgroundColor: Colors.grey,
101 | ),
102 | CircularPercentIndicator(
103 | radius: 60.0,
104 | lineWidth: 13.0,
105 | animation: true,
106 | animationDuration: 3000,
107 | percent: 0.7,
108 | animateFromLastPercent: true,
109 | center: Text(
110 | "70.0%",
111 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
112 | ),
113 | footer: Text(
114 | "Sales this week",
115 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0),
116 | ),
117 | circularStrokeCap: CircularStrokeCap.round,
118 | progressColor: Colors.purple,
119 | widgetIndicator: RotatedBox(
120 | quarterTurns: 1,
121 | child: Icon(Icons.airplanemode_active, size: 30),
122 | ),
123 | ),
124 | CircularPercentIndicator(
125 | radius: 50.0,
126 | lineWidth: 10.0,
127 | percent: 0.5,
128 | center: Text("50%"),
129 | circularStrokeCap: CircularStrokeCap.round,
130 | backgroundColor: Colors.grey,
131 | maskFilter: MaskFilter.blur(BlurStyle.solid, 3),
132 | linearGradient: LinearGradient(
133 | begin: Alignment.topCenter,
134 | end: Alignment.bottomCenter,
135 | colors: [Colors.orange, Colors.yellow],
136 | ),
137 | ),
138 | CircularPercentIndicator(
139 | radius: 50.0,
140 | lineWidth: 10.0,
141 | percent: 0.8,
142 | header: Text("Icon header"),
143 | center: Icon(
144 | Icons.person_pin,
145 | size: 50.0,
146 | color: Colors.blue,
147 | ),
148 | reverse: true,
149 | backgroundColor: Colors.grey,
150 | progressColor: Colors.blue,
151 | ),
152 | Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
153 | CircularPercentIndicator(
154 | radius: 50.0,
155 | animation: true,
156 | animationDuration: 2000,
157 | lineWidth: 10.0,
158 | percent: 0.5,
159 | arcBackgroundColor: Colors.orangeAccent,
160 | arcType: ArcType.HALF,
161 | center: Text(
162 | "40 hours",
163 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14.0),
164 | ),
165 | circularStrokeCap: CircularStrokeCap.butt,
166 | backgroundColor: Colors.transparent,
167 | progressColor: Colors.red,
168 | ),
169 | CircularPercentIndicator(
170 | radius: 60.0,
171 | animation: true,
172 | animationDuration: 2000,
173 | lineWidth: 10.0,
174 | percent: 0.5,
175 | reverse: true,
176 | arcBackgroundColor: Colors.teal,
177 | arcType: ArcType.FULL,
178 | center: Text(
179 | "20 hours",
180 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14.0),
181 | ),
182 | circularStrokeCap: CircularStrokeCap.butt,
183 | backgroundColor: Colors.yellow,
184 | progressColor: Colors.red,
185 | ),
186 | ]),
187 | CircularPercentIndicator(
188 | radius: 50.0,
189 | animation: true,
190 | animationDuration: 2000,
191 | lineWidth: 10.0,
192 | percent: 0.5,
193 | startAngle: 90,
194 | center: Text(
195 | "Start angle 250",
196 | textAlign: TextAlign.center,
197 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14.0),
198 | ),
199 | circularStrokeCap: CircularStrokeCap.butt,
200 | backgroundColor: Colors.grey,
201 | progressColor: Colors.red,
202 | ),
203 | CircularPercentIndicator(
204 | radius: 60.0,
205 | lineWidth: 13.0,
206 | animation: true,
207 | animationDuration: 3000,
208 | percent: 0.7,
209 | animateFromLastPercent: true,
210 | center: Text(
211 | "70.0%",
212 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
213 | ),
214 | footer: Text(
215 | "Sales this week",
216 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0),
217 | ),
218 | circularStrokeCap: CircularStrokeCap.round,
219 | progressColor: Colors.purple,
220 | ),
221 | Padding(
222 | padding: EdgeInsets.all(15.0),
223 | child: CircularPercentIndicator(
224 | radius: 30.0,
225 | lineWidth: 5.0,
226 | percent: 1.0,
227 | center: Text("100%"),
228 | progressColor: Colors.green,
229 | ),
230 | ),
231 | Container(
232 | padding: EdgeInsets.all(15.0),
233 | child: SingleChildScrollView(
234 | scrollDirection: Axis.horizontal,
235 | child: Row(
236 | mainAxisAlignment: MainAxisAlignment.center,
237 | children: [
238 | CircularPercentIndicator(
239 | radius: 22.0,
240 | lineWidth: 4.0,
241 | percent: 0.10,
242 | center: Text("10%"),
243 | progressColor: Colors.red,
244 | ),
245 | Padding(
246 | padding: EdgeInsets.symmetric(horizontal: 10.0),
247 | ),
248 | CircularPercentIndicator(
249 | radius: 22.0,
250 | lineWidth: 4.0,
251 | backgroundWidth: 1.0,
252 | percent: 0.2,
253 | animation: true,
254 | center: Text("20%"),
255 | progressColor: Colors.orangeAccent,
256 | ),
257 | Padding(
258 | padding: EdgeInsets.symmetric(horizontal: 10.0),
259 | ),
260 | CircularPercentIndicator(
261 | radius: 22.0,
262 | lineWidth: 4.0,
263 | percent: 0.30,
264 | center: Text("30%"),
265 | progressColor: Colors.orange,
266 | ),
267 | Padding(
268 | padding: EdgeInsets.symmetric(horizontal: 10.0),
269 | ),
270 | CircularPercentIndicator(
271 | radius: 22.0,
272 | lineWidth: 4.0,
273 | backgroundWidth: 8,
274 | animation: true,
275 | animationDuration: 200,
276 | percent: 0.60,
277 | center: Text("60%"),
278 | progressColor: Colors.yellow,
279 | ),
280 | Padding(
281 | padding: EdgeInsets.symmetric(horizontal: 10.0),
282 | ),
283 | CircularPercentIndicator(
284 | radius: 22.0,
285 | lineWidth: 4.0,
286 | percent: 0.90,
287 | center: Text("90%"),
288 | progressColor: Colors.green,
289 | ),
290 | Padding(
291 | padding: EdgeInsets.symmetric(horizontal: 10.0),
292 | ),
293 | CircularPercentIndicator(
294 | radius: 22.0,
295 | lineWidth: 4.0,
296 | percent: 1.0,
297 | animation: true,
298 | restartAnimation: true,
299 | center: CircleAvatar(
300 | child: Icon(Icons.person),
301 | ),
302 | progressColor: Colors.redAccent,
303 | ),
304 | ],
305 | ),
306 | ),
307 | ),
308 | CircularPercentIndicator(
309 | radius: 40.0,
310 | lineWidth: 5.0,
311 | animation: true,
312 | percent: .5,
313 | animationDuration: 2500,
314 | animateFromLastPercent: true,
315 | center: Text(
316 | "50.0%",
317 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
318 | ),
319 | footer: Text(
320 | state,
321 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0),
322 | ),
323 | circularStrokeCap: CircularStrokeCap.round,
324 | progressColor: Colors.blueAccent,
325 | onAnimationEnd: () =>
326 | setState(() => state = 'End Animation at 50%'),
327 | ),
328 | const SizedBox(height: 20),
329 | CircularPercentIndicator(
330 | radius: 40.0,
331 | backgroundColor: Colors.white,
332 | percent: .7,
333 | lineWidth: 10,
334 | backgroundWidth: 15,
335 | fillColor: Colors.transparent,
336 | circularStrokeCap: CircularStrokeCap.round,
337 | arcBackgroundColor: Colors.transparent,
338 | arcType: ArcType.HALF,
339 | ),
340 | const SizedBox(height: 20),
341 | Padding(
342 | padding: const EdgeInsets.only(bottom: 18.0),
343 | child: Center(
344 | child: Text(
345 | "With different border color",
346 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0),
347 | ),
348 | ),
349 | ),
350 | Row(
351 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
352 | children: [
353 | CircularPercentIndicator(
354 | radius: 40.0,
355 | progressColor: Colors.yellow,
356 | progressBorderColor: Colors.green,
357 | percent: .2,
358 | lineWidth: 15,
359 | center: Text(
360 | "20.0%",
361 | style:
362 | TextStyle(fontWeight: FontWeight.bold, fontSize: 12.0),
363 | ),
364 | backgroundWidth: 15,
365 | fillColor: Colors.transparent,
366 | circularStrokeCap: CircularStrokeCap.round,
367 | arcBackgroundColor: Colors.yellow[200],
368 | arcType: ArcType.FULL,
369 | ),
370 | CircularPercentIndicator(
371 | radius: 40.0,
372 | progressColor: Colors.white,
373 | progressBorderColor: Colors.blue,
374 | percent: .9,
375 | lineWidth: 10,
376 | center: Text(
377 | "90.0%",
378 | style:
379 | TextStyle(fontWeight: FontWeight.bold, fontSize: 12.0),
380 | ),
381 | backgroundWidth: 15,
382 | fillColor: Colors.transparent,
383 | circularStrokeCap: CircularStrokeCap.round,
384 | arcBackgroundColor: Colors.transparent,
385 | arcType: ArcType.FULL,
386 | ),
387 | CircularPercentIndicator(
388 | radius: 40.0,
389 | progressColor: Colors.red,
390 | progressBorderColor: Colors.black,
391 | percent: .4,
392 | lineWidth: 10,
393 | backgroundWidth: 15,
394 | fillColor: Colors.transparent,
395 | circularStrokeCap: CircularStrokeCap.round,
396 | arcBackgroundColor: Colors.grey,
397 | arcType: ArcType.FULL,
398 | center: Text(
399 | "40.0%",
400 | style:
401 | TextStyle(fontWeight: FontWeight.bold, fontSize: 12.0),
402 | ),
403 | ),
404 | ],
405 | ),
406 | ],
407 | ),
408 | ),
409 | );
410 | }
411 | }
412 |
--------------------------------------------------------------------------------
/example/lib/sample_linear_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:percent_indicator/percent_indicator.dart';
3 |
4 | class SampleLinearPage extends StatefulWidget {
5 | @override
6 | _SampleLinearPageState createState() => _SampleLinearPageState();
7 | }
8 |
9 | class _SampleLinearPageState extends State {
10 | String state = 'Animation start';
11 | bool isRunning = true;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Scaffold(
16 | appBar: AppBar(
17 | title: Text("Linear Percent Indicators"),
18 | actions: [
19 | IconButton(
20 | icon: Icon(Icons.stop),
21 | onPressed: () {
22 | setState(() {
23 | isRunning = false;
24 | });
25 | })
26 | ],
27 | ),
28 | body: Center(
29 | child: SingleChildScrollView(
30 | child: Column(
31 | mainAxisAlignment: MainAxisAlignment.center,
32 | children: [
33 | Padding(
34 | padding: EdgeInsets.all(15.0),
35 | child: LinearPercentIndicator(
36 | width: MediaQuery.of(context).size.width - 50,
37 | animation: isRunning,
38 | lineHeight: 20.0,
39 | animationDuration: 3000,
40 | percent: 0.5,
41 | animateFromLastPercent: true,
42 | center: Text("50.0%"),
43 | progressColor: Colors.red,
44 | widgetIndicator: RotatedBox(
45 | quarterTurns: 1,
46 | child: Icon(Icons.airplanemode_active, size: 50)),
47 | ),
48 | ),
49 | Padding(
50 | padding: EdgeInsets.all(15.0),
51 | child: LinearPercentIndicator(
52 | width: MediaQuery.of(context).size.width - 50,
53 | lineHeight: 20.0,
54 | animationDuration: 3000,
55 | percent: 0.5,
56 | animateFromLastPercent: true,
57 | center: Text("50.0%"),
58 | linearGradient: LinearGradient(
59 | colors: [Color(0xffB07BE6), Color(0xff5BA2E0)],
60 | ),
61 | linearGradientBackgroundColor: LinearGradient(
62 | colors: [Color(0xffe5d6fa), Color(0xffc8dff8)],
63 | ),
64 | ),
65 | ),
66 | Padding(
67 | padding: EdgeInsets.all(15.0),
68 | child: FittedBox(
69 | child: LinearPercentIndicator(
70 | width: 140.0,
71 | fillColor: Colors.green,
72 | linearGradient: LinearGradient(
73 | colors: [Colors.red, Colors.blue],
74 | ),
75 | lineHeight: 14.0,
76 | percent: 0.7,
77 | center: Text(
78 | "70.0%",
79 | style: TextStyle(fontSize: 12.0),
80 | ),
81 | trailing: Icon(Icons.mood),
82 | barRadius: Radius.circular(7),
83 | backgroundColor: Colors.grey,
84 | ),
85 | ),
86 | ),
87 | Padding(
88 | padding: EdgeInsets.all(15.0),
89 | child: FittedBox(
90 | child: LinearPercentIndicator(
91 | width: 140.0,
92 | fillColor: Colors.green,
93 | lineHeight: 14.0,
94 | percent: 0.5,
95 | center: Text(
96 | "50.0%",
97 | style: TextStyle(fontSize: 12.0),
98 | ),
99 | trailing: Icon(Icons.mood),
100 | barRadius: Radius.circular(7),
101 | backgroundColor: Colors.grey,
102 | progressColor: Colors.blue,
103 | ),
104 | ),
105 | ),
106 | Padding(
107 | padding: EdgeInsets.all(15.0),
108 | child: FittedBox(
109 | child: LinearPercentIndicator(
110 | width: 140.0,
111 | lineHeight: 40.0,
112 | percent: 0.05,
113 | center: Text(
114 | "5.0%",
115 | style: TextStyle(fontSize: 12.0),
116 | ),
117 | barRadius: Radius.circular(20),
118 | backgroundColor: Colors.grey,
119 | progressColor: Colors.red,
120 | ),
121 | ),
122 | ),
123 | Padding(
124 | padding: EdgeInsets.all(15.0),
125 | child: LinearPercentIndicator(
126 | animation: true,
127 | animationDuration: 500,
128 | lineHeight: 20.0,
129 | leading: Expanded(
130 | child: Text("left content"),
131 | ),
132 | trailing: Expanded(
133 | child: Text(
134 | "right content",
135 | textAlign: TextAlign.end,
136 | )),
137 | percent: 0.2,
138 | center: Text("20.0%"),
139 | progressColor: Colors.red,
140 | ),
141 | ),
142 | Padding(
143 | padding: EdgeInsets.all(15.0),
144 | child: LinearPercentIndicator(
145 | width: MediaQuery.of(context).size.width - 50,
146 | animation: true,
147 | lineHeight: 20.0,
148 | animationDuration: 2000,
149 | percent: 0.9,
150 | animateFromLastPercent: true,
151 | center: Text("90.0%"),
152 | isRTL: true,
153 | barRadius: Radius.elliptical(5, 15),
154 | progressColor: Colors.greenAccent,
155 | maskFilter: MaskFilter.blur(BlurStyle.solid, 3),
156 | ),
157 | ),
158 | Padding(
159 | padding: EdgeInsets.all(15.0),
160 | child: LinearPercentIndicator(
161 | width: MediaQuery.of(context).size.width - 50,
162 | animation: true,
163 | lineHeight: 20.0,
164 | animationDuration: 2500,
165 | percent: 0.8,
166 | center: Text("80.0%"),
167 | barRadius: Radius.circular(5),
168 | progressColor: Colors.green,
169 | ),
170 | ),
171 | Padding(
172 | padding: EdgeInsets.all(15.0),
173 | child: LinearPercentIndicator(
174 | animation: true,
175 | lineHeight: 20.0,
176 | animationDuration: 2500,
177 | percent: 0.55,
178 | center: Text("55.0%"),
179 | barRadius: Radius.circular(3),
180 | progressColor: Colors.green,
181 | ),
182 | ),
183 | Padding(
184 | padding: EdgeInsets.all(15.0),
185 | child: Column(
186 | children: [
187 | LinearPercentIndicator(
188 | width: 100.0,
189 | lineHeight: 8.0,
190 | percent: 0.2,
191 | progressColor: Colors.red,
192 | ),
193 | SizedBox(
194 | height: 10,
195 | ),
196 | LinearPercentIndicator(
197 | width: 100.0,
198 | lineHeight: 8.0,
199 | percent: 0.5,
200 | progressColor: Colors.orange,
201 | ),
202 | SizedBox(
203 | height: 10,
204 | ),
205 | LinearPercentIndicator(
206 | width: 100.0,
207 | lineHeight: 8.0,
208 | percent: 0.9,
209 | progressColor: Colors.blue,
210 | ),
211 | SizedBox(
212 | height: 10,
213 | ),
214 | LinearPercentIndicator(
215 | width: 100.0,
216 | lineHeight: 8.0,
217 | percent: 1.0,
218 | progressColor: Colors.lightBlueAccent,
219 | restartAnimation: true,
220 | animation: true,
221 | )
222 | ],
223 | ),
224 | ),
225 | Padding(
226 | padding: EdgeInsets.all(15),
227 | child: LinearPercentIndicator(
228 | lineHeight: 20,
229 | center: Text('50%'),
230 | progressColor: Colors.blueAccent,
231 | percent: .5,
232 | animation: true,
233 | animationDuration: 5000,
234 | onAnimationEnd: () =>
235 | setState(() => state = 'End Animation at 50%'),
236 | ),
237 | ),
238 | Text(state),
239 | Padding(
240 | padding: EdgeInsets.all(15),
241 | child: LinearPercentIndicator(
242 | lineHeight: 20,
243 | center: Text('50%'),
244 | progressColor: Colors.pinkAccent,
245 | barRadius: Radius.circular(10),
246 | percent: .5,
247 | animation: true,
248 | animationDuration: 1000,
249 | ),
250 | ),
251 | Text('Rounded Edges'),
252 | Padding(
253 | padding: EdgeInsets.all(15),
254 | child: LinearPercentIndicator(
255 | lineHeight: 20,
256 | center: Text('60%'),
257 | progressColor: Colors.purple,
258 | barRadius: Radius.circular(5),
259 | percent: .6,
260 | animation: true,
261 | animationDuration: 1200,
262 | ),
263 | ),
264 | Text('Soft Corner Edges'),
265 | Padding(
266 | padding: EdgeInsets.all(15),
267 | child: LinearPercentIndicator(
268 | lineHeight: 20,
269 | center: Text('70%'),
270 | progressColor: Colors.deepPurple,
271 | barRadius: Radius.elliptical(5, 10),
272 | percent: .7,
273 | animation: true,
274 | animationDuration: 1400,
275 | ),
276 | ),
277 | Text('Custom Edges'),
278 | Padding(
279 | padding: EdgeInsets.all(15),
280 | child: LinearPercentIndicator(
281 | lineHeight: 20,
282 | center: Text('90%'),
283 | progressColor: Colors.white,
284 | progressBorderColor: Colors.green,
285 | barRadius: Radius.elliptical(5, 10),
286 | percent: .9,
287 | animation: true,
288 | animationDuration: 1400,
289 | ),
290 | ),
291 | Text('Custom Border Color'),
292 | ],
293 | ),
294 | ),
295 | ),
296 | );
297 | }
298 | }
299 |
--------------------------------------------------------------------------------
/example/lib/segment_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:percent_indicator/flutter_percent_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 MaterialApp(
14 | title: 'Multi-Segment Indicator Demo',
15 | debugShowCheckedModeBanner: false,
16 | theme: ThemeData(),
17 | home: const MultiSegmentDemo(),
18 | );
19 | }
20 | }
21 |
22 | class MultiSegmentDemo extends StatefulWidget {
23 | const MultiSegmentDemo({Key? key}) : super(key: key);
24 |
25 | @override
26 | State createState() => _MultiSegmentDemoState();
27 | }
28 |
29 | class _MultiSegmentDemoState extends State {
30 | double firstSegment = 0.3;
31 | double secondSegment = 0.4;
32 | double thirdSegment = 0.3;
33 | double lineHeight = 30.0;
34 | bool hasStripes = true;
35 | double borderRadius = 10.0;
36 | Color firstColor = const Color(0xFF4285F4);
37 | Color secondColor = const Color(0xFF6DD5F6);
38 | Color thirdColor = const Color(0xFFEFEFEF);
39 | bool animate = true;
40 | int animationDuration = 1000;
41 | bool animateFromLastPercent = true;
42 | Curve animationCurve = Curves.easeInOut;
43 |
44 | final List curves = [
45 | Curves.linear,
46 | Curves.easeIn,
47 | Curves.easeOut,
48 | Curves.easeInOut,
49 | Curves.elasticIn,
50 | Curves.elasticOut,
51 | Curves.bounceIn,
52 | Curves.bounceOut,
53 | ];
54 |
55 | String _getCurveName(Curve curve) {
56 | return curve.toString().split('.').last;
57 | }
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | return Scaffold(
62 | appBar: AppBar(
63 | title: const Text('Multi-Segment Indicator Demo'),
64 | ),
65 | body: SingleChildScrollView(
66 | padding: const EdgeInsets.all(16.0),
67 | child: Column(
68 | crossAxisAlignment: CrossAxisAlignment.start,
69 | children: [
70 | Card(
71 | color: Colors.white,
72 | child: Padding(
73 | padding: const EdgeInsets.all(16.0),
74 | child: Column(
75 | crossAxisAlignment: CrossAxisAlignment.start,
76 | children: [
77 | MultiSegmentLinearIndicator(
78 | width: MediaQuery.of(context).size.width - 64,
79 | lineHeight: lineHeight,
80 | segments: [
81 | SegmentLinearIndicator(
82 | percent: firstSegment, color: firstColor),
83 | SegmentLinearIndicator(
84 | percent: secondSegment,
85 | color: secondColor,
86 | enableStripes: hasStripes),
87 | SegmentLinearIndicator(
88 | percent: thirdSegment,
89 | color: thirdColor,
90 | ),
91 | ],
92 | barRadius: Radius.circular(borderRadius),
93 | animation: animate,
94 | animationDuration: animationDuration,
95 | curve: animationCurve,
96 | animateFromLastPercent: animateFromLastPercent,
97 | onAnimationEnd: () {
98 | ScaffoldMessenger.of(context).showSnackBar(
99 | const SnackBar(
100 | content: Text('Animation completed!'),
101 | duration: Duration(seconds: 1),
102 | ),
103 | );
104 | },
105 | ),
106 | ],
107 | ),
108 | ),
109 | ),
110 | const SizedBox(height: 16),
111 | ],
112 | ),
113 | ),
114 | );
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/example/percent_indicator_example.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/example/percent_indicator_example_android.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | characters:
5 | dependency: transitive
6 | description:
7 | name: characters
8 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "1.3.0"
12 | collection:
13 | dependency: transitive
14 | description:
15 | name: collection
16 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "1.18.0"
20 | flutter:
21 | dependency: "direct main"
22 | description: flutter
23 | source: sdk
24 | version: "0.0.0"
25 | material_color_utilities:
26 | dependency: transitive
27 | description:
28 | name: material_color_utilities
29 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
30 | url: "https://pub.dev"
31 | source: hosted
32 | version: "0.11.1"
33 | meta:
34 | dependency: transitive
35 | description:
36 | name: meta
37 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
38 | url: "https://pub.dev"
39 | source: hosted
40 | version: "1.15.0"
41 | percent_indicator:
42 | dependency: "direct main"
43 | description:
44 | path: ".."
45 | relative: true
46 | source: path
47 | version: "4.2.5"
48 | sky_engine:
49 | dependency: transitive
50 | description: flutter
51 | source: sdk
52 | version: "0.0.99"
53 | vector_math:
54 | dependency: transitive
55 | description:
56 | name: vector_math
57 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
58 | url: "https://pub.dev"
59 | source: hosted
60 | version: "2.1.4"
61 | sdks:
62 | dart: ">=3.3.0-0 <4.0.0"
63 | flutter: ">=2.12.0"
64 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: percent_indicator_example
2 | description: A new Flutter project.
3 |
4 | version: 1.0.0+1
5 | publish_to: "none"
6 |
7 | dependencies:
8 | flutter:
9 | sdk: flutter
10 |
11 | percent_indicator:
12 | path: ../
13 | environment:
14 | sdk: ">=2.12.0-0 <3.0.0"
15 | # For information on the generic Dart part of this file, see the
16 | # following page: https://www.dartlang.org/tools/pub/pubspec
17 |
18 | # The following section is specific to Flutter.
19 | flutter:
20 | # The following line ensures that the Material Icons font is
21 | # included with your application, so that you can use the icons in
22 | # the material Icons class.
23 | uses-material-design: true
24 |
--------------------------------------------------------------------------------
/lib/circular_percent_indicator.dart:
--------------------------------------------------------------------------------
1 | //import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'dart:math' as math;
5 |
6 | enum ArcType { HALF, FULL, FULL_REVERSED }
7 |
8 | enum CircularStrokeCap { butt, round, square }
9 |
10 | extension CircularStrokeCapExtension on CircularStrokeCap {
11 | StrokeCap get strokeCap {
12 | switch (this) {
13 | case CircularStrokeCap.butt:
14 | return StrokeCap.butt;
15 | case CircularStrokeCap.round:
16 | return StrokeCap.round;
17 | case CircularStrokeCap.square:
18 | return StrokeCap.square;
19 | }
20 | }
21 | }
22 |
23 | num radians(num deg) => deg * (math.pi / 180.0);
24 |
25 | // ignore: must_be_immutable
26 | class CircularPercentIndicator extends StatefulWidget {
27 | ///Percent value between 0.0 and 1.0
28 | final double percent;
29 | final double radius;
30 |
31 | ///Width of the progress bar of the circle
32 | final double lineWidth;
33 |
34 | ///Width of the unfilled background of the progress bar
35 | final double backgroundWidth;
36 |
37 | ///First color applied to the complete circle
38 | final Color fillColor;
39 |
40 | ///Color of the border of the progress bar , default = null
41 | final Color? progressBorderColor;
42 |
43 | ///Color of the background of the circle , default = transparent
44 | final Color backgroundColor;
45 |
46 | Color get progressColor => _progressColor;
47 |
48 | late Color _progressColor;
49 |
50 | ///true if you want the circle to have animation
51 | final bool animation;
52 |
53 | ///duration of the animation in milliseconds, It only applies if animation attribute is true
54 | final int animationDuration;
55 |
56 | ///widget at the top of the circle
57 | final Widget? header;
58 |
59 | ///widget at the bottom of the circle
60 | final Widget? footer;
61 |
62 | ///widget inside the circle
63 | final Widget? center;
64 |
65 | final LinearGradient? linearGradient;
66 |
67 | ///The kind of finish to place on the end of lines drawn, values supported: butt, round, square
68 | final CircularStrokeCap circularStrokeCap;
69 |
70 | ///the angle which the circle will start the progress (in degrees, eg: 0.0, 45.0, 90.0)
71 | final double startAngle;
72 |
73 | /// set true if you want to animate the linear from the last percent value you set
74 | final bool animateFromLastPercent;
75 |
76 | /// set to false if you do not want the default behavior of initially animating up from 0%
77 | final bool animateToInitialPercent;
78 |
79 | /// set false if you don't want to preserve the state of the widget
80 | final bool addAutomaticKeepAlive;
81 |
82 | /// set the arc type
83 | final ArcType? arcType;
84 |
85 | /// set a circular background color when use the arcType property
86 | final Color? arcBackgroundColor;
87 |
88 | /// set true when you want to display the progress in reverse mode
89 | final bool reverse;
90 |
91 | /// Creates a mask filter that takes the progress shape being drawn and blurs it.
92 | final MaskFilter? maskFilter;
93 |
94 | /// set a circular curve animation type
95 | final Curve curve;
96 |
97 | /// set true when you want to restart the animation, it restarts only when reaches 1.0 as a value
98 | /// defaults to false
99 | final bool restartAnimation;
100 |
101 | /// Callback called when the animation ends (only if `animation` is true)
102 | final VoidCallback? onAnimationEnd;
103 |
104 | /// Display a widget indicator at the end of the progress. It only works when `animation` is true
105 | final Widget? widgetIndicator;
106 |
107 | /// Set to true if you want to rotate linear gradient in accordance to the [startAngle].
108 | final bool rotateLinearGradient;
109 |
110 | /// Set true if you want to display only part of [linearGradient] based on percent value.
111 | /// Works only if [rotateLinearGradient] is true.
112 | final bool clipRotatedLinearGradient;
113 |
114 | /// Return current percent value if animation is true.
115 | final Function(double value)? onPercentValue;
116 |
117 | CircularPercentIndicator({
118 | Key? key,
119 | this.percent = 0.0,
120 | this.lineWidth = 5.0,
121 | this.startAngle = 0.0,
122 | required this.radius,
123 | this.fillColor = Colors.transparent,
124 | this.backgroundColor = const Color(0xFFB8C7CB),
125 | Color? progressColor,
126 | //negative values ignored, replaced with lineWidth
127 | this.backgroundWidth = -1,
128 | this.linearGradient,
129 | this.animation = false,
130 | this.animationDuration = 500,
131 | this.header,
132 | this.footer,
133 | this.center,
134 | this.addAutomaticKeepAlive = true,
135 | this.circularStrokeCap = CircularStrokeCap.butt,
136 | this.arcBackgroundColor,
137 | this.arcType,
138 | this.animateFromLastPercent = false,
139 | this.animateToInitialPercent = true,
140 | this.reverse = false,
141 | this.curve = Curves.linear,
142 | this.maskFilter,
143 | this.restartAnimation = false,
144 | this.onAnimationEnd,
145 | this.widgetIndicator,
146 | this.rotateLinearGradient = false,
147 | this.clipRotatedLinearGradient = false,
148 | this.progressBorderColor,
149 | this.onPercentValue,
150 | }) : super(key: key) {
151 | if (linearGradient != null && progressColor != null) {
152 | throw ArgumentError(
153 | 'Cannot provide both linearGradient and progressColor');
154 | }
155 | _progressColor = progressColor ?? Colors.red;
156 |
157 | assert(startAngle >= 0.0);
158 | if (percent < 0.0 || percent > 1.0) {
159 | throw Exception(
160 | "Percent value must be a double between 0.0 and 1.0, but it's $percent");
161 | }
162 |
163 | if (arcType == null && arcBackgroundColor != null) {
164 | throw ArgumentError('arcType is required when you arcBackgroundColor');
165 | }
166 | }
167 |
168 | @override
169 | _CircularPercentIndicatorState createState() =>
170 | _CircularPercentIndicatorState();
171 | }
172 |
173 | class _CircularPercentIndicatorState extends State
174 | with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
175 | AnimationController? _animationController;
176 | Animation? _animation;
177 | double _percent = 0.0;
178 | double _diameter = 0.0;
179 |
180 | @override
181 | void dispose() {
182 | if (_animationController != null) {
183 | _animationController!.dispose();
184 | }
185 | super.dispose();
186 | }
187 |
188 | @override
189 | void initState() {
190 | if (widget.animation) {
191 | if (!widget.animateToInitialPercent) _percent = widget.percent;
192 | _animationController = AnimationController(
193 | vsync: this,
194 | duration: Duration(milliseconds: widget.animationDuration),
195 | );
196 | _animation = Tween(begin: _percent, end: widget.percent).animate(
197 | CurvedAnimation(parent: _animationController!, curve: widget.curve),
198 | )..addListener(() {
199 | setState(() {
200 | _percent = _animation!.value;
201 | widget.onPercentValue?.call(_percent);
202 | });
203 | if (widget.restartAnimation && _percent == 1.0) {
204 | _animationController!.repeat(min: 0, max: 1.0);
205 | }
206 | });
207 | _animationController!.addStatusListener((status) {
208 | if (widget.onAnimationEnd != null &&
209 | status == AnimationStatus.completed) {
210 | widget.onAnimationEnd!();
211 | }
212 | });
213 | _animationController!.forward();
214 | } else {
215 | _updateProgress();
216 | }
217 | _diameter = widget.radius * 2;
218 | super.initState();
219 | }
220 |
221 | void _checkIfNeedCancelAnimation(CircularPercentIndicator oldWidget) {
222 | if (oldWidget.animation &&
223 | !widget.animation &&
224 | _animationController != null) {
225 | _animationController!.stop();
226 | }
227 | }
228 |
229 | @override
230 | void didUpdateWidget(CircularPercentIndicator oldWidget) {
231 | super.didUpdateWidget(oldWidget);
232 | if (oldWidget.percent != widget.percent ||
233 | oldWidget.startAngle != widget.startAngle) {
234 | if (_animationController != null) {
235 | _animationController!.duration =
236 | Duration(milliseconds: widget.animationDuration);
237 | _animation = Tween(
238 | begin: widget.animateFromLastPercent ? oldWidget.percent : 0.0,
239 | end: widget.percent,
240 | ).animate(
241 | CurvedAnimation(parent: _animationController!, curve: widget.curve),
242 | );
243 | _animationController!.forward(from: 0.0);
244 | } else {
245 | _updateProgress();
246 | }
247 | }
248 | _checkIfNeedCancelAnimation(oldWidget);
249 | }
250 |
251 | _updateProgress() {
252 | setState(() => _percent = widget.percent);
253 | }
254 |
255 | @override
256 | Widget build(BuildContext context) {
257 | super.build(context);
258 | var items = List.empty(growable: true);
259 | if (widget.header != null) {
260 | items.add(widget.header!);
261 | }
262 | items.add(
263 | Container(
264 | height: _diameter,
265 | width: _diameter,
266 | child: Stack(
267 | children: [
268 | CustomPaint(
269 | painter: _CirclePainter(
270 | progress: _percent * 360,
271 | progressColor: widget.progressColor,
272 | progressBorderColor: widget.progressBorderColor,
273 | backgroundColor: widget.backgroundColor,
274 | startAngle: widget.startAngle,
275 | circularStrokeCap: widget.circularStrokeCap,
276 | radius: widget.radius - widget.lineWidth / 2,
277 | lineWidth: widget.lineWidth,
278 | //negative values ignored, replaced with lineWidth
279 | backgroundWidth: widget.backgroundWidth >= 0.0
280 | ? (widget.backgroundWidth)
281 | : widget.lineWidth,
282 | arcBackgroundColor: widget.arcBackgroundColor,
283 | arcType: widget.arcType,
284 | reverse: widget.reverse,
285 | linearGradient: widget.linearGradient,
286 | maskFilter: widget.maskFilter,
287 | rotateLinearGradient: widget.rotateLinearGradient,
288 | clipRotatedLinearGradient: widget.clipRotatedLinearGradient,
289 | ),
290 | child: (widget.center != null)
291 | ? Center(child: widget.center)
292 | : SizedBox.expand(),
293 | ),
294 | if (widget.widgetIndicator != null && widget.animation)
295 | Positioned.fill(
296 | child: Transform.rotate(
297 | angle: radians(
298 | (widget.circularStrokeCap != CircularStrokeCap.butt &&
299 | widget.reverse)
300 | ? -15
301 | : 0)
302 | .toDouble(),
303 | child: Transform.rotate(
304 | angle: getCurrentPercent(_percent),
305 | child: Transform.translate(
306 | offset: Offset(
307 | (widget.circularStrokeCap != CircularStrokeCap.butt)
308 | ? widget.lineWidth / 2
309 | : 0,
310 | (-widget.radius + widget.lineWidth / 2),
311 | ),
312 | child: widget.widgetIndicator,
313 | ),
314 | ),
315 | ),
316 | ),
317 | ],
318 | ),
319 | ),
320 | );
321 |
322 | if (widget.footer != null) {
323 | items.add(widget.footer!);
324 | }
325 |
326 | return Material(
327 | color: widget.fillColor,
328 | child: Container(
329 | child: Column(
330 | mainAxisAlignment: MainAxisAlignment.center,
331 | mainAxisSize: MainAxisSize.min,
332 | children: items,
333 | ),
334 | ),
335 | );
336 | }
337 |
338 | double getCurrentPercent(double percent) {
339 | if (widget.arcType != null) {
340 | final angle = _getStartAngleFixedMargin(widget.arcType!).fixedStartAngle;
341 | final fixedPercent =
342 | widget.percent > 0 ? 1.0 / widget.percent * _percent : 0;
343 | late double margin;
344 | if (widget.arcType == ArcType.HALF) {
345 | margin = 180 * widget.percent;
346 | } else {
347 | margin = 280 * widget.percent;
348 | }
349 | return radians(angle + margin * fixedPercent).toDouble();
350 | } else {
351 | final angle = 360;
352 | return radians((widget.reverse ? -angle : angle) * _percent).toDouble();
353 | }
354 | }
355 |
356 | @override
357 | bool get wantKeepAlive => widget.addAutomaticKeepAlive;
358 | }
359 |
360 | _ArcAngles _getStartAngleFixedMargin(ArcType arcType) {
361 | double fixedStartAngle, startAngleFixedMargin;
362 | if (arcType == ArcType.FULL_REVERSED) {
363 | fixedStartAngle = 399;
364 | startAngleFixedMargin = 312 / fixedStartAngle;
365 | } else if (arcType == ArcType.FULL) {
366 | fixedStartAngle = 220;
367 | startAngleFixedMargin = 172 / fixedStartAngle;
368 | } else {
369 | fixedStartAngle = 270;
370 | startAngleFixedMargin = 135 / fixedStartAngle;
371 | }
372 | return _ArcAngles(
373 | fixedStartAngle: fixedStartAngle,
374 | startAngleFixedMargin: startAngleFixedMargin,
375 | );
376 | }
377 |
378 | class _ArcAngles {
379 | const _ArcAngles(
380 | {required this.fixedStartAngle, required this.startAngleFixedMargin});
381 | final double fixedStartAngle;
382 | final double startAngleFixedMargin;
383 | }
384 |
385 | class _CirclePainter extends CustomPainter {
386 | final Paint _paintBackground = Paint();
387 | final Paint _paintLine = Paint();
388 | final Paint _paintLineBorder = Paint();
389 | final Paint _paintBackgroundStartAngle = Paint();
390 | final double lineWidth;
391 | final double backgroundWidth;
392 | final double progress;
393 | final double radius;
394 | final Color progressColor;
395 | final Color? progressBorderColor;
396 | final Color backgroundColor;
397 | final CircularStrokeCap circularStrokeCap;
398 | final double startAngle;
399 | final LinearGradient? linearGradient;
400 | final Color? arcBackgroundColor;
401 | final ArcType? arcType;
402 | final bool reverse;
403 | final MaskFilter? maskFilter;
404 | final bool rotateLinearGradient;
405 | final bool clipRotatedLinearGradient;
406 |
407 | _CirclePainter({
408 | required this.lineWidth,
409 | required this.backgroundWidth,
410 | required this.progress,
411 | required this.radius,
412 | required this.progressColor,
413 | required this.backgroundColor,
414 | this.progressBorderColor,
415 | this.startAngle = 0.0,
416 | this.circularStrokeCap = CircularStrokeCap.butt,
417 | this.linearGradient,
418 | required this.reverse,
419 | this.arcBackgroundColor,
420 | this.arcType,
421 | this.maskFilter,
422 | required this.rotateLinearGradient,
423 | required this.clipRotatedLinearGradient,
424 | }) {
425 | _paintBackground.color = backgroundColor;
426 | _paintBackground.style = PaintingStyle.stroke;
427 | _paintBackground.strokeWidth = backgroundWidth;
428 | _paintBackground.strokeCap = circularStrokeCap.strokeCap;
429 |
430 | if (arcBackgroundColor != null) {
431 | _paintBackgroundStartAngle.color = arcBackgroundColor!;
432 | _paintBackgroundStartAngle.style = PaintingStyle.stroke;
433 | _paintBackgroundStartAngle.strokeWidth = lineWidth;
434 | _paintBackgroundStartAngle.strokeCap = circularStrokeCap.strokeCap;
435 | }
436 |
437 | _paintLine.color = progressColor;
438 | _paintLine.style = PaintingStyle.stroke;
439 | _paintLine.strokeWidth =
440 | progressBorderColor != null ? lineWidth - 2 : lineWidth;
441 | _paintLine.strokeCap = circularStrokeCap.strokeCap;
442 |
443 | if (progressBorderColor != null) {
444 | _paintLineBorder.color = progressBorderColor!;
445 | _paintLineBorder.style = PaintingStyle.stroke;
446 | _paintLineBorder.strokeWidth = lineWidth;
447 | _paintLineBorder.strokeCap = circularStrokeCap.strokeCap;
448 | }
449 | }
450 |
451 | @override
452 | void paint(Canvas canvas, Size size) {
453 | final center = Offset(size.width / 2, size.height / 2);
454 | double fixedStartAngle = startAngle;
455 | double startAngleFixedMargin = 1.0;
456 | if (arcType != null) {
457 | final arcAngles = _getStartAngleFixedMargin(arcType!);
458 | fixedStartAngle = arcAngles.fixedStartAngle;
459 | startAngleFixedMargin = arcAngles.startAngleFixedMargin;
460 | }
461 | if (arcType == null) {
462 | canvas.drawCircle(center, radius, _paintBackground);
463 | }
464 |
465 | if (maskFilter != null) {
466 | _paintLineBorder.maskFilter = _paintLine.maskFilter = maskFilter;
467 | }
468 | if (linearGradient != null) {
469 | if (rotateLinearGradient && progress > 0) {
470 | double correction = 0;
471 | if (_paintLine.strokeCap != StrokeCap.butt) {
472 | correction = math.atan(_paintLine.strokeWidth / 2 / radius);
473 | }
474 | _paintLineBorder.shader = _paintLine.shader = SweepGradient(
475 | transform: reverse
476 | ? GradientRotation(
477 | radians(-90 - progress + startAngle) - correction)
478 | : GradientRotation(radians(-90.0 + startAngle) - correction),
479 | startAngle: radians(0).toDouble(),
480 | endAngle: clipRotatedLinearGradient
481 | ? radians(360).toDouble()
482 | : radians(progress).toDouble(),
483 | tileMode: TileMode.clamp,
484 | colors: reverse
485 | ? linearGradient!.colors.reversed.toList()
486 | : linearGradient!.colors,
487 | ).createShader(
488 | Rect.fromCircle(center: center, radius: radius),
489 | );
490 | } else if (!rotateLinearGradient) {
491 | _paintLineBorder.shader =
492 | _paintLine.shader = linearGradient!.createShader(
493 | Rect.fromCircle(center: center, radius: radius),
494 | );
495 | }
496 | }
497 |
498 | if (arcBackgroundColor != null) {
499 | canvas.drawArc(
500 | Rect.fromCircle(center: center, radius: radius),
501 | radians(-90.0 + fixedStartAngle).toDouble(),
502 | radians(360 * startAngleFixedMargin).toDouble(),
503 | false,
504 | _paintBackgroundStartAngle,
505 | );
506 | }
507 |
508 | if (reverse) {
509 | final start =
510 | radians(360 * startAngleFixedMargin - 90.0 + fixedStartAngle)
511 | .toDouble();
512 | final end = radians(-progress * startAngleFixedMargin).toDouble();
513 | if (progressBorderColor != null) {
514 | canvas.drawArc(
515 | Rect.fromCircle(
516 | center: center,
517 | radius: radius,
518 | ),
519 | start,
520 | end,
521 | false,
522 | _paintLineBorder,
523 | );
524 | }
525 | canvas.drawArc(
526 | Rect.fromCircle(
527 | center: center,
528 | radius: radius,
529 | ),
530 | start,
531 | end,
532 | false,
533 | _paintLine,
534 | );
535 | } else {
536 | final start = radians(-90.0 + fixedStartAngle).toDouble();
537 | final end = radians(progress * startAngleFixedMargin).toDouble();
538 | if (progressBorderColor != null) {
539 | canvas.drawArc(
540 | Rect.fromCircle(
541 | center: center,
542 | radius: radius,
543 | ),
544 | start,
545 | end,
546 | false,
547 | _paintLineBorder,
548 | );
549 | }
550 | canvas.drawArc(
551 | Rect.fromCircle(
552 | center: center,
553 | radius: radius,
554 | ),
555 | start,
556 | end,
557 | false,
558 | _paintLine,
559 | );
560 | }
561 | }
562 |
563 | @override
564 | bool shouldRepaint(CustomPainter oldDelegate) => true;
565 | }
566 |
--------------------------------------------------------------------------------
/lib/flutter_percent_indicator.dart:
--------------------------------------------------------------------------------
1 | library flutter_percent_indicator;
2 |
3 | export 'circular_percent_indicator.dart';
4 | export 'linear_percent_indicator.dart';
5 | export 'multi_segment_linear_indicator.dart';
6 |
--------------------------------------------------------------------------------
/lib/linear_percent_indicator.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: unused_element
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | @Deprecated('This property is no longer used, please use barRadius instead.')
6 | enum LinearStrokeCap { butt, round, roundAll }
7 |
8 | extension ExtDouble on double {
9 | bool get isZero => this.toString() == '0.0';
10 | }
11 |
12 | // ignore: must_be_immutable
13 | class LinearPercentIndicator extends StatefulWidget {
14 | ///Percent value between 0.0 and 1.0
15 | final double percent;
16 | final double? width;
17 |
18 | ///Height of the line
19 | final double lineHeight;
20 |
21 | ///Color of the background of the Line , default = transparent
22 | final Color fillColor;
23 |
24 | ///Color of the border of the progress bar , default = null
25 | final Color? progressBorderColor;
26 |
27 | ///First color applied to the complete line
28 | Color get backgroundColor => _backgroundColor;
29 | late Color _backgroundColor;
30 |
31 | ///First color applied to the complete line
32 | final LinearGradient? linearGradientBackgroundColor;
33 |
34 | Color get progressColor => _progressColor;
35 |
36 | late Color _progressColor;
37 |
38 | ///true if you want the Line to have animation
39 | final bool animation;
40 |
41 | ///duration of the animation in milliseconds, It only applies if animation attribute is true
42 | final int animationDuration;
43 |
44 | ///widget at the left of the Line
45 | final Widget? leading;
46 |
47 | ///widget at the right of the Line
48 | final Widget? trailing;
49 |
50 | ///widget inside the Line
51 | final Widget? center;
52 |
53 | ///The kind of finish to place on the end of lines drawn, values supported: butt, round, roundAll
54 | @Deprecated('This property is no longer used, please use barRadius instead.')
55 | final LinearStrokeCap? linearStrokeCap;
56 |
57 | /// The border radius of the progress bar (Will replace linearStrokeCap)
58 | final Radius? barRadius;
59 |
60 | ///alignment of the Row (leading-widget-center-trailing)
61 | final MainAxisAlignment alignment;
62 |
63 | ///padding to the LinearPercentIndicator
64 | final EdgeInsets padding;
65 |
66 | /// set true if you want to animate the linear from the last percent value you set
67 | final bool animateFromLastPercent;
68 |
69 | /// set to false if you do not want the default behavior of initially animating up from 0%
70 | final bool animateToInitialPercent;
71 |
72 | /// If present, this will make the progress bar colored by this gradient.
73 | ///
74 | /// This will override [progressColor]. It is an error to provide both.
75 | final LinearGradient? linearGradient;
76 |
77 | /// set false if you don't want to preserve the state of the widget
78 | final bool addAutomaticKeepAlive;
79 |
80 | /// set true if you want to animate the linear from the right to left (RTL)
81 | final bool isRTL;
82 |
83 | /// Creates a mask filter that takes the progress shape being drawn and blurs it.
84 | final MaskFilter? maskFilter;
85 |
86 | /// Set true if you want to display only part of [linearGradient] based on percent value
87 | /// (ie. create 'VU effect'). If no [linearGradient] is specified this option is ignored.
88 | final bool clipLinearGradient;
89 |
90 | /// set a linear curve animation type
91 | final Curve curve;
92 |
93 | /// set true when you want to restart the animation, it restarts only when reaches 1.0 as a value
94 | /// defaults to false
95 | final bool restartAnimation;
96 |
97 | /// Callback called when the animation ends (only if `animation` is true)
98 | final VoidCallback? onAnimationEnd;
99 |
100 | /// Display a widget indicator at the end of the progress. It only works when `animation` is true
101 | final Widget? widgetIndicator;
102 |
103 | /// Return current percent value if animation is true.
104 | final Function(double value)? onPercentValue;
105 |
106 | LinearPercentIndicator({
107 | Key? key,
108 | this.fillColor = Colors.transparent,
109 | this.percent = 0.0,
110 | this.lineHeight = 5.0,
111 | this.width,
112 | Color? backgroundColor,
113 | this.linearGradientBackgroundColor,
114 | this.linearGradient,
115 | Color? progressColor,
116 | this.animation = false,
117 | this.animationDuration = 500,
118 | this.animateFromLastPercent = false,
119 | this.animateToInitialPercent = true,
120 | this.isRTL = false,
121 | this.leading,
122 | this.trailing,
123 | this.center,
124 | this.addAutomaticKeepAlive = true,
125 | this.linearStrokeCap,
126 | this.barRadius,
127 | this.padding = const EdgeInsets.symmetric(horizontal: 10.0),
128 | this.alignment = MainAxisAlignment.start,
129 | this.maskFilter,
130 | this.clipLinearGradient = false,
131 | this.curve = Curves.linear,
132 | this.restartAnimation = false,
133 | this.onAnimationEnd,
134 | this.widgetIndicator,
135 | this.progressBorderColor,
136 | this.onPercentValue,
137 | }) : super(key: key) {
138 | if (linearGradient != null && progressColor != null) {
139 | throw ArgumentError(
140 | 'Cannot provide both linearGradient and progressColor');
141 | }
142 | _progressColor = progressColor ?? Colors.red;
143 |
144 | if (linearGradientBackgroundColor != null && backgroundColor != null) {
145 | throw ArgumentError(
146 | 'Cannot provide both linearGradientBackgroundColor and backgroundColor');
147 | }
148 | _backgroundColor = backgroundColor ?? Color(0xFFB8C7CB);
149 |
150 | if (percent < 0.0 || percent > 1.0) {
151 | throw new Exception(
152 | "Percent value must be a double between 0.0 and 1.0, but it's $percent");
153 | }
154 | }
155 |
156 | @override
157 | _LinearPercentIndicatorState createState() => _LinearPercentIndicatorState();
158 | }
159 |
160 | class _LinearPercentIndicatorState extends State
161 | with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
162 | AnimationController? _animationController;
163 | Animation? _animation;
164 | double _percent = 0.0;
165 | final _containerKey = GlobalKey();
166 | final _keyIndicator = GlobalKey();
167 | double _containerWidth = 0.0;
168 | double _containerHeight = 0.0;
169 | double _indicatorWidth = 0.0;
170 | double _indicatorHeight = 0.0;
171 |
172 | @override
173 | void dispose() {
174 | _animationController?.dispose();
175 | super.dispose();
176 | }
177 |
178 | @override
179 | void initState() {
180 | WidgetsBinding.instance.addPostFrameCallback((_) {
181 | if (mounted) {
182 | setState(() {
183 | _containerWidth = _containerKey.currentContext?.size?.width ?? 0.0;
184 | _containerHeight = _containerKey.currentContext?.size?.height ?? 0.0;
185 | if (_keyIndicator.currentContext != null) {
186 | _indicatorWidth = _keyIndicator.currentContext?.size?.width ?? 0.0;
187 | _indicatorHeight =
188 | _keyIndicator.currentContext?.size?.height ?? 0.0;
189 | }
190 | });
191 | }
192 | });
193 | if (widget.animation) {
194 | if (!widget.animateToInitialPercent) _percent = widget.percent;
195 | _animationController = AnimationController(
196 | vsync: this,
197 | duration: Duration(milliseconds: widget.animationDuration));
198 | _animation = Tween(begin: _percent, end: widget.percent).animate(
199 | CurvedAnimation(parent: _animationController!, curve: widget.curve),
200 | )..addListener(() {
201 | setState(() {
202 | _percent = _animation!.value;
203 | widget.onPercentValue?.call(_percent);
204 | });
205 | if (widget.restartAnimation && _percent == 1.0) {
206 | _animationController!.repeat(min: 0, max: 1.0);
207 | }
208 | });
209 | _animationController!.addStatusListener((status) {
210 | if (widget.onAnimationEnd != null &&
211 | status == AnimationStatus.completed) {
212 | widget.onAnimationEnd!();
213 | }
214 | });
215 | _animationController!.forward();
216 | } else {
217 | _updateProgress();
218 | }
219 | super.initState();
220 | }
221 |
222 | void _checkIfNeedCancelAnimation(LinearPercentIndicator oldWidget) {
223 | if (oldWidget.animation &&
224 | !widget.animation &&
225 | _animationController != null) {
226 | _animationController!.stop();
227 | }
228 | }
229 |
230 | @override
231 | void didUpdateWidget(LinearPercentIndicator oldWidget) {
232 | super.didUpdateWidget(oldWidget);
233 | if (oldWidget.percent != widget.percent) {
234 | if (_animationController != null) {
235 | _animationController!.duration =
236 | Duration(milliseconds: widget.animationDuration);
237 | _animation = Tween(
238 | begin: widget.animateFromLastPercent ? oldWidget.percent : 0.0,
239 | end: widget.percent)
240 | .animate(
241 | CurvedAnimation(parent: _animationController!, curve: widget.curve),
242 | );
243 | _animationController!.forward(from: 0.0);
244 | } else {
245 | _updateProgress();
246 | }
247 | }
248 | _checkIfNeedCancelAnimation(oldWidget);
249 | }
250 |
251 | _updateProgress() {
252 | setState(() {
253 | _percent = widget.percent;
254 | });
255 | }
256 |
257 | @override
258 | Widget build(BuildContext context) {
259 | super.build(context);
260 | var items = List.empty(growable: true);
261 | if (widget.leading != null) {
262 | items.add(widget.leading!);
263 | }
264 | final hasSetWidth = widget.width != null;
265 | final percentPositionedHorizontal =
266 | _containerWidth * _percent - _indicatorWidth / 3;
267 | //LayoutBuilder is used to get the size of the container where the widget is rendered
268 | var containerWidget = LayoutBuilder(builder: (context, constraints) {
269 | _containerWidth = constraints.maxWidth;
270 | _containerHeight = constraints.maxHeight;
271 | return Container(
272 | width: hasSetWidth ? widget.width : double.infinity,
273 | height: widget.lineHeight,
274 | padding: widget.padding,
275 | child: Stack(
276 | clipBehavior: Clip.none,
277 | children: [
278 | CustomPaint(
279 | key: _containerKey,
280 | painter: _LinearPainter(
281 | isRTL: widget.isRTL,
282 | progress: _percent,
283 | progressColor: widget.progressColor,
284 | linearGradient: widget.linearGradient,
285 | backgroundColor: widget.backgroundColor,
286 | barRadius: widget.barRadius ??
287 | Radius.zero, // If radius is not defined, set it to zero
288 | linearGradientBackgroundColor:
289 | widget.linearGradientBackgroundColor,
290 | maskFilter: widget.maskFilter,
291 | clipLinearGradient: widget.clipLinearGradient,
292 | ),
293 | child: (widget.center != null)
294 | ? Center(child: widget.center)
295 | : Container(),
296 | ),
297 | if (widget.widgetIndicator != null && _indicatorWidth == 0)
298 | Opacity(
299 | opacity: 0.0,
300 | key: _keyIndicator,
301 | child: widget.widgetIndicator,
302 | ),
303 | if (widget.widgetIndicator != null &&
304 | _containerWidth > 0 &&
305 | _indicatorWidth > 0)
306 | Positioned(
307 | right: widget.isRTL ? percentPositionedHorizontal : null,
308 | left: !widget.isRTL ? percentPositionedHorizontal : null,
309 | top: _containerHeight / 2 - _indicatorHeight,
310 | child: widget.widgetIndicator!,
311 | ),
312 | ],
313 | ),
314 | );
315 | });
316 |
317 | if (hasSetWidth) {
318 | items.add(containerWidget);
319 | } else {
320 | items.add(Expanded(
321 | child: containerWidget,
322 | ));
323 | }
324 | if (widget.trailing != null) {
325 | items.add(widget.trailing!);
326 | }
327 |
328 | return Material(
329 | color: Colors.transparent,
330 | child: Container(
331 | color: widget.fillColor,
332 | child: Row(
333 | mainAxisAlignment: widget.alignment,
334 | crossAxisAlignment: CrossAxisAlignment.center,
335 | children: items,
336 | ),
337 | ),
338 | );
339 | }
340 |
341 | @override
342 | bool get wantKeepAlive => widget.addAutomaticKeepAlive;
343 | }
344 |
345 | class _LinearPainter extends CustomPainter {
346 | final Paint _paintBackground = new Paint();
347 | final Paint _paintLine = new Paint();
348 | final Paint _paintLineBorder = new Paint();
349 | final double progress;
350 | final bool isRTL;
351 | final Color progressColor;
352 | final Color? progressBorderColor;
353 | final Color backgroundColor;
354 | final Radius barRadius;
355 | final LinearGradient? linearGradient;
356 | final LinearGradient? linearGradientBackgroundColor;
357 | final MaskFilter? maskFilter;
358 | final bool clipLinearGradient;
359 |
360 | _LinearPainter({
361 | required this.progress,
362 | required this.isRTL,
363 | required this.progressColor,
364 | required this.backgroundColor,
365 | required this.barRadius,
366 | this.progressBorderColor,
367 | this.linearGradient,
368 | this.maskFilter,
369 | required this.clipLinearGradient,
370 | this.linearGradientBackgroundColor,
371 | }) {
372 | _paintBackground.color = backgroundColor;
373 |
374 | _paintLine.color =
375 | progress == 0 ? progressColor.withOpacity(0.0) : progressColor;
376 |
377 | if (progressBorderColor != null) {
378 | _paintLineBorder.color = progress == 0
379 | ? progressBorderColor!.withOpacity(0.0)
380 | : progressBorderColor!;
381 | }
382 | }
383 |
384 | @override
385 | void paint(Canvas canvas, Size size) {
386 | // Draw background first
387 | Path backgroundPath = Path();
388 | backgroundPath.addRRect(RRect.fromRectAndRadius(
389 | Rect.fromLTWH(0, 0, size.width, size.height), barRadius));
390 | canvas.drawPath(backgroundPath, _paintBackground);
391 | canvas.clipPath(backgroundPath);
392 |
393 | if (maskFilter != null) {
394 | _paintLineBorder.maskFilter = maskFilter;
395 | _paintLine.maskFilter = maskFilter;
396 | }
397 |
398 | if (linearGradientBackgroundColor != null) {
399 | Offset shaderEndPoint =
400 | clipLinearGradient ? Offset.zero : Offset(size.width, size.height);
401 | _paintBackground.shader = linearGradientBackgroundColor
402 | ?.createShader(Rect.fromPoints(Offset.zero, shaderEndPoint));
403 | }
404 |
405 | // Then draw progress line
406 | final xProgress = size.width * progress;
407 | Path linePath = Path();
408 | Path linePathBorder = Path();
409 | double factor = progressBorderColor != null ? 2 : 0;
410 | double correction = factor * 2; //Left and right or top an down
411 | if (isRTL) {
412 | if (linearGradient != null) {
413 | _paintLineBorder.shader =
414 | _createGradientShaderRightToLeft(size, xProgress);
415 | _paintLine.shader = _createGradientShaderRightToLeft(size, xProgress);
416 | }
417 | linePath.addRRect(RRect.fromRectAndRadius(
418 | Rect.fromLTWH(
419 | size.width - size.width * progress, 0, xProgress, size.height),
420 | barRadius));
421 | } else {
422 | if (linearGradient != null) {
423 | _paintLineBorder.shader =
424 | _createGradientShaderLeftToRight(size, xProgress);
425 | _paintLine.shader = _createGradientShaderLeftToRight(size, xProgress);
426 | }
427 | if (progressBorderColor != null) {
428 | linePathBorder.addRRect(RRect.fromRectAndRadius(
429 | Rect.fromLTWH(0, 0, xProgress, size.height), barRadius));
430 | }
431 | linePath.addRRect(RRect.fromRectAndRadius(
432 | Rect.fromLTWH(
433 | factor, factor, xProgress - correction, size.height - correction),
434 | barRadius));
435 | }
436 | if (progressBorderColor != null) {
437 | canvas.drawPath(linePathBorder, _paintLineBorder);
438 | }
439 | canvas.drawPath(linePath, _paintLine);
440 | }
441 |
442 | Shader _createGradientShaderRightToLeft(Size size, double xProgress) {
443 | Offset shaderEndPoint =
444 | clipLinearGradient ? Offset.zero : Offset(xProgress, size.height);
445 | return linearGradient!.createShader(
446 | Rect.fromPoints(
447 | Offset(size.width, size.height),
448 | shaderEndPoint,
449 | ),
450 | );
451 | }
452 |
453 | Shader _createGradientShaderLeftToRight(Size size, double xProgress) {
454 | Offset shaderEndPoint = clipLinearGradient
455 | ? Offset(size.width, size.height)
456 | : Offset(xProgress, size.height);
457 | return linearGradient!.createShader(
458 | Rect.fromPoints(
459 | Offset.zero,
460 | shaderEndPoint,
461 | ),
462 | );
463 | }
464 |
465 | @override
466 | bool shouldRepaint(CustomPainter oldDelegate) => true;
467 | }
468 |
--------------------------------------------------------------------------------
/lib/multi_segment_linear_indicator.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// A multi-segment linear progress indicator that displays three segments with different colors and styles.
4 | /// One segment can optionally display stripes.
5 | ///
6 | /// The total of all segments must be less than or equal to 1.0 (100%).
7 | /// Each segment percentage must be between 0.0 and 1.0.
8 | ///
9 | /// Example:
10 | /// ```dart
11 | /// MultiSegmentLinearIndicator(
12 | /// segments: [
13 | /// SegmentLinearIndicator(percent: 0.3, color: Colors.red, enableStripes: true),
14 | /// SegmentLinearIndicator(percent: 0.4, color: Colors.blue),
15 | /// SegmentLinearIndicator(percent: 0.3, color: Colors.green),
16 | /// ],
17 | /// lineHeight: 20,
18 | /// barRadius: Radius.circular(10),
19 | /// animation: true,
20 | /// enableStripes: [1, 2], // Enable stripes for first and second segments
21 | /// )
22 | /// ```
23 |
24 | /// Represents a segment in the linear indicator with a percentage and color.
25 | class SegmentLinearIndicator {
26 | final double percent;
27 | final Color color;
28 | final bool enableStripes;
29 |
30 | SegmentLinearIndicator({
31 | required this.percent,
32 | required this.color,
33 | this.enableStripes = false,
34 | });
35 | }
36 |
37 | class MultiSegmentLinearIndicator extends StatefulWidget {
38 | /// List of segments to display in the indicator.
39 | final List segments;
40 |
41 | /// Height of the progress bar
42 | final double lineHeight;
43 |
44 | /// Optional width of the progress bar. If null, fills the parent width
45 | final double? width;
46 |
47 | /// Border radius of the progress bar
48 | final Radius? barRadius;
49 |
50 | /// Padding around the progress bar
51 | final EdgeInsets padding;
52 |
53 | /// Whether to animate changes in the progress values
54 | final bool animation;
55 |
56 | /// Duration of the animation in milliseconds
57 | final int animationDuration;
58 |
59 | /// Animation curve to use
60 | final Curve curve;
61 |
62 | /// Whether to animate from the last percentage value or from 0
63 | final bool animateFromLastPercent;
64 |
65 | /// Callback when animation completes
66 | final VoidCallback? onAnimationEnd;
67 |
68 | /// Creates a multi-segment linear progress indicator.
69 | ///
70 | /// The sum of all segment percentages must be less than or equal to 1.0.
71 | MultiSegmentLinearIndicator({
72 | Key? key,
73 | required this.segments,
74 | this.lineHeight = 5.0,
75 | this.width,
76 | this.barRadius,
77 | this.padding = const EdgeInsets.symmetric(horizontal: 10.0),
78 | this.animation = false,
79 | this.animationDuration = 500,
80 | this.curve = Curves.linear,
81 | this.animateFromLastPercent = false,
82 | this.onAnimationEnd,
83 | }) : super(key: key) {
84 | final sum = segments.fold(
85 | 0.0,
86 | (sum, segment) => sum + segment.percent,
87 | );
88 | if (sum > 1.0) {
89 | throw Exception(
90 | 'The sum of all segment percentages must be less than or equal to 1.0, but got $sum',
91 | );
92 | }
93 | }
94 |
95 | @override
96 | State createState() =>
97 | _MultiSegmentLinearIndicatorState();
98 | }
99 |
100 | class _MultiSegmentLinearIndicatorState
101 | extends State
102 | with SingleTickerProviderStateMixin {
103 | AnimationController? _animationController;
104 | late List> _segmentAnimations;
105 | late List _segmentPercents;
106 |
107 | @override
108 | void dispose() {
109 | _animationController?.dispose();
110 | super.dispose();
111 | }
112 |
113 | @override
114 | void initState() {
115 | super.initState();
116 | _segmentPercents = List.filled(widget.segments.length, 0.0);
117 |
118 | if (!widget.animation) {
119 | for (int i = 0; i < widget.segments.length; i++) {
120 | _segmentPercents[i] = widget.segments[i].percent;
121 | }
122 | return;
123 | }
124 |
125 | _animationController = AnimationController(
126 | vsync: this,
127 | duration: Duration(milliseconds: widget.animationDuration),
128 | );
129 |
130 | _setupAnimations();
131 |
132 | _animationController!.addStatusListener((status) {
133 | if (widget.onAnimationEnd != null &&
134 | status == AnimationStatus.completed) {
135 | widget.onAnimationEnd!();
136 | }
137 | });
138 |
139 | _animationController!.forward();
140 | }
141 |
142 | void _setupAnimations() {
143 | _segmentAnimations = List.generate(widget.segments.length, (index) {
144 | final double start =
145 | widget.animateFromLastPercent ? _segmentPercents[index] : 0.0;
146 |
147 | return Tween(
148 | begin: start,
149 | end: widget.segments[index].percent,
150 | ).animate(CurvedAnimation(
151 | parent: _animationController!,
152 | curve: widget.curve,
153 | ))
154 | ..addListener(() {
155 | setState(() {
156 | _segmentPercents[index] = _segmentAnimations[index].value;
157 | });
158 | });
159 | });
160 | }
161 |
162 | void _checkIfNeedCancelAnimation(MultiSegmentLinearIndicator oldWidget) {
163 | if (oldWidget.animation &&
164 | !widget.animation &&
165 | _animationController != null) {
166 | _animationController!.stop();
167 | }
168 | }
169 |
170 | @override
171 | void didUpdateWidget(MultiSegmentLinearIndicator oldWidget) {
172 | super.didUpdateWidget(oldWidget);
173 |
174 | if (widget.animation &&
175 | !_areSegmentsEqual(oldWidget.segments, widget.segments)) {
176 | _animationController!.duration =
177 | Duration(milliseconds: widget.animationDuration);
178 | _setupAnimations();
179 | _animationController!.forward(from: 0.0);
180 | } else if (!widget.animation) {
181 | for (int i = 0; i < widget.segments.length; i++) {
182 | _segmentPercents[i] = widget.segments[i].percent;
183 | }
184 | }
185 |
186 | _checkIfNeedCancelAnimation(oldWidget);
187 | }
188 |
189 | bool _areSegmentsEqual(
190 | List a, List b) {
191 | if (a.length != b.length) return false;
192 |
193 | for (int i = 0; i < a.length; i++) {
194 | if (a[i].percent != b[i].percent) return false;
195 | }
196 |
197 | return true;
198 | }
199 |
200 | @override
201 | Widget build(BuildContext context) {
202 | return Container(
203 | width: widget.width,
204 | padding: widget.padding,
205 | child: CustomPaint(
206 | painter: _MultiSegmentPainter(
207 | segments: widget.segments,
208 | segmentPercents: _segmentPercents,
209 | barRadius: widget.barRadius ?? Radius.zero,
210 | ),
211 | child: SizedBox(
212 | height: widget.lineHeight,
213 | ),
214 | ),
215 | );
216 | }
217 | }
218 |
219 | /// Custom painter for drawing the multi-segment progress bar
220 | class _MultiSegmentPainter extends CustomPainter {
221 | final List segments;
222 | final List segmentPercents;
223 | final Radius barRadius;
224 |
225 | _MultiSegmentPainter({
226 | required this.segments,
227 | required this.segmentPercents,
228 | required this.barRadius,
229 | });
230 |
231 | @override
232 | void paint(Canvas canvas, Size size) {
233 | double startX = 0.0;
234 |
235 | for (int i = 0; i < segments.length; i++) {
236 | final segmentWidth = size.width * segmentPercents[i];
237 | final segmentPaint = Paint()
238 | ..color = segments[i].color
239 | ..style = PaintingStyle.fill;
240 |
241 | final segmentPath = Path();
242 | if (i == 0 && segmentPercents[i] == 1.0) {
243 | // Full width with full border radius
244 | segmentPath.addRRect(RRect.fromRectAndRadius(
245 | Rect.fromLTWH(startX, 0, segmentWidth, size.height),
246 | barRadius,
247 | ));
248 | } else if (i == 0) {
249 | // Round only the left side
250 | segmentPath.addRRect(RRect.fromRectAndCorners(
251 | Rect.fromLTWH(startX, 0, segmentWidth, size.height),
252 | topLeft: barRadius,
253 | bottomLeft: barRadius,
254 | ));
255 | } else if (i == segments.length - 1) {
256 | // Round only the right side for the last segment
257 | segmentPath.addRRect(RRect.fromRectAndCorners(
258 | Rect.fromLTWH(startX, 0, segmentWidth, size.height),
259 | topRight: barRadius,
260 | bottomRight: barRadius,
261 | ));
262 | } else {
263 | // No rounding for middle segments
264 | segmentPath.addRect(
265 | Rect.fromLTWH(startX, 0, segmentWidth, size.height),
266 | );
267 | }
268 | canvas.drawPath(segmentPath, segmentPaint);
269 |
270 | // Draw stripes if enabled for this segment
271 | if (segments[i].enableStripes) {
272 | _drawStripes(canvas, startX, segmentWidth, size.height);
273 | }
274 |
275 | startX += segmentWidth;
276 | }
277 | }
278 |
279 | void _drawStripes(Canvas canvas, double startX, double width, double height) {
280 | final stripePaint = Paint()
281 | ..color = Colors.white.withOpacity(0.3)
282 | ..style = PaintingStyle.stroke
283 | ..strokeWidth = 2;
284 |
285 | const stripeSpacing = 8.0;
286 |
287 | canvas.save();
288 | canvas.clipRect(Rect.fromLTWH(startX, 0, width, height));
289 |
290 | for (double x = startX - height;
291 | x < startX + width + height;
292 | x += stripeSpacing) {
293 | canvas.drawLine(
294 | Offset(x, height),
295 | Offset(x + height, 0),
296 | stripePaint,
297 | );
298 | }
299 | canvas.restore();
300 | }
301 |
302 | @override
303 | bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
304 | }
305 |
--------------------------------------------------------------------------------
/lib/percent_indicator.dart:
--------------------------------------------------------------------------------
1 | library percent_indicator;
2 |
3 | export 'circular_percent_indicator.dart';
4 | export 'linear_percent_indicator.dart';
5 |
--------------------------------------------------------------------------------
/percent_indicator.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/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 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.18.0"
44 | fake_async:
45 | dependency: transitive
46 | description:
47 | name: fake_async
48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.3.1"
52 | flutter:
53 | dependency: "direct main"
54 | description: flutter
55 | source: sdk
56 | version: "0.0.0"
57 | flutter_test:
58 | dependency: "direct dev"
59 | description: flutter
60 | source: sdk
61 | version: "0.0.0"
62 | leak_tracker:
63 | dependency: transitive
64 | description:
65 | name: leak_tracker
66 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
67 | url: "https://pub.dev"
68 | source: hosted
69 | version: "10.0.5"
70 | leak_tracker_flutter_testing:
71 | dependency: transitive
72 | description:
73 | name: leak_tracker_flutter_testing
74 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
75 | url: "https://pub.dev"
76 | source: hosted
77 | version: "3.0.5"
78 | leak_tracker_testing:
79 | dependency: transitive
80 | description:
81 | name: leak_tracker_testing
82 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
83 | url: "https://pub.dev"
84 | source: hosted
85 | version: "3.0.1"
86 | matcher:
87 | dependency: transitive
88 | description:
89 | name: matcher
90 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
91 | url: "https://pub.dev"
92 | source: hosted
93 | version: "0.12.16+1"
94 | material_color_utilities:
95 | dependency: transitive
96 | description:
97 | name: material_color_utilities
98 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "0.11.1"
102 | meta:
103 | dependency: transitive
104 | description:
105 | name: meta
106 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "1.15.0"
110 | path:
111 | dependency: transitive
112 | description:
113 | name: path
114 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "1.9.0"
118 | sky_engine:
119 | dependency: transitive
120 | description: flutter
121 | source: sdk
122 | version: "0.0.99"
123 | source_span:
124 | dependency: transitive
125 | description:
126 | name: source_span
127 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
128 | url: "https://pub.dev"
129 | source: hosted
130 | version: "1.10.0"
131 | stack_trace:
132 | dependency: transitive
133 | description:
134 | name: stack_trace
135 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
136 | url: "https://pub.dev"
137 | source: hosted
138 | version: "1.11.1"
139 | stream_channel:
140 | dependency: transitive
141 | description:
142 | name: stream_channel
143 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
144 | url: "https://pub.dev"
145 | source: hosted
146 | version: "2.1.2"
147 | string_scanner:
148 | dependency: transitive
149 | description:
150 | name: string_scanner
151 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "1.2.0"
155 | term_glyph:
156 | dependency: transitive
157 | description:
158 | name: term_glyph
159 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "1.2.1"
163 | test_api:
164 | dependency: transitive
165 | description:
166 | name: test_api
167 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "0.7.2"
171 | vector_math:
172 | dependency: transitive
173 | description:
174 | name: vector_math
175 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "2.1.4"
179 | vm_service:
180 | dependency: transitive
181 | description:
182 | name: vm_service
183 | sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "14.2.5"
187 | sdks:
188 | dart: ">=3.3.0 <4.0.0"
189 | flutter: ">=3.18.0-18.0.pre.54"
190 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: percent_indicator
2 | description: Library that allows you to display progress widgets based on percentage, can be Circular or Linear, you can also customize it to your needs.
3 | version: 4.2.5
4 | homepage: https://www.diegoveloper.com
5 | repository: https://github.com/diegoveloper/flutter_percent_indicator/
6 |
7 | dependencies:
8 | flutter:
9 | sdk: flutter
10 |
11 | environment:
12 | sdk: ">=2.12.0 <4.0.0"
13 | flutter: ">=2.12.0"
14 |
15 | dev_dependencies:
16 | flutter_test:
17 | sdk: flutter
18 |
19 | screenshots:
20 | - description: Circular Percent Indicator
21 | path: screenshots/circular_percent_indicator.png
22 | - description: Linear Percent Indicator
23 | path: screenshots/linear_percent_indicator.png
24 | - description: Circular Percent Indicator Gif
25 | path: screenshots/circular_percent_indicator.gif
26 | - description: Linear Percent Indicator Gif
27 | path: screenshots/linear_percent_indicator.gif
28 |
29 | flutter:
30 |
--------------------------------------------------------------------------------
/screenshots/circular_percent_indicator.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/screenshots/circular_percent_indicator.gif
--------------------------------------------------------------------------------
/screenshots/circular_percent_indicator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/screenshots/circular_percent_indicator.png
--------------------------------------------------------------------------------
/screenshots/linear_percent_indicator.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/screenshots/linear_percent_indicator.gif
--------------------------------------------------------------------------------
/screenshots/linear_percent_indicator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/diegoveloper/flutter_percent_indicator/f390f4c08ad150a56104a3250d94875919b7d94f/screenshots/linear_percent_indicator.png
--------------------------------------------------------------------------------
/test/percent_indicator_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:percent_indicator/percent_indicator.dart';
3 | import 'package:flutter_test/flutter_test.dart';
4 |
5 | void main() {
6 | testWidgets('testing circular percent indicator widget',
7 | (WidgetTester tester) async {
8 | CircularPercentIndicator localWidget =
9 | CircularPercentIndicator(radius: 20.0);
10 | await tester.pumpWidget(MaterialApp(home: localWidget));
11 | expect(localWidget.percent, 0.0);
12 | });
13 |
14 | testWidgets('testing linear percent indicator widget',
15 | (WidgetTester tester) async {
16 | LinearPercentIndicator localWidget = LinearPercentIndicator(
17 | width: 100.0,
18 | );
19 | await tester.pumpWidget(MaterialApp(home: localWidget));
20 | expect(localWidget.percent, 0.0);
21 | });
22 |
23 | testWidgets('show center widget in linear percent indicator widget',
24 | (WidgetTester tester) async {
25 | LinearPercentIndicator localWidget = LinearPercentIndicator(
26 | width: 100.0,
27 | center: Text('linear'),
28 | );
29 |
30 | await tester.pumpWidget(MaterialApp(home: localWidget));
31 |
32 | expect(find.byType(Center), findsOne);
33 | });
34 |
35 | testWidgets('show center widget in circular percent indicator widget',
36 | (WidgetTester tester) async {
37 | CircularPercentIndicator localWidget = CircularPercentIndicator(
38 | radius: 20.0,
39 | center: Text('circular'),
40 | );
41 |
42 | await tester.pumpWidget(MaterialApp(home: localWidget));
43 |
44 | expect(find.byType(Center), findsOne);
45 | });
46 |
47 | testWidgets('show header widget in circular percent indicator widget',
48 | (WidgetTester tester) async {
49 | CircularPercentIndicator localWidget = CircularPercentIndicator(
50 | radius: 20.0,
51 | header: Text('header'),
52 | );
53 |
54 | await tester.pumpWidget(MaterialApp(home: localWidget));
55 |
56 | final header = find.descendant(
57 | of: find.byType(Column),
58 | matching: find.byType(Text),
59 | );
60 |
61 | final container = find.descendant(
62 | of: find.byType(Column),
63 | matching: find.byType(Container),
64 | );
65 | expect(header, findsExactly(1));
66 | expect(container, findsExactly(1));
67 |
68 | expect(tester.getTopLeft(header).dy,
69 | lessThan(tester.getTopLeft(container).dy));
70 | });
71 |
72 | testWidgets('show footer widget in circular percent indicator widget',
73 | (WidgetTester tester) async {
74 | CircularPercentIndicator localWidget = CircularPercentIndicator(
75 | radius: 20.0,
76 | footer: Text('footer'),
77 | );
78 |
79 | await tester.pumpWidget(MaterialApp(home: localWidget));
80 |
81 | final header = find.descendant(
82 | of: find.byType(Column),
83 | matching: find.byType(Text),
84 | );
85 |
86 | final container = find.descendant(
87 | of: find.byType(Column),
88 | matching: find.byType(Container),
89 | );
90 | expect(header, findsExactly(1));
91 | expect(container, findsExactly(1));
92 |
93 | expect(tester.getTopLeft(header).dy,
94 | greaterThan(tester.getTopLeft(container).dy));
95 | });
96 |
97 | testWidgets('apply fillColor in circular percent indicator widget',
98 | (WidgetTester tester) async {
99 | CircularPercentIndicator localWidget = CircularPercentIndicator(
100 | radius: 20.0,
101 | fillColor: Colors.red,
102 | );
103 |
104 | await tester.pumpWidget(MaterialApp(home: localWidget));
105 |
106 | final material = find.byType(Material);
107 |
108 | expect(material, findsOne);
109 |
110 | expect((tester.firstWidget(material) as Material).color, Colors.red);
111 | });
112 |
113 | testWidgets('apply fillColor in linear percent indicator widget',
114 | (WidgetTester tester) async {
115 | LinearPercentIndicator localWidget = LinearPercentIndicator(
116 | width: 100.0,
117 | fillColor: Colors.red,
118 | );
119 |
120 | await tester.pumpWidget(MaterialApp(home: localWidget));
121 |
122 | final container = find.ancestor(
123 | of: find.byType(Row),
124 | matching: find.byType(Container),
125 | );
126 |
127 | expect((tester.firstWidget(container) as Container).color, Colors.red);
128 | });
129 |
130 | testWidgets('testing percent animation in circular percent indicator widget',
131 | (WidgetTester tester) async {
132 | final animationController = AnimationController(
133 | vsync: tester,
134 | duration: const Duration(milliseconds: 200),
135 | );
136 |
137 | await tester.pumpWidget(
138 | MaterialApp(
139 | home: AnimatedBuilder(
140 | animation: animationController,
141 | builder: (context, child) {
142 | return CircularPercentIndicator(
143 | radius: 100.0,
144 | fillColor: Colors.red,
145 | percent: animationController.value,
146 | );
147 | },
148 | ),
149 | ),
150 | );
151 |
152 | animationController.forward();
153 |
154 | await tester.pump(const Duration(milliseconds: 200));
155 |
156 | await tester.pumpAndSettle();
157 |
158 | final linear = find.byType(CircularPercentIndicator);
159 |
160 | expect((tester.firstWidget(linear) as CircularPercentIndicator).percent, 1.0);
161 |
162 | animationController.dispose();
163 | });
164 | testWidgets('testing percent animation in linear percent indicator widget',
165 | (WidgetTester tester) async {
166 | final animationController = AnimationController(
167 | vsync: tester,
168 | duration: const Duration(milliseconds: 200),
169 | );
170 |
171 | await tester.pumpWidget(
172 | MaterialApp(
173 | home: AnimatedBuilder(
174 | animation: animationController,
175 | builder: (context, child) {
176 | return LinearPercentIndicator(
177 | width: 100.0,
178 | fillColor: Colors.red,
179 | percent: animationController.value,
180 | );
181 | },
182 | ),
183 | ),
184 | );
185 |
186 | animationController.forward();
187 |
188 | await tester.pump(const Duration(milliseconds: 200));
189 |
190 | await tester.pumpAndSettle();
191 |
192 | final linear = find.byType(LinearPercentIndicator);
193 |
194 | expect((tester.firstWidget(linear) as LinearPercentIndicator).percent, 1.0);
195 |
196 | animationController.dispose();
197 | });
198 | }
199 |
--------------------------------------------------------------------------------