animated_cycler 1.0.4

SDKflutter
Platformandroidioswindowslinuxmacosweb

A versatile Flutter widget that automatically cycles through items with smooth bidirectional animations. Perfect for tickers, banners, news feeds, and rotating content.

AnimatedCycler

pub package License: MIT Platform: Flutter

A versatile Flutter widget that automatically cycles through a list of items with smooth animations. Perfect for creating rolling tickers, rotating banners, news feeds, and any content that needs to be displayed in a cycling manner.

✨ Features

  • Bidirectional Support: Both vertical and horizontal animation directions
  • Generic Type Safe: Works with any data type using Dart generics
  • Smooth Animations: Customizable animation curves and durations
  • Auto-play Control: Start, stop, pause, and resume functionality
  • Manual Navigation: Programmatically navigate to next, previous, or specific items
  • Flexible Styling: Complete control over item appearance through custom builders
  • Performance Optimized: Efficient rendering with minimal widget rebuilds
  • Easy Integration: Simple API with sensible defaults

📱 Demo

Vertical ranking ticker

Horizontal banner slider

Custom animations

🚀 Getting Started

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  animated_cycler: ^1.0.0

Then run:

flutter pub get

Import

import 'package:animated_cycler/animated_cycler.dart';

📱 Usage Examples

Basic Vertical Ticker

Perfect for rankings, news headlines, or notifications:

AnimatedCycler<String>(
  direction: Axis.vertical,
  items: [
    '🥇 John - 1,250 points',
    '🥈 Sarah - 1,180 points',
    '🥉 Mike - 1,150 points',
    '4️⃣ Emma - 1,100 points',
  ],
  size: 60,
  itemBuilder: (context, item, index) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 16),
      alignment: Alignment.centerLeft,
      decoration: BoxDecoration(
        color: Colors.blue.shade50,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Text(
        item,
        style: TextStyle(
          fontSize: 16,
          fontWeight: FontWeight.w500,
          color: Colors.blue.shade700,
        ),
      ),
    );
  },
)

Horizontal Banner Slider

Great for advertisements, promotions, or product showcases:

AnimatedCycler<Product>(
  direction: Axis.horizontal,
  items: productList,
  size: 300,
  displayDuration: Duration(seconds: 4),
  animationDuration: Duration(milliseconds: 600),
  itemBuilder: (context, product, index) {
    return Card(
      elevation: 4,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.network(product.imageUrl, height: 120),
          SizedBox(height: 8),
          Text(
            product.name,
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
          Text(
            '\$${product.price}',
            style: TextStyle(fontSize: 16, color: Colors.green),
          ),
        ],
      ),
    );
  },
  onItemTap: (product, index) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => ProductDetailPage(product: product),
      ),
    );
  },
)

Using Named Constructors

For cleaner code, use the direction-specific constructors:

// Vertical ticker
AnimatedCycler.vertical(
  items: notifications,
  height: 50,
  itemBuilder: (context, notification, index) {
    return NotificationTile(notification: notification);
  },
)

// Horizontal slider
AnimatedCycler.horizontal(
  items: banners,
  width: 350,
  itemBuilder: (context, banner, index) {
    return BannerCard(banner: banner);
  },
)

Advanced Usage with Manual Control

class _MyPageState extends State<MyPage> {
  final GlobalKey<AnimatedCyclerState> _cyclerKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        AnimatedCycler<String>(
          key: _cyclerKey,
          items: ['Item 1', 'Item 2', 'Item 3'],
          autoPlay: false, // Manual control
          itemBuilder: (context, item, index) {
            return Center(child: Text(item));
          },
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            ElevatedButton(
              onPressed: () => _cyclerKey.currentState?.previousItem(),
              child: Icon(Icons.arrow_back),
            ),
            ElevatedButton(
              onPressed: () => _cyclerKey.currentState?.togglePlayPause(),
              child: Icon(Icons.play_pause),
            ),
            ElevatedButton(
              onPressed: () => _cyclerKey.currentState?.nextItem(),
              child: Icon(Icons.arrow_forward),
            ),
          ],
        ),
      ],
    );
  }
}

