Alex
Alex

Reputation: 547

Dynamic height of card

I want to be able to have a ListView within a Card and the Card's height will be decided by the ListView's height (later on I will limit the list to x items so that it won't take the entire space if there are many items.

This is an example I wrote that illustrates the "challenge", see the comment below the ListView.builder():

import 'package:flutter/material.dart';

void main() => runApp(new MyExample());

class MyExample extends StatelessWidget {
  final List<int> _data = [1, 2, 3, 4, 5, 6, 7];

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new Scaffold(
        appBar: new AppBar(title: new Text('Example')),
        body: new Container(
          child: new Column(
            children: <Widget>[
              new Card(
                child: new SizedBox(
                  height: 100.0,
                  width: 440.0,
                  child: new Center(
                    child: new Text('First card'),
                  ),
                ),
              ),
              new Card(
                child: new SizedBox(
                  child: new ListView.builder(
                    itemBuilder: (BuildContext context, int index) =>
                        new MyExampleListItemWidget(_data[index]),
                    itemCount: _data.length,
                  ),
                  /// This is the one I want to be dynamic. The more items there are the more space it takes and the less space section 3 gets.
                  height: 100.0,
                ),
              ),
              new Flexible(
                child: new Center(
                  child: new Text('The third section'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class MyExampleListItemWidget extends StatelessWidget {
  final int _index;

  MyExampleListItemWidget(this._index) : super();

  @override
  Widget build(BuildContext context) {
    return new Padding(
        padding: new EdgeInsets.all(5.0),
        child: new Text('Text ' + _index.toString()));
  }
}

Upvotes: 0

Views: 6493

Answers (1)

TWL
TWL

Reputation: 6646

Okay, late answer, but incase people are still looking for some kind of solution. There are probably many ways to do this, but building off of the OPs sample/requirements, the key is to combo it with the ListView's shrinkWrap property. A simplified example:

Card(
  child: SizedBox(
    height: _boxHeight,
    child: ListView.builder(
      shrinkWrap: _shrinkWrap,
      controller: _controller,
      itemCount: _myList.length,
      itemBuilder: (context, index) {
        return _myList[index];
      }),
  )
),

In initState(), you will initiate the minimum height of this SizedBox based on some conditions like the minimum amount of items in the list:

if (_myList.length <= listMinSize) {
  _boxHeight = minBoxHeight;
  _shrinkWrap = false;
}

Then whenever the list grows, you will check the conditions to either enable shrinkWrap or to set the maximum height of the box.

setState(() {
  listChanged();

  if (_myList.length > listMaxSize) { // lock box height, disable shrinkWrap
    _boxHeight = maxBoxHeight;
    _shrinkWrap = false;
  } else if (_myList.length > listMinSize) { // unconstrain box height, enable shrinkWrap
    _boxHeight = null;
    _shrinkWrap = true;
  } // can also check if it ever returns to listMinSize
});

In short, this will let your Card/SizedBox start off at minBoxHeight, grow with the ListView until it reaches listMaxSize, locking in at maxBoxHeight, at which point it'll have a scrolling behavior.

Now, if you wanted to make sure that the min/max boxHeights are nicely aligned with the list items, then that's a whole other issue, where you'll need to calculate the list item widget sizes and their list offsets.

Upvotes: 1

Related Questions