Flutter Image Slider with Dots and Autoplay: PageView, CarouselView or carousel_slider
The quickest Flutter image slider is the carousel_slider package (5.1.2): set autoPlay: true and add smooth_page_indicator 3.0.0 for dots. Want no dependency? Use PageView for full-width banners, or the built-in Material 3 CarouselView (Flutter 3.24+) for slides that peek at the next one. Analyzed code for all three is below, checked on 3 October 2026.

In a hurry? Jump to: which one to use · PageView with dots and autoplay · carousel_slider · CarouselView · network images · responsive · before/after slider · not working?
Which Flutter image slider should you use?
Three approaches cover almost every case, and a newer all-in-one package is listed too. Versions and dates below were checked on 3 October 2026; Flutter 3.47.6 (released 1 October 2026) is the current stable release.
| Approach | Autoplay | Dots | Pick it when |
|---|---|---|---|
| PageView (built in) | Timer + nextPage() | Your own, or a package | Full-width banners, no dependency |
| CarouselView (built in, Flutter 3.24+) | None built in | None built in | Material 3 cards that peek at the next |
| carousel_slider 5.1.2 | autoPlay: true | None built in | Least code for autoplay and looping |
| flutter_carousel_widget 4.0.1 | Built in | Built in | All-in-one, but needs Flutter 3.47+ |
Short version: carousel_slider for the least code (it is the most downloaded carousel package in this comparison, about 917,000 downloads in 30 days), PageView when you do not want a package to maintain, and CarouselView when the design is Material 3 and slides should peek at the next one. Check any older package before adopting it: card_swiper has had no release since July 2023, and carousel_pro, still recommended by older tutorials, was last released in June 2019 and will not install on Dart 3.
Looking for a draggable value slider (volume, brightness) instead? That is Flutter's Slider widget, not an image carousel. Our SeekBar tutorial covers the Android equivalent.
How to make a Flutter image slider with PageView (dots and autoplay)
This version has no dependency and works as a self-advancing slideshow. It loops forward forever, pauses while a finger is on the slider, cancels its Timer when the screen goes away, and keeps the dots in sync. Paste it into lib/main.dart and run it on a device or emulator; the Android Studio emulator is covered in our Android emulator guide, and there is an Apple Silicon Mac version.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(
const MaterialApp(
home: Scaffold(
body: SafeArea(child: Center(child: PageViewSlider())),
),
),
);
const _slides = <String>[
'https://picsum.photos/id/1015/1200/675',
'https://picsum.photos/id/1016/1200/675',
'https://picsum.photos/id/1018/1200/675',
];
class PageViewSlider extends StatefulWidget {
const PageViewSlider({super.key});
@override
State<PageViewSlider> createState() => _PageViewSliderState();
}
class _PageViewSliderState extends State<PageViewSlider> {
// Start past page 0 so the user can also swipe backwards around the loop.
final _controller = PageController(
viewportFraction: 0.9,
initialPage: _slides.length * 100,
);
Timer? _timer;
int _dot = 0;
@override
void initState() {
super.initState();
_startAutoplay();
}
void _startAutoplay() {
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 3), (_) {
if (_controller.hasClients) {
_controller.nextPage(
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
}
});
}
@override
void dispose() {
_timer?.cancel(); // without this the timer outlives the screen
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
AspectRatio(
aspectRatio: 16 / 9,
child: Listener(
// Pause while a finger is down, resume when it lifts.
onPointerDown: (_) => _timer?.cancel(),
onPointerUp: (_) => _startAutoplay(),
onPointerCancel: (_) => _startAutoplay(),
child: PageView.builder(
controller: _controller,
onPageChanged: (page) =>
setState(() => _dot = page % _slides.length),
itemBuilder: (context, page) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
_slides[page % _slides.length],
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
const Center(child: Icon(Icons.broken_image_outlined)),
),
),
),
),
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var i = 0; i < _slides.length; i++)
AnimatedContainer(
duration: const Duration(milliseconds: 250),
margin: const EdgeInsets.symmetric(horizontal: 4),
width: i == _dot ? 20 : 8,
height: 8,
decoration: BoxDecoration(
color: i == _dot
? Theme.of(context).colorScheme.primary
: Colors.grey.shade400,
borderRadius: BorderRadius.circular(4),
),
),
],
),
],
);
}
}
- Looping.
itemCountis left null and each slide is picked withpage % _slides.length, so autoplay always moves forward instead of rewinding through every slide. Starting at_slides.length * 100lets people swipe backwards too. - Pausing. The
Listenercancels the timer when a finger touches down and restarts it on release. carousel_slider does this for you. - Cleanup.
dispose()cancels the timer. Without that line the periodic timer keeps firing after the screen is gone.
carousel_slider: CarouselSlider with autoplay and dots
Add both packages with flutter pub add carousel_slider smooth_page_indicator, which picks versions your SDK supports. As of 3 October 2026 the latest are carousel_slider 5.1.2 (published 5 February 2026) and smooth_page_indicator 3.0.0 (21 August 2026).
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
const _slides = <String>[
'https://picsum.photos/id/1015/1200/675',
'https://picsum.photos/id/1016/1200/675',
'https://picsum.photos/id/1018/1200/675',
];
class PackageSlider extends StatefulWidget {
const PackageSlider({super.key});
@override
State<PackageSlider> createState() => _PackageSliderState();
}
class _PackageSliderState extends State<PackageSlider> {
final _controller = CarouselSliderController(); // not CarouselController
final _index = ValueNotifier<int>(0); // only the dots rebuild, not the slider
@override
void dispose() {
_index.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
CarouselSlider.builder(
carouselController: _controller,
itemCount: _slides.length,
itemBuilder: (context, index, pageViewIndex) => ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
_slides[index],
fit: BoxFit.cover,
width: double.infinity,
errorBuilder: (context, error, stackTrace) =>
const Center(child: Icon(Icons.broken_image_outlined)),
),
),
options: CarouselOptions(
aspectRatio: 16 / 9,
viewportFraction: 0.9,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 3),
enlargeCenterPage: true,
onPageChanged: (index, reason) => _index.value = index,
),
),
const SizedBox(height: 12),
ValueListenableBuilder<int>(
valueListenable: _index,
builder: (context, active, _) => AnimatedSmoothIndicator(
activeIndex: active,
count: _slides.length,
onDotClicked: (index) => _controller.animateToPage(index),
effect: const WormEffect(
dotWidth: 8,
dotHeight: 8,
dotColor: Colors.black26,
activeDotColor: Colors.indigo,
),
),
),
],
);
}
}
Details that trip people up:
- Use
CarouselSliderController, notCarouselController. Version 5.0.0 (August 2024) renamed it, days after Flutter 3.24 added its ownCarouselControllerfor CarouselView. Code written for carousel_slider 4.x that usesCarouselControllerfails on Flutter 3.24 or newer with an ambiguous-import error. - Keep the active index in a
ValueNotifier. carousel_slider creates a new internalPageControllerwhenever its parent rebuilds, sosetStateinsideonPageChangedreplaces the controller on every slide change. The notifier rebuilds only the dots. - Set the dot colors yourself. smooth_page_indicator 3.0.0 no longer reads the app theme; its fallback colors are fixed indigo and gray. Pass
activeDotColoranddotColorto the effect. heightbeatsaspectRatio. Set both andaspectRatiois ignored.
Defaults worth knowing in 5.1.2: viewportFraction 0.8, aspectRatio 16/9, autoPlayInterval 4 seconds, autoPlayAnimationDuration 800 ms, enableInfiniteScroll true, pauseAutoPlayOnTouch true, enlargeCenterPage false. The dots_indicator package (5.0.0, 16 September 2026) also draws dots, but it needs Flutter 3.44 or newer.
Maintenance: the last release, 5.1.2 on 5 February 2026, fixed a PageController leak. The repository is not archived and lists 216 open issues and pull requests (checked 3 October 2026).
Material 3 CarouselView: the built-in carousel
CarouselView lives in Flutter's Material library, so it needs no package. CarouselView.weighted sizes slides by proportion: weights of [7, 1] give one big slide and a peek of the next at any screen width. The example switches to [3, 3, 3, 1] from 700 dp up.
import 'package:flutter/material.dart';
const _slides = <String>[
'https://picsum.photos/id/1015/1200/675',
'https://picsum.photos/id/1016/1200/675',
'https://picsum.photos/id/1018/1200/675',
];
class MaterialCarousel extends StatelessWidget {
const MaterialCarousel({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
// Weights are proportions, so the slides scale with the screen.
final wide = constraints.maxWidth >= 700;
return ConstrainedBox(
// CarouselView scrolls, so it needs a bounded height.
constraints: BoxConstraints(maxHeight: wide ? 240 : 220),
child: CarouselView.weighted(
flexWeights: wide ? const [3, 3, 3, 1] : const [7, 1],
itemSnapping: true,
onTap: (index) => debugPrint('Tapped slide $index'),
children: [
for (final url in _slides)
Image.network(
url,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) =>
const Center(child: Icon(Icons.broken_image_outlined)),
),
],
),
);
},
);
}
}
Which Flutter release added what, from the framework source at each release tag and the official release notes:
| Flutter release | CarouselView gained |
|---|---|
| 3.24 (6 Aug 2024) | CarouselView with a fixed itemExtent, plus CarouselController |
| 3.27 (11 Dec 2024) | CarouselView.weighted and flexWeights |
| 3.32 (20 May 2025) | CarouselController.animateToItem |
| 3.41 (11 Feb 2026) | CarouselView.builder and .weightedBuilder |
| 3.44 (18 May 2026) | infinite, onIndexChanged, CarouselController.leadingItem |
- No autoplay, no dots. For autoplay, call
CarouselController.animateToItemfrom aTimer. For dots,onIndexChangedreports the leading (first visible) item and fires only once that item has fully left the viewport, so it is most useful with a hero layout such as[7, 1]or[1]. - One full-width slide per page: use weights of
[1]. - Bounded height required. CarouselView scrolls, so inside a Column it throws the same unbounded-height error as PageView. The example wraps it in
ConstrainedBox.
Heads-up: Material is moving into its own package. Flutter 3.47 (12 August 2026) lets you opt in to package:material_ui/material_ui.dart. Flutter's migration guide says the in-SDK Material library has been frozen since 3.44 and is scheduled for formal deprecation in an upcoming stable release. The snippets here use package:flutter/material.dart, which Flutter 3.47.6 still ships; to migrate later, run dart fix --apply --code=migrate_design_widgets.
Network images, assets and caching
All three sliders accept any widget. Use Image.network for URLs and Image.asset for bundled files, which you list under flutter: assets: in pubspec.yaml. Keep an errorBuilder on network images, as the examples do, and add a loadingBuilder if you want a spinner. For slides that reappear often, cached_network_image keeps them on disk:
import 'package:cached_network_image/cached_network_image.dart';
// In an itemBuilder, instead of Image.network:
CachedNetworkImage(
imageUrl: url,
fit: BoxFit.cover,
placeholder: (context, url) =>
const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
const Center(child: Icon(Icons.broken_image_outlined)),
)
Version note: cached_network_image 4.0 (August 2026) requires Flutter 3.44 or newer and Dart 3.12, because it now depends on material_ui. On an older SDK, flutter pub add cached_network_image falls back to 3.4.1 (August 2024), which uses the same API as the snippet above. The photo URLs in the examples are placeholders so the code shows pictures on first run; replace them with your own.
Make a Flutter image slider responsive
Size slides by ratio and let the width come from the layout:
- PageView: wrap it in
AspectRatio(aspectRatio: 16 / 9).PageController.viewportFractionis final, so changing it at a breakpoint means creating a new controller. - carousel_slider: set
aspectRatioinstead ofheight, and changeviewportFractionfrom aLayoutBuilder. The package re-applies both when they change, including on window resize. - CarouselView.weighted: weights are proportions, so slides already scale with the width. Switch the weights at a breakpoint with
LayoutBuilder, as in the example above.
Before and after (image compare) slider in Flutter
A compare slider is a different widget from a carousel: two images are stacked and a draggable divider decides how much of the top one shows. This version uses only Flutter widgets. The top image is clipped to a fraction of the width that follows the finger.
import 'package:flutter/material.dart';
/// Before/after slider: drag the handle to reveal more of one image.
class CompareSlider extends StatefulWidget {
const CompareSlider({super.key, required this.before, required this.after});
final ImageProvider before;
final ImageProvider after;
@override
State<CompareSlider> createState() => _CompareSliderState();
}
class _CompareSliderState extends State<CompareSlider> {
double _split = 0.5; // share of the width that shows "before"
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 16 / 9,
child: LayoutBuilder(
builder: (context, box) => GestureDetector(
onHorizontalDragUpdate: (details) => setState(() {
_split = (details.localPosition.dx / box.maxWidth).clamp(0.0, 1.0);
}),
child: Stack(
fit: StackFit.expand,
children: [
Image(image: widget.after, fit: BoxFit.cover),
ClipRect(
clipper: _SplitClipper(_split),
child: Image(image: widget.before, fit: BoxFit.cover),
),
Positioned(
left: box.maxWidth * _split - 18,
top: 0,
bottom: 0,
width: 36,
child: Stack(
alignment: Alignment.center,
children: [
Container(width: 3, color: Colors.white),
const CircleAvatar(
radius: 18,
backgroundColor: Colors.white,
child: Icon(Icons.compare_arrows, color: Colors.black87),
),
],
),
),
],
),
),
),
);
}
}
class _SplitClipper extends CustomClipper<Rect> {
const _SplitClipper(this.split);
final double split;
@override
Rect getClip(Size size) =>
Rect.fromLTWH(0, 0, size.width * split, size.height);
@override
bool shouldReclip(_SplitClipper oldClipper) => oldClipper.split != split;
}
Use it as CompareSlider(before: AssetImage('assets/before.jpg'), after: AssetImage('assets/after.jpg')). If you need zoom, pan or rotation, image_compare_slider 3.2.0 (30 June 2026) offers them according to its pub.dev listing; before_after 3.2.0 dates from November 2024.
Why is my Flutter image slider not working?
Most failures come down to a handful of causes. Messages were reproduced on Flutter 3.38.1 unless noted.
| What you see | Cause | Fix |
|---|---|---|
Horizontal viewport was given unbounded height | PageView or CarouselView in a Column | Wrap in AspectRatio or SizedBox |
The name 'CarouselController' is defined in the libraries… | carousel_slider 4.x on Flutter 3.24+ | Use 5.x and CarouselSliderController |
| Images blank in a release build or on Android 9+ | No INTERNET permission, or an http:// URL | Add the permission to the main manifest; use https |
PageController.page cannot be accessed before a PageView is built with it | Timer outlived the screen | Cancel it in dispose() |
| Will not scroll with a mouse (web, desktop) | Mouse is not a default drag device | Set scrollBehavior (below) |
carousel_pro… doesn't support null safety from pub add | Older tutorial, 2019 package | Use carousel_slider 5.x or PageView |
| Dots out of step with the slide | Using the raw page number | Use page % length or the onPageChanged index |
| Dot colors changed after an upgrade | smooth_page_indicator 3.0.0 ignores the app theme | Set dotColor and activeDotColor |
The named parameter 'infinite' isn't defined, or pub add fails | Flutter older than the API or package needs | Upgrade Flutter; let flutter pub add choose versions |
Assertion firstIndex == 0 || childScrollOffset… on some Android phones | Open reports: infinite scroll with viewportFraction below 1 | Try enableInfiniteScroll: false; not reproduced here |
Two notes on the table. The Android permission is easy to miss because debug and profile manifests in a new Flutter project already declare INTERNET while the main manifest does not, and Android 9 (API 28) blocks cleartext HTTP by default. With enableInfiniteScroll: false, carousel_slider autoplay sweeps back through the slides to the first one instead of looping forward. For the mouse fix, set the scroll behavior once on MaterialApp:
// needs: import 'package:flutter/gestures.dart';
MaterialApp(
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {PointerDeviceKind.touch, PointerDeviceKind.mouse},
),
home: const MyHomePage(),
)
How we picked and verified this
The three approaches were compared on four criteria: it works on the current Flutter release, it is maintained, it needs few or no dependencies, and it handles dots and autoplay without hacks. What was checked on 3 October 2026:
- Versions and maintenance. Package versions, release dates and SDK limits came from pub.dev's public data, together with the carousel_slider changelog, source and issue tracker.
- Flutter itself. The current stable release (3.47.6, 1 October 2026) came from Flutter's release feed. The CarouselView history comes from the framework source at the 3.24.0, 3.27.0, 3.32.0, 3.41.0, 3.44.0 and 3.47.6 tags and the official release notes. The Material change comes from Flutter's own migration page.
- Code. Every snippet was analyzed with
flutter analyze(no issues) on Flutter 3.38.1 and Dart 3.10.0 and exercised in automated widget tests: autoplay timing, dot sync and looping, pause on touch, timer cleanup, a phone-to-tablet resize and the compare-slider drag. The error messages in the table were reproduced on the same SDK. - Limits. Nothing was run on a physical phone or emulator, and nothing was compiled against Flutter 3.47.
CarouselView.builder,infinite,onIndexChanged, cached_network_image 4.x, dots_indicator 5.0 and flutter_carousel_widget 4.0 are described from Flutter's source, package changelogs and pub.dev data only. The Android assertion in the last table row is reported by other developers and was not reproduced here.
Bottom line
For a home-screen banner with autoplay and dots, use carousel_slider 5.1.2 with smooth_page_indicator 3.0.0 if you want the least code, or the PageView version if you would rather not depend on a package. Choose CarouselView.weighted when the design is Material 3 and slides should peek at the next one. Whichever you pick, give the slider a bounded height, cancel any Timer in dispose(), and add the INTERNET permission before shipping a release build that loads network images.
Frequently Asked Questions
How do I make an image slider in Flutter?
Use PageView, which is built in, or a carousel package. For the fastest route run flutter pub add carousel_slider, then wrap your images in CarouselSlider with autoPlay: true; carousel_slider 5.1.2 (5 February 2026) is the current release. Without a package, put the images in a PageView.builder inside an AspectRatio and drive the dots from onPageChanged. Complete, analyzed code for both is above.
How do I add a dots indicator to a Flutter image slider?
Neither carousel_slider nor CarouselView draws dots. With carousel_slider, keep the index from onPageChanged in a ValueNotifier and show smooth_page_indicator 3.0.0's AnimatedSmoothIndicator. With PageView, set the active dot from onPageChanged using page % length. From smooth_page_indicator 3.0.0 (21 August 2026), set dotColor and activeDotColor yourself, because it no longer reads the app theme.
PageView, CarouselView or carousel_slider: which should you use for an image slider?
Use carousel_slider 5.1.2 for the least code with autoplay and looping, PageView for full-width pages with no dependency, and CarouselView (Flutter 3.24+) for Material 3 layouts where slides peek at the next one. CarouselView has no autoplay or dots built in. flutter_carousel_widget 4.0.1 includes both but needs Flutter 3.47 or newer.
How do I make a Flutter image slider scroll automatically?
With carousel_slider set autoPlay: true and autoPlayInterval; it pauses on touch by default. With PageView use a Timer.periodic that calls PageController.nextPage, cancel it on pointer down, and cancel it again in dispose(). CarouselView has no autoplay, so call CarouselController.animateToItem from a Timer; that method needs Flutter 3.32 or newer.
Why is my Flutter image slider not working?
The usual causes are no bounded height (PageView throws 'Horizontal viewport was given unbounded height'), a missing Android INTERNET permission in release builds, http:// image URLs blocked on Android 9 and newer, carousel_slider 4.x clashing with Flutter's CarouselController (use 5.x and CarouselSliderController), or mouse dragging being off by default on web and desktop. The fix table above covers each.
Does Flutter have a built-in carousel widget?
Yes. CarouselView is a Material 3 widget that arrived in Flutter 3.24 on 6 August 2024. CarouselView.weighted followed in 3.27, CarouselView.builder in 3.41, and infinite looping plus onIndexChanged in 3.44. It has no autoplay or dot indicator. As of 3 October 2026 the current stable release is Flutter 3.47.6.
How do I make a responsive image slider in Flutter?
Size the slider by ratio, not pixels: wrap PageView in AspectRatio(aspectRatio: 16 / 9) or set CarouselOptions(aspectRatio: 16 / 9) without also setting height. With CarouselView.weighted the flexWeights are proportions, so slides scale with the screen width; switch them with LayoutBuilder, for example [7, 1] on phones and [3, 3, 3, 1] from 700 dp up.
How do I build a before and after image slider in Flutter?
Stack two images, clip the top one to a fraction of the width with ClipRect and a CustomClipper, and update that fraction from onHorizontalDragUpdate. The CompareSlider code above does this in about 70 lines with no package. Packages such as image_compare_slider 3.2.0 (30 June 2026) add zoom and rotation if you need them.





