-2

I am trying to work with concurrency in Flutter, so I have three get-request from server and I need to get values from them at the same time. Each request was parsed into a model. Now I'm trying to figure out how I can combine all the models into one list with three get-request and run this the final list in ListView.builder. Also my task is quite hard for such noobie as I am, beacuse besides parsed concurrently all three lists I need to filter them out, because as said in API all requests are nested and depends on id. How can i resolve this?

This is my models:

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

I need to get final values from Stop models. As you can see all models have nested stucture and I can't avoid this.

Now I am trying to make concurrent call like this:

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=json&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();
          }
        }));

} }

At the end of the main task is to run three parallel request filtered by ID and get data from the Stops model. How can you do it right?

I am noob and don't get how properly to do it and I will really glad if someone help me to figure this task out.

inkwelll075
  • 494
  • 4
  • 19
  • What do you mean by "filter them aout"? What do you mean by "get data from the Stops model"? What is `fetchList`? You will need to improve your question by adding some examples because it's hard to understand what you want. I would also recommend you fix your formatting too. – wxker Aug 23 '21 at 09:20
  • Hi @wxker I did some changes for better undertanding what I am trying to do. And I will try to explain idea better in comments: my main task is to get the stops filtered by id. But the API developers explained to me that in order to complete this task I first need to use the Schedule Variant model, then through the ScheduleVariant model I have to use the FlightCard model and already from the FlightCard by the ID I get all the filtered stops. I tried to merge these models together, but I still get a blank screen and was advised to use three get-requests to the server at the same time. – inkwelll075 Aug 23 '21 at 10:55
  • @wxker I am looking for a better solution because I have never tried to solve such task as I have got now. I was thinking that I need to merge all models and filter them using some algorithm but it also didn't work or I jist did something wrong – inkwelll075 Aug 23 '21 at 10:57

1 Answers1

0

I am going to make some assumptions, because there is not enough information:

  1. Dire is a combination of the three classes ScheduleVariants, FlightCard and Stop where ScheduleVariants.mvId == FlightCard.mvId and FlightCard.stId == Stop.stId.
  2. All three APIs will return a list as their response.
  3. All ScheduleVariants have unique mvId, all FlightCards have unique mvId and stId, and all Stops have unique stId.

There is nothing wrong with the way you execute multiple asynchronous requests. Future.wait in this case takes in a list of futures and returns a list of responses. The problem you are facing is just that you do not know how to merge the responses from the three API requests.

You seem to also be mixing up the use of state with the use of futures. At least in the code snippet you provided, it does not seem like you ever need to change the state after you initialize it, which means you do not need to use state at all.

Dire should just be a model class.

class Dire {
  final ScheduleVariants scheduleVariant;
  final FlightCard flightCard;
  final Stop stop;

  Dire(this.scheduleVariant, this.flightCard, this.stop);
}

In your widget where you want to get the Dires from the APIs, you can use this function in the 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=json&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();
  }

A disclaimer: I did not check this code snippet for syntax errors, so there might be some but the general idea is there.

wxker
  • 1,841
  • 1
  • 6
  • 16
  • thanks you so much. I want to confirm what I understood from your code everything correctly (I am just learning and try not to miss anything). And so, to combine all 3 API requests, I had to create a separate model where ScheduleVariants, FlightCard, Stop are located. Did I understand this correctly? After that use Future builder where I use all 3 url and make get-requests and after that I use a hash map for each request? Also i wanted to ask if I should use @JsonSerializable() for good practice or it is not mandatory? – inkwelll075 Aug 23 '21 at 13:55
  • Use `Future.wait` to obtain the responses of all 3 API requests. Convert the responses for FlightCards and Stops into HashMaps with the relevant id as key. Iterate through ScheduleVariants and combine it with the corresponding FlightCard and Stop using the Dire class. Return the list of Dire from the function. The function can be passed into a FutureBuilder where `snapshot.data` will be the list of Dire. It is up to you whether to use JsonSerializable or not. – wxker Aug 24 '21 at 02:30