Flutter get list elements from API result with BloC Pattern

4,467

You have not defined a state for your Bloc. You should create a state

class WeatherState
class WeatherLoaded extends WeatherState{
      final Weather weather;
  WeatherLoaded(this.weather);
}

Then your Bloc class should look like this WeatherEvent and WeatherState

class WeatherBloc extends Bloc<WeatherEvent, WeatherState> {
  WeatherBloc(Weather initialState) : super(initialState);

  @override
  Stream<Weather> mapEventToState(WeatherEvent event) async*{
    switch (event) {
      case WeatherEvent.getWeather:
        // Api call call that returns a weather object.
        final res = await WeatherRequest().fetchWeathers();
        yield WeatherLoaded(res);
        break;

      case WeatherEvent.getDetails:
        break;
    }
  }
}

Next, your Bloc Builder should look like this

         BlocBuilder<WeatherBloc, WeatherState>(
            builder: (context, WeatherState state) {
              if(state is WeatherLoaded) {
                return (Text(state.date));
              }
              
            },
          ),

You should rewrite your API call to return a Weather object

Share:
4,467
bss
Author by

bss

Updated on December 22, 2022

Comments

  • bss
    bss over 1 year

    I'm new at BloC Pattern. I'm sending a request to an weather API and it returns a JSON result like in below. As you can see the result is array of json object

    I formatted it to weather model in my response class.

    Weather Model Class

    class Weather {
      String date;
      String day;
      String icon;
      String description;
      String status;
      String degree;
      String min;
      String max;
      String night;
      String humidity;
    
      Weather({this.date, this.day, this.icon, this.description, this.status,
          this.degree, this.min, this.max, this.night, this.humidity});
    
      Weather.init(this.date, this.day, this.icon);
    
      factory Weather.fromJson(Map<String, dynamic> json){
        return Weather(
          date: json['date'] as String,
          day:  json['day'] as String,
          icon: json['icon'] as String,
          description: json['description'] as String,
          status: json['status'] as String,
          degree: json['degree'] as String,
          min: json['min'] as String,
          max: json['max'] as String,
          night: json['night'] as String,
          humidity: json['humidity'] as String,
        );
      }
    

    Response Class

    class WeatherResponse {
      bool success;
      String city;
      List<Weather> result;
    
      WeatherResponse({this.success, this.city, this.result});
    
      factory WeatherResponse.fromJson(Map<String, dynamic> json) {
        var weathersFromJson = json['result'] as List<dynamic>;
        List<Weather> weatherList = List<Weather>();
        weathersFromJson.forEach((element) {
          Weather weather = Weather.fromJson(element);
          weatherList.add(weather);
        });
    
        return WeatherResponse(
          success: json['success'] as bool,
          city:  json['city'] as String,
          result: weatherList
        );
    
      }
    }
    

    Weather Bloc

    enum WeatherEvent { getWeather, getDetails }
    
    class WeatherBloc extends Bloc<WeatherEvent, Weather> {
      WeatherBloc(Weather initialState) : super(initialState);
    
      @override
      Stream<Weather> mapEventToState(WeatherEvent event) async*{
        switch (event) {
          case WeatherEvent.getWeather:
            WeatherRequest().fetchWeathers().then((weatherResponse) =>
              weatherResponse.result.forEach((element) {
                return element;
              })
            );
            break;
    
          case WeatherEvent.getDetails:
            break;
        }
      }
    }
    

    I want to use yield instead of return element and when data comes I want to send this datas weather list results element to UI.

    UI Bloc Builder

     BlocBuilder<WeatherBloc, Weather>(
                builder: (context, result) {
                  print(result.date);
                  return (Text(result.date));
                },
              ),
    

    Maybe I'm missing something. When I write
    print(result.date); to my Bloc class it prints. But in the UI class and BlocBuilder there is nothing. When I debug it I see that: after the return element line work it's not jump to the BlocBuilder cause for each is continues. But when it finish it still not jump. Maybe my architecture or approach can be wrong. Also I'm open to suggestions about thats.

    So to summarize I can't get "stream result's list element" one by one as stream in bloc builder. Can anyone help?

    • kabayaba
      kabayaba almost 4 years
      as said by @tayomi you should extend your weatherbloc with bloc<WeatherEvent, WeatherState>. Feel free to browse my repo if you need reference to bloc usage
  • bss
    bss over 3 years
    Thank you for answer and explanation. It worked. Also now I can understand "state logic".
  • Tayormi
    Tayormi over 3 years
    Glad it worked for you @burcus. Please consider upvoting the answer so others can find it.