├── lib ├── carousel_pro.dart └── src │ ├── carousel_pro_widgets.dart │ └── carousel_pro.dart ├── test └── carousel_pro_test.dart ├── screenshots ├── screenshot01.png ├── screenshot02.png ├── screenshot03.png ├── screenshot04.png └── screenshot05.png ├── .gitignore ├── CHANGELOG.md ├── pubspec.yaml ├── carousel_pro.iml ├── LICENSE ├── example └── main.dart ├── pubspec.lock └── README.md /lib/carousel_pro.dart: -------------------------------------------------------------------------------- 1 | library carousel_pro; 2 | 3 | export 'src/carousel_pro.dart'; 4 | -------------------------------------------------------------------------------- /test/carousel_pro_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:carousel_pro/carousel_pro.dart'; 2 | 3 | void main() { 4 | } 5 | -------------------------------------------------------------------------------- /screenshots/screenshot01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlouage/flutter-carousel-pro/HEAD/screenshots/screenshot01.png -------------------------------------------------------------------------------- /screenshots/screenshot02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlouage/flutter-carousel-pro/HEAD/screenshots/screenshot02.png -------------------------------------------------------------------------------- /screenshots/screenshot03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlouage/flutter-carousel-pro/HEAD/screenshots/screenshot03.png -------------------------------------------------------------------------------- /screenshots/screenshot04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlouage/flutter-carousel-pro/HEAD/screenshots/screenshot04.png -------------------------------------------------------------------------------- /screenshots/screenshot05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlouage/flutter-carousel-pro/HEAD/screenshots/screenshot05.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | .idea 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | ios/.generated/ 9 | ios/Flutter/Generated.xcconfig 10 | ios/Runner/GeneratedPluginRegistrant.* 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.0] 2 | 3 | * Bug Fixes 4 | 5 | ## [0.0.12] 6 | 7 | * Animation added 8 | 9 | ## [0.0.11] 10 | 11 | * Screenshot added 12 | * Example added 13 | * Readme Modified 14 | 15 | ## [0.0.1] 16 | 17 | * Initial Release 18 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: carousel_pro 2 | description: A Flutter Carousel Slider widget. Support Network and Asset Images. You can modify the UI according your design easily. 3 | version: 1.0.0 4 | author: JLouage (Julien Louage) 5 | homepage: https://github.com/jlouage/flutter-carousel-pro 6 | 7 | environment: 8 | sdk: '>=2.0.0 <3.0.0' 9 | flutter: ">=1.2.0 <2.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | -------------------------------------------------------------------------------- /carousel_pro.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JLouage 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:carousel_pro/carousel_pro.dart'; 3 | 4 | void main() { 5 | runApp(MaterialApp( 6 | debugShowCheckedModeBanner: true, 7 | title: 'Carousel Pro', 8 | home: CarouselPage(), 9 | )); 10 | } 11 | 12 | class CarouselPage extends StatelessWidget { 13 | @override 14 | Widget build(BuildContext context) { 15 | return Scaffold( 16 | body: Center( 17 | child: SizedBox( 18 | height: 150.0, 19 | width: 300.0, 20 | child: Carousel( 21 | boxFit: BoxFit.cover, 22 | autoplay: false, 23 | animationCurve: Curves.fastOutSlowIn, 24 | animationDuration: Duration(milliseconds: 1000), 25 | dotSize: 6.0, 26 | dotIncreasedColor: Color(0xFFFF335C), 27 | dotBgColor: Colors.transparent, 28 | dotPosition: DotPosition.topRight, 29 | dotVerticalPadding: 10.0, 30 | showIndicator: true, 31 | indicatorBgPadding: 7.0, 32 | images: [ 33 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*GqdzzfB_BHorv7V2NV7Jgg.jpeg'), 34 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*wnIEgP1gNMrK5gZU7QS0-A.jpeg'), 35 | ExactAssetImage("assets/images/LaunchImage.jpg"), 36 | ], 37 | ), 38 | ), 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.2.0" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.4" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.2" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.14.11" 32 | flutter: 33 | dependency: "direct main" 34 | description: flutter 35 | source: sdk 36 | version: "0.0.0" 37 | flutter_test: 38 | dependency: "direct dev" 39 | description: flutter 40 | source: sdk 41 | version: "0.0.0" 42 | matcher: 43 | dependency: transitive 44 | description: 45 | name: matcher 46 | url: "https://pub.dartlang.org" 47 | source: hosted 48 | version: "0.12.5" 49 | meta: 50 | dependency: transitive 51 | description: 52 | name: meta 53 | url: "https://pub.dartlang.org" 54 | source: hosted 55 | version: "1.1.6" 56 | path: 57 | dependency: transitive 58 | description: 59 | name: path 60 | url: "https://pub.dartlang.org" 61 | source: hosted 62 | version: "1.6.2" 63 | pedantic: 64 | dependency: transitive 65 | description: 66 | name: pedantic 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "1.7.0" 70 | quiver: 71 | dependency: transitive 72 | description: 73 | name: quiver 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "2.0.3" 77 | sky_engine: 78 | dependency: transitive 79 | description: flutter 80 | source: sdk 81 | version: "0.0.99" 82 | source_span: 83 | dependency: transitive 84 | description: 85 | name: source_span 86 | url: "https://pub.dartlang.org" 87 | source: hosted 88 | version: "1.5.5" 89 | stack_trace: 90 | dependency: transitive 91 | description: 92 | name: stack_trace 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "1.9.3" 96 | stream_channel: 97 | dependency: transitive 98 | description: 99 | name: stream_channel 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "2.0.0" 103 | string_scanner: 104 | dependency: transitive 105 | description: 106 | name: string_scanner 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.0.4" 110 | term_glyph: 111 | dependency: transitive 112 | description: 113 | name: term_glyph 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.1.0" 117 | test_api: 118 | dependency: transitive 119 | description: 120 | name: test_api 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "0.2.5" 124 | typed_data: 125 | dependency: transitive 126 | description: 127 | name: typed_data 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.1.6" 131 | vector_math: 132 | dependency: transitive 133 | description: 134 | name: vector_math 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "2.0.8" 138 | sdks: 139 | dart: ">=2.2.2 <3.0.0" 140 | flutter: ">=1.2.0 <2.0.0" 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Carousel Pro [![pub package](https://img.shields.io/pub/v/carousel_pro.svg)](https://pub.dartlang.org/packages/carousel_pro) 2 | 3 | A Flutter Carousel widget. 4 | 5 | ## Usage 6 | 7 | As simple as using any flutter Widget. 8 | 9 | **Example:** 10 | Add the module to your project ``pubspec.yaml``: 11 | ```yaml 12 | ... 13 | dependencies: 14 | ... 15 | carousel_pro: ^1.0.0 16 | ... 17 | ``` 18 | And install it using ``flutter packages get`` on your project folder. After that, just import the module and use it: 19 | ```dart 20 | import 'package:carousel_pro/carousel_pro.dart'; 21 | 22 | //... 23 | SizedBox( 24 | height: 150.0, 25 | width: 300.0, 26 | child: Carousel( 27 | images: [ 28 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*GqdzzfB_BHorv7V2NV7Jgg.jpeg'), 29 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*wnIEgP1gNMrK5gZU7QS0-A.jpeg'), 30 | ExactAssetImage("assets/images/LaunchImage.jpg") 31 | ], 32 | ) 33 | ), 34 | 35 | ``` 36 | ![ScreenShot 01](https://github.com/jlouage/flutter-carousel-pro/blob/master/screenshots/screenshot01.png?raw=true "ScreenShot 01") 37 | 38 | ```dart 39 | import 'package:carousel_pro/carousel_pro.dart'; 40 | 41 | //... 42 | SizedBox( 43 | height: 200.0, 44 | width: 350.0, 45 | child: Carousel( 46 | images: [ 47 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*GqdzzfB_BHorv7V2NV7Jgg.jpeg'), 48 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*wnIEgP1gNMrK5gZU7QS0-A.jpeg'), 49 | ExactAssetImage("assets/images/LaunchImage.jpg") 50 | ], 51 | dotSize: 4.0, 52 | dotSpacing: 15.0, 53 | dotColor: Colors.lightGreenAccent, 54 | indicatorBgPadding: 5.0, 55 | dotBgColor: Colors.purple.withOpacity(0.5), 56 | borderRadius: true, 57 | ) 58 | ), 59 | 60 | ``` 61 | ![ScreenShot 02](https://github.com/jlouage/flutter-carousel-pro/blob/master/screenshots/screenshot02.png?raw=true "ScreenShot 02") 62 | 63 | 64 | ```dart 65 | import 'package:carousel_pro/carousel_pro.dart'; 66 | 67 | //... 68 | SizedBox( 69 | height: 200.0, 70 | width: 350.0, 71 | child: Carousel( 72 | images: [ 73 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*GqdzzfB_BHorv7V2NV7Jgg.jpeg'), 74 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*wnIEgP1gNMrK5gZU7QS0-A.jpeg'), 75 | ExactAssetImage("assets/images/LaunchImage.jpg") 76 | ], 77 | dotSize: 4.0, 78 | dotSpacing: 15.0, 79 | dotColor: Colors.lightGreenAccent, 80 | indicatorBgPadding: 5.0, 81 | dotBgColor: Colors.purple.withOpacity(0.5), 82 | borderRadius: true, 83 | moveIndicatorFromBottom: 180.0, 84 | noRadiusForIndicator: true, 85 | ) 86 | ), 87 | 88 | ``` 89 | ![ScreenShot 03](https://github.com/jlouage/flutter-carousel-pro/blob/master/screenshots/screenshot03.png?raw=true "ScreenShot 03") 90 | 91 | 92 | ```dart 93 | import 'package:carousel_pro/carousel_pro.dart'; 94 | 95 | //... 96 | SizedBox( 97 | height: 200.0, 98 | width: 350.0, 99 | child: Carousel( 100 | images: [ 101 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*GqdzzfB_BHorv7V2NV7Jgg.jpeg'), 102 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*wnIEgP1gNMrK5gZU7QS0-A.jpeg'), 103 | ExactAssetImage("assets/images/LaunchImage.jpg") 104 | ], 105 | dotSize: 4.0, 106 | dotSpacing: 15.0, 107 | dotColor: Colors.lightGreenAccent, 108 | indicatorBgPadding: 5.0, 109 | dotBgColor: Colors.purple.withOpacity(0.5), 110 | borderRadius: false, 111 | moveIndicatorFromBottom: 180.0, 112 | noRadiusForIndicator: true, 113 | overlayShadow: true, 114 | overlayShadowColors: Colors.white, 115 | overlayShadowSize: 0.7, 116 | ) 117 | ), 118 | 119 | ``` 120 | ![ScreenShot 04](https://github.com/jlouage/flutter-carousel-pro/blob/master/screenshots/screenshot04.png?raw=true "ScreenShot 04") 121 | 122 | 123 | ```dart 124 | import 'package:carousel_pro/carousel_pro.dart'; 125 | 126 | //... 127 | SizedBox( 128 | height: 200.0, 129 | width: 350.0, 130 | child: Carousel( 131 | images: [ 132 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*GqdzzfB_BHorv7V2NV7Jgg.jpeg'), 133 | NetworkImage('https://cdn-images-1.medium.com/max/2000/1*wnIEgP1gNMrK5gZU7QS0-A.jpeg'), 134 | ExactAssetImage("assets/images/LaunchImage.jpg") 135 | ], 136 | showIndicator: false, 137 | borderRadius: false, 138 | moveIndicatorFromBottom: 180.0, 139 | noRadiusForIndicator: true, 140 | overlayShadow: true, 141 | overlayShadowColors: Colors.white, 142 | overlayShadowSize: 0.7, 143 | ) 144 | ), 145 | 146 | ``` 147 | ![ScreenShot 05](https://github.com/jlouage/flutter-carousel-pro/blob/master/screenshots/screenshot05.png?raw=true "ScreenShot 05") 148 | 149 | ## Parameters and Values 150 | 151 | ### images 152 | All the images on this Carousel. 153 | **Type:** List 154 | 155 | ### animationCurve 156 | The transition animation timing curve 157 | **Default value:** Curves.ease 158 | **Type:** Curve 159 | ##### Values 160 | * Curves.linear; 161 | * Curves.fastOutSlowIn; 162 | * Curves.ease; 163 | * Curves.bounceOut; 164 | * Curves.bounceIn; 165 | * Curves.bounceInOut; 166 | * Curves.decelerate; 167 | * Curves.ease; 168 | * Curves.easeIn; 169 | * Curves.easeInOut; 170 | * Curves.easeOut; 171 | * Curves.elasticIn; 172 | * Curves.elasticInOut; 173 | * Curves.elasticOut; 174 | 175 | ### animationDuration 176 | The transition animation duration 177 | **Type:** Duration 178 | **Default value:** 300ms. 179 | 180 | ### dotSize 181 | The base size of the dots 182 | **Type:** double 183 | **Default value:** 8.0 184 | 185 | ### dotIncreaseSize 186 | The increase in the size of the selected dot 187 | **Type:** double 188 | **Default value:** 2.0 189 | 190 | 191 | ### dotSpacing 192 | The distance between the center of each dot 193 | **Type:** double 194 | **Default value:** 25.0 195 | 196 | ### dotColor 197 | The Color of each dot 198 | **Type:** Color 199 | **Default value:** Colors.white 200 | 201 | ### dotBgColor 202 | The background Color of the dots 203 | **Type:** Color 204 | **Default value:** Colors.grey[800].withOpacity(0.5) 205 | 206 | ### showIndicator 207 | Enable or Disable the indicator (dots) 208 | **Type:** bool 209 | **Default value:** true 210 | 211 | ### indicatorBgPadding 212 | Padding Size of the background Indicator 213 | **Type:** double 214 | **Default value:** 20.0 215 | 216 | ### boxFit 217 | How to show the images in the box 218 | **Type:** BoxFit 219 | **Default value:** cover 220 | ##### Values 221 | * BoxFit.cover; 222 | * BoxFit.fitWidth; 223 | * BoxFit.fitHeight; 224 | * BoxFit.scaleDown; 225 | * BoxFit.fill; 226 | * BoxFit.contain; 227 | * BoxFit.none; 228 | 229 | ### borderRadius 230 | Enable/Disable radius Border for the images 231 | **Type:** bool 232 | **Default value:** false 233 | 234 | ### radius 235 | Border Radius of the images 236 | **Type:** Radius 237 | **Default value:** Radius.circular(8.0) 238 | 239 | ### moveIndicatorFromBottom 240 | Move the Indicator From the Bottom 241 | **Type:** double 242 | **Default value:** 0.0 243 | 244 | ### noRadiusForIndicator 245 | Remove the radius bottom from the indicator background 246 | **Type:** bool 247 | **Default value:** false 248 | 249 | ### overlayShadow 250 | Enable/Disable Image Overlay Shadow 251 | **Type:** bool 252 | **Default value:** false 253 | 254 | ### overlayShadowColors 255 | Choose the color of the overlay Shadow color 256 | **Type:** Color 257 | **Default value:** Colors.grey[800] 258 | 259 | ### overlayShadowSize 260 | Choose the size of the Overlay Shadow, from 0.0 to 1.0 261 | **Type:** double 262 | **Default value:** 0.5 263 | 264 | ### autoplay 265 | Enable/Disable autoplay carousel 266 | **Type:** bool 267 | **Default value:** true 268 | 269 | ### autoplayDuration 270 | The autoplay transition duration 271 | **Type:** Duration 272 | **Default value:** 3s 273 | 274 | ## Credits 275 | 276 | Developed by [JLouage (Julien Louage)](http://www.jlouage.com) 277 | 278 | ## Contributing 279 | 280 | Feel free to help! 281 | -------------------------------------------------------------------------------- /lib/src/carousel_pro_widgets.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'package:flutter/material.dart'; 3 | import 'dart:async'; 4 | 5 | class WidgetCarousel extends StatefulWidget { 6 | //All the pages on this Carousel. 7 | final List pages; 8 | 9 | //The transition animation timing curve. Default is [Curves.ease] 10 | final Curve animationCurve; 11 | 12 | //The transition animation duration. Default is 300ms. 13 | final Duration animationDuration; 14 | 15 | // The base size of the dots. Default is 8.0 16 | final double dotSize; 17 | 18 | // The increase in the size of the selected dot. Default is 2.0 19 | final double dotIncreaseSize; 20 | 21 | // The distance between the center of each dot. Default is 25.0 22 | final double dotSpacing; 23 | 24 | // The Color of each dot. Default is Colors.white 25 | final Color dotColor; 26 | 27 | // The background Color of the dots. Default is [Colors.grey[800].withOpacity(0.5)] 28 | final Color dotBgColor; 29 | 30 | // Enable or Disable the indicator (dots). Default is true 31 | final bool showIndicator; 32 | 33 | //Padding Size of the background Indicator. Default is 20.0 34 | final double indicatorBgPadding; 35 | 36 | //How to show the images in the box. Default is cover 37 | final BoxFit boxFit; 38 | 39 | //Enable/Disable radius Border for the images. Default is false 40 | final bool borderRadius; 41 | 42 | //Border Radius of the images. Default is [Radius.circular(8.0)] 43 | final Radius radius; 44 | 45 | //Move the Indicator From the Bottom 46 | final double moveIndicatorFromBottom; 47 | 48 | //Remove the radius bottom from the indicator background. Default false 49 | final bool noRadiusForIndicator; 50 | 51 | //Enable/Disable Image Overlay Shadow. Default false 52 | final bool overlayShadow; 53 | 54 | //Choose the color of the overlay Shadow color. Default Colors.grey[800] 55 | final Color overlayShadowColors; 56 | 57 | //Choose the size of the Overlay Shadow, from 0.0 to 1.0. Default 0.5 58 | final double overlayShadowSize; 59 | 60 | //Enable/Disable the auto play of the slider. Default true 61 | final bool autoplay; 62 | 63 | //Duration of the Auto play slider by seconds. Default 3 seconds 64 | final Duration autoplayDuration; 65 | 66 | WidgetCarousel( 67 | {this.pages, 68 | this.animationCurve = Curves.ease, 69 | this.animationDuration = const Duration(milliseconds: 300), 70 | this.dotSize = 8.0, 71 | this.dotSpacing = 25.0, 72 | this.dotIncreaseSize = 2.0, 73 | this.dotColor = Colors.white, 74 | this.dotBgColor, 75 | this.showIndicator = true, 76 | this.indicatorBgPadding = 20.0, 77 | this.boxFit = BoxFit.cover, 78 | this.borderRadius = false, 79 | this.radius, 80 | this.moveIndicatorFromBottom = 0.0, 81 | this.noRadiusForIndicator = false, 82 | this.overlayShadow = false, 83 | this.overlayShadowColors, 84 | this.overlayShadowSize = 0.5, 85 | this.autoplay = true, 86 | this.autoplayDuration = const Duration(seconds: 3)}) 87 | : assert(pages != null), 88 | assert(animationCurve != null), 89 | assert(animationDuration != null), 90 | assert(dotSize != null), 91 | assert(dotSpacing != null), 92 | assert(dotIncreaseSize != null), 93 | assert(dotColor != null); 94 | 95 | @override 96 | State createState() => WidgetCarouselState(); 97 | } 98 | 99 | class WidgetCarouselState extends State { 100 | final _controller = PageController(); 101 | 102 | @override 103 | void initState() { 104 | super.initState(); 105 | 106 | if (widget.autoplay) { 107 | Timer.periodic(widget.autoplayDuration, (_) { 108 | if (_controller.page == widget.pages.length - 1) { 109 | _controller.animateToPage( 110 | 0, 111 | duration: widget.animationDuration, 112 | curve: widget.animationCurve, 113 | ); 114 | } else { 115 | _controller.nextPage( 116 | duration: widget.animationDuration, curve: widget.animationCurve); 117 | } 118 | }); 119 | } 120 | } 121 | 122 | @override 123 | void dispose() { 124 | super.dispose(); 125 | } 126 | 127 | @override 128 | Widget build(BuildContext context) { 129 | final List listPages = widget.pages 130 | .map((widget) => Container( 131 | child: widget, 132 | )) 133 | .toList(); 134 | 135 | return Stack( 136 | children: [ 137 | Container( 138 | child: PageView( 139 | physics: AlwaysScrollableScrollPhysics(), 140 | controller: _controller, 141 | children: listPages, 142 | ), 143 | ), 144 | widget.showIndicator 145 | ? Positioned( 146 | bottom: widget.moveIndicatorFromBottom, 147 | left: 0.0, 148 | right: 0.0, 149 | child: Container( 150 | decoration: BoxDecoration( 151 | color: widget.dotBgColor == null 152 | ? Colors.grey[800].withOpacity(0.5) 153 | : widget.dotBgColor, 154 | borderRadius: widget.borderRadius 155 | ? (widget.noRadiusForIndicator 156 | ? null 157 | : BorderRadius.only( 158 | bottomLeft: widget.radius != null 159 | ? widget.radius 160 | : Radius.circular(8.0), 161 | bottomRight: widget.radius != null 162 | ? widget.radius 163 | : Radius.circular(8.0))) 164 | : null, 165 | ), 166 | padding: EdgeInsets.all(widget.indicatorBgPadding), 167 | child: Center( 168 | child: DotsIndicator( 169 | controller: _controller, 170 | itemCount: listPages.length, 171 | color: widget.dotColor, 172 | dotSize: widget.dotSize, 173 | dotSpacing: widget.dotSpacing, 174 | dotIncreaseSize: widget.dotIncreaseSize, 175 | onPageSelected: (int page) { 176 | _controller.animateToPage( 177 | page, 178 | duration: widget.animationDuration, 179 | curve: widget.animationCurve, 180 | ); 181 | }, 182 | ), 183 | ), 184 | ), 185 | ) 186 | : Container(), 187 | ], 188 | ); 189 | } 190 | } 191 | 192 | /// An indicator showing the currently selected page of a PageController 193 | class DotsIndicator extends AnimatedWidget { 194 | DotsIndicator( 195 | {this.controller, 196 | this.itemCount, 197 | this.onPageSelected, 198 | this.color, 199 | this.dotSize, 200 | this.dotIncreaseSize, 201 | this.dotSpacing}) 202 | : super(listenable: controller); 203 | 204 | // The PageController that this DotsIndicator is representing. 205 | final PageController controller; 206 | 207 | // The number of items managed by the PageController 208 | final int itemCount; 209 | 210 | // Called when a dot is tapped 211 | final ValueChanged onPageSelected; 212 | 213 | // The color of the dots. 214 | final Color color; 215 | 216 | // The base size of the dots 217 | final double dotSize; 218 | 219 | // The increase in the size of the selected dot 220 | final double dotIncreaseSize; 221 | 222 | // The distance between the center of each dot 223 | final double dotSpacing; 224 | 225 | Widget _buildDot(int index) { 226 | double selectedness = Curves.easeOut.transform( 227 | max( 228 | 0.0, 229 | 1.0 - ((controller.page ?? controller.initialPage) - index).abs(), 230 | ), 231 | ); 232 | double zoom = 1.0 + (dotIncreaseSize - 1.0) * selectedness; 233 | return Container( 234 | width: dotSpacing, 235 | child: Center( 236 | child: Material( 237 | color: color, 238 | type: MaterialType.circle, 239 | child: Container( 240 | width: dotSize * zoom, 241 | height: dotSize * zoom, 242 | child: InkWell( 243 | onTap: () => onPageSelected(index), 244 | ), 245 | ), 246 | ), 247 | ), 248 | ); 249 | } 250 | 251 | Widget build(BuildContext context) { 252 | return Row( 253 | mainAxisAlignment: MainAxisAlignment.center, 254 | children: List.generate(itemCount, _buildDot), 255 | ); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /lib/src/carousel_pro.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | enum DotPosition { 7 | topLeft, 8 | topCenter, 9 | topRight, 10 | bottomLeft, 11 | bottomCenter, 12 | bottomRight 13 | } 14 | 15 | class Carousel extends StatefulWidget { 16 | //All the images on this Carousel. 17 | final List images; 18 | 19 | //All the images on this Carousel. 20 | final defaultImage; 21 | 22 | //The transition animation timing curve. Default is [Curves.ease] 23 | final Curve animationCurve; 24 | 25 | //The transition animation duration. Default is 300ms. 26 | final Duration animationDuration; 27 | 28 | // The base size of the dots. Default is 8.0 29 | final double dotSize; 30 | 31 | // The increase in the size of the selected dot. Default is 2.0 32 | final double dotIncreaseSize; 33 | 34 | // The distance between the center of each dot. Default is 25.0 35 | final double dotSpacing; 36 | 37 | // The Color of each dot. Default is Colors.white 38 | final Color dotColor; 39 | 40 | // The background Color of the dots. Default is [Colors.grey[800].withOpacity(0.5)] 41 | final Color dotBgColor; 42 | 43 | // The Color of each increased dot. Default is Colors.white 44 | final Color dotIncreasedColor; 45 | 46 | // Enable or Disable the indicator (dots). Default is true 47 | final bool showIndicator; 48 | 49 | //Padding Size of the background Indicator. Default is 20.0 50 | final double indicatorBgPadding; 51 | 52 | //How to show the images in the box. Default is cover 53 | final BoxFit boxFit; 54 | 55 | //Enable/Disable radius Border for the images. Default is false 56 | final bool borderRadius; 57 | 58 | //Border Radius of the images. Default is [Radius.circular(8.0)] 59 | final Radius radius; 60 | 61 | //Indicator position. Default bottomCenter 62 | final DotPosition dotPosition; 63 | 64 | //Move the Indicator Horizontally relative to the dot position 65 | final double dotHorizontalPadding; 66 | 67 | //Move the Indicator Vertically relative to the dot position 68 | final double dotVerticalPadding; 69 | 70 | //Move the Indicator From the Bottom 71 | final double moveIndicatorFromBottom; 72 | 73 | //Remove the radius bottom from the indicator background. Default false 74 | final bool noRadiusForIndicator; 75 | 76 | //Enable/Disable Image Overlay Shadow. Default false 77 | final bool overlayShadow; 78 | 79 | //Choose the color of the overlay Shadow color. Default Colors.grey[800] 80 | final Color overlayShadowColors; 81 | 82 | //Choose the size of the Overlay Shadow, from 0.0 to 1.0. Default 0.5 83 | final double overlayShadowSize; 84 | 85 | //Enable/Disable the auto play of the slider. Default true 86 | final bool autoplay; 87 | 88 | //Duration of the Auto play slider by seconds. Default 3 seconds 89 | final Duration autoplayDuration; 90 | 91 | //On image tap event, passes current image index as an argument 92 | final void Function(int) onImageTap; 93 | 94 | //On image change event, passes previous image index and current image index as arguments 95 | final void Function(int, int) onImageChange; 96 | 97 | Carousel({ 98 | this.images, 99 | this.animationCurve = Curves.ease, 100 | this.animationDuration = const Duration(milliseconds: 300), 101 | this.dotSize = 8.0, 102 | this.dotSpacing = 25.0, 103 | this.dotIncreaseSize = 2.0, 104 | this.dotColor = Colors.white, 105 | this.dotBgColor, 106 | this.dotIncreasedColor = Colors.white, 107 | this.showIndicator = true, 108 | this.indicatorBgPadding = 20.0, 109 | this.boxFit = BoxFit.cover, 110 | this.borderRadius = false, 111 | this.radius, 112 | this.dotPosition = DotPosition.bottomCenter, 113 | this.dotHorizontalPadding = 0.0, 114 | this.dotVerticalPadding = 0.0, 115 | this.moveIndicatorFromBottom = 0.0, 116 | this.noRadiusForIndicator = false, 117 | this.overlayShadow = false, 118 | this.overlayShadowColors, 119 | this.overlayShadowSize = 0.5, 120 | this.autoplay = true, 121 | this.autoplayDuration = const Duration(seconds: 3), 122 | this.onImageTap, 123 | this.onImageChange, 124 | this.defaultImage, 125 | }); 126 | 127 | @override 128 | State createState() => CarouselState(); 129 | } 130 | 131 | class CarouselState extends State { 132 | Timer timer; 133 | int _currentImageIndex = 0; 134 | PageController _controller = PageController(); 135 | 136 | @override 137 | void initState() { 138 | super.initState(); 139 | 140 | if (widget.images != null && widget.images.isNotEmpty) { 141 | if (widget.autoplay) { 142 | timer = Timer.periodic(widget.autoplayDuration, (_) { 143 | if (_controller.hasClients) { 144 | if (_controller.page.round() == widget.images.length - 1) { 145 | _controller.animateToPage( 146 | 0, 147 | duration: widget.animationDuration, 148 | curve: widget.animationCurve, 149 | ); 150 | } else { 151 | _controller.nextPage( 152 | duration: widget.animationDuration, 153 | curve: widget.animationCurve); 154 | } 155 | } 156 | }); 157 | } 158 | } 159 | } 160 | 161 | @override 162 | void dispose() { 163 | _controller.dispose(); 164 | _controller = null; 165 | timer?.cancel(); 166 | timer = null; 167 | super.dispose(); 168 | } 169 | 170 | @override 171 | Widget build(BuildContext context) { 172 | final List listImages = (widget.images != null && 173 | widget.images.isNotEmpty) 174 | ? widget.images.map( 175 | (netImage) { 176 | if (netImage is ImageProvider) { 177 | return Container( 178 | decoration: BoxDecoration( 179 | borderRadius: widget.borderRadius 180 | ? BorderRadius.all(widget.radius != null 181 | ? widget.radius 182 | : Radius.circular(8.0)) 183 | : null, 184 | image: DecorationImage( 185 | //colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.2), BlendMode.dstATop), 186 | image: netImage, 187 | fit: widget.boxFit, 188 | ), 189 | ), 190 | child: widget.overlayShadow 191 | ? Container( 192 | decoration: BoxDecoration( 193 | gradient: LinearGradient( 194 | begin: Alignment.bottomCenter, 195 | end: Alignment.center, 196 | stops: [0.0, widget.overlayShadowSize], 197 | colors: [ 198 | widget.overlayShadowColors != null 199 | ? widget.overlayShadowColors 200 | .withOpacity(1.0) 201 | : Colors.grey[800].withOpacity(1.0), 202 | widget.overlayShadowColors != null 203 | ? widget.overlayShadowColors 204 | .withOpacity(0.0) 205 | : Colors.grey[800].withOpacity(0.0) 206 | ], 207 | ), 208 | ), 209 | ) 210 | : Container(), 211 | ); 212 | } else if (netImage is FadeInImage) { 213 | return ClipRRect( 214 | borderRadius: widget.borderRadius 215 | ? BorderRadius.all(widget.radius != null 216 | ? widget.radius 217 | : Radius.circular(8.0)) 218 | : null, 219 | child: Container( 220 | decoration: BoxDecoration( 221 | gradient: LinearGradient( 222 | begin: Alignment.bottomCenter, 223 | end: Alignment.center, 224 | stops: [0.0, widget.overlayShadowSize], 225 | colors: [ 226 | widget.overlayShadowColors != null 227 | ? widget.overlayShadowColors.withOpacity(1.0) 228 | : Colors.grey[800].withOpacity(1.0), 229 | widget.overlayShadowColors != null 230 | ? widget.overlayShadowColors.withOpacity(0.0) 231 | : Colors.grey[800].withOpacity(0.0) 232 | ], 233 | ), 234 | ), 235 | child: netImage), 236 | ); 237 | } else { 238 | return netImage; 239 | } 240 | }, 241 | ).toList() 242 | : [ 243 | widget.defaultImage is ImageProvider 244 | ? Container( 245 | decoration: BoxDecoration( 246 | borderRadius: widget.borderRadius 247 | ? BorderRadius.all(widget.radius != null 248 | ? widget.radius 249 | : Radius.circular(8.0)) 250 | : null, 251 | image: DecorationImage( 252 | //colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.2), BlendMode.dstATop), 253 | image: widget.defaultImage, 254 | fit: widget.boxFit, 255 | ), 256 | ), 257 | child: widget.overlayShadow 258 | ? Container( 259 | decoration: BoxDecoration( 260 | gradient: LinearGradient( 261 | begin: Alignment.bottomCenter, 262 | end: Alignment.center, 263 | stops: [0.0, widget.overlayShadowSize], 264 | colors: [ 265 | widget.overlayShadowColors != null 266 | ? widget.overlayShadowColors 267 | .withOpacity(1.0) 268 | : Colors.grey[800].withOpacity(1.0), 269 | widget.overlayShadowColors != null 270 | ? widget.overlayShadowColors 271 | .withOpacity(0.0) 272 | : Colors.grey[800].withOpacity(0.0) 273 | ], 274 | ), 275 | ), 276 | ) 277 | : Container(), 278 | ) 279 | : widget.defaultImage, 280 | ]; 281 | 282 | final bottom = [ 283 | DotPosition.bottomLeft, 284 | DotPosition.bottomCenter, 285 | DotPosition.bottomRight 286 | ].contains(widget.dotPosition) 287 | ? widget.dotVerticalPadding 288 | : null; 289 | final top = [ 290 | DotPosition.topLeft, 291 | DotPosition.topCenter, 292 | DotPosition.topRight 293 | ].contains(widget.dotPosition) 294 | ? widget.dotVerticalPadding 295 | : null; 296 | double left = [DotPosition.topLeft, DotPosition.bottomLeft] 297 | .contains(widget.dotPosition) 298 | ? widget.dotHorizontalPadding 299 | : null; 300 | double right = [DotPosition.topRight, DotPosition.bottomRight] 301 | .contains(widget.dotPosition) 302 | ? widget.dotHorizontalPadding 303 | : null; 304 | 305 | if (left == null && right == null) { 306 | left = right = 0.0; 307 | } 308 | 309 | return Stack( 310 | children: [ 311 | Container( 312 | child: Builder( 313 | builder: (_) { 314 | Widget pageView = PageView( 315 | physics: AlwaysScrollableScrollPhysics(), 316 | controller: _controller, 317 | children: listImages, 318 | onPageChanged: (currentPage) { 319 | if (widget.onImageChange != null) { 320 | widget.onImageChange(_currentImageIndex, currentPage); 321 | } 322 | 323 | _currentImageIndex = currentPage; 324 | }, 325 | ); 326 | 327 | if (widget.onImageTap == null) { 328 | return pageView; 329 | } 330 | 331 | return GestureDetector( 332 | child: pageView, 333 | onTap: () => widget.onImageTap(_currentImageIndex), 334 | ); 335 | }, 336 | ), 337 | ), 338 | widget.showIndicator 339 | ? Positioned( 340 | bottom: bottom, 341 | top: top, 342 | left: left, 343 | right: right, 344 | child: Container( 345 | decoration: BoxDecoration( 346 | color: widget.dotBgColor == null 347 | ? Colors.grey[800].withOpacity(0.5) 348 | : widget.dotBgColor, 349 | borderRadius: widget.borderRadius 350 | ? (widget.noRadiusForIndicator 351 | ? null 352 | : BorderRadius.only( 353 | bottomLeft: widget.radius != null 354 | ? widget.radius 355 | : Radius.circular(8.0), 356 | bottomRight: widget.radius != null 357 | ? widget.radius 358 | : Radius.circular(8.0))) 359 | : null, 360 | ), 361 | padding: EdgeInsets.all(widget.indicatorBgPadding), 362 | child: Center( 363 | child: DotsIndicator( 364 | controller: _controller, 365 | itemCount: listImages.length, 366 | color: widget.dotColor, 367 | increasedColor: widget.dotIncreasedColor, 368 | dotSize: widget.dotSize, 369 | dotSpacing: widget.dotSpacing, 370 | dotIncreaseSize: widget.dotIncreaseSize, 371 | onPageSelected: (int page) { 372 | _controller.animateToPage( 373 | page, 374 | duration: widget.animationDuration, 375 | curve: widget.animationCurve, 376 | ); 377 | }, 378 | ), 379 | ), 380 | ), 381 | ) 382 | : Container(), 383 | ], 384 | ); 385 | } 386 | } 387 | 388 | /// An indicator showing the currently selected page of a PageController 389 | class DotsIndicator extends AnimatedWidget { 390 | DotsIndicator( 391 | {this.controller, 392 | this.itemCount, 393 | this.onPageSelected, 394 | this.color, 395 | this.increasedColor, 396 | this.dotSize, 397 | this.dotIncreaseSize, 398 | this.dotSpacing}) 399 | : super(listenable: controller); 400 | 401 | // The PageController that this DotsIndicator is representing. 402 | final PageController controller; 403 | 404 | // The number of items managed by the PageController 405 | final int itemCount; 406 | 407 | // Called when a dot is tapped 408 | final ValueChanged onPageSelected; 409 | 410 | // The color of the dots. 411 | final Color color; 412 | 413 | // The color of the increased dot. 414 | final Color increasedColor; 415 | 416 | // The base size of the dots 417 | final double dotSize; 418 | 419 | // The increase in the size of the selected dot 420 | final double dotIncreaseSize; 421 | 422 | // The distance between the center of each dot 423 | final double dotSpacing; 424 | 425 | Widget _buildDot(int index) { 426 | double selectedness = Curves.easeOut.transform( 427 | max( 428 | 0.0, 429 | 1.0 - ((controller.page ?? controller.initialPage) - index).abs(), 430 | ), 431 | ); 432 | double zoom = 1.0 + (dotIncreaseSize - 1.0) * selectedness; 433 | final dotColor = zoom > 1.0 ? increasedColor : color; 434 | return Container( 435 | width: dotSpacing, 436 | child: Center( 437 | child: Material( 438 | color: dotColor, 439 | type: MaterialType.circle, 440 | child: Container( 441 | width: dotSize * zoom, 442 | height: dotSize * zoom, 443 | child: InkWell( 444 | onTap: () => onPageSelected(index), 445 | ), 446 | ), 447 | ), 448 | ), 449 | ); 450 | } 451 | 452 | Widget build(BuildContext context) { 453 | return Row( 454 | mainAxisAlignment: MainAxisAlignment.center, 455 | children: List.generate(itemCount, _buildDot), 456 | ); 457 | } 458 | } 459 | --------------------------------------------------------------------------------