Как динамически добавить виджет?

Всем привет, мне с сервера приходит json, и если is_new == treu - мне нужно отображать контейнер. Как я могу это реализовать?

Вот что я пытаюсь сделать -

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    if(HomeStore.HSisNew[itemIndex] == true) {
      return Container(
  child: Text('new'),
}
)
)

Ответы (1 шт):

Автор решения: Михаил Ребров

Асинхронные вычисления

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

Стоит понимать, что далеко не все задачи и вычисления происходят быстро и незаметно, а также далеко не все задачи и вычисления зависят от нашего приложения.
Есть множество примеров длительных вычислений и задач, которые от нас не зависят:

  • построение отчетов
  • обращение по сети к веб-сервисам
  • обращение к базе данных
  • и пр.

Все эти задачи, как правило, требуют времени.
Но вот незадача: если они будут выполняться последовательно в том же потоке что и пользовательский интерфейс, то приложение просто "повиснет" и не будет отвечать ни на какие действия пользователя до тех пор, пока данная длительная задача не завершится.

Именно поэтому во многих современных фреймворках(включая Android) запрещается выполнять такие задачи в основном потоке. А основной поток предназначается только для работы с пользовательским интерфейсом.

В то время как длительные по выполнению задачи выполняются в отдельном потоке и по завершению их выполнения, об этом уведомляется пользовательский интерфейс и происходит его обновление.

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

И так появились языковые конструкции async-await.

В языки начали добавлять

  • ключевое слово async, которое ставится во время определения метода, который будет выполнять асинхронную задачу.
    выполнение данного метода автоматически будет производиться в отдельном потоке
  • ключевое слово await, которое опционально может ставится перед ВЫЗОВОМ асинхронного метода для ЯВНОГО ожидания результатов работы асинхронного метода.
    данное ключевое слово помогает как бы "отключить" асинхронный режим для конкретного вызова и осознанно остановить поток в котором происходит его вызов и дождаться результатов работы данного метода
    В качестве примера, такое может понадобиться в методе построения того же самого отчета: нам последовательно нужно делать обращений к базе данных и без результатов работы предыдущего вызова выполнение оставшейся части метода не имеет никакого смысла и нам проще остановиться и явно подождать.

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

Остается одна проблема...
Как получить результат выполнения асинхронного запроса?
Это все здорово, что больше не нужно писать кучу дополнительного кода, но эти асинхронные методы должны что-то возвращать и этим чем-то нужно уметь как-то пользоваться...

Все результаты работы асинхронных методов в Dart'e оборачиваются в экземпляры класса Future<T>

Соответственно если вы в асинхронном методе выполняете

       // возвращаем строку
return "результат работы моего асинхронного метода";

То он Вам вернёт Future<String>

Объект типа Future предоставляет интерфейс для доступа к результатам работы асинхронного метода.

А если быть точнее то это метод then() в который можно передать лямбда-выражение, которое должно будет обработать результаты вашего асинхронного метода:

Future<String> resultOfAsyncMethod = myAwesomeAsyncMethod();
resultOfAsyncMethod.then((value) => print(value))
                   .catchError((error) => handleError(error));

Что предлагает Flutter?

Для таких вещей Flutter рекомендует использовать FutureBuilder, в который можно передать:

  • результаты работы асинхронного метода в виде Future<T>
    в нашем конкретном кейсе мы передаем Future<http.Response> ибо именно его возвращает http.get()
  • и builder который на основе данного результата сможет вам построить ваш виджет

Что это вообще такое?
На самом деле очень важный и полезный вопрос, потому что FutureBuilder<T> это обычный виджет!

class FutureBuilder<T> extends StatefulWidget

Соответственно, вы его можете вставлять и использовать везде где предполагалась работа с виджетами и вы его модете вставлять во все child, children и пр. поля предполагающие вставку виджета.

За одной единственной особенностью:
Данный виджет умеет работать с результатами работы асинхронных методов и сможет сконструировать в теле билдера любой виджет (они в зависимости от результатов работы асинхронного метода и его статуса могут отличаться)

в упрощенном виде это выглядит так:

new FutureBuilder<http.Response>(
          // делаете запрос и передаете его во future
  future: http.get(Uri.parse('https://jsonplaceholder.typicode.com/users')), 
          // определяете Builder, который сотворит нужный виджет
  builder: (BuildContext context, AsyncSnapshot<http.Response> snapshot) {
    // на вход нам приходит AsyncSnapshot<http.Response>
    // и было бы неплохо проверить его состояние
    switch (snapshot.connectionState) {
      case ConnectionState.none: return new Text('Ничего не происходит');
      case ConnectionState.waiting: return new Text('Ждем результаты запроса...');
      default:
        // проверяем не было ли ошибки во время выполнения запроса
        if (snapshot.hasError){
          return new Text('Error: ${snapshot.error}');
        }
        else{
          // ну и если все ок, то получаем тело запроса
          String? body = snapshot.data?.body;
          return new Text('Response body: ${body}');
        }
    }
  },
)

Вот рабочий пример получения списка пользователей из REST API

  1. тут мы делаем запрос к API,
  2. и создаем на основе запроса список пользователей
new FutureBuilder<http.Response>(
  future: http.get(Uri.parse('https://jsonplaceholder.typicode.com/users')), // a Future<String> or null
  builder: (BuildContext context, AsyncSnapshot<http.Response> snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.none: return new Text('Ничего не происходит');
      case ConnectionState.waiting: return new Text('Ждем результаты запроса...');
      default:
        if (snapshot.hasError){
          return new Text('Error: ${snapshot.error}');
        }
        else{
          String? body = snapshot.data?.body;
          List<dynamic> users =jsonDecode(body!);
          List<Card> cards = <Card>[];
          for(Map<String,dynamic> user in users) {
            cards.add(Card(
                child: ListTile(
                    title: Text(user['name'])
                )
            ));
          }
          return Expanded(child: ListView(
              children: cards
          ));
        }
    }
  },
)

введите сюда описание изображения

→ Ссылка