how to use infinite_scroll_pagination for bloc pattern - flutter

I'm currently learning and converting my code to BLoc pattern. Before I'm using flutter_pagewise ^1.2.3 for my infinite scroll using Future<> but I don't know how to use it using bloc or is it compatible with it.
So now I'm trying infinite_scroll_pagination: ^2.3.0 since it says in its docs that it supports Bloc. But I don't understand the example code in the docs for bloc. Can you give me a simple example of how to use it with bloc? I'm currently using flutter_bloc: ^6.1.3.
Here are my bloc script:
class TimeslotViewBloc extends Bloc<TimeslotViewEvent, TimeslotViewState> {
final GetTimeslotView gettimeslotView;
TimeslotViewBloc({this.gettimeslotView}) : super(TimeslotViewInitialState());
#override
Stream<TimeslotViewState> mapEventToState(
TimeslotViewEvent event,
) async* {
if (event is GetTimeslotViewEvent) {
yield TimeslotViewLoadingState();
final failureOrSuccess = await gettimeslotView(Params(
id: event.id,
date: event.date,
));
yield* _eitherLoadedOrErrorState(failureOrSuccess);
}
}
Stream<TimeslotViewState> _eitherLoadedOrErrorState(
Either<Failure, List<TimeslotViewEntity>> failureOrTrivia,
) async* {
yield failureOrTrivia.fold(
(failure) => TimeslotViewErrorState(
message: _mapFailureToMessage(failure), failure: failure),
(result) => TimeslotViewLoadedState(result),
);
}
//Bloc Events----------------------------------------
abstract class TimeslotViewEvent extends Equatable {
const TimeslotViewEvent();
#override
List<Object> get props => [];
}
class GetTimeslotViewEvent extends TimeslotViewEvent {
final String id;
final String date;
final int offset;
final int limit;
GetTimeslotViewEvent(
{this.id,
this.date,
this.offset,
this.limit});
}
//Bloc States----------------------------------------
abstract class TimeslotViewState extends Equatable {
const TimeslotViewState();
#override
List<Object> get props => [];
}
class TimeslotViewLoadingState extends TimeslotViewState {}
class TimeslotViewLoadedState extends TimeslotViewState {
final List<TimeslotViewEntity> records;
TimeslotViewLoadedState(this.records);
#override
List<Object> get props => [records];
}
UPDATE: Here is the revised code from Davii that works for me
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => _timeLotBloc,
child: BlocListener<TimeslotViewBloc, TimeslotViewState>(
listener: (context, state) {
if (state is TimeslotViewLoadedState) {
//Save record count instead of records list
totalRecordCount += state.records.length;
final _next = 1 + totalRecordCount;
final isLastPage = state.records.length < PAGE_SIZE;
if (isLastPage) {
_pagingController.appendLastPage(state.records);
} else {
_pagingController.appendPage(state.records, _next);
}
}
if (state is TimeslotViewErrorState) {
_pagingController.error = state.error;
}
},
//Removed pagedListview from bloc builder
child: PagedListView<int, TimeslotViewEntity>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<TimeslotViewEntity>(
itemBuilder: (context, time, index) => TimeslotViewEntityListItem(
character: time,
),
),
),),
);
}

