Флаттер: Множественная вставка данных в реальную базу данных Firebase, приложение зависает

Вопрос или проблема

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);
  }
}

Старайтесь избегать длительных операций в главном потоке и следите за уникальностью ключей в ваших картах. Это поможет избежать зависаний и улучшить общую производительность приложения.

Оцените материал
Добавить комментарий

Капча загружается...