Вопрос или проблема
Future<void> addExpenseInDatabase(BuildContext context) async {
Map<String, dynamic> splitByMap = {};
Map<String, dynamic> paidByMap = {};
for (var element in splitByList) {
String dateTime = DateTime.now().microsecondsSinceEpoch.toString();
splitByMap[dateTime] = element.toJson();
}
for (var element in paidByList) {
String dateTime = DateTime.now().microsecondsSinceEpoch.toString();
paidByMap[dateTime] = element.toJson();
}
getIt<CustomDialogs>().showCircularDialog(context);
Future.delayed(Duration(seconds: 5)).then((value) async {
DateFormat formatter = DateFormat('yyyy-MM-dd');
formattedDate.value = formatter.format(selectedDate.value);
if (!isNullEmptyOrFalse(amountController.value.text)) {
if (paidByList.isNotEmpty && splitByList.isNotEmpty) {
await FirebaseHelper.addAndEditExpense(
isEdit: isFromExpenseEdit.isTrue,
groupID: groupModel.value.groupID.toString(),
expenseId: expenseList.value.expenseID.toString(),
description: descriptionController.value.text,
iconPath: selectedCategory.value.icon.toString(),
currency: selectedCurrency.value,
amount: amountController.value.text,
expenseDate: formattedDate.value,
notes: noteController.value.text,
paidByList: paidByMap,
splitByList: splitByMap,
).then((value) {
activityList.add(ActivityModel(
image: groupModel.value.groupImage.toString(),
groupType: groupModel.value.groupType.toString(),
title: isFromExpenseEdit.isTrue == true
? "Вы обновили '${descriptionController.value.text}' в '${groupModel.value.groupName}'"
: "Вы добавили '${descriptionController.value.text}' в '${groupModel.value.groupName}'",
date: DateTime.now().toString(),
));
List<Map<String, dynamic>> activityListJson = activityList.map((friend) => friend.toJson()).toList();
box.write(ArgumentConstant.activityList, activityListJson);
List<Map<String, dynamic>> splitByListJson = [];
box.write(ArgumentConstant.passSplitByList, splitByListJson);
List<Map<String, dynamic>> paidByListJson = [];
box.write(ArgumentConstant.passPaidByList, paidByListJson);
getIt<CustomDialogs>().hideCircularDialog(context);
Get.back();
},);
} else {
getIt<CustomDialogs>().hideCircularDialog(context);
Fluttertoast.showToast(msg: "Введите, кто оплатил и с кем делится");
}
} else {
getIt<CustomDialogs>().hideCircularDialog(context);
Fluttertoast.showToast(msg: "Введите описание и сумму");
FocusScope.of(context).requestFocus(focusNode);
}
});
}
Мне нужно одновременно вставить несколько данных в реальную базу данных Firebase. Эта функция отвечает за добавление записи расходов в базу данных и обновление локальных компонентов интерфейса в зависимости от того, была ли операция успешной или нет. Она включает валидацию данных, асинхронные операции с базой данных и обратную связь для интерфейса.
Ответ или решение
Ваша функция для добавления расходов в Firebase Realtime Database может зависать по нескольким причинам, включая асинхронные операции и взаимодействие с пользовательским интерфейсом. Давайте подробно рассмотрим ваш код и предложим улучшения, которые помогут избежать зависаний и улучшить производительность.
1. Проблема с асинхронностью
Использование Future.delayed
может привести к неожиданному поведению, поскольку код внутри then
может выполняться не по порядку. Лучше использовать await
, чтобы гарантировать последовательность выполнения:
getIt<CustomDialogs>().showCircularDialog(context);
await Future.delayed(Duration(seconds: 5)); // Дождитесь, прежде чем продолжить
2. Дублирование ключей в splitByMap
и paidByMap
Вы используете microsecondsSinceEpoch
в качестве ключа для обеих карт. Если функция вызывается несколько раз в течение микросекунд, это может привести к конфликтам ключей. Вместо этого рекомендуется использовать уникальные идентификаторы, такие как UUID:
import 'package:uuid/uuid.dart';
final Uuid uuid = Uuid();
for (var element in splitByList) {
splitByMap[uuid.v4()] = element.toJson();
}
for (var element in paidByList) {
paidByMap[uuid.v4()] = element.toJson();
}
3. Асинхронная операция записи в базу данных
Проблемы с зависанием могут также возникать из-за длительных операций записи в базу данных. Убедитесь, что метод FirebaseHelper.addAndEditExpense
возвращает Future<void>
и выполняет записи асинхронно.
Если метод не реализует асинхронность должным образом, это может блокировать поток, что приводит к зависанию.
4. Обработка ошибок
Добавьте обработку ошибок для вашей базы данных и отобразите ошибки пользователю:
try {
await FirebaseHelper.addAndEditExpense(/* ваши параметры */);
// Операция успешна
} catch (e) {
// Обработка ошибки
getIt<CustomDialogs>().hideCircularDialog(context);
Fluttertoast.showToast(msg: "Ошибка при добавлении расходов: ${e.toString()}");
return;
}
5. Рекомендации по улучшению кода
Ваш код также можно улучшить за счет повышения читаемости и управления состоянием. Например, вместо многократных вызовов getIt<CustomDialogs>().hideCircularDialog(context)
можно использовать finally
, чтобы гарантировать, что диалог закроется независимо от успешности операции:
getIt<CustomDialogs>().showCircularDialog(context);
try {
await Future.delayed(Duration(seconds: 5)); // Отложите выполнение
// ваша логика добавления расходов
} catch (e) {
// обработка ошибок
} finally {
getIt<CustomDialogs>().hideCircularDialog(context); // Закрытие диалога в любом случае
}
Полный вариант функции
Вот улучшенная версия вашей функции с учетом всех вышеописанных рекомендаций:
Future<void> addExpenseInDatabase(BuildContext context) async {
Map<String, dynamic> splitByMap = {};
Map<String, dynamic> paidByMap = {};
final Uuid uuid = Uuid();
for (var element in splitByList) {
splitByMap[uuid.v4()] = element.toJson();
}
for (var element in paidByList) {
paidByMap[uuid.v4()] = element.toJson();
}
getIt<CustomDialogs>().showCircularDialog(context);
try {
await Future.delayed(Duration(seconds: 5)); // Задержка выполнения
DateFormat formatter = DateFormat('yyyy-MM-dd');
formattedDate.value = formatter.format(selectedDate.value);
if (!isNullEmptyOrFalse(amountController.value.text)) {
if (paidByList.isNotEmpty && splitByList.isNotEmpty) {
await FirebaseHelper.addAndEditExpense(
isEdit: isFromExpenseEdit.isTrue,
groupID: groupModel.value.groupID.toString(),
expenseId: expenseList.value.expenseID.toString(),
description: descriptionController.value.text,
iconPath: selectedCategory.value.icon.toString(),
currency: selectedCurrency.value,
amount: amountController.value.text,
expenseDate: formattedDate.value,
notes: noteController.value.text,
paidByList: paidByMap,
splitByList: splitByMap,
);
// Обновление интерфейса
activityList.add(ActivityModel( /* параметры */ ));
// Сохранение в локальное хранилище
box.write(ArgumentConstant.activityList, activityListJson);
Get.back();
} else {
Fluttertoast.showToast(msg: "Введите, кто заплатил и как поделить расходы");
}
} else {
Fluttertoast.showToast(msg: "Введите описание и сумму");
FocusScope.of(context).requestFocus(focusNode);
}
} catch (e) {
Fluttertoast.showToast(msg: "Ошибка: ${e.toString()}");
} finally {
getIt<CustomDialogs>().hideCircularDialog(context);
}
}
Старайтесь избегать длительных операций в главном потоке и следите за уникальностью ключей в ваших картах. Это поможет избежать зависаний и улучшить общую производительность приложения.