Flutter: ListView в SimpleDialog

Я хочу показать SimpleDialog с ListView.builder в моем приложении Flutter с помощью этого кода:

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

который дает эту ошибку (извините, я не мог обернуть журналы как код, потому что Stackoverflow жалуется, что там слишком много кода):

═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ (4481): Во время выполненияLayout(): I/flutter (4481) было выбрано следующее утверждение: RenderViewport не поддерживает возвращаемые внутренние размеры. I/flutter (4481): для вычисления собственных размеров потребуется создать экземпляр каждого дочернего объекта в окне просмотра, который I/flutter (4481): поражает точку просмотра в лени. I/flutter (4481): Если вы просто пытаетесь сжимать окно просмотра в направлении основной оси, рассмотрите флажок I/flutter (4481): объект рендеринга RenderShrinkWrappingViewport (виджет ShrinkWrappingViewport), который достигает этого I/flutter (4481): эффект без реализации API с внутренним размером. I/flutter (4481):... I/flutter (4481): Выброшено еще одно исключение: RenderBox не был выложен: RenderPhysicalShape # 83d92 relayoutBoundary = up2 NEEDS-PAINT I/flutter (4481): Еще одно исключение было выбрано: package: flutter/src/rendering/shifted_box.dart ': Failed assertion: строка 310 pos 12:' child.hasSize ': не верно. I/flutter (4481): Другое исключение было брошено: RenderBox не был выложен: RenderPhysicalShape # 83d92 relayoutBoundary = up2

Я попытался использовать Контейнер с определенной высотой и шириной, и он работает, но я хочу, чтобы ListView располагался в диалоге.

Как включить ListView в SimpleDialog?

Ответ 1

Просто оберните ListView.builder в Container с определенной высотой и шириной.

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'),
          );
        },
      ),
    );
  }

И вызвать вышеуказанный метод в showDialog.

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

Редакция:

Вы можете пойти с комментарием @Rap тоже.

Ответ 2

Это более общий ответ для будущих посетителей.

Как создать диалог со списком

Если вы хотите диалог с ListView, вы должны рассмотреть SimpleDialog. SimpleDialog предназначен для отображения параметров в списке (в отличие от AlertDialog, который предназначен для уведомления пользователя о чем-либо).

Вот простой пример:

enter image description here

Процесс создания SimpleDialog в основном такой же, как и для AlertDialog (оба они основаны на Dialog), за исключением того, что вместо кнопок вы определяете виджеты элементов списка, называемые SimpleDialogOptions. При нажатии опции списка вызывается обратный вызов, на который вы можете ответить.

  // 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;
    },
  );

Варианты обработки прессов

Когда пользователь щелкает элемент, вы можете закрыть диалоговое окно и выполнить какое-либо действие.

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

Заметки

Код

Вот полный код для примера выше.

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;
    },
  );
}

Ответ 3

вы можете создать отдельный метод метода для кода SimpleDialogOptions ниже:

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;

Ответ 4

Оборачивание ListView в контейнер и присвоение ему ширины: double.maxFinite, решает проблему с iOS/Android, имеющей проблемы с ListView внутри диалога:

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

В случае ListView внутри столбца, который находится внутри AlertDialog:

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

Ответ 5

Я нашел способ... Хотя он немного взломан, и поэтому может быть лучший вариант.

Вам понадобится этот пакет:

import 'package:flutter/services.dart';

Создайте виджет:

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

Если экран вращается, он завинчивается, потому что диалог поддерживает его оригинальный размер. Вероятно, вы можете исправить это с небольшими усилиями, но я просто заблокирую его, чтобы предотвратить вращение, например:

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

Затем я разблокирую его так:

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

Таким образом, чтобы диалог не зависал. Затем я получаю размер и ширину экрана в методе построения:

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

Затем следует этот макет:

 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
          )
        ),
      ]
    ),
  );
}

Я не думаю, что это лучше, но я работал до сих пор.

Ответ 6

Сначала пробовал это с помощью свойства itemExtent, но это не работает. Просто оберните ListView в контейнере с определенной высотой и шириной, если ваши элементы статичны.

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"),
            ],
          ),
        )
      ],
    );
  },
);