Flutter: ListView in a SimpleDialog

ListviewFlutter

Listview Problem Overview


I want to show a SimpleDialog with ListView.builder in my Flutter app with this code:

showDialog(
  context: context,
  builder: (BuildContext context) {
    return new SimpleDialog(
      children: <Widget>[
        new FittedBox(
          child: new ListView(
            children: <Widget>[
              new Text("one"),
              new Text("two"),
            ],
          ),
        )
      ],
    );
  },
);

which gives this error (sorry, I couldn't wrap the logs as code because Stackoverflow complains that there's too much code):

>══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════ I/flutter ( 4481): The following assertion was thrown during performLayout(): I/flutter ( 4481): RenderViewport does not support returning intrinsic dimensions. I/flutter ( 4481): Calculating the intrinsic dimensions would require instantiating every child of the viewport, which I/flutter ( 4481): defeats the point of viewports being lazy. I/flutter ( 4481): If you are merely trying to shrink-wrap the viewport in the main axis direction, consider a I/flutter ( 4481): RenderShrinkWrappingViewport render object (ShrinkWrappingViewport widget), which achieves that I/flutter ( 4481): effect without implementing the intrinsic dimension API. I/flutter ( 4481): ... I/flutter ( 4481): Another exception was thrown: RenderBox was not laid out: RenderPhysicalShape#83d92 relayoutBoundary=up2 NEEDS-PAINT I/flutter ( 4481): Another exception was thrown: 'package:flutter/src/rendering/shifted_box.dart': Failed assertion: line 310 pos 12: 'child.hasSize': is not true. I/flutter ( 4481): Another exception was thrown: RenderBox was not laid out: RenderPhysicalShape#83d92 relayoutBoundary=up2

I tried using Container with specific height and width, and it works, but I want the ListView to fit itself in the Dialog.

How to include a ListView in a SimpleDialog?

Listview Solutions


Solution 1 - Listview

Just wrap ListView.builder in a Container with a specific height and width.

Widget setupAlertDialoadContainer() {
  return Container(
    height: 300.0, // Change as per your requirement
    width: 300.0, // Change as per your requirement
    child: ListView.builder(
      shrinkWrap: true,
      itemCount: 5,
      itemBuilder: (BuildContext context, int index) {
        return ListTile(
          title: Text('Gujarat, India'),
        );
      },
    ),
  );
}

And call the above method in showDialog.

showDialog(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text('Country List'),
        content: setupAlertDialoadContainer(),
      );
    });

EDITED:

You can go with @Rap's comment too.

Solution 2 - Listview

Wraping the ListView in a Container and giving it a width: double.maxFinite, solves the problem with iOS/Android having trouble with ListView inside a dialog:

showDialog(
   context: context,
   builder: (BuildContext context) {
      return AlertDialog(
         content: Container(
            width: double.maxFinite,
            child: ListView(
               children: <Widget>[]
            ),
         ),
      );
   }
);

In the case of a ListView inside a Column that is inside an AlertDialog:

showDialog(
   context: context,
   builder: (BuildContext context) {
      return AlertDialog(
         content: Container(
            width: double.maxFinite,
            child: Column(
               mainAxisSize: MainAxisSize.min,
               children: <Widget>[
                  Expanded(
                     child: ListView(
                        shrinkWrap: true,
                        children: <Widget>[]
                     )
                  )
               ]
            ),
         ),
      );
   }
);

Solution 3 - Listview

This is a more general answer for future visitors.

How to create a dialog with a list

If you want a dialog with a ListView, you should consider a SimpleDialog. A SimpleDialog is designed to show options in a list (as opposed to an AlertDialog, which is meant to notify the user of something).

Here is a simple example:

enter image description here

The process of creating a SimpleDialog is basically the same as for an AlertDialog (they are both based on Dialog), except that you define list item widgets called SimpleDialogOptions instead of buttons. When a list option is pressed a callback is fired that you can respond to.

  // set up the list options
  Widget optionOne = SimpleDialogOption(
    child: const Text('horse'),
    onPressed: () {},
  );
  Widget optionTwo = SimpleDialogOption(
    child: const Text('cow'),
    onPressed: () {},
  );
  Widget optionThree = SimpleDialogOption(
    child: const Text('camel'),
    onPressed: () {},
  );
  Widget optionFour = SimpleDialogOption(
    child: const Text('sheep'),
    onPressed: () {},
  );
  Widget optionFive = SimpleDialogOption(
    child: const Text('goat'),
    onPressed: () {},
  );

  // set up the SimpleDialog
  SimpleDialog dialog = SimpleDialog(
    title: const Text('Choose an animal'),
    children: <Widget>[
      optionOne,
      optionTwo,
      optionThree,
      optionFour,
      optionFive,
    ],
  );

  // show the dialog
  showDialog(
    context: context,
    builder: (BuildContext context) {
      return dialog;
    },
  );

