Flutter - автоматический размер alertDialog, чтобы соответствовать содержимому списка - PullRequest
0 голосов
/ 13 февраля 2019

Мне нужно динамически загружать список городов из остальных веб-сервисов и позволить пользователю выбрать один город из диалогового окна с предупреждением.Мой код:

createDialog() {

    fetchCities().then((response) {

      showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text('Wybierz miasto'),
              content: Container(
                height: 200.0,
                width: 400.0,
                child: ListView.builder(
                  shrinkWrap: true,
                  itemCount: response.length,
                  itemBuilder: (BuildContext context, int index) {
                    return ListTile(
                      title: Text(response[index].name),
                      onTap: () => citySelected(response[index].id),
                    );
                  },
                ),
              ),
            );
          }
      );
    });
  }

Результат - диалоговое окно всегда 200x400, даже если доступны только 2 города, внизу остается ненужная комната:

enter image description here

Как сделать ширину / высоту диалогового окна, чтобы соответствовать фактическому размеру элементов?Если я пропускаю параметры height и width, я получаю исключение, и диалог не отображается.В нативной Android Java мне никогда не нужно указывать какие-либо размеры, потому что размеры диалогов автоматически подгоняются.

Как исправить мой код для правильного размера диалогового окна?Обратите внимание, что я не знаю количество элементов, оно динамическое.

[править]

Как и предполагалось, я обернул содержимое столбцом:

createDialog() {
    fetchCities().then((response) {
      showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text('Wybierz miasto'),
              content: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Container(
                      child: ListView.builder(
                        shrinkWrap: true,
                        itemCount: response.length,
                        itemBuilder: (BuildContext context, int index) {
                          return ListTile(
                            title: Text(response[index].name),
                            onTap: () => citySelected(response[index].id),
                          );
                        },
                      ),
                    )
                  ]
              ),
            );
          }
      );
    });
  }

Результат - исключение:

I / трепетание (5917): ══╡ ИСКЛЮЧЕНИЕ ПРИОБРЕТЕНО БИБЛИОТЕКОЙ ╞══════════════════════════════════════════════════════════════ I / flutter (5917): во время executeLayout () было выдвинуто следующее утверждение: I/ flutter (5917): RenderViewport не поддерживает возврат внутренних размеров.I / flutter (5917): вычисление внутренних измерений потребовало бы создания экземпляра каждого дочернего элемента окна просмотра, которое I / flutter (5917): побеждает ленивую точку области просмотра.

Более общий код для тестирования:

showDialog(
       context: context,
       builder: (BuildContext context) {
         return AlertDialog(
           title: Text('Select city'),
           content: Column(
               mainAxisSize: MainAxisSize.min,
               children: <Widget>[
                 Container(
                   child: ListView.builder(
                     shrinkWrap: true,
                     itemCount: 2,
                     itemBuilder: (BuildContext context, int index) {
                       return ListTile(
                         title: Text("City"),
                         onTap: () => {},
                       );
                     },
                   ),
                 )
               ]
           ),
         );
       }
   );

Ответы [ 3 ]

0 голосов
/ 13 февраля 2019

Не могли бы вы попробовать это?
Это сработало, по крайней мере, для меня.Если вам нужен пример, скажите мне.

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class SmartDialog extends StatelessWidget {
  const SmartDialog({
    Key key,
    this.title,
    this.titlePadding,
    this.content,
    this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
    this.actions,
    this.semanticLabel,
  }) : assert(contentPadding != null),
       super(key: key);

  final Widget title;
  final EdgeInsetsGeometry titlePadding;
  final Widget content;
  final EdgeInsetsGeometry contentPadding;
  final List<Widget> actions;
  final String semanticLabel;

  @override
  Widget build(BuildContext context) {
    final List<Widget> children = <Widget>[];
    String label = semanticLabel;

    if (title != null) {
      children.add(new Padding(
        padding: titlePadding ?? new EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0),
        child: new DefaultTextStyle(
          style: Theme.of(context).textTheme.title,
          child: new Semantics(child: title, namesRoute: true),
        ),
      ));
    } else {
      switch (defaultTargetPlatform) {
        case TargetPlatform.iOS:
          label = semanticLabel;
          break;
        case TargetPlatform.android:
        case TargetPlatform.fuchsia:
          label = semanticLabel ?? MaterialLocalizations.of(context)?.alertDialogLabel;
      }
    }

    if (content != null) {
      children.add(new Flexible(
        child: new Padding(
          padding: contentPadding,
          child: new DefaultTextStyle(
            style: Theme.of(context).textTheme.subhead,
            child: content,
          ),
        ),
      ));
    }

    if (actions != null) {
      children.add(new ButtonTheme.bar(
        child: new ButtonBar(
          children: actions,
        ),
      ));
    }

    Widget dialogChild = new Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: children,
    );

    if (label != null)
      dialogChild = new Semantics(
        namesRoute: true,
        label: label,
        child: dialogChild
      );

    return new Dialog(child: dialogChild);
  }
}

ОБНОВЛЕНИЕ

Вам просто нужно показать этот AreaPicker после нажатия кнопки или чего-то еще.

class AreaPicker extends StatelessWidget {
  final List<Area> items;
  AreaPicker(this.items);
  @override
  Widget build(BuildContext context) {
    return SmartDialog(
      title: Text('Select Area'),
      actions: <Widget>[
        FlatButton(
          textColor: Colors.black,
          child: Text('Rather not say'),
          onPressed: () {
            Navigator.of(context, rootNavigator: true).pop();
          },
        )
      ],
      content: Container(
        height: MediaQuery.of(context).size.height / 4,
        child: ListView.builder(
          shrinkWrap: true,
          itemExtent: 70.0,
          itemCount: areas.length,
          itemBuilder: (BuildContext context, int index) {
            final Area area = areas[index];
            return GestureDetector(
              child: Center(
                child: Text(area.name),
              ),
              onTap: () { 
                Navigator.of(context, rootNavigator: true).pop();
                // some callback here.
              }
            );
          },
        ),
      )
    );
  }
}
0 голосов
/ 27 августа 2019

Я знаю, что уже поздно, но вы пробовали это?

Column(
    mainAxisSize: MainAxisSize.min,
    children: <Widget>[
       Container(
         child: ListView.builder(
           shrinkWrap: true,
           ...
         ).build(context),
       );
    ],
);
0 голосов
/ 13 февраля 2019

Оберните ваш Container внутри Column, в параметре содержимого, внутри него установите mainAxisSize.min, в свойстве Column

...