Флаттер: как уведомить основной виджет после вставки всех данных в базу данных - PullRequest
0 голосов
/ 06 марта 2020

В моем StatefulWidget в initState у меня есть метод:

  @override
  void initState() {
    super.initState();
    getMyChannels();
  } 

getMyChannels метод запустить метод http, чтобы получить данные из службы и сохранить данные в базе данных:

  void getMyChannels() async {
    // get data from servise and store them
    _myChannel = await MyToolsProvider()
        .getChannelMe("df6b88b6-f47d****");
    getToolsRelToChannels(); // get data from database
    setState(() {});
  }

Как видите, у меня есть метод getToolsRelToChannels. Этот метод извлекает данные из локальной базы данных. Эти данные должны храниться методом await MyToolsProvider() .getChannelMe("df6b88b6-f47d****"); в базе данных.

Это метод .getChannelMe:

  Future<ProgramsByActiveToolsModel> getChannelMe(String auth) async {
    Map<String, dynamic> header = {
      'Content-Type': "application/json",
      "Authorization": 'Bearer $auth'
    };
    try {
      var result = await NetworkCLient()
          .getRequest(url: '$URL/api/me', header: header);
      if (result != null) {
        var programsByActiveToolsModel =
            ProgramsByActiveToolsModel.fromJson(result);
        if (programsByActiveToolsModel.responseCode == 200) {
          programsByActiveToolsModel.data.forEach((item) async {
            await DBProvider.db.addMyTools(item);
            saveToolsbyChannelId(header, item.id);
          });
          return programsByActiveToolsModel;
        } else
          return null;
      }
    } catch (e) {
      throw e;
    }
  }

В методе addMyTools я храню все данные в одной таблице моей базы данных. и я вызываю saveToolsbyChannelId метод для каждого элемента. Это основные данные, которые мне тоже нужны.

  Future<void> saveToolsbyChannelId(Map header, int channelId) async {
    header["Authorization"] = 'Bearer 92122926-****';

    try {
      var count = await DBProvider.db.getCountToolsbyChannelId(channelId);
      if (count == 0) {
        var result = await NetworkCLient().getRequest(
            url: '$URL/api/channel/$channelId', header: header);
        if (result != null) {
          var logToolsRunModel = LogTools.LogToolsRunModel.fromJson(result);
          if (logToolsRunModel.responseCode == 200) {
            logToolsRunModel.data.forEach((item) {
              DBProvider.db.addTools(item);
            });
          }
        }
      }
    } catch (e) {
      throw e;
    }
  }

После извлечения данных из моего сервиса я храню эти данные в базе данных sqlite. Теперь это await MyToolsProvider().getChannelMe работа выполнена!

Пришло время объяснить getToolsRelToChannels();:

  void getToolsRelToChannels() async {

    _toolsRun =
        await MyToolsProvider().getToolsRelatedMyChannel(_selectedChannel);
    setState(() {});
  }

getToolsRelatedMyChannel этот метод должен ждать, пока все данные этого метода DBProvider.db.addTools(item) не будут добавлены в базу данных, а после вставки моего виджета должны быть созданы заново.

  Future<List<ToolsByChannelIdDbModel>> getToolsRelatedMyChannel(
      int channelId) async {
    List<ToolsByChannelIdDbModel> list = List<ToolsByChannelIdDbModel>();

    try {
      var result = await DBProvider.db.getToolsById(channelId);
      result.forEach((item) {
        list.add(ToolsByChannelIdDbModel.fromJson(item));
      });
      return list;
    } catch (e) {
      print(e);
    }
  }
}

но мой код неверен, потому что после запуска await MyToolsProvider().getChannelMe(***) getToolsRelToChannels выполняется метод и пока ничего не сохраняется в базе данных для извлечения !!!

Как я могу уведомить свой основной виджет после окончания вставки базы данных ??? Я не могу использовать FutureBuilder, потому что при первом запуске моя база данных пуста !!!

Ответы [ 2 ]

1 голос
/ 06 марта 2020

Вы должны await saveToolsbyChannelId в getChannelMe и await DBProvider.db.addTools(item); в saveToolsbyChannelId, в противном случае вы пытаетесь прочитать из базы данных до того, как в нее будут записаны данные. Это предполагает, что остальная часть вашего кода верна, что мы не можем сказать наверняка, потому что есть много переменных, таких как _selectedChannel, о которых мы ничего не знаем.

0 голосов
/ 06 марта 2020

ОБНОВЛЕНО - проверьте ниже. То, что вы хотите, это ждать ВСЕХ async операций. В вашем случае

@override
  void initState() async {
    super.initState();
    await getMyChannels();
  }

и

await saveToolsbyChannelId(header, item.id);

, а если DBProvider.db.addTools асинхронный, то

await DBProvider.db.addTools(item);

ОБНОВЛЕНИЕ:

Поскольку невозможно сделать initState() async, вы можете использовать обратный вызов в будущем:

@override
  void initState() {
    super.initState();
     var channelsFuture = getMyChannels();
     channelsFuture.then((resp){
         setState(() {});
     });
  }

Я бы посоветовал вам пересмотреть весь ваш подход (из чистой архитектуры точка зрения). Посмотрите на BLo C шаблон . Это сводится к следующему:

  • Есть еще один класс (называемый BLo C), который обрабатывает бизнес-логи c (получение данных из сети и добавление в базу данных). Класс виджета обрабатывает только пользовательский интерфейс.
  • Вы запускаете асинхронную обработку в классе BLo C из initState() виджета. В виджете есть потоковый прослушиватель, который прослушивает завершение задачи BLo C.
  • . Как только BLo C завершает асинхронную задачу c, он уведомляет виджет с помощью поток. Слушатель потока в виджете знает, что данные готовы, и он обновляет интерфейс, вызывая setState ();
  • Done !!
...