fluid_carousel 0.1.0 copy "fluid_carousel: ^0.1.0" to clipboard
fluid_carousel: ^0.1.0 copied to clipboard

A modern, fluid, and extensible carousel widget for Flutter with zero-rebuild reactive indicators, 3D/parallax transformers, desktop gestures, and sliver support.

fluid_carousel 🌊 #

pub package likes popularity license

A modern, buttery-smooth, and extensible carousel widget for Flutter. Designed as a drop-in 10x upgrade to legacy carousel packages with zero-rebuild reactive indicators, Impeller-optimized 3D/parallax transformers, desktop & web gesture parity, and native Sliver support.


Most Flutter carousel packages were created during the Flutter 1.x/2.x eras and carry significant technical debt:

Feature carousel_slider fluid_carousel
Infinite Looping Artificial 10,000 index hack; duplicate key crashes when itemCount < 3 Seamless circular indexing with safe slot isolation
Indicators Requires parent setState() causing full page rebuilds Zero parent rebuilds; reactive ValueListenable with continuous sub-pixel morphing
Visual Transformers Hardcoded boolean enlargeCenterPage Pluggable transformers: Scale, 3D Coverflow, Parallax, Card Stack
Desktop & Web Clunky; mouse drag disabled by default Native trackpad flings, mouse drag, and keyboard arrow navigation
Impeller Ready Frequent unconstrained canvas repaints Built-in RepaintBoundary isolation for stable 120 FPS
Slivers Requires rigid SliverToBoxAdapter wrapping First-class SliverFluidCarousel

Getting Started #

Add fluid_carousel to your pubspec.yaml:

dependencies:
  fluid_carousel: ^0.1.0

Import the package in your Dart code:

import 'package:fluid_carousel/fluid_carousel.dart';

Quick Example #

import 'package:flutter/material.dart';
import 'package:fluid_carousel/fluid_carousel.dart';

class SimpleCarouselExample extends StatelessWidget {
  const SimpleCarouselExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FluidCarousel(
      itemCount: 5,
      options: const FluidCarouselOptions(
        viewportFraction: 0.85,
        autoPlay: true,
      ),
      itemBuilder: (context, index) {
        return Card(
          color: Colors.primaries[index % Colors.primaries.length],
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
          child: Center(
            child: Text(
              'Slide #$index',
              style: const TextStyle(fontSize: 24, color: Colors.white, fontWeight: FontWeight.bold),
            ),
          ),
        );
      },
    );
  }
}

3D & Cinematic Visual Transformers #

Switch between beautiful prebuilt visual effects with a single parameter or create your own custom transformer:

1. 3D Coverflow #

Perspective depth and Y-axis rotation:

FluidCarousel(
  itemCount: items.length,
  transformer: const FluidCoverflowTransformer(
    maxRotationAngle: 0.35,
    depthOffset: 60.0,
    perspective: 0.0015,
  ),
  itemBuilder: (context, index) => MyCard(items[index]),
)

2. Cinematic Parallax #

Inner card content translates in counter-direction to scroll:

FluidCarousel(
  itemCount: items.length,
  transformer: const FluidParallaxTransformer(
    parallaxFactor: 0.4,
    minScale: 0.9,
  ),
  itemBuilder: (context, index) => MyCard(items[index]),
)

3. Layered Card Stack / Deck #

Upcoming cards tuck behind the front card like an Apple Wallet deck:

FluidCarousel(
  itemCount: items.length,
  transformer: const FluidCardStackTransformer(
    stackOffset: 24.0,
    scaleStep: 0.08,
    maxVisibleCards: 3,
  ),
  itemBuilder: (context, index) => MyCard(items[index]),
)

4. Custom Transformer #

FluidCarousel(
  itemCount: items.length,
  transformer: FluidBuilderTransformer((context, child, info) {
    // info.position is 0.0 at center, 1.0 one page to the right, -1.0 to the left
    return Transform.scale(
      scale: 1.0 - (info.position.abs() * 0.2),
      child: child,
    );
  }),
  itemBuilder: (context, index) => MyCard(items[index]),
)

