Martin
Martin

Reputation: 2914

Flutter: How to make ListView to occupy available parent height

I'm getting error after showing ListView. What I want to achieve is 'PlacesControls' widget is fixed size and rest of it should occupy remaining space below. So I wrapped my ListView with Expanded to achieve that but now I'm getting this error.

Error: The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type FlexParentData to a RenderObject, which has been set up to accept ParentData of incompatible type BoxParentData.

Page:

BlocProvider<PlacesBloc> buildBody(BuildContext context) {
    return BlocProvider(
      builder: (_) => sl<PlacesBloc>(),
      child: Center(
        child: Padding(
            padding: const EdgeInsets.all(10),
            child: Expanded(
              child: Column(
                children: <Widget>[
                  // Top view
                  SizedBox(height: 20),
                  PlacesControls(),
                  // Bottom view
                  BlocBuilder<PlacesBloc, PlacesState>(
                    builder: (context, state) {
                      if (state is Empty) {
                        return MessageDisplay(
                          message: 'Search places around you!',
                        );
                      } else if (state is Loading) {
                        return LoadingWidget();
                      } else if (state is Loaded) {
                        return PlacesDisplay(places: state.places);
                      } else if (state is Error) {
                        return MessageDisplay(
                          message: state.message,
                        );
                      }
                    },
                  ),
                ],
              ),
            )),
      ),
    );
  }

`PlacesDisplay - ListView':

class PlacesDisplay extends StatelessWidget {
  final List<Place> places;

  const PlacesDisplay({
    Key key,
    @required this.places,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Expanded(
          child: ListView.builder(
              itemCount: places.length,
              itemBuilder: (context, index) {
                return ListTile(title: Text(places[index].name));
              })),
    );
  }
}

Here is layout expectation design Green widget height should be dynamic according to parent window height (its ListView so should be also scrollable):

enter image description here

Upvotes: 0

Views: 2886

Answers (2)

Alok
Alok

Reputation: 9018

Changes required:

  • Return with Expaned() and wrap your PlacesDisplay
  • mainAxissize: MainAxisSize.min should be there inside the Page Column
  • Don't use Expanded to wrap your Padding. Container with height and width of device should be fine

Page

BlocProvider<PlacesBloc> buildBody(BuildContext context) {
    return BlocProvider(
      builder: (_) => sl<PlacesBloc>(),
      child: Center(
        child: Container( // <---- changed
            height: MediaQuery.of(context).size:height, // <---- changed
            width: MediaQuery.of(context).of.width, // <---- changed
            padding: const EdgeInsets.all(10),
            child: Column(
                mainAxisSize: MainAxisSize.min, // <---- changed
                children: <Widget>[
                  // Top view
                  SizedBox(height: 20),
                  PlacesControls(),
                  // Bottom view
                  BlocBuilder<PlacesBloc, PlacesState>(
                    builder: (context, state) {
                      if (state is Empty) {
                        return MessageDisplay(
                          message: 'Search places around you!',
                        );
                      } else if (state is Loading) {
                        return LoadingWidget();
                      } else if (state is Loaded) {
                        return Expanded(
                           child: PlacesDisplay(places: state.places)   // <--- changed
                        );
                      } else if (state is Error) {
                        return MessageDisplay(
                          message: state.message,
                        );
                      }
                    },
                  ),
                ],
              ),
            ))
    );
  }

In your PlacesDisplay, simply return your ListView.builder()

PlacesDisplay

class PlacesDisplay extends StatelessWidget {
  final List<Place> places;

  const PlacesDisplay({
    Key key,
    @required this.places,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(   // <--- changed
       itemCount: places.length,
       itemBuilder: (context, index) {
          return ListTile(title: Text(places[index].name));
       });
  }
}

Upvotes: 2

Jigar Patel
Jigar Patel

Reputation: 5423

The error is because of the Expanded widget which you have used to wrap the Column. If you want the column to take up all the height, you should use mainAxisSize: MainAxisSize.max, instead of wrapping it with an Expanded. Expanded widget should only be used inside a Row, Column or Flex.

Upvotes: 1

Related Questions