我在 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 目前的样子
请帮助我解决这个问题,或者给我任何关于这篇文章的参考或关于密钥的任何内容或我的逻辑中的任何建议,比如做这件事的常见做法或其他事情。 谢谢
答案 0 :(得分:0)
listView.removeAt(i);
这一行是许多错误的根源。每当您修复一个时,就会弹出另一个。首先,您从未初始化 i
。但不要去那里。这是一个兔子洞。根据过去某个时间正确的索引从该列表中删除是愚蠢的差事。
改用 removeWhere
并通过使用数据(也许您有 id?)而不只是索引找到正确的行。
一般来说,概念就是持有数据,重建视图。重建视图相对便宜。从数据中移除更容易完成,因为您拥有……嗯……当时的数据。
答案 1 :(得分:0)
您的 StatefulWidget
可能不需要 DataPreviewScreen
。
相反,您应该从您的 DataModel
中获取数据,我想应该是某种 Provider
。
以下是使用 Flutter Hooks 和 Riverpod 的示例:
My dataProvider
提供状态(一个 List
当用户点击删除按钮时,我要求我的 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),
},
);
AppWidget
中的状态,DataPreviewScreen
也可以是一个 HookWidget,同时监听和使用 dataProvider
。答案 2 :(得分:0)
与其尝试直接从 UI 中删除项目,不如从列表中删除元素,并且您的小部件取决于数据。
但是,您在这里误用了 StatefulWidget。
一般来说,当我们使用 StatefulWidget 时,我们会在 didChangeDependencies() 方法中获取数据,并在 setState((){}) 方法中更新这些数据。调用 setState() 将根据更新的数据重建 UI(成本低)。
在这里,您的小部件数据取决于其父级,因此:
例如:
在您的 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,
),
),
),
],
)
];
}