├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── image_carousel.iml ├── lib ├── image_carousel.dart └── src │ └── image_carousel.dart ├── pubspec.yaml ├── show_case_gif.gif └── test └── image_carousel_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea 4 | .packages 5 | .dart_tool/ 6 | .pub/ 7 | packages 8 | pubspec.lock 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.4.0+1] 2 | * you can provide a TabController if you want to add a TabPageSelector 3 | 4 | ## [0.4.0] 5 | 6 | * **API CHANGE**: must provide `ImageProvider` object instead of custom class 7 | 8 | ## [0.3.0] 9 | 10 | * added option to cache the network image 11 | 12 | ## [0.2.0] 13 | 14 | * added pinch to zoom 15 | 16 | ## [0.1.3] 17 | 18 | * added visualization 19 | 20 | ## [0.1.2] 21 | 22 | * added visualization 23 | 24 | ## [0.1.1] 25 | 26 | * added the option to automatically loop through the photos with the given `Duration` 27 | 28 | ## [0.1.0] 29 | 30 | * Initial release 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_image_carousel 2 | 3 | A flutter package for image carousels. 4 | 5 | Supports both Asset and Network images. 6 | 7 | ## Example 8 | 9 | ```dart 10 | new ImageCarousel( 11 | [ 12 | new NetworkImage('http://www.hilversum.ferraridealers.com/siteasset/ferraridealer/54f07ac8c35b6/961/420/selected/0/0/0/54f07ac8c35b6.jpg'), 13 | new NetworkImage('http://auto.ferrari.com/en_EN/wp-content/uploads/sites/5/2017/08/ferrari-portofino-reveal-2017-featured-new.jpg'), 14 | new NetworkImage('http://www.hilversum.ferraridealers.com/siteasset/ferraridealer/54f07ac8c35b6/961/420/selected/0/0/0/54f07ac8c35b6.jpg'), 15 | ], 16 | interval: new Duration(seconds: 1), 17 | ) 18 | ``` 19 | 20 | ## Showcase 21 | 22 | ![](https://github.com/theobouwman/flutter_image_carousel/blob/master/show_case_gif.gif) 23 | -------------------------------------------------------------------------------- /image_carousel.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lib/image_carousel.dart: -------------------------------------------------------------------------------- 1 | library image_carousel; 2 | 3 | export 'src/image_carousel.dart'; 4 | -------------------------------------------------------------------------------- /lib/src/image_carousel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/widgets.dart'; 7 | import 'package:zoomable_image/zoomable_image.dart'; 8 | 9 | class ImageCarousel extends StatefulWidget { 10 | final List imageProviders; 11 | final double height; 12 | final TargetPlatform platform; 13 | final Duration interval; 14 | final bool allowZoom; 15 | final TabController tabController; 16 | final BoxFit fit; 17 | final bool showCloseButtonOnZoom; 18 | final bool canCloseZoomOnTap; 19 | 20 | // Images will shrink according to the value of [height] 21 | // If you prefer to use the Material or Cupertino style activity indicator set the [platform] parameter 22 | // Set [interval] to let the carousel loop through each photo automatically 23 | // Pinch to zoom will be turned on by default 24 | ImageCarousel( 25 | this.imageProviders, { 26 | this.height = 250.0, 27 | this.platform, 28 | this.interval, 29 | this.allowZoom = true, 30 | this.tabController, 31 | this.fit = BoxFit.cover, 32 | this.showCloseButtonOnZoom = false, 33 | this.canCloseZoomOnTap = false 34 | }); 35 | 36 | @override 37 | State createState() => new _ImageCarouselState(); 38 | } 39 | 40 | class _ImageCarouselState extends State with SingleTickerProviderStateMixin { 41 | TabController _tabController; 42 | Timer _timer; 43 | 44 | @override 45 | void initState() { 46 | super.initState(); 47 | _tabController = widget.tabController ?? 48 | new TabController(vsync: this, length: widget.imageProviders.length); 49 | 50 | if (widget.interval != null) { 51 | _timer = new Timer.periodic(widget.interval, (_) { 52 | _tabController.animateTo( 53 | _tabController.index == _tabController.length - 1 54 | ? 0 55 | : ++_tabController.index); 56 | }); 57 | } 58 | } 59 | 60 | @override 61 | void dispose() { 62 | _timer?.cancel(); 63 | _tabController?.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return new SizedBox( 70 | height: widget.height, 71 | child: new TabBarView( 72 | controller: _tabController, 73 | children: widget.imageProviders.map((ImageProvider provider) { 74 | return new CarouselImageWidget(widget, provider, widget.fit, widget.height, widget.showCloseButtonOnZoom); 75 | }).toList(), 76 | ), 77 | ); 78 | } 79 | } 80 | 81 | class CarouselImageWidget extends StatefulWidget { 82 | final ImageCarousel carousel; 83 | final ImageProvider imageProvider; 84 | final BoxFit fit; 85 | final double height; 86 | final bool showDismissButtonOnZoom; 87 | 88 | CarouselImageWidget(this.carousel, this.imageProvider, this.fit, this.height, this.showDismissButtonOnZoom); 89 | 90 | @override 91 | State createState() => new _CarouselImageState(); 92 | } 93 | 94 | class _CarouselImageState extends State { 95 | bool _loading = true; 96 | 97 | Widget _getIndicator(TargetPlatform platform) { 98 | if (platform == TargetPlatform.iOS) { 99 | return new CupertinoActivityIndicator(); 100 | } else { 101 | return new Container( 102 | height: 40.0, 103 | width: 40.0, 104 | child: new CircularProgressIndicator(), 105 | ); 106 | } 107 | } 108 | 109 | void _toZoomRoute() { 110 | Widget scaffold = new Scaffold( 111 | backgroundColor: Colors.black, 112 | body: _buildBody(), 113 | ); 114 | 115 | Navigator.of(context).push( 116 | defaultTargetPlatform == TargetPlatform.iOS 117 | ? new CupertinoPageRoute(builder: (BuildContext context) => scaffold, fullscreenDialog: true) 118 | : new MaterialPageRoute(builder: (BuildContext context) => scaffold, fullscreenDialog: true), 119 | ); 120 | } 121 | 122 | Widget _buildBody() { 123 | var body; 124 | 125 | if (widget.carousel.allowZoom && widget.showDismissButtonOnZoom) { 126 | body = new Stack( 127 | children: [ 128 | new ZoomableImage( 129 | widget.imageProvider, 130 | scale: 16.0 131 | ), 132 | new Positioned( 133 | child: new IconButton( 134 | icon: const Icon(Icons.close), 135 | onPressed: () { 136 | Navigator.pop(context); 137 | }, 138 | color: Colors.white 139 | ), 140 | top: 30.0, 141 | right: 6.0 142 | ) 143 | ], 144 | ); 145 | } else { 146 | body = new ZoomableImage( 147 | widget.imageProvider, 148 | scale: 16.0 149 | ); 150 | } 151 | 152 | if (widget.carousel.canCloseZoomOnTap) { 153 | return new GestureDetector( 154 | child: body, 155 | onTap: () { 156 | Navigator.pop(context); 157 | }, 158 | ); 159 | } 160 | 161 | return body; 162 | } 163 | 164 | @override 165 | void initState() { 166 | super.initState(); 167 | 168 | widget.imageProvider.resolve(new ImageConfiguration()).addListener((i, b) { 169 | if (mounted) { 170 | setState(() { 171 | _loading = false; 172 | }); 173 | } 174 | }); 175 | } 176 | 177 | @override 178 | Widget build(BuildContext context) { 179 | return new Container( 180 | height: widget.height, 181 | child: _loading 182 | ? _getIndicator(widget.carousel.platform == null ? defaultTargetPlatform : widget.carousel.platform) 183 | : new GestureDetector( 184 | child: new Image( 185 | image: widget.imageProvider, 186 | fit: widget.fit, 187 | ), 188 | onTap: () { 189 | if (widget.carousel.allowZoom) { 190 | _toZoomRoute(); 191 | } 192 | }, 193 | ), 194 | ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: image_carousel 2 | author: Theo Bouwman 3 | homepage: https://github.com/theobouwman/flutter_image_carousel 4 | description: A image carousel flutter plugin 5 | version: 0.4.2 6 | 7 | environment: 8 | sdk: '>=2.00.0' 9 | 10 | dependencies: 11 | zoomable_image: ^1.3.1 12 | 13 | flutter: 14 | sdk: flutter 15 | -------------------------------------------------------------------------------- /show_case_gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theobouwman/flutter_image_carousel/ee16eee8877e739044cab5eff08a82ee47ce11aa/show_case_gif.gif -------------------------------------------------------------------------------- /test/image_carousel_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | } 3 | --------------------------------------------------------------------------------