A Beautiful Infinite Carousel Slider Flutter Widget, carousel_slider
Google describes Carousel as a “merry-go-round at a fair“. As this suggest carousel_slider flutter widget is a widget that provides a infinite carousel like effect to your flutter app’s content.
Contents
carousel_slider
A carousel slider widget.
Features
- Infinite scroll
- Custom child widgets
- Auto play
Supported platforms
- Flutter Android
- Flutter iOS
- Flutter web
- Flutter desktop
Live preview
https://serenader2014.github.io/flutter_carousel_slider/#/
Note: this page is built with flutter-web. For a better user experience, please use a mobile device to open this link.
Installation
Add carousel_slider: ^4.0.0
to your pubspec.yaml
dependencies. And import it:
import 'package:carousel_slider/carousel_slider.dart';
How to use
Simply create a CarouselSlider
widget, and pass the required params:
CarouselSlider( options: CarouselOptions(height: 400.0), items: [1,2,3,4,5].map((i) { return Builder( builder: (BuildContext context) { return Container( width: MediaQuery.of(context).size.width, margin: EdgeInsets.symmetric(horizontal: 5.0), decoration: BoxDecoration( color: Colors.amber ), child: Text('text $i', style: TextStyle(fontSize: 16.0),) ); }, ); }).toList(), )
Parameters
CarouselSlider( items: items, options: CarouselOptions( height: 400, aspectRatio: 16/9, viewportFraction: 0.8, initialPage: 0, enableInfiniteScroll: true, reverse: false, autoPlay: true, autoPlayInterval: Duration(seconds: 3), autoPlayAnimationDuration: Duration(milliseconds: 800), autoPlayCurve: Curves.fastOutSlowIn, enlargeCenterPage: true, onPageChanged: callbackFunction, scrollDirection: Axis.horizontal, ) )
Since v2.0.0
, you’ll need to pass the options to CarouselOptions
. For each option’s usage you can refer to carousel_options.dart.
If you pass the height
parameter, the aspectRatio
parameter will be ignored.
Build item widgets on demand
This method will save memory by building items once it becomes necessary. This way they won’t be built if they’re not currently meant to be visible on screen. It can be used to build different child item widgets related to content or by item index.
CarouselSlider.builder( itemCount: 15, itemBuilder: (BuildContext context, int itemIndex, int pageViewIndex) => Container( child: Text(itemIndex.toString()), ), )
Carousel controller
In order to manually control the pageview’s position, you can create your own CarouselController
, and pass it to CarouselSlider
. Then you can use the CarouselController
instance to manipulate the position.
class CarouselDemo extends StatelessWidget { CarouselController buttonCarouselController = CarouselController(); @override Widget build(BuildContext context) => Column( children: <Widget>[ CarouselSlider( items: child, carouselController: buttonCarouselController, options: CarouselOptions( autoPlay: false, enlargeCenterPage: true, viewportFraction: 0.9, aspectRatio: 2.0, initialPage: 2, ), ), RaisedButton( onPressed: () => buttonCarouselController.nextPage( duration: Duration(milliseconds: 300), curve: Curves.linear), child: Text('→'), ) ] ); }
CarouselController
methods
.nextPage({Duration duration, Curve curve})
Animate to the next page
.previousPage({Duration duration, Curve curve})
Animate to the previous page
.jumpToPage(int page)
Jump to the given page.
.animateToPage(int page, {Duration duration, Curve curve})
Animate to the given page.
Screenshot
Basic text carousel demo:
Basic image carousel demo:
A more complicated image carousel slider demo:
Fullscreen image carousel slider demo:
Image carousel slider with custom indicator demo:
Custom CarouselController
and manually control the pageview position demo:
Vertical carousel slider demo:
Simple on-demand image carousel slider, with image auto prefetch demo:
No infinite scroll demo:
All screenshots above can be found at the example project.
Example
This is a example code of the A Beautiful Infinite Carousel Slider Flutter Widget, carousel_slider:
- main.dart
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
final List<String> imgList = [
'https://images.unsplash.com/photo-1520342868574-5fa3804e551c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=6ff92caffcdd63681a35134a6770ed3b&auto=format&fit=crop&w=1951&q=80',
'https://images.unsplash.com/photo-1522205408450-add114ad53fe?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=368f45b0888aeb0b7b08e3a1084d3ede&auto=format&fit=crop&w=1950&q=80',
'https://images.unsplash.com/photo-1519125323398-675f0ddb6308?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=94a1e718d89ca60a6337a6008341ca50&auto=format&fit=crop&w=1950&q=80',
'https://images.unsplash.com/photo-1523205771623-e0faa4d2813d?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89719a0d55dd05e2deae4120227e6efc&auto=format&fit=crop&w=1953&q=80',
'https://images.unsplash.com/photo-1508704019882-f9cf40e475b4?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=8c6e5e3aba713b17aa1fe71ab4f0ae5b&auto=format&fit=crop&w=1352&q=80',
'https://images.unsplash.com/photo-1519985176271-adb1088fa94c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=a0c8d632e977f94e5d312d9893258f59&auto=format&fit=crop&w=1355&q=80'
];
void main() => runApp(CarouselDemo());
final themeMode = ValueNotifier(2);
class CarouselDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
builder: (context, value, g) {
return MaterialApp(
initialRoute: '/',
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.values.toList()[value as int],
debugShowCheckedModeBanner: false,
routes: {
'/': (ctx) => CarouselDemoHome(),
'/basic': (ctx) => BasicDemo(),
'/nocenter': (ctx) => NoCenterDemo(),
'/image': (ctx) => ImageSliderDemo(),
'/complicated': (ctx) => ComplicatedImageDemo(),
'/enlarge': (ctx) => EnlargeStrategyDemo(),
'/manual': (ctx) => ManuallyControlledSlider(),
'/noloop': (ctx) => NoonLoopingDemo(),
'/vertical': (ctx) => VerticalSliderDemo(),
'/fullscreen': (ctx) => FullscreenSliderDemo(),
'/ondemand': (ctx) => OnDemandCarouselDemo(),
'/indicator': (ctx) => CarouselWithIndicatorDemo(),
'/prefetch': (ctx) => PrefetchImageDemo(),
'/reason': (ctx) => CarouselChangeReasonDemo(),
'/position': (ctx) => KeepPageviewPositionDemo(),
'/multiple': (ctx) => MultipleItemDemo(),
},
);
},
valueListenable: themeMode,
);
}
}
class DemoItem extends StatelessWidget {
final String title;
final String route;
DemoItem(this.title, this.route);
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(title),
onTap: () {
Navigator.pushNamed(context, route);
},
);
}
}
class CarouselDemoHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Carousel demo'),
actions: [
IconButton(
icon: Icon(Icons.nightlight_round),
onPressed: () {
themeMode.value = themeMode.value == 1 ? 2 : 1;
})
],
),
body: ListView(
children: <Widget>[
DemoItem('Basic demo', '/basic'),
DemoItem('No center mode demo', '/nocenter'),
DemoItem('Image carousel slider', '/image'),
DemoItem('More complicated image slider', '/complicated'),
DemoItem('Enlarge strategy demo slider', '/enlarge'),
DemoItem('Manually controlled slider', '/manual'),
DemoItem('Noon-looping carousel slider', '/noloop'),
DemoItem('Vertical carousel slider', '/vertical'),
DemoItem('Fullscreen carousel slider', '/fullscreen'),
DemoItem('Carousel with indicator controller demo', '/indicator'),
DemoItem('On-demand carousel slider', '/ondemand'),
DemoItem('Image carousel slider with prefetch demo', '/prefetch'),
DemoItem('Carousel change reason demo', '/reason'),
DemoItem('Keep pageview position demo', '/position'),
DemoItem('Multiple item in one screen demo', '/multiple'),
],
),
);
}
}
class BasicDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
List<int> list = [1, 2, 3, 4, 5];
return Scaffold(
appBar: AppBar(title: Text('Basic demo')),
body: Container(
child: CarouselSlider(
options: CarouselOptions(),
items: list
.map((item) => Container(
child: Center(child: Text(item.toString())),
color: Colors.green,
))
.toList(),
)),
);
}
}
class NoCenterDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
List<int> list = [1, 2, 3, 4, 5];
return Scaffold(
appBar: AppBar(title: Text('Basic demo')),
body: Container(
child: CarouselSlider(
options: CarouselOptions(
disableCenter: true,
),
items: list
.map((item) => Container(
child: Text(item.toString()),
color: Colors.green,
))
.toList(),
)),
);
}
}
class ImageSliderDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Image slider demo')),
body: Container(
child: CarouselSlider(
options: CarouselOptions(),
items: imgList
.map((item) => Container(
child: Center(
child:
Image.network(item, fit: BoxFit.cover, width: 1000)),
))
.toList(),
)),
);
}
}
final List<Widget> imageSliders = imgList
.map((item) => Container(
child: Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Stack(
children: <Widget>[
Image.network(item, fit: BoxFit.cover, width: 1000.0),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(200, 0, 0, 0),
Color.fromARGB(0, 0, 0, 0)
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
padding: EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
child: Text(
'No. ${imgList.indexOf(item)} image',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
),
],
)),
),
))
.toList();
class ComplicatedImageDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Complicated image slider demo')),
body: Container(
child: CarouselSlider(
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
),
items: imageSliders,
),
),
);
}
}
class EnlargeStrategyDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Complicated image slider demo')),
body: Container(
child: CarouselSlider(
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
enlargeStrategy: CenterPageEnlargeStrategy.height,
),
items: imageSliders,
),
),
);
}
}
class ManuallyControlledSlider extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ManuallyControlledSliderState();
}
}
class _ManuallyControlledSliderState extends State<ManuallyControlledSlider> {
final CarouselController _controller = CarouselController();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Manually controlled slider')),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
CarouselSlider(
items: imageSliders,
options: CarouselOptions(enlargeCenterPage: true, height: 200),
carouselController: _controller,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
child: ElevatedButton(
onPressed: () => _controller.previousPage(),
child: Text('←'),
),
),
Flexible(
child: ElevatedButton(
onPressed: () => _controller.nextPage(),
child: Text('→'),
),
),
...Iterable<int>.generate(imgList.length).map(
(int pageIndex) => Flexible(
child: ElevatedButton(
onPressed: () => _controller.animateToPage(pageIndex),
child: Text("$pageIndex"),
),
),
),
],
)
],
),
));
}
}
class NoonLoopingDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Noon-looping carousel demo')),
body: Container(
child: CarouselSlider(
options: CarouselOptions(
aspectRatio: 2.0,
enlargeCenterPage: true,
enableInfiniteScroll: false,
initialPage: 2,
autoPlay: true,
),
items: imageSliders,
)),
);
}
}
class VerticalSliderDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Vertical sliding carousel demo')),
body: Container(
child: CarouselSlider(
options: CarouselOptions(
aspectRatio: 2.0,
enlargeCenterPage: true,
scrollDirection: Axis.vertical,
autoPlay: true,
),
items: imageSliders,
)),
);
}
}
class FullscreenSliderDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Fullscreen sliding carousel demo')),
body: Builder(
builder: (context) {
final double height = MediaQuery.of(context).size.height;
return CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
// autoPlay: false,
),
items: imgList
.map((item) => Container(
child: Center(
child: Image.network(
item,
fit: BoxFit.cover,
height: height,
)),
))
.toList(),
);
},
),
);
}
}
class OnDemandCarouselDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('On-demand carousel demo')),
body: Container(
child: CarouselSlider.builder(
itemCount: 100,
options: CarouselOptions(
aspectRatio: 2.0,
enlargeCenterPage: true,
autoPlay: true,
),
itemBuilder: (ctx, index, realIdx) {
return Container(
child: Text(index.toString()),
);
},
)),
);
}
}
class CarouselWithIndicatorDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _CarouselWithIndicatorState();
}
}
class _CarouselWithIndicatorState extends State<CarouselWithIndicatorDemo> {
int _current = 0;
final CarouselController _controller = CarouselController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Carousel with indicator controller demo')),
body: Column(children: [
Expanded(
child: CarouselSlider(
items: imageSliders,
carouselController: _controller,
options: CarouselOptions(
autoPlay: true,
enlargeCenterPage: true,
aspectRatio: 2.0,
onPageChanged: (index, reason) {
setState(() {
_current = index;
});
}),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: imgList.asMap().entries.map((entry) {
return GestureDetector(
onTap: () => _controller.animateToPage(entry.key),
child: Container(
width: 12.0,
height: 12.0,
margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black)
.withOpacity(_current == entry.key ? 0.9 : 0.4)),
),
);
}).toList(),
),
]),
);
}
}
class PrefetchImageDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _PrefetchImageDemoState();
}
}
class _PrefetchImageDemoState extends State<PrefetchImageDemo> {
final List<String> images = [
'https://images.unsplash.com/photo-1586882829491-b81178aa622e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2850&q=80',
'https://images.unsplash.com/photo-1586871608370-4adee64d1794?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2862&q=80',
'https://images.unsplash.com/photo-1586901533048-0e856dff2c0d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1650&q=80',
'https://images.unsplash.com/photo-1586902279476-3244d8d18285?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2850&q=80',
'https://images.unsplash.com/photo-1586943101559-4cdcf86a6f87?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1556&q=80',
'https://images.unsplash.com/photo-1586951144438-26d4e072b891?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1650&q=80',
'https://images.unsplash.com/photo-1586953983027-d7508a64f4bb?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1650&q=80',
];
@override
void initState() {
WidgetsBinding.instance!.addPostFrameCallback((_) {
images.forEach((imageUrl) {
precacheImage(NetworkImage(imageUrl), context);
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Prefetch image slider demo')),
body: Container(
child: CarouselSlider.builder(
itemCount: images.length,
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
),
itemBuilder: (context, index, realIdx) {
return Container(
child: Center(
child: Image.network(images[index],
fit: BoxFit.cover, width: 1000)),
);
},
)),
);
}
}
class CarouselChangeReasonDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _CarouselChangeReasonDemoState();
}
}
class _CarouselChangeReasonDemoState extends State<CarouselChangeReasonDemo> {
String reason = '';
final CarouselController _controller = CarouselController();
void onPageChange(int index, CarouselPageChangedReason changeReason) {
setState(() {
reason = changeReason.toString();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Change reason demo')),
body: Column(
children: <Widget>[
Expanded(
child: CarouselSlider(
items: imageSliders,
options: CarouselOptions(
enlargeCenterPage: true,
aspectRatio: 16 / 9,
onPageChanged: onPageChange,
autoPlay: true,
),
carouselController: _controller,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
child: ElevatedButton(
onPressed: () => _controller.previousPage(),
child: Text('←'),
),
),
Flexible(
child: ElevatedButton(
onPressed: () => _controller.nextPage(),
child: Text('→'),
),
),
...Iterable<int>.generate(imgList.length).map(
(int pageIndex) => Flexible(
child: ElevatedButton(
onPressed: () => _controller.animateToPage(pageIndex),
child: Text("$pageIndex"),
),
),
),
],
),
Center(
child: Column(
children: [
Text('page change reason: '),
Text(reason),
],
),
)
],
));
}
}
class KeepPageviewPositionDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Keep pageview position demo')),
body: ListView.builder(itemBuilder: (ctx, index) {
if (index == 3) {
return Container(
child: CarouselSlider(
options: CarouselOptions(
aspectRatio: 2.0,
enlargeCenterPage: true,
pageViewKey: PageStorageKey<String>('carousel_slider'),
),
items: imageSliders,
));
} else {
return Container(
margin: EdgeInsets.symmetric(vertical: 20),
color: Colors.blue,
height: 200,
child: Center(
child: Text('other content'),
),
);
}
}),
);
}
}
class MultipleItemDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Multiple item in one slide demo')),
body: Container(
child: CarouselSlider.builder(
options: CarouselOptions(
aspectRatio: 2.0,
enlargeCenterPage: false,
viewportFraction: 1,
),
itemCount: (imgList.length / 2).round(),
itemBuilder: (context, index, realIdx) {
final int first = index * 2;
final int second = first + 1;
return Row(
children: [first, second].map((idx) {
return Expanded(
flex: 1,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 10),
child: Image.network(imgList[idx], fit: BoxFit.cover),
),
);
}).toList(),
);
},
)),
);
}
}
License
MIT
GitHub
Source Code: A Beautiful Infinite Carousel Slider Flutter Widget, carousel_slider .