class PaginatedList extends StatefulWidget {
const PaginatedList({Key? key}) : super(key: key);
#override
_PaginatedListState createState() => _PaginatedListState();
}
class _PaginatedListState extends State<PaginatedList> {
//*bloc assuming you use getIt and injectable
late final _timeLotBloc = getIt<TimeslotViewBloc>();
List<TimeslotViewEntity> records = [];
//*initialize page controller
final PagingController<int, TimeslotViewEntity> _pagingController =
PagingController(firstPageKey: 0);
#override
void initState() {
super.initState();
//*so at event add list of records
_pagingController.addPageRequestListener(
(pageKey) => _timeLotBloc
.add(GetTimeslotViewEvent(records: records, offset: pageKey,limit: 10)),
);
}
#override
void dispose() {
super.dispose();
_timeLotBloc.close();
_pagingController.dispose();
}
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => _timeLotBloc,
child: BlocListener<TimeslotViewBloc, TimeslotViewState>(
listener: (context, state) {
if (state is TimeslotViewLoadedState) {
records =state.records;
//forget about existing record
//about the last page, fetch last page number from
//backend
int lastPage = state.lastPage
final _next = 1 + records.length;
if(_next>lastPage){
_pagingController.appendLastPage(records);
}
else{
_pagingController.appendPage(records, _next);
}
}
if (state is TimeslotViewErrorState) {
_pagingController.error = state.error;
}
},child: BlocBuilder<TimeslotViewBloc,TimeslotViewState>(
builder: (context,state)=> PagedListView<int, TimeslotViewEntity>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<TimeslotViewEntity>(
itemBuilder: (context, time, index) => TimeslotViewEntityListItem(
character: time,
),
),
),),
),
);
}
}
now on the bloc event class
class GetTimeslotViewEvent extends TimeslotViewEvent {
final String id;
final String date;
final int offset;
final int limit;
//add this on event
final List<TimeslotViewEntity> records;
GetTimeslotViewEvent({
this.id,
this.date,
this.offset,
this.limit,
required this.records,
});
}
on state class
class TimeslotViewLoadedState extends TimeslotViewState {
final List<TimeslotViewEntity> records;
final List<TimeslotViewEntity> existingRecords;
TimeslotViewLoadedState(this.records, this.existingRecords);
#override
List<Object> get props => [records, existingRecords];
}
and on bloc now
yield* _eitherLoadedOrErrorState(failureOrSuccess,event);
Stream<TimeslotViewState> _eitherLoadedOrErrorState(
Either<Failure, List<TimeslotViewEntity>> failureOrTrivia,
GetTimeslotViewEvent event,
) async* {
yield failureOrTrivia.fold(
(failure) => TimeslotViewErrorState(
message: _mapFailureToMessage(failure), failure: failure),
//existing records from the event,
(result) => TimeslotViewLoadedState(result,event.records),
);
}
yap this method worked on me

Related

Flutter bloc does not fetch data from API

