Flutter: bloc not removing data from the list

Issue

I’m trying to create list of favourite news using bloc, now if I want to add to the favourite list it does happen but if I want to remove it then list is not getting updated so it is not removing from UI.

My bloc logic,

class FavouriteBloc extends Bloc<FavouriteEvent, List<Articles>> {
  FavouriteBloc() : super(null);
  List<Articles> articles = [];
  @override
  Stream<List<Articles>> mapEventToState(FavouriteEvent event) async* {
    switch (event.eventType) {
      case EventType.add:
         articles.add(event.articles);
         yield articles;
        break;
      case EventType.delete:
        articles.remove(event.articles);
        yield articles;
        break;
    }
  }
}

event class,

enum EventType {add, delete}

class FavouriteEvent{
  Articles articles;
  EventType eventType;
  FavouriteEvent.add({this.articles,this.eventType});
  FavouriteEvent.remove({this.articles,this.eventType});
}

the UI part,

In this screen when I add to favourites it shows list of cards which I have added and then I use onTap to remove it from the list but that is not happening

class FavouriteScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var height = MediaQuery.of(context).size.height;
    var width = MediaQuery.of(context).size.width;
    return Scaffold(
      appBar: AppBar(),
      body: BlocBuilder<FavouriteBloc, List<Articles>>(
        buildWhen: (previous, current) {
          if(previous.length<current.length){
            return true;
          }
          return false;

        },
        builder: (context, newsList) {
          if (newsList == null) {
            return Center(
              child: Text(
                week7.Strings.noFav,
                style: Theme.of(context).textTheme.headline6,
              ),
            );
          }
          return ListView.builder(
              itemCount: newsList.length,
              shrinkWrap: true,
              itemBuilder: (context, index) {
                return GestureDetector(
                  onTap: () { 
                    BlocProvider.of<FavouriteBloc>(context).add(  //<--- this is how I'm trying to remove
                        FavouriteEvent.remove(
                            articles: Articles(
                                urlToImage: newsList[index].urlToImage,
                                title: newsList[index].title,
                                author: newsList[index].author
                            ),
                            eventType: EventType.delete));
                  },
                  child: Card(...),
                );
              });
        },
      ),
    );
  }
}

model class,

@JsonSerializable()
class Articles {
  Source source;
  String author;
  String title;
  String description;
  String url;
  String urlToImage;
  DateTime publishedAt;
  String content;
  Articles({
    this.source,
    this.author,
    this.title,
    this.description,
    this.url,
    this.urlToImage,
    this.publishedAt,
    this.content,
  });

  factory Articles.fromJson(Map<String, dynamic> json) =>
      _$ArticlesFromJson(json);
}

so can anyone tell me what I’m doing wrong here?

Solution

Dart compares if the two objects are the same instance. You need to override == operator or use library like equatable.

The first thing you need to do is to delete buildWhen. Right now it will only update(rebuild) when you add items but not when you remove them.

        buildWhen: (previous, current) {
          if(previous.length<current.length){
            return true;
          }
          return false;

        },

Use State class to represent the state because the list is always the same and it will not rebuild. After that adjust your widget code to use state.articles.

class FavouriteState {
 final List<Articles> articles;
 FavouriteState(this.artticles);
}

class FavouriteBloc extends Bloc<FavouriteEvent, FavouriteState> {
  FavouriteBloc() : super(null);
  List<Articles> _articles = [];
  @override
  Stream<FavouriteState> mapEventToState(FavouriteEvent event) async* {
    switch (event.eventType) {
      case EventType.add:
         _articles.add(event.articles);
         yield FavouriteState(_articles);
        break;
      case EventType.delete:
        _articles.remove(event.articles);
        yield FavouriteState(_articles);
        break;
    }
  }
}

Example comparing urlToImage, title and author

@JsonSerializable()
class Articles {
  Source source;
  String author;
  String title;
  String description;
  String url;
  String urlToImage;
  DateTime publishedAt;
  String content;
  Articles({
    this.source,
    this.author,
    this.title,
    this.description,
    this.url,
    this.urlToImage,
    this.publishedAt,
    this.content,
  });

    @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Articles && runtimeType == other.runtimeType && urlToImage == other.urlToImage &&
      title == other.title && author == other.author;

  @override
  int get hashCode => urlToImage.hashCode ^ title.hashCode ^ author.hashCode;

  factory Articles.fromJson(Map<String, dynamic> json) =>
      _$ArticlesFromJson(json);
}

Example using Equatable package

@JsonSerializable()
class Articles  extends Equatable{
  Source source;
  String author;
  String title;
  String description;
  String url;
  String urlToImage;
  DateTime publishedAt;
  String content;
  Articles({
    this.source,
    this.author,
    this.title,
    this.description,
    this.url,
    this.urlToImage,
    this.publishedAt,
    this.content,
  });

 @override
  List<Object> get props => [author, title, description, url, urlToImage, content];

  factory Articles.fromJson(Map<String, dynamic> json) =>
      _$ArticlesFromJson(json);
}

Answered By – YoBo

Answer Checked By – Senaida (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *