Flutter: значения виджета внутри ListView не обновляются при удалении элемента

Здравствуйте, я новичок в флаттере, я работаю в виджете с именем split row, я использую ListView с тремя дочерними элементами: текстовый виджет, виджет ListView Builder и виджет контейнера.

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

Чтобы лучше объяснить себя, я сделал пример с текстовым полем и текстом, в первом я ввел значение ключа карты три раза, а во втором текст сформирован с данными слова и ключом карты. Это то, что произошло

Как вы можете видеть, когда я удаляю data1, элемент исчезает, оставляя только данные 0, данные 2 и данные 3, но значение 111 остается, а значение 333 исчезает. очевидно, что значения не соответствуют элементам карты, кроме 000 => data 0

Я уже занимался исследованиями на форумах, искал stackru, на веб-сайтах и ​​не нашел ничего, что могло бы мне помочь.

Это код:

import 'package:flutter/material.dart';

class SplitRow extends StatefulWidget {
  @override
  _SplitRowState createState() => _SplitRowState();
}

class _SplitRowState extends State<SplitRow> {
  int _index = 0;
  Map<String, Widget> splitRows = {};
  Map records = {};
  Map col1 = {};

  // Crete Widget Split Row
  Widget _splitRow(index) {
    return Column(
      children: <Widget>[
        Container(
          color: Colors.white,
          height: 40.0,
          child: Row(
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.remove_circle),
                iconSize: 24,
                color: Color(0xFFFF3B30),
                onPressed: () {
                  removeItem(index);
                },
              ),
              Container(
                width: 100,
                child: TextField(
                  keyboardType: TextInputType.numberWithOptions(
                    signed: false,
                    decimal: true,
                  ),
                  decoration: InputDecoration(
                    hintText: 'Put direction here...',
                    border: InputBorder.none,
                  ),
                  style: TextStyle(fontFamily: 'Lekton'),
                  onChanged: (value) {
                    col1["$index"] = value;
                  },
                ),
              ),
              _VerticalDivider(),
              Text('data $index'),
            ],
          ),
        ),
        _HorizontalDivider(),
      ],
    );
  }

  // Create Widget Add Row
  Widget _addRow() {
    return Container(
      height: 40,
      color: Colors.white,
      child: Row(
        children: <Widget>[
          Container(
            height: 40,
            color: Colors.white,
            child: IconButton(
              icon: Icon(Icons.add_circle),
              iconSize: 24,
              color: Color(0xFF34C759),
              onPressed: () {
                addItem();
              },
            ),
          ),
          Text('New Item')
        ],
      ),
    );
  }

  // Add splitRow
  void addItem() {
    int key = _index;
    print('Index antes $_index');
    setState(() {
      splitRows['$key'] = _splitRow(key);
      ++_index;
    });
    print(splitRows);
  }

  // Remove splitRow
  void removeItem(key) {
    setState(() {
      splitRows.remove('$key');
      col1.remove('$key');
    });
  }

  // Save Values
  void saveItems() {
    records = {'qty': col1};
    print(records);
  }

  @override
  void initState() {
    super.initState();
    splitRows['$_index'] = _splitRow(_index);
    ++_index;
    // splitRows['$_index'] = _addRow(_index);
    // ++_index;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Directions'),
          actions: <Widget>[
            FlatButton(
              child: Text(
                'Save',
                style: TextStyle(color: Colors.white),
              ),
              onPressed: () {
                saveItems();
              },
            )
          ],
        ),
        backgroundColor: Color(0xFFE5E5EA),
        body: Column(
          children: <Widget>[
            Container(
                constraints: BoxConstraints(
                  minHeight: 40.0,
                  maxHeight: 500.0,
                  minWidth: double.infinity,
                  maxWidth: double.infinity,
                ),
                child: ListView(
                  children: <Widget>[
                    Padding(padding: EdgeInsets.only(top: 10.0)),
                    Text('DIRECTIONS'),
                    ListView.builder(
                      shrinkWrap: true,
                      itemCount: splitRows.length,
                      itemBuilder: (BuildContext context, int index) {
                        String key = splitRows.keys.elementAt(index);
                        return splitRows[key];
                      },
                      physics: NeverScrollableScrollPhysics(),
                    ),
                    _addRow()
                  ],
                ))
          ],
        ));
  }
}

class _HorizontalDivider extends StatelessWidget {
  const _HorizontalDivider({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: Divider(
        height: 2.0,
      ),
    );
  }
}

class _VerticalDivider extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 20,
      width: 2,
      margin: EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
      child: VerticalDivider(
        width: 2.0,
      ),
    );
  }
}

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

1 ответ

Решение

ListView.builder создает список виджетов для отображения, когда вы удаляете один виджет и обновляете состояние ListView.builder просто повторно запускает виджеты для сборки и видит, что TextField уже существует, сравнивая элементы, похоже, они одного типа виджета, поэтому он ничего не меняет, также текстовое поле не сохраняет какое-либо сохраненное состояние, поэтому, даже если его нужно было перестроить, оно просто вернется назад "Поместите направление сюда...". Даже если вы думаете, что сохранили состояние виджета внутри карты, которая ничего не меняет, создаваемый виджет сохраняется, но состояния нет. Вы либо добавляете ключ, чтобы они выглядели по-другому, либо добавляете Textcontroller в TextField (он не сохраняет состояние, но вы можете добавить к нему начальное текстовое значение)

class MySplitRow extends StatelessWidget{
  final Map col1;
  final String index;
  final VoidCallback onPressed;

  MySplitRow({this.col1, this.index, this.onPressed, Key key}) : super(key: key);


  @override
  Widget build(BuildContext context){
    return Column(
      children: <Widget>[
        Container(
          color: Colors.white,
          height: 40.0,
          child: Row(
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.remove_circle),
                iconSize: 24,
                color: Color(0xFFFF3B30),
                onPressed: onPressed,
              ),
              Container(
                width: 100,
                child: TextField(
                  controller: TextEditingController(text: col1["$index"]), //it will check the map col1 and if its not null it will give you the value that was saved at that key
                  keyboardType: TextInputType.numberWithOptions(
                    signed: false,
                    decimal: true,
                  ),
                  decoration: InputDecoration(
                    hintText: 'Put direction here...',
                    border: InputBorder.none,
                  ),
                  style: TextStyle(fontFamily: 'Lekton'),
                  onChanged: (value) {
                    col1["$index"] = value;
                  },
                ),
              ),
              const VerticalDivider(width: 10, endIndent: 10, indent: 10), //Works exactly as _VerticalDivider with less code
              Text('data $index')
            ],
          ),
        ),
        const Divider(color: Colors.white, height: 2) //Works exactly as _HorizontalDivider with less code
      ],
    );
  }
}

и внутри Listview.builder

 (BuildContext context, int index) {
  String key = splitRows.keys.elementAt(index);
  return MySplitRow(
     key: ValueKey<String>(key),
     col1: col1,
     index: key,
     onPressed: () => removeItem(key),
  );
}

Несколько рекомендаций, предпочитайте использовать класс вместо метода для создания виджетов. Ваши _HorizontalDivider и _VerticalDivider можно построить без упаковки разделителя в контейнер

Другие вопросы по тегам