Create a Flutter Carousel Slider with dynamic heigth for each page

7,046

Solution 1

I finally found a solution to this with the help of runtime height determination. Link to runtime size determination question : How to get height of a Widget?

Just define a height variable and update it in Measure size's onChanged Function.

MeasureSizeRenderObject.dart

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

typedef void OnWidgetSizeChange(Size size);

class MeasureSizeRenderObject extends RenderProxyBox {
  Size oldSize;
  final OnWidgetSizeChange onChange;

  MeasureSizeRenderObject(this.onChange);

  @override
  void performLayout() {
    super.performLayout();

    Size newSize = child.size;
    if (oldSize == newSize) return;

    oldSize = newSize;
    WidgetsBinding.instance.addPostFrameCallback((_) {
      onChange(newSize);
    });
  }
}

class MeasureSize extends SingleChildRenderObjectWidget {
  final OnWidgetSizeChange onChange;

  const MeasureSize({
    Key key,
    @required this.onChange,
    @required Widget child,
  }) : super(key: key, child: child);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return MeasureSizeRenderObject(onChange);
  }
}

Import the above dart file and you are good to go.

In my case I had a list of days and a Map<String,List> of operational hours per day. So when ever a new operational hour was added the ListView length would grow in size.

List _days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

Map<String, List> _operationalHours = {};

I was populating operational hours from Firebase Cloud Firestore.

CarouselSlider Code :

CarouselSlider.builder(
            itemCount: days.length,
            itemBuilder: (BuildContext context, int index, int realIdx) {
              return Card(
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(20)),
                color: Color(0xff3b3b3b),
                child: Container(
                  width: displayWidth(context),
                  padding: EdgeInsets.all(15.0),
                  child: Column(
                    children: [
                      Text(
                        days[index],
                        style: TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.bold,
                            fontSize: 20),
                      ),
                      SizedBox(
                        height: 15,
                      ),
                      Column(
                        children: [
                           MeasureSize(
                                      onChange: (size) {
                                        height = size.height;
                                        print("log" + height.toString());
                                        setState(() {});
                                      },
                                      child: ListView.builder(
                                          shrinkWrap: true,
                                          itemCount: operationalHours[days[index]]
.length,
                                              itemBuilder: (context, i) {
                                                return getTimeSlot(
                                             operationalHours[days[index]][i]);
                                              }),
                                        )
                            ],
                          )
                        ],
                      ),
                    ),
                  );
                },
                options: new CarouselOptions(
                    autoPlay: false,
                    viewportFraction: 1.0,
                    height: 100 + height,
                    enlargeCenterPage: false,
                    enableInfiniteScroll: false,
                    onPageChanged: (index, reason) {
                      setState(() {
                        _current = index;
                      });
                    }),
              ),

If I made any typo or bug while copying my code from IDE to StackOverflow let me know. I will update the answer.

Solution 2

This is a known issue in the library. This is the answer from the creator of the lib Currently there is no way to define a dynamic height for each carousel item. But I think it can be done by creating a dynamic height container in the carousel item.

For the whole conversation have a look at the issue: https://github.com/serenader2014/flutter_carousel_slider/issues/106

Solution 3

this is an open issue of the library but you can still achieve partially by the below code.
carousel.dart

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

class CarouselWithIndicator extends StatefulWidget {
  final List cards;

  CarouselWithIndicator(this.cards);

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

class _CarouselWithIndicatorState extends State<CarouselWithIndicator> {
  List<T> map<T>(List list, Function handler) {
    List<T> result = [];
    for (var i = 0; i < list.length; i++) {
      result.add(handler(i, list[i]));
    }
    return result;
  }

  int _current = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Wrap(
          children: <Widget>[
            CarouselSlider(
              height: double.maxFinite,
              viewportFraction: 1.0,
              enlargeCenterPage: true,
              enableInfiniteScroll: false,
              onPageChanged: (index) {
                setState(() {
                  _current = index;
                });
              },
              items: widget.cards.map((card) {
                return Builder(
                  builder: (BuildContext context) {
                    return Container(
                      width: MediaQuery.of(context).size.width,
                      child: card,
                    );
                  },
                );
              }).toList(),
            ),
          ],
        ),
        SizedBox(
          height: 5,
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: map<Widget>(widget.cards, (index, card) {
            return Container(
              width: 10.0,
              height: 10.0,
              margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 3.0),
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                color: _current == index
                    ? Colors.grey
                    : Color.fromRGBO(200, 200, 200, 1),
              ),
            );
          }),
        ),
      ],
    );
  }
}

main.dart file

import 'package:flutter/material.dart';

import 'carousel.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Slider App',
      home: MyHomePage(),
    );
  }
}

class GeneralEvent {
  final String title;
  final String desc;
  GeneralEvent(this.title, this.desc);
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

final List<GeneralEvent> userEvents = [
  GeneralEvent(
    "List Item 1",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 2",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 3",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 4",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
  GeneralEvent(
    "List Item 5",
    "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
  ),
];

class _MyHomePageState extends State<MyHomePage> {
  List<Widget> cards = [
    Wrap(
      children: <Widget>[
        Card(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Text(
                  'Card 1',
                  style: TextStyle(fontSize: 24),
                ),
              ),
              Container(
                height: 1,
                width: double.infinity,
                color: Color.fromRGBO(0, 0, 0, 0.12),
              ),
              ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (ctx, index) {
                  return ListTile(
                    title: Text(
                      userEvents.sublist(0, 3)[index].title,
                    ),
                    subtitle: Text(userEvents.sublist(0, 3)[index].desc),
                  );
                },
                itemCount: userEvents.sublist(0, 3).length,
              ),
            ],
          ),
        ),
      ],
    ),
    Wrap(
      children: <Widget>[
        Card(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Text(
                  'Card 2',
                  style: TextStyle(fontSize: 24),
                ),
              ),
              Container(
                height: 1,
                width: double.infinity,
                color: Color.fromRGBO(0, 0, 0, 0.12),
              ),
              ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (ctx, index) {
                  return ListTile(
                    title: Text(
                      userEvents.sublist(3)[index].title,
                    ),
                    subtitle: Text(userEvents.sublist(3)[index].desc),
                  );
                },
                itemCount: userEvents.sublist(3).length,
              ),
            ],
          ),
        ),
      ],
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color.fromRGBO(235, 235, 235, 1),
      appBar: AppBar(title: Text('iWantASliderAPP')),
      body: Padding(
        padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
        child: LayoutBuilder(
          builder: (context, constraints) {
            return ListView(
              children: <Widget>[
                Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    CarouselWithIndicator(cards),
                  ],
                ),
                Padding(
                  padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
                  child: Text(
                    "if the Card is not in the slider it resize properly:",
                  ),
                ),
                cards[0]
              ],
            );
          },
        ),
      ),
    );
  }
}

