animated_hero_carousel 1.0.2 copy "animated_hero_carousel: ^1.0.2" to clipboard
animated_hero_carousel: ^1.0.2 copied to clipboard

A highly customizable and animated hero carousel for Flutter, designed to create engaging and visually appealing user interfaces.

example/lib/main.dart

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:animated_hero_carousel/animated_hero_carousel.dart';

// A simple data model for our movie items.
class Movie {
  final String title;
  final String posterUrl;
  final String backdropUrl;
  final String description;
  final String releaseYear;
  final double rating;

  const Movie({
    required this.title,
    required this.posterUrl,
    required this.backdropUrl,
    required this.description,
    required this.releaseYear,
    required this.rating,
  });
}

// Sample movie data using the provided image paths.
const List<Movie> movieData = [
  Movie(
    title: 'Final Destination: Bloodlines',
    posterUrl:
        'https://image.tmdb.org/t/p/w500/6WxhEvFsauuACfv8HyoVX6mZKFj.jpg',
    backdropUrl:
        'https://image.tmdb.org/t/p/original/6WxhEvFsauuACfv8HyoVX6mZKFj.jpg',
    description: 'The sixth installment in the Final Destination franchise.',
    releaseYear: '2025',
    rating: 7.5,
  ),
  Movie(
    title: 'Lilo & Stitch',
    posterUrl:
        'https://image.tmdb.org/t/p/w500/7c5VBuCbjZOk7lSfj9sMpmDIaKX.jpg',
    backdropUrl:
        'https://image.tmdb.org/t/p/original/7c5VBuCbjZOk7lSfj9sMpmDIaKX.jpg',
    description:
        'A tale of a young girl\'s close encounter with the galaxy\'s most wanted extraterrestrial.',
    releaseYear: '2002',
    rating: 7.3,
  ),
  Movie(
    title: 'The Twisters',
    posterUrl:
        'https://image.tmdb.org/t/p/w500/8OP3h80BzIDgmMNANVaYlQ6H4Oc.jpg',
    backdropUrl:
        'https://image.tmdb.org/t/p/original/8OP3h80BzIDgmMNANVaYlQ6H4Oc.jpg',
    description: 'A current-day chapter of the 1996 blockbuster, Twister.',
    releaseYear: '2024',
    rating: 6.8,
  ),
  Movie(
    title: 'Distant',
    posterUrl:
        'https://image.tmdb.org/t/p/w500/czh8HOhsbBUKoKsmRmLQMCLHUev.jpg',
    backdropUrl:
        'https://image.tmdb.org/t/p/original/czh8HOhsbBUKoKsmRmLQMCLHUev.jpg',
    description:
        'An asteroid miner crash-lands on an alien planet and must make his way across the harsh terrain.',
    releaseYear: '2024',
    rating: 5.9,
  ),
  Movie(
    title: 'First Shift',
    posterUrl:
        'https://image.tmdb.org/t/p/w500/ajsGI4JYaciPIe3gPgiJ3Vw5Vre.jpg',
    backdropUrl:
        'https://image.tmdb.org/t/p/original/ajsGI4JYaciPIe3gPgiJ3Vw5Vre.jpg',
    description:
        'As a new rookie, one of the cops has to apprehend a gang of violent criminals on his first night shift.',
    releaseYear: '2024',
    rating: 4.5,
  ),
  Movie(
    title: 'K-Pop: Demon Hunters',
    posterUrl:
        'https://image.tmdb.org/t/p/w500/jfS5KEfiwsS35ieZvdUdJKkwLlZ.jpg',
    backdropUrl:
        'https://image.tmdb.org/t/p/original/jfS5KEfiwsS35ieZvdUdJKkwLlZ.jpg',
    description:
        'A world-renowned K-Pop girl group secretly moonlights as demon hunters.',
    releaseYear: '2025',
    rating: 8.0,
  ),
  Movie(
    title: 'How to Train Your Dragon',
    posterUrl:
        'https://image.tmdb.org/t/p/w500/q5pXRYTycaeW6dEgsCrd4mYPmxM.jpg',
    backdropUrl:
        'https://image.tmdb.org/t/p/original/q5pXRYTycaeW6dEgsCrd4mYPmxM.jpg',
    description:
        'A hapless young Viking who aspires to hunt dragons becomes the unlikely friend of a young dragon himself.',
    releaseYear: '2010',
    rating: 7.9,
  ),
];

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Animated Hero Carousel Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: const Color(0xFF121212),
        primaryColor: Colors.blueAccent,
        tabBarTheme: TabBarThemeData(indicatorColor: Colors.blueAccent),
      ),
      home: const MovieCarouselExample(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Movie Carousels'),
          backgroundColor: const Color(0xFF121212),
          elevation: 0,
          bottom: const TabBar(
            tabs: [
              Tab(text: 'Featured'),
              Tab(text: 'Parallax'),
              Tab(text: 'Watchlist'),
            ],
          ),
        ),
        body: TabBarView(
          children: [
            _buildNetflixCarousel(),
            _buildParallaxCarousel(),
            _buildVerticalListCarousel(),
          ],
        ),
      ),
    );
  }

  /// Tab 1: A Netflix-inspired carousel.
  Widget _buildNetflixCarousel() {
    return Center(
      child: SizedBox(
        height: 380,
        child: AnimatedHeroCarousel(
          items: movieData,
          style: CarouselStyle.netflix(),
          showIndicators: true,
          indicatorType: IndicatorType.worm,
          itemBuilder: (context, movie, index, pageController) =>
              _buildPosterCard(movie),
          detailBuilder: (movie, index) => _buildDetailScreen(movie),
          heroTagBuilder: (movie, actualIndex, pageViewIndex) =>
              'netflix_${movie.title}_$actualIndex',
        ),
      ),
    );
  }

  /// Tab 2: A carousel demonstrating the parallax effect.
  Widget _buildParallaxCarousel() {
    return Center(
      child: SizedBox(
        height: 380,
        child: AnimatedHeroCarousel(
          items: movieData.reversed.toList(),
          showIndicators: true,
          indicatorType: IndicatorType.bar,
          viewportFraction: 0.7,
          itemBuilder: (context, movie, index, pageController) {
            return ParallaxMovieCard(
              movie: movie,
              pageController: pageController,
              pageIndex: index,
            );
          },
          detailBuilder: (movie, index) => _buildDetailScreen(movie),
          heroTagBuilder: (movie, actualIndex, pageViewIndex) =>
              'parallax_${movie.title}_$actualIndex',
        ),
      ),
    );
  }

  /// Tab 3: A redesigned vertical carousel that looks like a watchlist.
  Widget _buildVerticalListCarousel() {
    return Center(
      child: SizedBox(
        height: 600, // Increased height for a bigger watchlist
        child: Row(
          children: [
            Expanded(
              child: AnimatedHeroCarousel(
                items: movieData,
                scrollDirection: Axis.vertical,
                showIndicators: true, // Show indicators for vertical scroll
                indicatorType: IndicatorType.bar, // Bar indicators for vertical
                loop: false,
                spacing: 20.0,
                itemBuilder: (context, movie, index, pageController) =>
                    _buildWatchlistItem(movie),
                detailBuilder: (movie, index) => _buildDetailScreen(movie),
                heroTagBuilder: (movie, actualIndex, pageViewIndex) =>
                    'watchlist_${movie.title}_$actualIndex',
              ),
            ),
          ],
        ),
      ),
    );
  }

  /// The beautiful, reusable widget for displaying a movie poster.
  Widget _buildPosterCard(Movie movie) {
    return AspectRatio(
      aspectRatio: 2 / 3,
      child: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(16),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withValues(alpha: 0.5),
              blurRadius: 20,
              offset: const Offset(0, 10),
            ),
          ],
        ),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(16),
          child: Image.network(
            movie.posterUrl,
            fit: BoxFit.cover,
            loadingBuilder: (context, child, loadingProgress) {
              if (loadingProgress == null) return child;
              return const Center(child: CircularProgressIndicator());
            },
            errorBuilder: (context, error, stackTrace) {
              return const Center(child: Icon(Icons.error, color: Colors.red));
            },
          ),
        ),
      ),
    );
  }

  /// A new list item design for the vertical carousel.
  Widget _buildWatchlistItem(Movie movie) {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
      decoration: BoxDecoration(
        color: Colors.grey[900],
        borderRadius: BorderRadius.circular(12),
      ),
      child: Row(
        children: [
          SizedBox(
            width: 120,
            child: _buildPosterCard(movie),
          ), // Increased width
          const SizedBox(width: 20),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  movie.title,
                  style: const TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
                const SizedBox(height: 8),
                Row(
                  children: [
                    Icon(Icons.star, color: Colors.amber, size: 16),
                    const SizedBox(width: 4),
                    Text(
                      '${movie.rating}/10 IMDb',
                      style: TextStyle(
                        fontSize: 14,
                        color: Colors.white.withValues(alpha: 0.8),
                      ),
                    ),
                    const SizedBox(width: 16),
                    Text(
                      movie.releaseYear,
                      style: TextStyle(
                        fontSize: 14,
                        color: Colors.white.withValues(alpha: 0.8),
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 8),
                Text(
                  movie.description,
                  maxLines: 4, // Increased max lines
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(
                    fontSize: 14,
                    color: Colors.white.withValues(alpha: 0.7),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  /// A reusable widget to build the detail screen.
  Widget _buildDetailScreen(Movie movie) {
    return Scaffold(
      body: Stack(
        fit: StackFit.expand,
        children: [
          Image.network(movie.backdropUrl, fit: BoxFit.cover),
          ClipRRect(
            child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
              child: Container(color: Colors.black.withValues(alpha: 0.6)),
            ),
          ),
          SingleChildScrollView(
            child: Padding(
              padding: const EdgeInsets.all(24.0),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const SizedBox(height: 80),
                  _buildPosterCard(movie),
                  const SizedBox(height: 24),
                  Text(
                    movie.title,
                    textAlign: TextAlign.center,
                    style: const TextStyle(
                      color: Colors.white,
                      fontSize: 32,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 16),
                  Text(
                    movie.description,
                    textAlign: TextAlign.center,
                    style: TextStyle(
                      color: Colors.white.withValues(alpha: 0.8),
                      fontSize: 16,
                      height: 1.5,
                    ),
                  ),
                ],
              ),
            ),
          ),
          Positioned(
            top: 40,
            left: 16,
            child: CircleAvatar(
              backgroundColor: Colors.black.withValues(alpha: 0.5),
              child: BackButton(color: Colors.white),
            ),
          ),
        ],
      ),
    );
  }
}

/// A specialized movie card that creates a parallax and 3D effect as you scroll.
class ParallaxMovieCard extends StatefulWidget {
  final Movie movie;
  final PageController pageController;
  final int pageIndex;

  const ParallaxMovieCard({
    super.key,
    required this.movie,
    required this.pageController,
    required this.pageIndex,
  });

  @override
  ParallaxMovieCardState createState() => ParallaxMovieCardState();
}

class ParallaxMovieCardState extends State<ParallaxMovieCard> {
  double _offset = 0.0;

  @override
  void initState() {
    super.initState();
    widget.pageController.addListener(_updateOffset);
  }

  @override
  void dispose() {
    widget.pageController.removeListener(_updateOffset);
    super.dispose();
  }

  void _updateOffset() {
    if (widget.pageController.hasClients &&
        widget.pageController.position.hasContentDimensions) {
      setState(() {
        _offset = widget.pageController.page! - widget.pageIndex;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Transform.scale(
      scale: 1 - (0.2 * _offset.abs()),
      child: Opacity(
        opacity: 1 - (0.4 * _offset.abs()),
        child: _buildPosterCard(widget.movie),
      ),
    );
  }

  /// The beautiful, reusable widget for displaying a movie poster.
  Widget _buildPosterCard(Movie movie) {
    return AspectRatio(
      aspectRatio: 2 / 3,
      child: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(16),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withValues(alpha: 0.5),
              blurRadius: 20,
              offset: const Offset(0, 10),
            ),
          ],
        ),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(16),
          child: Image.network(
            movie.posterUrl,
            fit: BoxFit.cover,
            loadingBuilder: (context, child, loadingProgress) {
              if (loadingProgress == null) return child;
              return const Center(child: CircularProgressIndicator());
            },
            errorBuilder: (context, error, stackTrace) {
              return const Center(child: Icon(Icons.error, color: Colors.red));
            },
          ),
        ),
      ),
    );
  }
}
4
likes
155
points
17
downloads

Publisher

unverified uploader

Weekly Downloads

A highly customizable and animated hero carousel for Flutter, designed to create engaging and visually appealing user interfaces.

Homepage
Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on animated_hero_carousel