Как сделать расширяемый ListView с RadioListTiles? - PullRequest
0 голосов
/ 03 июля 2018

Я изменил пример ExpansionTile , добавил несколько радио. Но они не работают правильно. Я хотел бы иметь выбор не более одного. Скриншот GIF
Это мой код:

import 'package:flutter/material.dart';

class ExpansionTileSample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: const Text('ExpansionTile'),
        ),
        body: new ListView.builder(
          itemBuilder: (BuildContext context, int index) =>
          new EntryItem(data[index]),
          itemCount: data.length,
        ),
      ),
    );
  }
}

// One entry in the multilevel list displayed by this app.
class Entry {
  Entry(this.title, [this.children = const <Entry>[]]);

  final String title;
  final List<Entry> children;
}

// The entire multilevel list displayed by this app.
final List<Entry> data = <Entry>[
  new Entry(
    'Chapter A',
    <Entry>[
      new Entry(
        'Section A0',
        <Entry>[
          new Entry('Item A0.1'),
          new Entry('Item A0.2'),
          new Entry('Item A0.3'),
        ],
      ),
      new Entry('Section A1'),
      new Entry('Section A2'),
    ],
  ),
  new Entry(
    'Chapter B',
    <Entry>[
      new Entry('Section B0'),
      new Entry('Section B1'),
    ],
  ),
  new Entry(
    'Chapter C',
    <Entry>[
      new Entry('Section C0'),
      new Entry('Section C1'),
      new Entry(
        'Section C2',
        <Entry>[
          new Entry('Item C2.0'),
          new Entry('Item C2.1'),
          new Entry('Item C2.2'),
          new Entry('Item C2.3'),
        ],
      ),
    ],
  ),
];

// Displays one Entry. If the entry has children then it's displayed
// with an ExpansionTile.
class EntryItem extends StatefulWidget {
  const EntryItem(this.entry);
  final Entry entry;

  @override
  EntryItemState createState() {
    return new EntryItemState(entry);
  }
}

class EntryItemState extends State<EntryItem> {
  Entry entry;
  EntryItemState(Entry entry){
    this.entry = entry;
  }
  int radioValue = 0;

  void handleRadioValueChanged(int value) {
    setState(() {
      radioValue = value;
    });
  }

  Widget _buildTiles(Entry root) {
    if (root.children.isEmpty)
      return new ListTile(
          title: Row(
            children: <Widget>[
              new Text(root.title),
              new Radio(
                  key: new Key(root.title),
                  value: 1,
                  groupValue: radioValue,
                  onChanged: handleRadioValueChanged)
            ],
          )
      );
    return new ExpansionTile(
      key: new PageStorageKey<Entry>(root),
      title: new Text(root.title),
      children: root.children.map(_buildTiles).toList(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return _buildTiles(widget.entry);
  }
}

void main() {
  runApp(new ExpansionTileSample());
}

Пожалуйста, покажите, как исправить код, чтобы в каждой группе было не более одного значения. Я не могу найти решение. Спасибо за помощь!

1 Ответ

0 голосов
/ 04 июля 2018

Если вы хотите выбрать параметры, один за другим я изменил ваш код следующим образом.

class ExpansionTileSample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: const Text('ExpansionTile'),
        ),
        body: new ListView.builder(
          itemBuilder: (BuildContext context, int index) =>
          new EntryItem(data[index]),
          itemCount: data.length,
        ),
      ),
    );
  }
}

// One entry in the multilevel list displayed by this app.
class Entry {
  Entry(this.title, [this.children = const <Entry>[]]);

  final String title;
  int radioValue = 0;
  final List<Entry> children;
}

// The entire multilevel list displayed by this app.
final List<Entry> data = <Entry>[
  new Entry(
    'Chapter A',
    <Entry>[
      new Entry(
        'Section A0',
        <Entry>[
          new Entry('Item A0.1'),
          new Entry('Item A0.2'),
          new Entry('Item A0.3'),
        ],
      ),
      new Entry('Section A1'),
      new Entry('Section A2'),
    ],
  ),
  new Entry(
    'Chapter B',
    <Entry>[
      new Entry('Section B0'),
      new Entry('Section B1'),
    ],
  ),
  new Entry(
    'Chapter C',
    <Entry>[
      new Entry('Section C0'),
      new Entry('Section C1'),
      new Entry(
        'Section C2',
        <Entry>[
          new Entry('Item C2.0'),
          new Entry('Item C2.1'),
          new Entry('Item C2.2'),
          new Entry('Item C2.3'),
        ],
      ),
    ],
  ),
];

// Displays one Entry. If the entry has children then it's displayed
// with an ExpansionTile.
class EntryItem extends StatefulWidget {
  const EntryItem(this.entry);
  final Entry entry;

  @override
  EntryItemState createState() {
    return new EntryItemState(entry);
  }
}

class EntryItemState extends State<EntryItem> {
  Entry entry;
  EntryItemState(Entry entry){
    this.entry = entry;
  }

  void handleRadioValueChanged(int value, Entry root) {
    setState(() {
      root.radioValue = value;
    });
  }

  Widget _buildTiles(Entry root) {
    if (root.children.isEmpty) {
      print("title: ${root.title} , radioValue: ${root.radioValue}");
      return new ListTile(
          title: Row(
            children: <Widget>[
              new Text(root.title),
              new Radio(
                  key: new Key(root.title),
                  value: 1,
                  groupValue: root.radioValue,
                  onChanged: (value) => handleRadioValueChanged(value, root))
            ],
          )
      );
    }
    return new ExpansionTile(
      key: new PageStorageKey<Entry>(root),
      title: new Text(root.title),
      children: root.children.map(_buildTiles).toList(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return _buildTiles(widget.entry);
  }
}

Изменения, которые я сделал, это назначить разные radioValue для каждой записи и обновить только соответствующие radioValue в Entry.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ

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

Widget buildRadio() {
    return new Align(
      alignment: const Alignment(0.0, -0.2),
      child: new Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          new Row(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              new Radio<int>(
                value: 0,
                groupValue: radioValue,
                onChanged: handleRadioValueChanged
              ),
              new Radio<int>(
                value: 1,
                groupValue: radioValue,
                onChanged: handleRadioValueChanged
              ),
              new Radio<int>(
                value: 2,
                groupValue: radioValue,
                onChanged: handleRadioValueChanged
              )
            ]
          ),
          // Disabled radio buttons
          new Row(
            mainAxisSize: MainAxisSize.min,
            children: const <Widget>[
              const Radio<int>(
                value: 0,
                groupValue: 0,
                onChanged: null
              ),
              const Radio<int>(
                value: 1,
                groupValue: 0,
                onChanged: null
              ),
              const Radio<int>(
                value: 2,
                groupValue: 0,
                onChanged: null
              )
            ]
          )
        ]
      )
    );   }
...