如何从颤振中的小部件列表中删除元素?

时间:2021-02-22 10:40:24

标签: flutter dart flutter-layout

我在 flutter 中制作了一个 TableRows 列表,我将其作为孩子传递给我的 Table 小部件。每行都有一些数据和一个平面按钮,它从数据库中删除数据,我想从该列表中删除它TableRows 也在路上。我知道我必须在 setState 函数下使用更改整个列表,但实际上并没有发生。发生的情况是它从数据库中删除,但我的 UI 保持不变,但是当我转到上一个屏幕并返回该屏幕时,它不在那里。所以基本上我可以从数据库中删除它,但不能从 UI 中删除

class _DataPreviewScreenState extends State<DataPreviewScreen> {
  List<TableRow> listView = [];
  void initState() {
    super.initState();
    finalData = widget.data; //<- final data is my data from Database its actually a Map<String,dynamic>
    listView=listCreater();
  }

  List<TableRow> listCreater() { //<-here im am making the list of TableRow Widgets
    List<TableRow> list = [
      TableRow(
        children: [
          RowHead(heading: 'Site'),
          RowHead(heading: 'UserName'),
          RowHead(heading: 'Password'),
          RowHead(heading: ' '),
        ],
      ),
    ];
    var i;
    for (var data in finalData) {
      var item = TableRow(
        key: UniqueKey(),
        decoration: BoxDecoration(shape: BoxShape.rectangle),
        children: [
          RowCell(data['site_name']),
          RowCell(data['user_name']),
          RowCell(data['password']),
          Container(
            height: 35.0,
            color: Colors.white,
            child: FlatButton(
              onPressed: () {   //<- removing That particular list item from database
                DataModel.instance.delete(data['_id']);
                setState(() {
                  listView.removeAt(i);   /<-trying to delete from UI
                });
              },
              child: Icon(
                Icons.delete,
                size: 20.0,
              ),
            ),
          ),
        ],
      );
      list.add(item);
      i++;
    }

    return list;
  }

我传递行的地方

Container(
  child: Table(
    columnWidths: {3: FractionColumnWidth(0.1)},
    children: listView,
  ),
),

UI 目前的样子

Thats how the UI looks

请帮助我解决这个问题,或者给我任何关于这篇文章的参考或关于密钥的任何内容或我的逻辑中的任何建议,比如做这件事的常见做法或其他事情。 谢谢

3 个答案:

答案 0 :(得分:0)

listView.removeAt(i);

这一行是许多错误的根源。每当您修复一个时,就会弹出另一个。首先,您从未初始化 i。但不要去那里。这是一个兔子洞。根据过去某个时间正确的索引从该列表中删除是愚蠢的差事。

改用 removeWhere 并通过使用数据(也许您有 id?)而不只是索引找到正确的行。

一般来说,概念就是持有数据,重建视图。重建视图相对便宜。从数据中移除更容易完成,因为您拥有……嗯……当时的数据。

答案 1 :(得分:0)

您的 StatefulWidget 可能不需要 DataPreviewScreen

相反,您应该从您的 DataModel 中获取数据,我想应该是某种 Provider

以下是使用 Flutter HooksRiverpod 的示例:

enter image description here

My dataProvider 提供状态(一个 List>)和删除条目的功能。我在 AppWidget 中聆听 State 并将数据提供给 DataPreviewScreen,一个纯粹的 StatelessWidget

当用户点击删除按钮时,我要求我的 dataProvider 删除它,然后新状态流入。

完整的源代码,便于复制粘贴

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:faker/faker.dart';

void main() {
  runApp(ProviderScope(child: AppWidget()));
}

class AppWidget extends HookWidget {
  const AppWidget({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final data = useProvider(dataProvider.state);
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      home: DataPreviewScreen(data: data),
    );
  }
}

class DataPreviewScreen extends StatelessWidget {
  final List<Map<String, dynamic>> data;

