Bill
Bill

Reputation: 527

Flutter dynamic height of ListView

I am developing a Flutter application and I want to dynamically adjust the height of the ListView.

I want the list to have a maximum height of 200. If the height is more than that, user will have to scroll to reach the bottom. If height is below 200, it will take only as much space as needed.

Preview of application: Screenshot. As you can see Restaurants nearby is pushed to the very bottom of the page. I want the ListView to only take height of 200 or less so that the content below isn't pushed to the bottom.

Here is my current code:

@override
Widget build(BuildContext context) {
  return Container(
    padding: EdgeInsets.all(10.0),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Text(
          'Restaurants nearby',
          style: TextStyle(
            fontSize: 20.0,
            fontWeight: FontWeight.bold,
          ),
        ),
        Divider(),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text('Enter restaurant manually'),
              onPressed: () {
                print('Button pressed');
              },
            ),
          ],
        ),
        Flexible(
          child: ListView.builder(
            itemBuilder: (BuildContext context, int index) {
              return ListTile(
                leading: CircleAvatar(
                  backgroundColor: Colors.cyan,
                ),
                title: Text('Test restaurant'),
                subtitle: Text('80m'),
              );
            },
            itemCount: 15,
          ),
        ),
        Text(
          'Restaurants nearby',
          style: TextStyle(
            fontSize: 20.0,
            fontWeight: FontWeight.bold,
          ),
        ),
      ],
    ),
  );
}

Upvotes: 31

Views: 54669

Answers (8)

Stefan S
Stefan S

Reputation: 741

What was preventing mine from sizing dynamically was having it inside a Scaffold. Once I removed the Scaffold it worked. I didn't have a maximum height requirement for mine so to make it size dynamically all I had to do was set shrinkWrap to true and put it inside a Flexible widget. eg.

Column(
  children: [
    Flexible(
      ListView.separated(shrinkWrap: true, ...)
    )
  ]
)

Upvotes: 0

Stefan Poppe
Stefan Poppe

Reputation: 39

I see that this question has never been answered only with giving a fixed height, so here is what works for me.

For some reason if you set the shrinkwrap to true it doesn't look like it is working but it does, the problem is in the padding settings of the ListView, set the padding to edgeinstets.zero. That fixes it for me.

  • Wrap inside a Flexible
  • ShrinkWrap true
  • Padding, zero
  • and if needed the column to MainAxisSize.min.

Hope it helps some people.

Example of my code:

Flexible(
                  child: Container(
                    decoration: BStyles.cardDecoration1,
                    child: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Column(
                        mainAxisSize: MainAxisSize.min,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          const Text(
                            'PRODUCTION TASKS',
                          ),
                          const SizedBox(
                            height: textVerticalSpacing,
                          ),
                          Flexible(
                            child: ListView.builder(
                              itemCount: recentTasks.length,
                              padding: EdgeInsets.zero,
                              physics: const NeverScrollableScrollPhysics(),
                              shrinkWrap: true,
                              itemBuilder: (BuildContext context, int index) {
                                return TaskCard(
                                  task: recentTasks[index],
                                  widthInfo: MediaQuery.of(context).size.width * 0.6,
                                );
                              },
                            ),
                          ),
                          const SizedBox(
                            height: itemSpacing,
                          ),
                          Align(
                            alignment: Alignment.centerRight,
                            child: InkWell(
                              onTap: () { },
                              child: const Text(
                                'View more',
                              ),
                            ),
                          ),
                          const SizedBox(
                            height: textVerticalSpacing,
                          ),
                        ],
                      ),
                    ),
                  ),
                ),

Upvotes: 3

Łukasz Garczynski
Łukasz Garczynski

Reputation: 367

You can always size the ListView container as % of the viewport (assuming of course that the other widgets also are sized in the same manner):

