#flutter #dart #flutter-futurebuilder
Вопрос:
Я пытаюсь работать с параллелизмом в Flutter, поэтому у меня есть три запроса на получение от сервера, и мне нужно получать от них значения одновременно. Каждый запрос был проанализирован в виде модели. Теперь я пытаюсь понять, как я могу объединить все модели в один список с тремя get-запросами и запустить этот окончательный список в ListView.builder. Также моя задача довольно сложна для такого ничтожества, как я, потому что, помимо одновременного анализа всех трех списков, мне нужно их отфильтровать, потому что, как сказано в API, все запросы вложены и зависят от идентификатора. Как я могу это решить?
Это мои модели:
ScheduleVariants{
final int mrId;
final int mvId;
ScheduleVariants({this.mrId, this.mvId});
}
FlightCard{
final int mvId;
final int stId;
FlightCard({this.mrId, this.stId});
}
Stop{
final int stId;
Stop({this.stId})
}
Мне нужно получить окончательные значения из моделей остановок. Как вы можете видеть, все модели имеют вложенную структуру, и я не могу этого избежать.
Теперь я пытаюсь сделать параллельный вызов, как это:
class Dire extends StatefulWidget {
final int mrId;
final int mvId;
final int stId;
const Dire({Key key, this.mrId, this.mvId, this.stId}) : super(key: key);
@override
_DireState createState() => _DireState();
}
class _DireState extends State<Dire> {
@override
void initState() {
fetchData();
super.initState();
stops.where((element) => element.stId == widget.stId).toList();
card.where((element) => element.mvId == widget.mvId).toList();
sheduler.where((element) => element.mrId == widget.mrId).toList();
}
List<ScheduleVariants> sheduler;
List<FlightCard> card;
List<Stop> stops;
Future fetchData() async {
String username = '';
String password = '';
String basicAuth =
'Basic ' base64Encode(utf8.encode('$username:$password'));
print(basicAuth);
final result = await Future.wait([
http.get(
Uri.parse(
"http://mysecurelink/getMarshVariants.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getFlightCard.php?fmt=jsonamp;mv_id"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getStops.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
]);
setState(() {
sheduler = json.decode(result[0].body) as List;
card = json.decode(result[1].body) as List;
stops = json.decode(result[2].body) as List;
});
}
@override
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder(
future: fetchData(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: stops.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(stops[index].stTitle),
);
});
} else {
return CircularProgressIndicator();
}
}));
}
}
В конце основной задачей является выполнение трех параллельных запросов, отфильтрованных по идентификатору, и получение данных из модели остановок. Как вы можете сделать это правильно?
Я нуб и не понимаю, как правильно это делать, и я буду очень рад, если кто-нибудь поможет мне разобраться в этой задаче.
Комментарии:
1. Что вы подразумеваете под «отфильтровать их»? Что вы подразумеваете под «получением данных из модели остановок»? Что такое
fetchList
? Вам нужно будет улучшить свой вопрос, добавив несколько примеров, потому что трудно понять, чего вы хотите. Я также рекомендовал бы вам также исправить форматирование.2. Привет @wxker Я внес некоторые изменения для лучшего понимания того, что я пытаюсь сделать. И я постараюсь лучше объяснить идею в комментариях: моя главная задача-отфильтровать остановки по идентификатору. Но разработчики API объяснили мне, что для выполнения этой задачи мне сначала нужно использовать модель варианта расписания, затем через модель ScheduleVariant я должен использовать модель FlightCard, и уже из FlightCard по идентификатору я получаю все отфильтрованные остановки. Я попытался объединить эти модели вместе, но все равно получаю пустой экран, и мне посоветовали использовать три get-запроса к серверу одновременно.
3. @wxker Я ищу лучшее решение, потому что я никогда не пытался решить такую задачу, как сейчас. Я думал, что мне нужно объединить все модели и отфильтровать их с помощью какого-то алгоритма, но это также не сработало, или я просто сделал что-то не так
Ответ №1:
Я собираюсь сделать некоторые предположения, потому что информации недостаточно:
Dire
представляет собой комбинацию трех классовScheduleVariants
,FlightCard
иStop
гдеScheduleVariants.mvId == FlightCard.mvId
иFlightCard.stId == Stop.stId
.- Все три API вернут список в качестве ответа.
- Все
ScheduleVariants
они уникальныmvId
, всеFlightCards
они уникальныmvId
иstId
, и всеStops
они уникальныstId
.
Нет ничего плохого в том, как вы выполняете несколько асинхронных запросов. Future.wait
в этом случае принимает список фьючерсов и возвращает список ответов. Проблема, с которой вы столкнулись, заключается в том, что вы просто не знаете, как объединить ответы на три запроса API.
Вы, похоже, также путаете использование состояния с использованием фьючерсов. По крайней мере, в предоставленном вами фрагменте кода не похоже, что вам когда-либо понадобится изменять состояние после его инициализации, что означает, что вам вообще не нужно использовать состояние.
Dire
должен быть просто класс модели.
class Dire {
final ScheduleVariants scheduleVariant;
final FlightCard flightCard;
final Stop stop;
Dire(this.scheduleVariant, this.flightCard, this.stop);
}
В вашем виджете, где вы хотите получить Dires
доступ к API, вы можете использовать эту функцию в FutureBuilder
:
Future<List<Dire>> fetchData() async {
String username = '';
String password = '';
String basicAuth =
'Basic ' base64Encode(utf8.encode('$username:$password'));
print(basicAuth);
final result = await Future.wait([
http.get(
Uri.parse(
"http://mysecurelink/getMarshVariants.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getFlightCard.php?fmt=jsonamp;mv_id"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getStops.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
]);
flightCardMap = HashMap.fromIterable(json.decode(result[1].body), (fc) => fc["mvId"], (fc) => FlightCard(fc));
stopMap = HashMap.fromIterable(json.decode(result[2].body), (s) => s["stId"], (s) => Stop(s));
return json.decode(result[0].body).map((sv) => {
flightCard = flightCardMap[sv["mvId"]];
return Dire(ScheduleVariants(sv), flightCard, stopMap[flightCard["stId"]]);
}).toList();
}
Отказ от ответственности: Я не проверял этот фрагмент кода на наличие синтаксических ошибок, так что некоторые из них могут быть, но общая идея есть.
Комментарии:
1. большое вам спасибо. Я хочу подтвердить, что я все правильно понял из вашего кода (я только учусь и стараюсь ничего не пропустить). И поэтому, чтобы объединить все 3 запроса API, мне пришлось создать отдельную модель, в которой расположены параметры расписания, FlightCard, Stop. Правильно ли я это понял? После этого используйте Future builder, где я использую все 3 URL-адреса и делаю get-запросы, а затем использую хэш-карту для каждого запроса? Также я хотел спросить, следует ли мне использовать @JsonSerializable() для хорошей практики или это не обязательно?
2. Используйте
Future.wait
для получения ответов на все 3 запроса API. Преобразуйте ответы на полетные карточки и остановки в хэш-карты с соответствующим идентификатором в качестве ключа. Выполните итерацию по вариантам расписания, объедините его с соответствующей летной карточкой и прекратите использовать класс Dire. Верните список функций из функции. Функция может быть передана в FutureBuilder, гдеsnapshot.data
будет список функций. Вам решать, использовать JsonSerializable или нет.