🎛️ API Reference

Constructor Parameters

ParameterTypeDefaultDescription
itemsList<T>requiredList of items to cycle through
itemBuilderWidget Function(BuildContext, T, int)requiredBuilder function for rendering each item
directionAxisAxis.verticalAnimation direction (vertical/horizontal)
displayDurationDuration3 secondsHow long each item is displayed
animationDurationDuration800msDuration of transition animation
sizedouble50.0Widget size (height for vertical, width for horizontal)
autoPlaybooltrueWhether to automatically cycle through items
animationCurveCurveCurves.easeInOutAnimation curve for transitions
loopbooltrueWhether to loop back to first item after last
onItemTapFunction(T, int)?nullCallback when an item is tapped
onAnimationCompleteFunction(T, int)?nullCallback when animation completes

Named Constructors

AnimatedCycler.vertical()

Optimized for vertical content like rankings, news feeds, notifications.

AnimatedCycler.horizontal()

Optimized for horizontal content like banners, product carousels, image sliders.

State Methods

Access these methods through a GlobalKey<AnimatedCyclerState>:

MethodDescription
nextItem()Move to next item manually
previousItem()Move to previous item manually
goToIndex(int index)Jump to specific item by index
togglePlayPause()Toggle auto-play on/off
currentIndexGet current item index
currentItemGet current item data
isPlayingCheck if auto-play is active

🎨 Customization Examples

Custom Animation Curves

AnimatedCycler<String>(
  items: ['Fast', 'Smooth', 'Bouncy'],
  animationCurve: Curves.bounceOut,
  animationDuration: Duration(milliseconds: 1200),
  itemBuilder: (context, item, index) {
    return Center(child: Text(item));
  },
)

Different Display Durations

AnimatedCycler<String>(
  items: ['Quick', 'Normal', 'Slow'],
  displayDuration: Duration(seconds: 5), // Longer display time
  itemBuilder: (context, item, index) {
    return Center(child: Text(item));
  },
)

Responsive Sizing

AnimatedCycler<String>(
  items: data,
  size: MediaQuery.of(context).size.width * 0.8, // 80% of screen width
  direction: Axis.horizontal,
  itemBuilder: (context, item, index) {
    return ResponsiveCard(data: item);
  },
)

🧪 Testing

To test widgets that use AnimatedCycler:

testWidgets('AnimatedCycler displays items correctly', (WidgetTester tester) async {
  final items = ['Test 1', 'Test 2', 'Test 3'];
  
  await tester.pumpWidget(
    MaterialApp(
      home: Scaffold(
        body: AnimatedCycler<String>(
          items: items,
          autoPlay: false, // Disable for testing
          itemBuilder: (context, item, index) {
            return Text(item, key: Key('item_$index'));
          },
        ),
      ),
    ),
  );

  // Verify first item is displayed
  expect(find.text('Test 1'), findsOneWidget);
  expect(find.text('Test 2'), findsNothing);
});

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

  1. Fork the repository
  2. Clone your fork: git clone https://github.com/BottlePumpkin/animated_cycler.git
  3. Create a feature branch: git checkout -b feature/amazing-feature
  4. Make your changes and add tests
  5. Run tests: flutter test
  6. Commit your changes: git commit -m 'Add amazing feature'
  7. Push to the branch: git push origin feature/amazing-feature
  8. Open a Pull Request

📄 License

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

🙏 Acknowledgments

  • Inspired by ticker displays and carousel components
  • Built with ❤️ using Flutter
  • Thanks to the Flutter community for feedback and suggestions

📞 Support

If you have any questions or issues, please:

  1. Check the example directory for usage examples
  2. Search existing issues
  3. Create a new issue if needed

⭐ If this package helped you, please give it a star on GitHub and a like on pub.dev!