return Container(
      height: MediaQuery.of(context).size.height * 0.75,
      child: ListView.builder(
        itemBuilder: (ctx, index) {
          return Card(...

Upvotes: -1

Ahmed Mahran
Ahmed Mahran

Reputation: 81

Consider wrapping the ListView into this

LimitedBox(
  maxHeight: 200,
  child: Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      Flexible(
        child: ListView.builder(
          shrinkWrap: true,
          itemBuilder: (BuildContext context, int index) {
            return ListTile(
              leading: CircleAvatar(
                backgroundColor: Colors.cyan,
              ),
              title: Text('Test restaurant'),
              subtitle: Text('80m'),
            );
          },
          itemCount: _itemsCount,
        ),
      ),
    ]
  )
),

Note that:

  • shrinkWrap of ListView is set to true
  • mainAxisSize of Column is set to MainAxisSize.min
  • maxHeight of LimitedBox is set to 200

A complete snippet:

import 'package:flutter/material.dart';

class DebugWidget extends StatefulWidget {
  @override
  _DebugWidgetState createState() => _DebugWidgetState();
}
class _DebugWidgetState extends State<DebugWidget> {
  int _itemsCount = 1;

  @override
  Widget build(BuildContext context) {
    Widget child = Container(
      padding: EdgeInsets.all(10.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text(
            'Restaurants nearby',
            style: TextStyle(
              fontSize: 20.0,
              fontWeight: FontWeight.bold,
            ),
          ),
          Divider(),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              RaisedButton(
                child: Text('Enter restaurant manually'),
                onPressed: () {
                  print('Button pressed');
                },
              ),
              RaisedButton(
                child: Text('+1'),
                onPressed: () {
                  setState(() {
                    _itemsCount += 1;
                  });
                },
              ),
              RaisedButton(
                child: Text('-1'),
                onPressed: () {
                  setState(() {
                    _itemsCount -= 1;
                  });
                },
              ),
            ],
          ),
          LimitedBox(
            maxHeight: 200,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Flexible(
                  child: ListView.builder(
                    shrinkWrap: true,
                    itemBuilder: (BuildContext context, int index) {
                      return ListTile(
                        leading: CircleAvatar(
                          backgroundColor: Colors.cyan,
                        ),
                        title: Text('Test restaurant'),
                        subtitle: Text('80m'),
                      );
                    },
                    itemCount: _itemsCount,
                  ),
                ),
              ]
            )
          ),
          Text(
            'Restaurants nearby',
            style: TextStyle(
              fontSize: 20.0,
              fontWeight: FontWeight.bold,
            ),
          ),
        ],
      ),
    );

    return Scaffold(
      body: child,
    );
  }
}

Upvotes: 8

Payam Zahedi
Payam Zahedi

Reputation: 855

I found a solution to this problem. you should wrap your ListView with LimittedBox or ConstraintBox and give them maxHeight and set shrinkWrap property of ListView to true. the solution would be something like this.

LimitedBox(
          maxHeight: 200,
          child: ListView.builder(
            shrinkWrap: true,
            itemBuilder: (BuildContext context, int index) {
              return ListTile(
                leading: CircleAvatar(
                  backgroundColor: Colors.cyan,
                ),
                title: Text('Test restaurant'),
                subtitle: Text('80m'),
              );
            },
            itemCount: 15,
          ),
        ),

Upvotes: 6

diegoveloper
diegoveloper

Reputation: 103421

You are using Flexible widget, that's why your ListView expands. You have to change Flexible to ConstrainedBox and add shrinkWrap: true to your ListView.

ConstrainedBox(
  constraints: BoxConstraints(maxHeight: 200, minHeight: 56.0),
  child: ListView.builder(
    shrinkWrap: true,
      itemBuilder: (BuildContext context, int index) {
        return ListTile(
          leading: CircleAvatar(
            backgroundColor: Colors.cyan,
          ),
          title: Text('Test restaurant'),
          subtitle: Text('80m'),
        );
      },
    itemCount: 15,
  ),
),

More info here: https://api.flutter.dev/flutter/widgets/ConstrainedBox-class.html

Upvotes: 40

Dmitry Garazhny
Dmitry Garazhny

Reputation: 374

ConstrainedBox(
  constraints: BoxConstraints(maxHeight: 200.0),
  child: [your child here],
)

This make your child's height not bigger than 200.0

Upvotes: 1

Andrii Turkovskyi
Andrii Turkovskyi

Reputation: 29438

You can use LimitedBox

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Text(...),
    Divider(),
    Row(...),
    LimitedBox(
      maxHeight: 200.0,
      child: ListView.builder(...),
    ),
    Text(...),
  ],
),

Recommended solution in case when the incoming constraints are unbounded

Upvotes: 22

Related Questions