Zero-Rebuild Reactive Indicators #

Unlike traditional packages that require calling setState() inside onPageChanged, FluidIndicator connects directly to FluidCarouselController.pageOffset. It animates smoothly with sub-pixel precision while dragging—without rebuilding your parent widget tree.

class ScreenWithIndicator extends StatefulWidget {
  const ScreenWithIndicator({super.key});

  @override
  State<ScreenWithIndicator> createState() => _ScreenWithIndicatorState();
}

class _ScreenWithIndicatorState extends State<ScreenWithIndicator> {
  late final FluidCarouselController _controller;

  @override
  void initState() {
    super.initState();
    _controller = FluidCarouselController();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        FluidCarousel(
          controller: _controller,
          itemCount: 5,
          itemBuilder: (context, index) => MyCard(index),
        ),
        const SizedBox(height: 16),
        // Reactively morphs during user drag with 0 parent rebuilds!
        FluidIndicator(
          controller: _controller,
          count: 5,
          effect: const FluidWormEffect(
            activeColor: Colors.deepPurpleAccent,
            dotWidth: 10,
            dotHeight: 10,
          ),
        ),
      ],
    );
  }
}

Prebuilt Indicator Effects #

  • FluidWormEffect: Active dot stretches organically like an elastic worm between positions.
  • FluidExpandingDotsEffect: Active dot expands into an elegant pill shape while dragging.
  • FluidSlidingBarEffect: Modern progress bar track with continuous sliding thumb.

Programmatic Controls #

Manipulate the carousel programmatically via FluidCarouselController:

// Animate to specific page
await controller.animateToPage(3, duration: const Duration(milliseconds: 500));

// Snap to page immediately
controller.jumpToPage(1);

// Step next / previous
await controller.nextPage();
await controller.previousPage();

// Pause / Resume AutoPlay
controller.stopAutoPlay();
controller.startAutoPlay();

Native Sliver Integration #

Place your carousel directly in a CustomScrollView without layout friction:

CustomScrollView(
  slivers: [
    SliverAppBar(title: const Text('Store')),
    SliverFluidCarousel(
      extent: 240,
      itemCount: banners.length,
      itemBuilder: (context, index) => BannerTile(banners[index]),
    ),
    SliverList(delegate: SliverChildBuilderDelegate(...)),
  ],
)

API Reference #

FluidCarouselOptions #

Property Type Default Description
viewportFraction double 0.85 Fraction of viewport each card occupies
initialPage int 0 Initial starting item index
loop bool true Infinite circular looping (auto-disabled if itemCount <= 1)
autoPlay bool false Automatic timed page scrolling
autoPlayInterval Duration Duration(seconds: 4) Duration between auto-scroll steps
pauseAutoPlayOnTouch bool true Pauses auto-scroll on pointer drag
pauseAutoPlayOnHover bool true Pauses auto-scroll on mouse cursor hover
scrollDirection Axis Axis.horizontal Carousel orientation (horizontal or vertical)
enableMouseDrag bool true Enables desktop/web mouse dragging
enableKeyboardNavigation bool true Enables Left/Right arrow key navigation
clipBehavior Clip Clip.antiAlias Content clipping behavior
onPageChanged Function null Callback when settled page index changes

Contributing #

Contributions, issues, and feature requests are welcome! Feel free to check the issues page.

License #

This project is licensed under the MIT License - see the LICENSE file for details.

1
likes
160
points
323
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A modern, fluid, and extensible carousel widget for Flutter with zero-rebuild reactive indicators, 3D/parallax transformers, desktop gestures, and sliver support.

Repository (GitHub)
View/report issues

Topics

#carousel #slider #animation #ui #widget

License

MIT (license)

Dependencies

flutter

More

Packages that depend on fluid_carousel