Handling option presses

When a user clicks an item you can close the dialog an perform some action.

  Widget optionOne = SimpleDialogOption(
    child: const Text('horse'),
    onPressed: () {
      Navigator.of(context).pop();
      _doSomething();
    },
  );

Notes

Code

Here is the full code for the example above.

main.dart

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SimpleDialog',
      home: Scaffold(
          appBar: AppBar(
            title: Text('SimpleDialog'),
          ),
          body: MyLayout()),
    );
  }
}

class MyLayout extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: RaisedButton(
        child: Text('Show alert'),
        onPressed: () {
          showAlertDialog(context);
        },
      ),
    );
  }
}

// replace this function with the examples above
showAlertDialog(BuildContext context) {

  // set up the list options
  Widget optionOne = SimpleDialogOption(
    child: const Text('horse'),
    onPressed: () {
      print('horse');
      Navigator.of(context).pop();
    },
  );
  Widget optionTwo = SimpleDialogOption(
    child: const Text('cow'),
    onPressed: () {
      print('cow');
      Navigator.of(context).pop();
    },
  );
  Widget optionThree = SimpleDialogOption(
    child: const Text('camel'),
    onPressed: () {
      print('camel');
      Navigator.of(context).pop();
    },
  );
  Widget optionFour = SimpleDialogOption(
    child: const Text('sheep'),
    onPressed: () {
      print('sheep');
      Navigator.of(context).pop();
    },
  );
  Widget optionFive = SimpleDialogOption(
    child: const Text('goat'),
    onPressed: () {
      print('goat');
      Navigator.of(context).pop();
    },
  );

  // set up the SimpleDialog
  SimpleDialog dialog = SimpleDialog(
    title: const Text('Choose an animal'),
    children: <Widget>[
      optionOne,
      optionTwo,
      optionThree,
      optionFour,
      optionFive,
    ],
  );

  // show the dialog
  showDialog(
    context: context,
    builder: (BuildContext context) {
      return dialog;
    },
  );
}

Solution 4 - Listview

adding width: double.maxFinite to the container solved my problem.

 @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('Sample Dialog'),
      content: Container(
        width: double.maxFinite,
        child: ListView(
          children: <Widget>[
            Text('Item 1'),
            Text('Item 2'),
          ],
        ),
      ),
    );
  }

Solution 5 - Listview

Used SingleChildScrollView with physics: NeverScrollableScrollPhysics() and shrinkWrap: true in your ListView

  showDialog(
      context: context,
      builder: (_) {
        return AlertDialog(
          title: widget,
          content: SingleChildScrollView( //MUST TO ADDED
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                ...//any widgets,
                ListView.builder(
                    shrinkWrap: true, //MUST TO ADDED
                    physics: NeverScrollableScrollPhysics(), //MUST TO ADDED
                    itemCount: model.length,
                    itemBuilder: (BuildContext c, int index) {
                      return ListTile();
                    })
              ],
            ),
          ),
        );
      },
    );

Solution 6 - Listview

you can create a separate method method for SimpleDialogOptions code below:

final SimpleDialog dialog = new SimpleDialog(
      title: const Text('Select assignment'),
      children: <Widget>[
        new SimpleDialogOption(
          onPressed: () { Navigator.pop(context); },
          child: const Text('Text one'),
        ),
        new SimpleDialogOption(
          onPressed: () {},
          child: const Text('Text two'),
        ),
      ],
    );
    return dialog;

Solution 7 - Listview

Iterate List with spread operator (...) or if List can be null then use null-aware spread operator (...?)

List<String> values = ['one', 'two', 'three'];
    await showDialog(
      context: context,
      builder: (BuildContext context) {
        return SimpleDialog(
          title: Text("Select Value"),
          children: [
            ...values.map((value) {
              return SimpleDialogOption(child: Text(value));
            }),
          ],
        );
      },
    );

Using Listview:

List<String> values = ['one', 'two', 'three'];
    await showDialog(
      context: context,
      builder: (BuildContext context) {
        return SimpleDialog(
          title: Text("Select Value"),
          children: [
            SizedBox(
              width: MediaQuery.of(context).size.width,
              child: ListView.builder(
                shrinkWrap: true,
                itemBuilder: (ctx, index) {
                  return SimpleDialogOption(
                    onPressed: () => Navigator.pop(context),
                    child: Center(
                      child: Text(values[index]),
                    ),
                  );
                },
                itemCount: values.length,
              ),
            )
          ],
        );
      },
    );

Output:

enter image description here

Solution 8 - Listview

Use predefined width for the container which wraps the ListView.builder(). This code will help you

  • Shrink wrap to control the height from going infinity.

  • Using Limited Width for the Listview

           showDialog(
                    context: context,
                    builder: (context) => AlertDialog(
                      title: Text('Orders'),
                      content: SizedBox(
                        width: double.maxFinite,  //  <------- Use SizedBox to limit width
                        child: ListView.builder(
                          shrinkWrap: true,  //            <------  USE SHRINK WRAP
                          itemCount: 5,
                          itemBuilder: (context, index) =>
                              Text('Order'),
                        ),
                      ),
                    ),
                  );
    