I hope this would be somewhat helpful for you. :)

Share:
7,046
ovi_b
Author by

ovi_b

Updated on December 19, 2022

Comments

  • ovi_b
    ovi_b over 1 year

    I have a Carousel Slider with a dotted indicator as shown in this image

    Each page of the slider is a Card widget with a ListView as a child.

    If the Card is not inside the Carousel widget, it expands as the elements inside the Listview increase.

    I would like to maintain this behavior for each page of the slider, but when I put the cards inside the slider, they no longer resize based on the content, but seem to have a pre-defined height (even if I haven't specified any!).

    How could I dynamically update the height of the cards inside the slider?

    This is my code:

    main.dart file

    import 'package:flutter/material.dart';
    import 'carousel.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Slider App',
          home: MyHomePage(),
        );
      }
    }
    
    class GeneralEvent {
      final String title;
      final String desc;
      GeneralEvent(this.title, this.desc);
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    final List<GeneralEvent> userEvents = [
      GeneralEvent(
        "List Item 1",
        "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
      ),
      GeneralEvent(
        "List Item 2",
        "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
      ),
      GeneralEvent(
        "List Item 3",
        "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
      ),
      GeneralEvent(
        "List Item 4",
        "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
      ),
      GeneralEvent(
        "List Item 5",
        "Lorem ipsum dolor sit amet, consectetur adipisci elit.",
      ),
    ];
    
    class _MyHomePageState extends State<MyHomePage> {
      final List<Card> cards = [
        Card(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Text(
                  'Card 1',
                  style: TextStyle(fontSize: 24),
                ),
              ),
              Container(
                height: 1,
                width: double.infinity,
                color: Color.fromRGBO(0, 0, 0, 0.12),
              ),
              ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (ctx, index) {
                  return ListTile(
                    title: Text(
                      userEvents.sublist(0, 3)[index].title,
                    ),
                    subtitle: Text(userEvents.sublist(0, 3)[index].desc),
                  );
                },
                itemCount: userEvents.sublist(0, 3).length,
              ),
            ],
          ),
        ),
        Card(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Text(
                  'Card 2',
                  style: TextStyle(fontSize: 24),
                ),
              ),
              Container(
                height: 1,
                width: double.infinity,
                color: Color.fromRGBO(0, 0, 0, 0.12),
              ),
              ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemBuilder: (ctx, index) {
                  return ListTile(
                    title: Text(
                      userEvents.sublist(3)[index].title,
                    ),
                    subtitle: Text(userEvents.sublist(3)[index].desc),
                  );
                },
                itemCount: userEvents.sublist(3).length,
              ),
            ],
          ),
        ),
      ];
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Color.fromRGBO(235, 235, 235, 1),
          appBar: AppBar(title: Text('iWantASliderAPP')),
          body: Padding(
            padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
            child: LayoutBuilder(
              builder: (context, constraints) {
                return ListView(
                  children: <Widget>[
                    CarouselWithIndicator(cards),
                    Padding(
                      padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
                      child: Text(
                        "if the Card is not in the slider it resize properly:",
                      ),
                    ),
                    cards[0]
                  ],
                );
              },
            ),
          ),
        );
      }
    } 
    

    and carousel.dart

    import 'package:flutter/material.dart';
    import 'package:carousel_slider/carousel_slider.dart';
    
    class CarouselWithIndicator extends StatefulWidget {
      final List cards;
    
      CarouselWithIndicator(this.cards);
    
      @override
      _CarouselWithIndicatorState createState() => _CarouselWithIndicatorState();
    }
    
    class _CarouselWithIndicatorState extends State<CarouselWithIndicator> {
      List<T> map<T>(List list, Function handler) {
        List<T> result = [];
        for (var i = 0; i < list.length; i++) {
          result.add(handler(i, list[i]));
        }
        return result;
      }
    
      int _current = 0;
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            CarouselSlider(
              // height: // NO HEIGHT SPECIFIED!
              viewportFraction: 1.0,
              enlargeCenterPage: true,
              enableInfiniteScroll: false,
              onPageChanged: (index) {
                setState(() {
                  _current = index;
                });
              },
              items: widget.cards.map((card) {
                return Builder(
                  builder: (BuildContext context) {
                    return Container(
                      width: MediaQuery.of(context).size.width,
                      child: card,
                    );
                  },
                );
              }).toList(),
            ),
            SizedBox(
              height: 5,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: map<Widget>(widget.cards, (index, card) {
                return Container(
                  width: 10.0,
                  height: 10.0,
                  margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 3.0),
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: _current == index ? Colors.grey : Color.fromRGBO(200, 200, 200, 1),
                  ),
                );
              }),
            ),
          ],
        );
      }
    }