Search code examples
dartflutterflutter-layoutflutter-animationflutter-sliver

Horizontally scrollable cards with Snap effect in flutter


I want to create a list of cards scrolling horizontally with snap to fit effect when swiped either from left or right.

Each card has some spacing between them and fit to screen similar to below image

enter image description here

Apart from that these horizontally scrollable list elements should be contained inside a vertically scrollable list.

I all I am able to achieve is only displaying a list of horizontal scrolling cards after following example in flutter docs.

class SnapCarousel extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final title = 'Horizontal List';

    return MaterialApp(
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: Container(
          margin: EdgeInsets.symmetric(vertical: 20.0),
          height: 200.0,
          child: ListView(
            scrollDirection: Axis.horizontal,
            children: <Widget>[
              Container(
                width: 160.0,
                color: Colors.red,
              ),
              Container(
                width: 160.0,
                color: Colors.blue,
              ),
              Container(
                width: 160.0,
                color: Colors.green,
              ),
              Container(
                width: 160.0,
                color: Colors.yellow,
              ),
              Container(
                width: 160.0,
                color: Colors.orange,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Solution

  • Use PageView and ListView:

    import 'package:flutter/material.dart';
    
    main() => runApp(MaterialApp(home: MyHomePage()));
    
    class MyHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Carousel in vertical scrollable'),
          ),
          body: ListView.builder(
            padding: EdgeInsets.symmetric(vertical: 16.0),
            itemBuilder: (BuildContext context, int index) {
              if(index % 2 == 0) {
                return _buildCarousel(context, index ~/ 2);
              }
              else {
                return Divider();
              }
            },
          ),
        );
      }
    
      Widget _buildCarousel(BuildContext context, int carouselIndex) {
        return Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Text('Carousel $carouselIndex'),
            SizedBox(
              // you may want to use an aspect ratio here for tablet support
              height: 200.0,
              child: PageView.builder(
                // store this controller in a State to save the carousel scroll position
                controller: PageController(viewportFraction: 0.8),
                itemBuilder: (BuildContext context, int itemIndex) {
                  return _buildCarouselItem(context, carouselIndex, itemIndex);
                },
              ),
            )
          ],
        );
      }
    
      Widget _buildCarouselItem(BuildContext context, int carouselIndex, int itemIndex) {
        return Padding(
          padding: EdgeInsets.symmetric(horizontal: 4.0),
          child: Container(
            decoration: BoxDecoration(
              color: Colors.grey,
              borderRadius: BorderRadius.all(Radius.circular(4.0)),
            ),
          ),
        );
      }
    }