  const DataPreviewScreen({Key key, this.data}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Table(
          columnWidths: {
            0: FlexColumnWidth(4),
            1: FlexColumnWidth(4),
            2: FlexColumnWidth(2),
            3: FlexColumnWidth(1),
          },
          children: [
            TableRow(
              children: [
                RowHead(heading: 'Site'),
                RowHead(heading: 'UserName'),
                RowHead(heading: 'Password'),
                RowHead(heading: ' '),
              ],
            ),
            ...data
                .map(
                  (item) => TableRow(
                    key: UniqueKey(),
                    children: [
                      RowCell(item['site_name']),
                      RowCell(item['user_name']),
                      RowCell(item['password']),
                      TextButton(
                        onPressed: () =>
                            context.read(dataProvider).delete(item['_id']),
                        child: Icon(
                          Icons.delete,
                          color: Color(0xFF9F1D35),
                          size: 20.0,
                        ),
                      ),
                    ],
                  ),
                )
                .toList(),
          ],
        ),
      ),
    );
  }
}

class RowHead extends StatelessWidget {
  final String heading;

  const RowHead({Key key, this.heading}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 36.0),
      child: Text(
        heading,
        style: TextStyle(fontWeight: FontWeight.bold),
      ),
    );
  }
}

class RowCell extends StatelessWidget {
  final String text;

  const RowCell(this.text, {Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Expanded(child: Text(text));
  }
}

final dataProvider =
    StateNotifierProvider<DataNotifier>((ref) => DataNotifier());

class DataNotifier extends StateNotifier<List<Map<String, dynamic>>> {
  DataNotifier([List<Map<String, dynamic>> state]) : super(state ?? dummyData);

  delete(String id) {
    // delete from database
    state = state.where((item) => item['_id'] != id).toList();
  }
}

final faker = Faker();
final dummyData = List.generate(
  20,
  (index) => {
    '_id': 'id_${faker.randomGenerator.string(6, min: 6)}',
    'site_name': faker.company.name(),
    'user_name': faker.person.name(),
    'password': faker.randomGenerator.string(8, min: 8),
  },
);

备注

  • 您可能应该为您的数据定义一个明确的实体,而不是使用 Map 类型。看看freezed
  • 除了监听 AppWidget 中的状态,DataPreviewScreen 也可以是一个 HookWidget,同时监听和使用 dataProvider

答案 2 :(得分:0)

与其尝试直接从 UI 中删除项目,不如从列表中删除元素,并且您的小部件取决于数据。

但是,您在这里误用了 StatefulWidget。

一般来说,当我们使用 StatefulWidget 时,我们会在 didChangeDependencies() 方法中获取数据,并在 setState((){}) 方法中更新这些数据。调用 setState() 将根据更新的数据重建 UI(成本低)。

在这里,您的小部件数据取决于其父级,因此:

  • 或者您有一个依赖于其父级数据的无状态小部件,并且当父级数据发生变化时,您在父级上调用 setState() 它将使用新数据重建子级,
  • 或者你有一个 StatefulWidget 来检索数据并在 setState() 方法中更新它。

例如:

在您的 didChangeDependencies() 方法中:

_myDataList = DataModel.instance.getMyData();

在您的 Build() 方法中:

Container(
              child: Table(
                columnWidths: {3: FractionColumnWidth(0.1)},
                children: listCreater(),
              ),
            )

还有你的 listCreater() 方法:

List<TableRow> listCreater() {
    return [
      TableRow(
        children: [
          RowHead(heading: 'Site'),
          RowHead(heading: 'UserName'),
          RowHead(heading: 'Password'),
          RowHead(heading: ' '),
        ],
      ),
      for (var data in myDataList) 
       TableRow(
        key: UniqueKey(), // This is probably not usefull
        decoration: BoxDecoration(shape: BoxShape.rectangle), // BoxShape.rectangle is the default value, you can also eventually remove this line
        children: [
          RowCell(data['site_name']),
          RowCell(data['user_name']),
          RowCell(data['password']),
          Container(
            height: 35.0,
            color: Colors.white,
            child: FlatButton(
              onPressed: () {
                DataModel.instance.delete(data['_id']);
                setState(() {
                  myDataList.removeWhere((e)=>e.id == data.id);
                });
              },
              child: Icon(
                Icons.delete,
                size: 20.0,
              ),
            ),
          ),
        ],
      )

    ];
  }