Solution 9 - Listview

I found a way... Although it's a bit hacky, and so there may be a better option.

You'll need this package:

import 'package:flutter/services.dart';

Create the widget:

class MyDialog extends StatefulWidget {
  MyDialog ({Key key}) : super(key: key);
  MyDialogState createState() => new MyDialogState();
}
class MyDialogState extends State<MyDialog> {

If the screen rotates it screws things up because the dialog maintains it's original size. You can probably fix that with a bit of effort, but I just lock it to prevent rotating, like this:

  @override
  initState() { super.initState();
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ]);
  }

Then I unlock it like this at the end:

  @override
  dispose() { super.dispose();
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.landscapeRight,
      DeviceOrientation.landscapeLeft,
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ]);
  }

So that stops the dialog from screwing up. Then I get the size and width of the screen in the build method:

@override
Widget build(BuildContext context) {
  double width = MediaQuery.of(context).size.width;
  double height = MediaQuery.of(context).size.height;

Then followed with this layout:

 return ConstrainedBox(
    constraints: BoxConstraints(maxHeight: height, maxWidth: width),
    child: Column(
      children: <Widget>[
        Expanded(
          child: GridView.count(
            primary: false,
            padding: const EdgeInsets.all(20.0),
            crossAxisSpacing: 10.0,
            crossAxisCount: 3,
            children: _images
          )
        ),
      ]
    ),
  );
}

..again, I don't think it's the best, but it's been working for me so far.

Solution 10 - Listview

Tried it with itemExtent property first, but that doesn't work. Simply wrap the ListView in a Container with defined height and width, if your items are static.

showDialog(
  context: context,
  builder: (BuildContext context) {
    return new SimpleDialog(
      children: <Widget>[
        new Container(
          height: 100.0,
          width: 100.0,
          child: new ListView(
            children: <Widget>[
              new Text("one"),
              new Text("two"),
            ],
          ),
        )
      ],
    );
  },
);

Solution 11 - Listview

showDialog(context: parentcontext,
    builder: (context){
      return SimpleDialog(
        title: Text('create post'),
        children: <Widget>[
          SimpleDialogOption(
            onPressed: handleImageTaking,
            child: Text('take a pic'),
          ),
          SimpleDialogOption(
            onPressed: handleImageSelecting,
            child: Text('select a pic'),
          ),
          SimpleDialogOption(
            child: Text('cancel'),
            onPressed: (){
              Navigator.pop(context);
            },
          )
        ],
      );
    });

Solution 12 - Listview

SImply in the content we need to use a Container with fixed height and width. and the use ListView as a child of Container.

 scrollDialogFunction(){
        return showDialog(
            context: context,
            builder: (BuildContext context) {
              return AlertDialog(
                title: Text('List of Index'),
                content: Container(
                  width: 350.0,
                  height: 250,// Change as per your requirement
                  child: ListView.builder(
                    itemCount: 150,
                    itemBuilder: (BuildContext context, int index) {
                      return ListTile(
                        title: Text(index.toString()),
                      );
                    },
                  ),
                ),
                actions: [Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Text("ok",style: TextStyle(fontSize: 18),),
                )],
              );
            });
      }

Solution 13 - Listview

A good alernative is setting scrollAble to true in AlertDialog and using a Column as content. This will scale to fit the screen

AlertDialog(
      scrollable: true,
      title: Text("Gjenta sjekkliste"),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: choices
            .map((e) => ListTile(
                  title: Text(e ?? "Ingen gjentakelse"),
                  onTap: () {
                    Navigator.pop(context, e);
                  },
                ))
            .toList(),
      ),
    );

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDarkhanView Question on Stackoverflow
Solution 1 - ListviewGovaadiyoView Answer on Stackoverflow
Solution 2 - ListviewJose JetView Answer on Stackoverflow
Solution 3 - ListviewSuragchView Answer on Stackoverflow
Solution 4 - ListviewUserView Answer on Stackoverflow
Solution 5 - ListviewBIS TechView Answer on Stackoverflow
Solution 6 - ListviewsilentsudoView Answer on Stackoverflow
Solution 7 - ListviewJitesh MohiteView Answer on Stackoverflow
Solution 8 - ListviewMBKView Answer on Stackoverflow
Solution 9 - ListviewJus10View Answer on Stackoverflow
Solution 10 - ListviewIsham MahajanView Answer on Stackoverflow
Solution 11 - ListviewS.R KeshavView Answer on Stackoverflow
Solution 12 - ListviewOsama SandhuView Answer on Stackoverflow
Solution 13 - ListviewSondre SørbyeView Answer on Stackoverflow