How do I use flutter bloc pattern to load list of data with paging? - flutter

I am using flutter_bloc in my application and referring to this sample. Now when I am running my application it gives me an error saying:
flutter: type 'MovieUninitialized' is not a subtype of type
'MovieLoaded' in type cast
My bloc contains three files: movie_state, movie_event, and movie_bloc
movie_state.dart
import 'package:equatable/equatable.dart';
import 'package:movie_project/models/movie.dart';
abstract class MovieState extends Equatable {
MovieState([List props = const []]) : super(props);
}
class MovieUninitialized extends MovieState {
#override
String toString() => 'MovieUninitialized';
}
class MovieError extends MovieState {
#override
String toString() => 'MovieError';
}
class MovieLoaded extends MovieState {
final List<Movie> movies;
final bool hasReachedMax;
// Keeps track of the page to fetch the latest movies from the api
final latestMoviesPage;
MovieLoaded({
this.movies,
this.hasReachedMax,
this.latestMoviesPage,
}) : super([movies, hasReachedMax, latestMoviesPage]);
MovieLoaded copyWith({
List<Movie> movies,
bool hasReachedMax,
int latestMoviesPage,
}) {
return MovieLoaded(
movies: movies ?? this.movies,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
latestMoviesPage: this.latestMoviesPage,
);
}
#override
String toString() =>
'PostLoaded { posts: ${movies.length}, hasReachedMax: $hasReachedMax }';
}
movie_event.dart
import 'package:equatable/equatable.dart';
abstract class MovieEvent extends Equatable {}
class Fetch extends MovieEvent {
#override
String toString() => 'Fetch';
}
movie_bloc.dart
class MovieBloc extends Bloc<MovieEvent, MovieState> {
final MovieRepository movieRepository;
MovieBloc({#required this.movieRepository});
#override
Stream<MovieState> transform(
Stream<MovieEvent> events,
Stream<MovieState> Function(MovieEvent event) next,
) {
return super.transform(
(events as Observable<MovieEvent>).debounceTime(
Duration(milliseconds: 500),
),
next,
);
}
#override
MovieState get initialState => MovieUninitialized();
#override
Stream<MovieState> mapEventToState(MovieEvent event) async* {
if (event is Fetch && !_hasReachedMax(currentState)) {
try {
if (currentState is MovieUninitialized) {
final movies = await movieRepository.fetchMovies(1);
yield MovieLoaded(
movies: movies,
hasReachedMax: false,
latestMoviesPage:
(currentState as MovieLoaded).latestMoviesPage + 1,
);
return;
}
if (currentState is MovieLoaded) {
final movies = await movieRepository
.fetchMovies((currentState as MovieLoaded).latestMoviesPage);
yield movies.isEmpty
? (currentState as MovieLoaded).copyWith(hasReachedMax: true)
: MovieLoaded(
movies: (currentState as MovieLoaded).movies + movies,
hasReachedMax: false,
latestMoviesPage:
(currentState as MovieLoaded).latestMoviesPage + 1,
);
}
} catch (_) {
print(_);
yield MovieError();
}
}
}
bool _hasReachedMax(MovieState state) =>
state is MovieLoaded && state.hasReachedMax;
}
I need to increment the latestMoviesPage until it reaches the max limit. If I remove latestMoviesPage from my bloc code issue gets resolves but I really need it to load more pages.

Instead of
latestMoviesPage: (currentState as MovieLoaded).latestMoviesPage + 1,
I need to write:
latestMoviesPage: 2,

Related

Flutter bloc rebuild on change state

For filter my list I use a state FilterState.
I have in this state my list filter but my widget for build list is not rebuild.
My print shows that the list is correct according to the state.
But GameList keeps its initial state which is not filtered : it's not rebuild.
Thanks,
my page for state :
BlocBuilder<GameBloc, GameState>(
builder: (context, state) {
if (state is LoadingState) {
return buildLoading();
} else if (state is FailState) {
return ErrorUI(message: state.message);
} else if (state is ListLoadedState) {
_list = state.list;
return GameList(list: state.list!);
} else if (state is FilterGamesState) {
print(state.list);
return GameList(list: state.list!);
}
return Container();
},
),
Bloc Page :
class GameBloc extends Bloc<GameEvent, GameState> {
GameBloc({required this.repository}) : super(LoadingState()) {
on<BackEvent>(_onBackEvent);
on<FetchGamesEvent>(_onFetchList);
on<FetchGameEvent>(_onFetchItem);
on<SavingEvent>(_onSavingEvent);
on<UpdateGameEvent>(_onUpdate);
on<CreateGameEvent>(_onCreate);
on<FilterGamesEvent>(_onFilter);
}
GameRepository repository;
final currentFilter = BehaviorSubject<Map<String, dynamic>>();
Future<void> _onFilter(
FilterGamesEvent event,
Emitter<GameState> emit,
) async {
try {
emit(LoadingState());
final list = event.list?.where((Game item) {
if (currentFilter.value.containsKey('player')) {
int players = currentFilter.value['player'].nb;
return players.isBetween(from: item.nopMin, to: item.nopMax);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('age')) {
return item.age!.isBetween(
from: currentFilter.value['age'].min,
to: currentFilter.value['age'].max);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('duration')) {
return compareToDuration(
item.durMin!,
item.durMax!,
currentFilter.value['duration'].min,
currentFilter.value['duration'].max);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('tags')) {
return item.tags!
.compareToList(currentFilter.value['tags'] as List<String>);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('collection')) {
return item.collection!
.compareToList(currentFilter.value['collection'] as List<String>);
}
return true;
}).toList();
emit(FilterGamesState(listGame: list!));
} catch (e) {
emit(const FailState(message: 'Failed to fetch all games data.'));
}
}
Event Page :
abstract class GameEvent extends Equatable {
final Game? item;
const GameEvent({this.item});
#override
List<Object> get props => [];
}
class InitialEvent extends GameEvent {
const InitialEvent({required Game item}) : super(item: item);
}
class BackEvent extends GameEvent {}
class SavingEvent extends GameEvent {}
class FetchGameEvent extends GameEvent {
const FetchGameEvent({required Game item}) : super(item: item);
}
class FetchGamesEvent extends GameEvent {}
class FilterGamesEvent extends GameEvent {
const FilterGamesEvent({required this.list});
final List<Game>? list;
}
State Page :
abstract class GameState extends Equatable {
final Game? item;
final List<Game>? list;
const GameState({this.item, this.list});
#override
List<Object> get props => [];
}
class GameInitial extends GameState {}
class FailState extends GameState {
const FailState({required this.message});
final String message;
}
class LoadingState extends GameState {}
class ListLoadedState extends GameState {
const ListLoadedState({required this.listGame}) : super(list: listGame);
final List<Game> listGame;
}
class ItemLoadedState extends GameState {
const ItemLoadedState({required this.game}) : super(item: game);
final Game game;
}
class FilterGamesState extends GameState {
const FilterGamesState({required this.listGame}) : super(list: listGame);
final List<Game> listGame;
}
I resolved this,
I send the key to GameList in FilterGameState.
else if (state is FilterGamesState) {
print(state.list);
return GameList(key: GlobalKey<GameFilterState>(), list: state.list!);
}
You are using Equatable but your props are empty. If you're using Equatable make sure to pass all properties to the props getter. (In both your state and event!)
Source: https://bloclibrary.dev/#/faqs?id=state-not-updating
The Flutter Todos Tutorial might also be helpful because it uses a filter too.

Flutter Bloc How to update Widget in BlocBuilder from the Widget itself?

How can to update a Bloc widget from the bloc Widget itself with the Slider?
The Event for the Chart Data is executed from another Widget.
When the data is fetched this Widget is opened.
When I change the Slider I want the chart to be updated withe the date but keep the other data.
Would be too much to fetch all the Data again.
How can I get access only the data changed from the same widget?
I have the following Bloc Builder Widget, bloc_event, bloc and bloc_state
The Widget:
class ChartWidget extends StatelessWidget {
ChartWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
double valueSliderDate;
return BlocBuilder<ChartDataBloc, ChartDataState>(
builder: (context, state) {
if (state is ChartDataLoadInProgress) {
return LoadingIndicator();
} else if (state is ChartDataLoadSuccess) {
final chartData = state.chartData;
final maxValueAll = getMaxValueAll(chartData);
final List<double> dates = getValuesDate(chartData);
valueSliderDate = dates.first;
return Column(children: <Widget>[
Expanded(
child: MyFancyChart(chartData, valueSliderDate),
),
Slider(
min: dates.first,
max: dates.last,
divisions: dates.length,
value: valueSliderDate,
onChanged: (value) {
context.read<ChartDataBloc>().add(DateSliderSet(value));
},
),
]);
} else {
return Container();
}
},
);
}
This is the bloc_event with two events:
abstract class ChartDataEvent {
const ChartDataEvent();
#override
List<Object> get props => []; }
class SpecificIndicatorIdSet extends ChartDataEvent {
const SpecificIndicatorIdSet(this.indicator);
final Indicator indicator;
#override
List<Object> get props => [indicator]; }
class DateSliderSet extends ChartDataEvent {
const DateSliderSet(this.dateSlider);
final double dateSlider;
#override
List<Object> get props => [dateSlider]; }
This is the bloc itself:
class ChartDataBloc extends Bloc<ChartDataEvent, ChartDataState> {
final ChartDataRepository chartDataRepository;
ChartDataBloc({#required this.chartDataRepository}) : super(ChartDataLoadInProgress());
#override
Stream<ChartDataState> mapEventToState(ChartDataEvent event) async* {
if (event is SpecificIndicatorIdSet) {
yield* _mapIndicatorsLoadedToState(event);
} else if (event is DateSliderSet) {
yield* _mapDateSliderToState(event); } }
Stream<ChartDataState> _mapDateSliderToState(
DateSliderSet event
) async* {
try {
final dateSlider = event.dateSlider;
yield DateSliderLoadSuccess(
dateSlider,
);
} catch (_) {
yield DateSliderLoadFailure(); } }
Stream<ChartDataState> _mapIndicatorsLoadedToState(
SpecificIndicatorIdSet event
) async* {
try {
final chartData = await this.chartDataRepository.loadChartData(event.indicator.id);
yield ChartDataLoadSuccess(
sortToListOfLists(chartData),
event.indicator.name
);
} catch (_) {
yield ChartDataLoadFailure(); } } }
This is the bloc_state:
abstract class ChartDataState {
const ChartDataState();
#override
List<Object> get props => []; }
class ChartDataLoadInProgress extends ChartDataState {}
class ChartDataLoadSuccess extends ChartDataState {
final List<List<ChartData>> chartData;
final String titleIndicator;
const ChartDataLoadSuccess(this.chartData,this.titleIndicator);
#override
List<Object> get props => [chartData, titleIndicator];
#override
String toString() => 'ChartDataLoadSuccess { topics: ' + chartData + ' }'; }
class ChartDataLoadFailure extends ChartDataState {}
class DateSliderLoadSuccess extends ChartDataState {
final double dateSlider;
const DateSliderLoadSuccess(this.dateSlider);
#override
List<Object> get props => [dateSlider];
#override
String toString() => 'DateSliderLoadSuccess { dateSlider: ' + dateSlider.toString() + ' }';
}
class DateSliderLoadFailure extends ChartDataState {}
Thanks in advance
Have you tried creating a variable inside your bloc to store the original data?
You would be able to store the data and be able to continue using your bloc and updating your widget.

Transition not triggered when adding element to list in BLoC state with flutter_bloc and Equatable

I'm developing a web application using flutter_bloc library for state management. In my app I have to keep track of the state of a form (AreaForm widget). In this form I have to manage a list of objects: I must be able to add or remove an object to the list.
This is the code for the AreaFormState:
part of 'area_form_bloc.dart';
class AreaFormState extends Equatable {
final Provincia provincia;
final Comune comune;
final String denominazione;
final List<TipoUdo> listaTipoUdo;
final List<Specialita> listaSpecialita;
final bool isDisciplineChecked;
final bool isBrancheChecked;
final String indirizzo;
const AreaFormState({
#required this.provincia,
#required this.comune,
#required this.denominazione,
#required this.listaTipoUdo,
#required this.listaSpecialita,
#required this.isDisciplineChecked,
#required this.isBrancheChecked,
#required this.indirizzo,
});
factory AreaFormState.empty() {
return AreaFormState(
provincia: null,
comune: null,
denominazione: null,
listaTipoUdo: <TipoUdo>[],
listaSpecialita: <Specialita>[],
isDisciplineChecked: false,
isBrancheChecked: false,
indirizzo: null,
);
}
AreaFormState update({
Provincia provincia,
Comune comune,
String denominazione,
List<TipoUdo> listaTipoUdo,
List<Specialita> listaSpecialita,
bool isDisciplineChecked,
bool isBrancheChecked,
String indirizzo,
}) {
return copyWith(
provincia: provincia,
comune: comune,
denominazione: denominazione,
listaTipoUdo: listaTipoUdo,
listaSpecialita: listaSpecialita,
isDisciplineChecked: isDisciplineChecked,
isBrancheChecked: isBrancheChecked,
indirizzo: indirizzo,
);
}
AreaFormState copyWith({
Provincia provincia,
Comune comune,
String denominazione,
List<TipoUdo> listaTipoUdo,
List<Specialita> listaSpecialita,
bool isDisciplineChecked,
bool isBrancheChecked,
String indirizzo,
}) {
return AreaFormState(
provincia: provincia ?? this.provincia,
comune: comune ?? this.comune,
denominazione: denominazione ?? this.denominazione,
listaTipoUdo: listaTipoUdo ?? this.listaTipoUdo,
listaSpecialita: listaSpecialita ?? this.listaSpecialita,
isDisciplineChecked: isDisciplineChecked ?? this.isDisciplineChecked,
isBrancheChecked: isBrancheChecked ?? this.isBrancheChecked,
indirizzo: indirizzo ?? this.indirizzo,
);
}
#override
List<Object> get props => [
provincia,
comune,
denominazione,
listaTipoUdo,
listaSpecialita,
isDisciplineChecked,
isBrancheChecked,
indirizzo,
];
#override
String toString() {
return '''
AreaFormState {
provincia: $provincia,
comune: $comune,
denominazione: $denominazione,
listaTipoUdo: $listaTipoUdo,
listaSpecialita: $listaSpecialita,
isDisciplineChecked: $isDisciplineChecked,
isBrancheChecked: $isBrancheChecked,
indirizzo: $indirizzo,
}''';
}
}
This is the code for the AreaFormEvent (I have reported only the two events of interest):
part of 'area_form_bloc.dart';
abstract class AreaFormEvent extends Equatable {
const AreaFormEvent();
#override
List<Object> get props => [];
}
...
class SpecialitaAdded extends AreaFormEvent {
final Specialita specialita;
const SpecialitaAdded({#required this.specialita});
#override
List<Object> get props => [specialita];
#override
String toString() => 'SpecialitaAdded { specialita: $specialita }';
}
class SpecialitaRemoved extends AreaFormEvent {
final Specialita specialita;
const SpecialitaRemoved({#required this.specialita});
#override
List<Object> get props => [specialita];
#override
String toString() => 'SpecialitaRemoved { specialita: $specialita }';
}
...
And finally this is the code for AreaFormBloc:
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import '../../../data/comune.dart';
import '../../../data/provincia.dart';
import '../../../data/specialita.dart';
import '../../../data/tipo_udo.dart';
import '../../repositories/area/area_repository.dart';
part 'area_form_event.dart';
part 'area_form_state.dart';
class AreaFormBloc extends Bloc<AreaFormEvent, AreaFormState> {
final AreaRepository areaRepository;
AreaFormBloc({#required this.areaRepository})
: assert(areaRepository != null);
#override
AreaFormState get initialState => AreaFormState.empty();
#override
Stream<AreaFormState> mapEventToState(
AreaFormEvent event,
) async* {
if (event is ProvinciaSelected) {
yield* _mapProvinciaSelectedToState(event.provincia);
} else if (event is ComuneSelected) {
yield* _mapComuneSelectedToState(event.comune);
} else if (event is DenominazioneChanged) {
yield* _mapDenominazioneChangedToState(event.denominazione);
} else if (event is TipoUdoAdded) {
yield* _mapTipoUdoAddedToState(event.tipoUdo);
} else if (event is TipoUdoRemoved) {
yield* _mapTipoUdoRemovedToState(event.tipoUdo);
} else if (event is SpecialitaAdded) {
yield* _mapSpecialitaAddedToState(event.specialita);
} else if (event is SpecialitaRemoved) {
yield* _mapSpecialitaRemovedToState(event.specialita);
} else if (event is DisciplineChanged) {
yield* _mapDisciplineChangedToState();
} else if (event is BrancheChanged) {
yield* _mapBrancheChangedToState();
} else if (event is IndirizzoChanged) {
yield* _mapIndirizzoChangedToState(event.indirizzo);
} else if (event is NearestUdoIconPressed) {
yield* _mapNearestUdoIconPressedToState();
} else if (event is PulisciPressed) {
yield* _mapPulisciPressedToState();
} else if (event is CercaPressed) {
yield* _mapCercaPressedToState();
}
}
...
Stream<AreaFormState> _mapSpecialitaAddedToState(
Specialita specialita,
) async* {
yield state.update(listaSpecialita: state.listaSpecialita..add(specialita));
}
Stream<AreaFormState> _mapSpecialitaRemovedToState(
Specialita specialita,
) async* {
yield state.update(
listaSpecialita: state.listaSpecialita..remove(specialita));
}
...
}
In this way, when the event SpecialitaAdded is added to the AreaFormBloc, the bloc should produce a Transition to a new AreaFormState which should have a listaSpecialita equal to the one before with the addition of the new Specialita object just added.
Unfortunately no transition at all is triggered! But the really strange thing is that if I add the element like this instead:
Stream<AreaFormState> _mapSpecialitaAddedToState(
Specialita specialita,
) async* {
yield state.update(listaSpecialita: state.listaSpecialita + [specialita]);
}
then the transition is triggered.
Unfortunately I can't keep this solution because I don't know how to manage the removal of an element.
I think the problem is that I use the Equatable package for equality comparison but I really don't understand where I'm wrong.
With Equatable you have to have immutable fields (your list is modifiable).
Something like this happened I think:
final someList = <int>[1, 2, 3]; // this is done when creating state
final anotherList = someList; // this is done with update, you just pass the reference
someList.add(4);
print(anotherList == someList); // true
So equatable, what is does it checks if the passed props are equal or not, in this case are equal.
The solution:
Stream<AreaFormState> _mapSpecialitaAddedToState(
Specialita specialita,
) async* {
// creating a new copy of the list
yield state.update(
listaSpecialita: List<Specialita>.of(
state.listaSpecialita..add(specialita),
),
);
}
This gives the List a new reference with the same values.
Which will make the internal == check false.
Bloc doesn't emit a new state unless they are not equal.
Would advise you to use freezed package instead, it will save you the
hussle of copyWith.
I solved the problem using Dart's spread operator:
Stream<AreaFormState> _mapSpecialitaAddedToState(
Specialita specialita,
) async* {
yield state.update(listaSpecialita: [...state.listaSpecialita]..add(specialita));
}
Stream<AreaFormState> _mapSpecialitaRemovedToState(
Specialita specialita,
) async* {
yield state.update(
listaSpecialita: [...state.listaSpecialita]..remove(specialita));
}

How to change state of individual list items using bloc flutter?

How to change the widgets in a list item in flutter using bloc pacakage.
Should i use BlockBuilder or listener on the whole ListView.builder or only the individual items.
It would be nice if u share an example or tutorial.
eg If i have a checkbox i need to change its state on clicking it.
These are my Bloc classes
Bloc
const String SERVER_FAILURE_MESSAGE = 'Server Failure';
const String CACHE_FAILURE_MESSAGE = 'Cache Failure';
class MarkAttendanceBloc extends Bloc<MarkAttendanceEvent, MarkAttendanceState> {
final MarkStudentPresent markStudentPresent;
final MarkStudentAbsent markStudentAbsent;
MarkAttendanceBloc({#required this.markStudentPresent,#required this.markStudentAbsent});
#override
MarkAttendanceState get initialState => MarkedInitial();
#override
Stream<MarkAttendanceState> mapEventToState(MarkAttendanceEvent event) async* {
yield MarkedLoading();
if(event is MarkAbsentEvent){
final remotelyReceived = await markStudentAbsent(MarkStudentParams(classId: event.classId, courseId: event.courseId,studentId: event.studentId));
yield* _eitherLoadedOrErrorState(remotelyReceived);
}
else if(event is MarkPresentEvent){
final remotelyReceived = await markStudentPresent(MarkStudentParams(classId: event.classId, courseId: event.courseId,studentId: event.studentId));
yield* _eitherLoadedOrErrorState(remotelyReceived);
}
}
Stream<MarkAttendanceState> _eitherLoadedOrErrorState(
Either<StudentDetailsFacultyFailure,int> failureOrClasses,
) async* {
yield failureOrClasses.fold(
(failure) => MarkedError(_mapFailureToMessage(failure)),
(studentId) => Marked(studentId),
);
}
String _mapFailureToMessage(StudentDetailsFacultyFailure failure) {
switch (failure.runtimeType) {
case ServerError:
return SERVER_FAILURE_MESSAGE;
default:
return 'No internet';
}
}
}
State
abstract class MarkAttendanceState extends Equatable{
const MarkAttendanceState();
}
class MarkedInitial extends MarkAttendanceState{
const MarkedInitial();
#override
List<Object> get props => [];
}
class MarkedLoading extends MarkAttendanceState{
const MarkedLoading();
#override
List<Object> get props => [];
}
class Marked extends MarkAttendanceState{
final int studentId;
Marked(this.studentId);
#override
List<Object> get props => [studentId];
}
class MarkedError extends MarkAttendanceState{
final String errorMessage;
MarkedError(this.errorMessage);
#override
List<Object> get props => [errorMessage];
}
Event
import 'package:equatable/equatable.dart';
abstract class MarkAttendanceEvent extends Equatable {
const MarkAttendanceEvent();
}
class MarkPresentEvent extends MarkAttendanceEvent {
final int studentId;
final int courseId;
final int classId;
MarkPresentEvent(this.studentId, this.courseId, this.classId);
#override
List<Object> get props =>[studentId,courseId,classId];
}
class MarkAbsentEvent extends MarkAttendanceEvent {
final int studentId;
final int courseId;
final int classId;
MarkAbsentEvent(this.studentId, this.courseId, this.classId);
#override
List<Object> get props =>[studentId,courseId,classId];
}
Maybe by now you have found a solution but this is how i managed to achieve the same functionality using flutter cubits.
This code is hand written and not tested but it should guide you to achieve your goal
1 Declare the class objects
class ClassItem{
int? price;
bool isChecked;
ClassItem({
this.price,
this.isChecked=false,
});
}
class ClassOverall{
List<ClassItem> items;
double? total;
ClassOverall(this.items,this.total);
}
Declare the cubit class
class OverallCubit extends Cubit<ClassOverall> {
OverallCubit(ClassOverallinitialState) : super(initialState);
void checkUncheckCart(int index) {
if (!state.items
.elementAt(index).isChecked) {
state.items
.elementAt(index).isChecked =
!state.items
.elementAt(index).isChecked;
var t_total = double.tryParse(state.items
.elementAt(index).price!)! * 1;
emit(OverallCubit (state.items,state.total));
} else {
state.items.elementAt(index).isChecked =
!state.items
.elementAt(index).isChecked;
emit(OverallCubit (state.items,state.total));
}
calculateTotal();
}
void calculateTotal() {
var tot = 0.0;
for (var tick in state.items) {
if (tick.isChecked) {
tot = (tick.t_total! + tot);
}
}
emit(OverallCubit (state.items,tot));
}
}
Declare the top class widget to hold the state
class TopState extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => OverallCubit(ClassOverall(items,0)),//fetch items from your source
child: Home(),
);
}
}
Declare the stateful widget and add a bloc builder
class Home extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<Home> {
#override
Widget build(BuildContext context) {
return BlocBuilder<OverallCubit, ClassOverall>(
builder: (ctx, state) {
return Column(children:[
ListView.builder(
padding: EdgeInsets.all(0.0),
shrinkWrap: true,
itemCount: state.items.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
ctx
.read<OverallCubit>()
.checkUncheckCart(index);
},
tileColor: state.elementAt(index).isChecked ? Colors.red : Colors.white
title: Text(state.items.elementAt(index).price!),
);
}),
Text(state.total.toString),
]);
});
}
}

State not changing (stuck on initial)

Recently i'm learning about bloc_pattern , so i follow this https://medium.com/flutter-community/implementing-bloc-pattern-for-parsing-json-from-api-5ac538d5179f .
Here is my script
My Bloc File
class HotProductBloc extends Bloc<HotProductEvent, HotProductState> {
HotProductRepository repository;
HotProductBloc({#required this.repository});
#override
HotProductState get initialState => InitialHotProductsState();
#override
Stream<HotProductState> mapEventToState(HotProductEvent event) async* {
print(event);
if (event is FetchHotProductEvent) {
yield HotProductsLoading();
try {
List<HotProducts> dataHotProduct = await repository.getHotProduct();
yield HotProductsLoaded(hotproduct: dataHotProduct);
} catch (e) {
yield HotProductsError(message: e.toString());
}
}
}
}
Repository file
abstract class HotProductRepository {
Future<List<HotProducts>> getHotProduct();
}
class HotProductImplement implements HotProductRepository {
#override
Future<List<HotProducts>> getHotProduct() async {
print("running");
final response = await http.post(Configuration.url + "api/getHotProducts",
body: {"userId": "abcde"});
if (response.statusCode == 200) {
var data = json.decode(response.body);
List<dynamic> responseData = jsonDecode(response.body);
final List<HotProducts> hotProducts = [];
responseData.forEach((singleUser) {
hotProducts.add(HotProducts(
productId: singleUser['productId'],
isNew: singleUser['isNew'],
productName: singleUser['productName'],
isHot: singleUser['isHot'],
productImage: singleUser['productImage'],
categoryId: singleUser['categoryId'],
productPrice: singleUser['productPrice'],
productDescription: singleUser['productDescription'],
isLiked: singleUser['isLiked'],
image1: singleUser['image1'],
image2: singleUser['image2'],
image3: singleUser['image3'],
productColorId: singleUser['productColorId'],
));
});
return hotProducts;
} else {
throw Exception();
}
}
Event file
abstract class HotProductEvent extends Equatable {
HotProductEvent([List props = const []]) : super(props);
}
class FetchHotProductEvent extends HotProductEvent {
#override
List<Object> get props => null;
}
State file
abstract class HotProductState extends Equatable {
HotProductState([List props = const []]) : super(props);
}
class InitialHotProductsState extends HotProductState {}
class HotProductsLoading extends HotProductState {}
class HotProductsLoaded extends HotProductState {
final List<HotProducts> hotproduct;
HotProductsLoaded({#required this.hotproduct})
: assert(hotproduct != null),
super([hotproduct]);
}
class HotProductsError extends HotProductState {
String message;
HotProductsError({#required this.message});
#override
// TODO: implement props
List<Object> get props => [message];
}
and here is how i implement the bloc
BlocListener <HotProductBloc, HotProductState>(
listener: (context, state) {
if (state is HotProductsError) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
),
);
}
},
child: Container(
child: BlocBuilder<HotProductBloc, HotProductState>(
builder: (context, state) {
print("BLoc State "+ state.toString());
if (state is InitialHotProductsState) {
return Text("Siaaap");
} else if (state is HotProductsLoading) {
return Text("Loading");
} else if (state is HotProductsLoaded) {
return Text("DONE");
} else if (state is HotProductsError) {
return Text("ERROR");
}else{
return Text("Unknown error");
}
},
),
),
),
when i run my script above, i get this on my Log
I/flutter ( 390): BLoc State InitialHotProductsState I/flutter (
390): BLoc State InitialHotProductsState
You need to fire an event to start any change of state. Try adding this to somewhere in your build() method:
BlocProvider.of<HotProductBloc>(context).add(FetchHotProductEvent());