Как динамически добавить виджет?
Всем привет, мне с сервера приходит 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
- тут мы делаем запрос к API,
- и создаем на основе запроса список пользователей
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
));
}
}
},
)
