每次重新打开页面时,我创建的列表视图都会不断重复。我在状态小部件中调用了一种方法来调用 Sqflite 包中的列表,但我希望该方法仅被调用一次。我尝试使用 dispose,但显然,无法处理方法(?) 代码片段如下,谢谢
class TodoList extends StatefulWidget {
const TodoList({Key? key}) : super(key: key);
@override
State<TodoList> createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
getAllTodos() async {
List<TodoModel> _todoList = await TodoDatabase.instance.queryAlltodos();
_todoList.forEach((todo) {
var todoModel = TodoModel(
title: todo.title,
description: todo.description,
created: todo.created,
isChecked: todo.isChecked);
context.read<TodoProvider>().todoitem.add(todoModel);
});
}
@override
void initState() {
super.initState();
getAllTodos();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
final theme = Theme.of(context);
return FutureBuilder<List<TodoModel>>(
future: TodoDatabase.instance.queryAlltodos(),
builder: (BuildContext context, AsyncSnapshot<List<TodoModel>> snapshot) {
if(!snapshot.hasData) {
return const Center(child: CircularProgressIndicator(color: Colors.amber),);
} return
Padding(
padding: const EdgeInsets.all(10),
child: Consumer<TodoProvider>(
builder: (context, value, child) => (value.newTodo.isEmpty)
? const Center(child: Text('No Todos'))
: ListView.separated(
itemCount: value.newTodo.length,
physics: const BouncingScrollPhysics(),
separatorBuilder: (context, index) => SizedBox(
height: size.height * 0.01,
),
itemBuilder: (_, i) => Slidable(
key: ValueKey(i),
startActionPane: ActionPane(
motion: StretchMotion(),
children: [
SlidableAction(
backgroundColor: Colors.green,
foregroundColor: Colors.black,
icon: Icons.edit,
label: 'Edit',
onPressed: (context) async {
TodoModel todo = value.newTodo[i];
editTodo(context, todo);
// Navigator.of(context).pushNamed(
// EditScreen.editscreen,
// arguments: todoData
// );
showSnackBar(
context, 'Edit ${todo.title}?');
},
)
],
),
endActionPane: ActionPane(
motion: ScrollMotion(),
children: [
SlidableAction(
backgroundColor: Colors.red,
foregroundColor: Colors.black,
spacing: 0,
icon: Icons.delete,
label: 'delete',
onPressed: (context) async {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
20),
),
title:
const Text('Delete this TODO',style: TextStyle(color: Colors.amber)),
content:
const Text('Are you Sure?'),
actions: [
TextButton(
child: const Text('No',style: TextStyle(color: Colors.amber)),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: const Text('Yes',style: TextStyle(color: Colors.amber)),
onPressed: () async {
TodoModel todo =
value.newTodo[i];
String result =
await context
.read<
TodoProvider>()
.deleteToDo(
todo);
if (result != 'OK!') {
return showSnackBar(
context,
result);
} else {
showSnackBar(
context,
'Deleted');
Navigator.pop(
context);
}
})
]);
});
})
],
),
child: InkWell(
onTap: () {},
child: ListTile(
tileColor: theme.primaryColorLight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
leading: Checkbox(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(3)),
value: value.newTodo[i].isChecked,
activeColor: theme.primaryColorDark,
checkColor: theme.primaryColorLight,
onChanged: (_) async {
// return setState(() { // this works too
// todo.isChecked = value;
// });
TodoModel todo = value.todoitem[i];
await context
.read<TodoProvider>()
.updateTodo(todo);
},
),
title: Text(value.newTodo[i].title),
subtitle: Text(value.newTodo[i].description),
),
),
))),
);
});
}
}
void editTodo(BuildContext context, TodoModel todo) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => EditScreen(todo: todo),
),
);
}
重新加载时先清除列表。这是例子
if(listName!=null) {
listName.clear();
}
试试这个:
getAllTodos() async {
List<TodoModel> _todoList = [];
_todoList = await TodoDatabase.instance.queryAlltodos();
_todoList.forEach((todo) {
var todoModel = TodoModel(
title: todo.title,
description: todo.description,
created: todo.created,
isChecked: todo.isChecked);
context.read<TodoProvider>().todoitem.add(todoModel);
});
}
你的问题似乎出在代码的内层。
我也遇到了类似的问题,数据重复。请看下面的截图:
我的问题在于提供者(Riverpod)对存储库的调用。重建屏幕时我一次又一次地拨打电话(我不确定“重建”是否是一个好的做法)。
我在该方法的使用中添加了一个条件,这解决了问题。
查看
// ADDED LINE
评论在哪里。
final pruebasListProvider =
StateNotifierProvider<PruebasListNotifier, List<Prueba>>((ref) {
final getPruebasList = ref.watch(pruebasRepositoryProvider).getPruebasList;
return PruebasListNotifier(fetchPruebasList: getPruebasList);
});
typedef GetPruebasListCallback = Future<List<Prueba>> Function();
class PruebasListNotifier extends StateNotifier<List<Prueba>> {
bool isLoading = false;
bool isLoaded = false; // ADDED LINE
GetPruebasListCallback fetchPruebasList;
PruebasListNotifier({required this.fetchPruebasList}) : super([]);
Future<void> loadPruebasList() async {
if (isLoading) return;
isLoading = true;
if (isLoaded) return; // ADDED LINE
final List<Prueba> pruebas = await fetchPruebasList();
state = [...state, ...pruebas];
await Future.delayed(const Duration(milliseconds: 300));
isLoading = false;
isLoaded = true; // ADDED LINE
}
}
已解决:
我想说你在这一行也有类似的问题:
future: TodoDatabase.instance.queryAlltodos(),
但这是我最好的猜测。我看不到该方法的内部。