I have some data that I want to fetch when the page loads up.
Below is the code for fetching the data in the screen
class _HighSchoolScreenState extends State<HighSchoolScreen> {
late PagingController<int, HighSchool> _pagingController;
#override
void initState() {
_pagingController = context.read<HighSchoolsBloc>().pageController;
_pagingController.addPageRequestListener(
(pageKey) {
context.read<HighSchoolsBloc>().add(
FetchHighSchools(page: pageKey, category: widget.category!),
);
},
);
super.initState();
}
I am using the infinite_scroll_pagination package to lazy load the data in the UI
Widget build(BuildContext context) => PagedListView<int, HighSchool>(
addAutomaticKeepAlives: false,
shrinkWrap: true,
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<HighSchool>(
animateTransitions: true,
newPageProgressIndicatorBuilder: (context) => const CircularProgressIndicator.adaptive(),
firstPageProgressIndicatorBuilder: (context) => const CircularProgressIndicator.adaptive(),
itemBuilder: (context, item, index) => SchoolsContent(
item: item,
theme: theme,
isIos: isIos,
),
),
),
Below is also my bloc for the data
class HighSchoolsBloc extends Bloc<HighSchoolsEvent, HighSchoolsState> {
final String token = Hive.box('user').get(kToken);
bool hasNextPage = true;
late HighSchoolRepo _highSchoolRepo;
final PagingController<int, HighSchool> pageController =
PagingController(firstPageKey: 0);
Future<void> _fetchPage(int pageKey, FetchHighSchools event) async {
try {
final results = await _highSchoolRepo.get(
page: event.page,
category: event.category,
token: token,
);
hasNextPage = results['hasNextPage'];
final List<HighSchool> newItems = results['schools'];
if (!hasNextPage) {
pageController.appendLastPage(newItems);
} else {
final nextPageKey = pageKey + 1;
pageController.appendPage(newItems, nextPageKey);
}
} catch (error) {
pageController.error = 'error';
}
}
HighSchoolsBloc(this._highSchoolRepo) : super(InitailState()) {
on<FetchHighSchools>((event, emit) {
print('new ${event.category}');
_fetchPage(event.page, event);
});
}
}
So the real issue is whenever visit the screen, the data fetches correctly and shows on the screen(UI) but when I leave the screen and press on another category, it should fetch data based on the different category now but it is not event fetching anything again. it just shows the same data that was fetched previously
Below is the states for my bloc
#immutable
abstract class HighSchoolsState extends Equatable {
#override
List<Object?> get props => [];
}
// ignore_for_file: public_member_api_docs, sort_constructors_first
class InitailState extends HighSchoolsState {}
class HighSchoolFetchError extends HighSchoolsState {
late final String error;
HighSchoolFetchError(this.error);
#override
List<Object?> get props => [error];
}
PLEASE NOT THAT LOADING AND ERROR ARE HANDLED BY THE PACKAGE SO THERE'S NO NEED TO MAKE IT'S RELATIVE STATES
ALSO, ONE MORE ERROR I AM FACING IS SOMETIMES, WHEN I SCROLL THROUGH THE DATA GIVEN IT GIVES ME AN ERROR OF
This widget has been unmounted, so the State no longer has a context (and should be considered defunct). // It appears on line 32 which is where the initstate it.

Why emit.forEach(..) doesn't persist the state of the bloc in this case?

Simplified example using a Todo App approach where data is submitted from different pages and bloc reacts to it subscribing to a stream.
View, I have four pages.
Page A: Shows a ListView of Todos.
Page B-1: Shows a Form to update the overview data related to a Todo.
Page B-2: Shows a ListView of Actions that has a Todo.
Page C: Shows a Form to update the data related to an Action.
Logic, I have four blocs. CollectionBloc which subscribes to a stream of data using Hive and it is supposed to emit states every time there is an update in the repository. Also, EditTodoBloc and ActionBloc which submit data to the same repository. TodoBloc is for managing a Todo in general.
EditTodoBloc
----------> Page B-1
Page A ----------> | ActionBloc
CollectionBloc ----------> Page B-2 ----------> Page C
TodoBloc
Models
#HiveType(typeId: 0)
class Action extends Equatable {
Action({this.id, this.name});
#HiveField(0)
String id;
#HiveField(1)
String name;
#override
List<Object?> get props => [id, name];
}
#HiveType(typeId: 1)
class Todo extends Equatable {
Todo({this.id, this.actions});
#HiveField(0)
String id;
#HiveField(1)
String name;
#HiveField(2)
List<Action> actions;
...
#override
List<Object?> get props => [id, name, actions];
}
Database / Repository
class HiveDatabase {
late Box<List<Todo>> todos;
...
Stream<List<Todo>> watchTodos() {
return todos
.watch()
.map((event) => todos.values.toList())
.startWith(todos.values.toList());
}
Future<void> saveTodo(Todo todo) async {
await todos.put(todo.id, todo);
}
Future<void> saveAction(Todo todo, Action action) async {
todo.actions.add(action);
await todos.put(todo.id, todo);
}
}
Blocs
EditTodoBloc:
class EditTodoBloc extends Bloc<EditTodoEvent, EditTodoState> {
EditTodoBloc({
required TodosRepository todosRepository,
required Todo? todo,
}) : _todosRepository = todosRepository, super(EditTodoState(todo)) {
on<TodoSubmitted>(_onTodoSubmitted);
}
...
Future<void> _onTodoSubmitted(
TodoSubmitted event,
Emitter<EditTodoState> emit,
) async {
emit(state.copyWith(status: EditTodoStaus.loading));
try {
await _todosRepository.saveTodo(state.todo!);
emit(state.copyWith(status: EditTodoStaus.success));
} catch (e) {}
}
}
class EditTodoState extends Equatable {
final EditTodoStatus status;
final Todo? todo;
...
}
TodoBloc:
class TodoBloc extends Bloc<TodoEvent, TodoState> {
TodoBloc({
required TodosRepository todosRepository,
required Todo todo,
}) : _todosRepository = todosRepository, super(TodoState(todo)) {
...
}
}
class TodoState extends Equatable {
final TodoStatus status;
final Todo todo,
final List<Action> actions;
...
}
Action Bloc:
class ActionBloc extends Bloc<ActionEvent, ActionState> {
ActionBloc({
required TodosRepository todosRepository,
required Todo todo,
required Action? action,
}) : _todosRepository = todosRepository, super(ActionState(todo, action)) {
on<ActionSubmitted>(_onActionSubmitted);
}
...
Future<void> _onActionSubmitted(
ActionSubmitted event,
Emitter<ActionState> emit,
) async {
emit(state.copyWith(status: ActionStatus.loading));
try {
await _todosRepository.saveAction(todo, state.action!);
emit(state.copyWith(status: ActionStatus.success));
} catch(e) {}
}
}
class ActionState extends Equatable {
final ActionStatus status;
final Todo todo,
final Action? action;
...
}
And the problem is here.
CollectionBloc can't persist the state of the bloc when I submit data by adding an event from Page C (ActionBloc). Unlike when sending data from Page B-1 (EditTodoBloc) which works successfully.
CollectionBloc:
class CollectionBloc extends Bloc<CollectionEvent, CollectionState> {
CollectionBloc({
required TodosRepository todosRepository,
}) : super(CollectionState()) {
on<CollectionRequested>(_onCollectionRequested);
}
...
Future<void> _onCollectionRequested(
CollectionRequested event,
Emitter<CollectionState> emit,
) async {
emit(state.copyWith(status: TodoStatus.loading));
await emit.forEach<List<Todo>>(
_todosRepository.watchTodos(),
onData: (todos) {
print('newTodos: ${todos}');
print('oldTodos: ${state.todos}');
// Why oldTodos shows the same modified todo list (with its actions)
// as the one returned from onData
// Page-B2 does not update coming back from Page-C unless I pop up to
// Page-A and then push to Page-B2.
return state.copyWith(status: CollectionStatus.success, todos: todos);
},
onError: (_, __) => state.copyWith(status: CollectionStatus.failure),
);
}
}
class CollectionState extends Equatable {
final CollectionStatus status;
final List<Todo> todos;
...
}
Page A:
class PageA extends StatelessWidget {
const PageA({Key? key}) : super(key: key);
...
#override
Widget build(BuildContext context) {
final todos = context.watch<CollectionBloc>().state.todos;
return ListView(
children: [
for (final todo in todos) ...[
ListTile(
title: Text(todo.name),
onTap: () {
Navigator.of(context).push(
PageB2.route(todo),
);
},
),
],
],
);
}
}
All in all, I would like to be able to show the list of actions updated when popping back from submitting the form in Page C to Page B-2.
class PageB2 extends StatelessWidget {
const PageB2({Key? key}) : super(key: key);
static Route<void> route(Todo todo) {
return MaterialPageRoute(
builder: (context) => BlocProvider(
create: (context) => TodoBloc(
todoRepository: context.read<TodosRepository>(),
todo: todo,
),
child: const PageB2(),
),
);
}
#override
Widget build(BuildContext context) {
final todos = context.watch<CollectionBloc>().state.todos;
final todo = todos.firstWhere((element) => element.id == state.todo.id);
return BlocBuilder<TodoBloc, TodoState>(
builder: (context, state) {
return ListView.separated(
itemCount: todo.actions.length,
separatorBuilder: (context, index) => const Divider(height: 8),
itemBuilder: (context, index) {
return ListTile(
title: Text(todo.actions[index].name),
onTap: () {
Navigator.of(context).push(
PageC.route(todo, todo.actions[index]),
);
},
);
},
);
},
);
}
}
I think I don't know how to apply emit.forEach() in this case. XD

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.

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),
]);
});
}
}

