web-dev-qa-db-fra.com

Flutter - alerte de taille automatique Dialogue pour adapter le contenu de la liste

J'ai besoin de charger la liste des villes de manière dynamique à partir du service Web de repos et de laisser l'utilisateur choisir une ville dans la boîte de dialogue d'alerte. Mon code:

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

Résultat - la boîte de dialogue est toujours 200x400, même si seulement 2 villes sont disponibles, il reste une pièce inutile en bas:

enter image description here

Comment faire la largeur/hauteur du dialogue pour s'adapter à la taille réelle des éléments? Si j'oublie les paramètres height et width, je reçois une exception et aucune boîte de dialogue ne s'affiche. En natif Android Java je n'ai jamais besoin de spécifier de dimensions, car la boîte de dialogue se taille automatiquement pour s'adapter).

Comment réparer mon code pour obtenir une taille de dialogue correcte? Notez que je ne connais pas le nombre d'articles, c'est dynamique.

[Éditer]

Comme suggéré, j'ai encapsulé le contenu avec une colonne:

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

Résultat - exception:

I/flutter (5917): ══╡ EXCEPTION PRISE EN RENDANT LA BIBLIOTHÈQUE ╞══════════════════════════════════ ═══════════════════════ I/flutter (5917): l'assertion suivante a été émise lors de performLayout (): I/flutter (5917): RenderViewport ne soutenir le retour des dimensions intrinsèques. I/flutter (5917): Le calcul des dimensions intrinsèques nécessiterait l'instanciation de chaque enfant de la fenêtre, ce que I/flutter (5917): vainc le point de vue des fenêtres paresseuses.

Code plus générique à tester:

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: () => {},
                       );
                     },
                   ),
                 )
               ]
           ),
         );
       }
   );
11
user1209216

Enveloppez votre Container dans un Column, dans le paramètre de contenu, à l'intérieur de celui-ci, définissez le mainAxisSize.min, dans la propriété Column

22
Fellipe Malta

Je sais qu'il est assez tard, mais avez-vous essayé cela?

Column(
    mainAxisSize: MainAxisSize.min,
    children: <Widget>[
       Container(
         child: ListView.builder(
           shrinkWrap: true,
           ...
         ),
       );
    ],
);
12
bshears

Pourriez-vous essayer cela?
Cela a fonctionné au moins pour moi. Si vous avez besoin d'un exemple, dites-moi.

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

MISE À JOUR

Vous avez juste besoin d'afficher ce AreaPicker après le bouton ou quelque chose de pressé.

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
Daibaku

Vous pouvez voir comment SimpleDialog le fait.

Widget dialogChild = IntrinsicWidth(
  stepWidth: 56.0,
  child: ConstrainedBox(
    constraints: const BoxConstraints(minWidth: 280.0),
    child: Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        if (title != null)
          Padding(
            padding: titlePadding,
            child: DefaultTextStyle(
              style: theme.textTheme.title,
              child: Semantics(namesRoute: true, child: title),
            ),
          ),
        if (children != null)
          Flexible(
            child: SingleChildScrollView(
              padding: contentPadding,
              child: ListBody(children: children),
            ),
          ),
      ],
    ),
  ),
);
0
Panda World

Voilà ma dernière solution:

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

typedef Widget ItemBuilder<T>(T item);

class CityChoiceDialog<T> extends StatefulWidget {
  final T initialValue;
  final List<T> items;
  final ValueChanged<T> onSelected;
  final ValueChanged<T> onSubmitted;
  final ValueChanged<T> onCancelled;
  final Widget title;
  final EdgeInsetsGeometry titlePadding;
  final EdgeInsetsGeometry contentPadding;
  final String semanticLabel;
  final ItemBuilder<T> itemBuilder;
  final List<Widget> actions;
  final Color activeColor;
  final String cancelActionButtonLabel;
  final String submitActionButtonLabel;
  final Color actionButtonLabelColor;

  final Widget divider;

  CityChoiceDialog({
    Key key,
    this.initialValue,
    @required this.items,
    this.onSelected,
    this.onSubmitted,
    this.onCancelled,
    this.title,
    this.titlePadding,
    this.contentPadding = const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
    this.semanticLabel,
    this.actions,
    this.itemBuilder,
    this.activeColor,
    this.cancelActionButtonLabel,
    this.submitActionButtonLabel,
    this.actionButtonLabelColor,
    this.divider = const Divider(height: 0.0),
  })  : assert(items != null),
        super(key: key);

  @override
  _CityChoiceDialogState<T> createState() =>
      _CityChoiceDialogState<T>();
}

class _CityChoiceDialogState<T>
    extends State<CityChoiceDialog<T>> {
  T _chosenItem;

  @override
  void initState() {
    _chosenItem = widget.initialValue;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MyAlertDialog(
      title: widget.title,
      titlePadding: widget.titlePadding,
      contentPadding: widget.contentPadding,
      semanticLabel: widget.semanticLabel,
      content: _buildContent(),
      actions: _buildActions(),
      divider: widget.divider,
    );
  }

  _buildContent() {
    return ListView(
      shrinkWrap: true,
      children: widget.items
          .map(
            (item) => RadioListTile(
          title: widget.itemBuilder != null
              ? widget.itemBuilder(item)
              : Text(item.toString()),
          activeColor:
          widget.activeColor ?? Theme.of(context).accentColor,
          value: item,
          groupValue: _chosenItem,
          onChanged: (value) {
            if (widget.onSelected != null) widget.onSelected(value);
            setState(() {
              _chosenItem = value;
            });
          },
        ),
      )
          .toList(),
    );
  }

  _buildActions() {
    return widget.actions ??
        <Widget>[
          FlatButton(
            textColor:
            widget.actionButtonLabelColor ?? Theme.of(context).accentColor,
            child: Text(widget.cancelActionButtonLabel ?? 'ANULUJ'),
            onPressed: () {
              Navigator.pop(context);
              if (widget.onCancelled!= null) widget.onCancelled(_chosenItem);
            },
          ),
          FlatButton(
            textColor:
            widget.actionButtonLabelColor ?? Theme.of(context).accentColor,
            child: Text(widget.submitActionButtonLabel ?? 'WYBIERZ'),
            onPressed: () {
              Navigator.pop(context);
              if (widget.onSubmitted != null) widget.onSubmitted(_chosenItem);
            },
          )
        ];
  }
}

class MyAlertDialog<T> extends StatelessWidget {
  const MyAlertDialog({
    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,
    this.divider = const Divider(
      height: 0.0,
    ),
    this.isDividerEnabled = true,
  })  : 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;
  final Widget divider;

  final bool isDividerEnabled;

  @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, isDividerEnabled ? 20.0 : 0.0),
        child: new DefaultTextStyle(
          style: Theme.of(context).textTheme.title,
          child: new Semantics(child: title, namesRoute: true),
        ),
      ));
      if (isDividerEnabled) children.add(divider);
    } 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) {
      if (isDividerEnabled) children.add(divider);
      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);
  }
}

Il est basé sur https://pub.dev/packages/easy_dialogs et jusqu'à présent, cela fonctionne bien. Je le partage, car il peut être utile, le problème n'est pas anodin.

0
user1209216