Data not being updated after change the placeID in flutter BLoC

I am working on one project with BLoC.I have made some classes to update the data.
Data will come once you pass the placeID.
But once you pass the PlaceID first time it will give the data and widgets updated.
But once I pass new placeID old data not being updated. It shows old data.
All Code Files:
RestaurantDetailBloc.dart
class RestaurantDetailBloc extends Bloc<RestaurantDetailEvent, RestaurantDetailState> {
static final RestaurantDetailBloc _restaurantDetailBlocSingleton = new RestaurantDetailBloc._internal();
factory RestaurantDetailBloc() {
return _restaurantDetailBlocSingleton;
}
RestaurantDetailBloc._internal();
RestaurantDetailState get initialState => new UnRestaurantDetailState();
#override
Stream<RestaurantDetailState> mapEventToState(
RestaurantDetailEvent event,
) async* {
try {
yield await event.applyAsync(currentState: currentState, bloc: this);
} catch (_, stackTrace) {
print('$_ $stackTrace');
yield currentState;
}
}
}
LoadRestaurantDetailEvent.dart
#immutable
abstract class RestaurantDetailEvent {
Future<RestaurantDetailState> applyAsync(
{RestaurantDetailState currentState, RestaurantDetailBloc bloc});
final RestaurantDetailProvider _provider = RestaurantDetailProvider();
}
class LoadRestaurantDetailEvent extends RestaurantDetailEvent {
#override
String toString() => 'LoadRestaurantDetailEvent';
String placeID;
LoadRestaurantDetailEvent({Key key,this.placeID});
#override
Future<RestaurantDetailState> applyAsync(
{RestaurantDetailState currentState, RestaurantDetailBloc bloc}) async {
try {
await Future.delayed(new Duration(seconds: 2));
var component = await _provider.getRestaurantReview(placeID);
print(component);
return new InRestaurantDetailState(component);
} catch (_, stackTrace) {
print('$_ $stackTrace');
return new ErrorRestaurantDetailState(_?.toString());
}
}
}
RestaurantDetailPage.dart
class RestaurantDetailPage extends StatelessWidget {
static const String routeName = "/restaurantDetail";
final String imageURL;
final String placeID;
const RestaurantDetailPage({Key key, this.imageURL,this.placeID}) : super(key: key);
#override
Widget build(BuildContext context) {
var _restaurantDetailBloc = new RestaurantDetailBloc();
return new RestaurantDetailScreen(restaurantDetailBloc: _restaurantDetailBloc,imageUrl: this.imageURL,placeId: this.placeID,);
}
}
RestaurantDetailProvider.dart
class RestaurantDetailProvider {
String getBaseUrl(String placeID){
final urlBase = "https://maps.googleapis.com/maps/api/place/details/json?placeid=$placeID&key=xxxxxxxxxxxxxGooglePlaceKey";
return urlBase;
}
Future<void> loadAsync(String token) async {
/// write from keystore/keychain
await Future.delayed(new Duration(seconds: 2));
}
Future<void> saveAsync(String token) async {
/// write from keystore/keychain
await Future.delayed(new Duration(seconds: 2));
}
Future<Map<String, dynamic>> getRestaurantReview(String placeId)async{
var response = await http.get(getBaseUrl(placeId));
RestaurantReviews reviews = RestaurantReviews();
if(response.statusCode == 200){
var decodedJson = jsonDecode(response.body);
print(decodedJson);
//reviews.result = decodedJson['result'];
return decodedJson;
}
else{
}
}
}
InRestaurantDetailState.dart
#immutable
abstract class RestaurantDetailState extends Equatable {
RestaurantDetailState([Iterable props]) : super(props);
/// Copy object for use in action
RestaurantDetailState getStateCopy();
}
/// UnInitialized
class UnRestaurantDetailState extends RestaurantDetailState {
#override
String toString() => 'UnRestaurantDetailState';
#override
RestaurantDetailState getStateCopy() {
return UnRestaurantDetailState();
}
}
class InRestaurantDetailState extends RestaurantDetailState {
final resReview;
InRestaurantDetailState(this.resReview);
#override
String toString() => 'InRestaurantDetailState';
#override
RestaurantDetailState getStateCopy() {
return InRestaurantDetailState(resReview);
}
}
class ErrorRestaurantDetailState extends RestaurantDetailState {
final String errorMessage;
ErrorRestaurantDetailState(this.errorMessage);
#override
String toString() => 'ErrorRestaurantDetailState';
#override
RestaurantDetailState getStateCopy() {
return ErrorRestaurantDetailState(this.errorMessage);
}
}
RestaurantDetailScreenState.dart
class RestaurantDetailScreen extends StatefulWidget {
const RestaurantDetailScreen({
Key key,
#required RestaurantDetailBloc restaurantDetailBloc,
this.imageUrl, this.placeId,
}) : _restaurantDetailBloc = restaurantDetailBloc,
super(key: key);
final RestaurantDetailBloc _restaurantDetailBloc;
final String imageUrl;
final String placeId;
#override
RestaurantDetailScreenState createState() {
return new RestaurantDetailScreenState(_restaurantDetailBloc, imageUrl,placeId);
}
}
class RestaurantDetailScreenState extends State<RestaurantDetailScreen> {
final RestaurantDetailBloc _restaurantDetailBloc;
final String imageUrl;
final String placeId;
RestaurantDetailScreenState(this._restaurantDetailBloc, this.imageUrl,this.placeId);
#override
void initState() {
super.initState();
this._restaurantDetailBloc.dispatch(LoadRestaurantDetailEvent(placeID:placeId));
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height;
return BlocBuilder<RestaurantDetailBloc, RestaurantDetailState>(
bloc: widget._restaurantDetailBloc,
builder: (
BuildContext context,
var currentState,
) {
if (currentState is UnRestaurantDetailState) {
return MaterialApp(
home: new Scaffold(
body: new Container(
color: Colors.white,
child: Center(
child: CircularProgressIndicator(),
),
),
));
}
if (currentState is ErrorRestaurantDetailState) {
return new Container(
child: new Center(
child: new Text(currentState.errorMessage ?? 'Error'),
));
}
if (currentState is InRestaurantDetailState) {
var resList = currentState.resReview;
print(resList);
return MaterialApp(
home: new Scaffold(
)
);
}
Please help me guys.I have spent whole day.
Thank you in advance.
You need to pass the data to the parent class for comparison. That's why we are using equatable. Do these changes and it should work. Let me know if it doesn’t.
class InRestaurantDetailState extends RestaurantDetailState {
final resReview;
//You need to change this line to
InRestaurantDetailState(this.resReview):super([resReview]);
#override
String toString() => 'InRestaurantDetailState';
#override
RestaurantDetailState getStateCopy() {
return InRestaurantDetailState(resReview);
}
}