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

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.

Related

Flutter bloc state is not emitting or updating. Method mapEventToState is never called

The following code was working before null safety with flutter_bloc 4.0.1 but after null safety migration the state is not updating / emitting / broadcasting as expected with flutter_bloc 7.3.3.
The below _reactToState and mapEventToState methods are never called. How can I fix it?
Splash Screen
class SplashScreen extends StatefulWidget {
final Strapper strapper;
final Service? service;
SplashScreen(this.strapper, this.service);
#override
State<StatefulWidget> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
SplashBloc? _splashBloc;
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (_splashBloc == null) {
_splashBloc = SplashBloc(widget.strapper, widget.service);
_splashBloc!.stream.listen(_reactToState);
}
}
#override
dispose() {
_splashBloc?.close();
_splashBloc = null;
super.dispose();
}
#override
Widget build(BuildContext context) {
return BlocProvider<SplashBloc>(
create: (context) => _splashBloc!,
child: BlocBuilder<SplashBloc, SplashBlocState>(
builder: (context, state) => Container(
child: Stack(
children: <Widget>[
LogoPanel(
_showWidgetForState(state),
),
],
),
),
),
);
}
void _reactToState(SplashBlocState state) {
if (state is InitializingSplashBlocState) {
if (widget.logOut) {
_splashBloc!.add(LogoutSplashBlocEvent());
} else {
_splashBloc!.add(CInitializationSplashBlocEvent());
}
} else if (state is AuthSuccessSplashBlocState) {
App.navigateToSomewhere(context, state.isNewUser);
}
}
Widget _showWidgetForState(SplashBlocState state) {
if (state is InitializingSplashBlocState) {
return _getProgressIndicator();
} else if (state is ChooseSomethingSplashBlockState ) {
return _showSignInWidget();
}
}
}
Splash Bloc
class SplashBloc extends Bloc<SplashBlocEvent, SplashBlocState> {
final Strapper? strapper;
final Service? service;
SplashBloc(this.strapper, this.service) : super(InitializingSplashBlocState());
#override
Stream<SplashBlocState> mapEventToState(event) async* {
if (event is CInitializationSplashBlocEvent) {
await strapper!.run();
}
bool chooseSomething = !service!.hasSomeSelection;
if (chooseSomething) {
yield ChooseSomethingSplashBlockState();
} else if (event is RAuthSplashBlocEvent) {
yield AuthSplashBlocState();
var authState = await _run();
yield authState;
}
}
Future<SplashBlocState> _run() async {
// Do something
}
}
Splash Bloc Event
abstract class SplashBlocEvent extends Equatable {
const SplashBlocEvent();
#override
List<Object> get props => [];
}
class CInitializationSplashBlocEvent extends SplashBlocEvent {}
class RAuthSplashBlocEvent extends SplashBlocEvent {}
Splash Bloc State
abstract class SplashBlocState extends Equatable {
const SplashBlocState();
#override
List<Object> get props => [];
}
class InitializingSplashBlocState extends SplashBlocState {}
class AuthSplashBlocState extends SplashBlocState {}
class ChooseSomethingSplashBlockState extends SplashBlocState {}
class AuthSuccessSplashBlocState extends SplashBlocState {
final CurrentUser? user;
final bool isNewUser;
AuthSuccessSplashBlocState(this.user, this.isNewUser);
}
As per the documentation:
In v6.0.0, the above snippet does not output the initial state and only outputs subsequent state changes. The previous behavior can be achieved with the following:
final bloc = MyBloc();
print(bloc.state);
bloc.listen(print);
So I changed my code in the Splash screen as following:
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (_splashBloc == null) {
_splashBloc = SplashBloc(widget.strapper, widget.service);
_reactToState(_splashBloc!.state); // Added this line
_splashBloc!.stream.listen(_reactToState);
}
}
And that's it. It worked!
_reactToState and mapEventToState are definitely being called.
when you use Streamcontrollers it greatly simplifies state. I build a bloc code to manage state. The materialapp child is the splashWidget whose job is to render the hour, minute, second from bloc code emitting Time state. If the user clicks the splash screen or 5 seconds elapses the splash screen will be replaced with the HomePageWidget. bloc code controls the starting and stopping of the timer using an timerState event.
'package:flutter/material.dart';
import 'bloc_splash.dart';
import 'main.dart';
class SplashWidget extends StatelessWidget {
const SplashWidget({Key? key}) : super(key: key);
_redirectToHome(BuildContext context)
{
Navigator.pushReplacement(context,MaterialPageRoute(builder:(_)=>MyHomePage(title:"helloWorld")));
}
String _displayClock(Time ? data)
{
String retVal="";
if (data!=null)
{
retVal="Time: ${data.hour} : ${data.minute} : ${data.second}";
}
return retVal;
}
#override
Widget build(BuildContext context) {
SplashBloc _bloc=SplashBloc();
_bloc.timerOnChange(StartTimer());
return Scaffold(
body:InkWell(
onTap: (){_bloc.timerOnChange(StopTimer());
_redirectToHome(context);
},
child:Container(
child:
StreamBuilder<TimeState>(
stream:_bloc.timeStream,
builder:(context,snapshot)
{
if(snapshot.hasData && (snapshot.data is RedirectState))
{
return MyHomePage(title:"helloWorld");
}
return Center(child:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Splash Screen", style:TextStyle(fontSize: 24,fontWeight: FontWeight.bold)),
Text(_displayClock(snapshot.data?.time)),
]));
}
)
))
);
}
}
bloc code
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'dart:ui';
import 'dart:async';
abstract class TimerEvent extends Equatable{
const TimerEvent();
#override
List<Object>get props=>[];
}
class StartTimer extends TimerEvent{
const StartTimer();
}
class StopTimer extends TimerEvent{
const StopTimer();
}
class Time{
final int hour;
final int minute;
final int second;
Time(this.hour,this.minute,this.second);
}
class TimeState extends Equatable{
final Time time;
TimeState(this.time);
#override
List<Object> get props=>[time];
}
class RedirectState implements TimeState{
final Time time;
RedirectState(this.time);
#override
List<Object> get props=>[time];
#override
// TODO: implement stringify
bool? get stringify => throw UnimplementedError();
}
class TimerState extends Equatable{
final bool started;
const TimerState(this.started);
#override
List<Object> get props => [started];
}
class SplashBloc
{
SplashBloc();
Timer ?_timer;
var countDown=5;
Stream<TimeState> get timeStream=> _timeController.stream;
final _timeController =BehaviorSubject<TimeState>();
void dispose()
{
_timeController.close();
}
void _pushTimeOnTheStream(Timer timer)
{
DateTime now=DateTime.now();
_timeController.sink.add(TimeState(Time(now.hour,now.minute,now.second)));
this.countDown-=1;
if (this.countDown==0)
{
timerOnChange(StopTimer());
_timeController.sink.add(RedirectState(Time(0,0,0)));
}
}
void timerOnChange(TimerEvent event) {
if (event is StartTimer)
{
_timer=Timer.periodic(Duration(seconds: 1),_pushTimeOnTheStream);
}
else if(event is StopTimer){
//_timerController.sink.add(TimerState(false));
_timer?.cancel();
}
}
}
app
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const SplashWidget(),
);
}
}

how to use infinite_scroll_pagination for bloc pattern

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

Error: Type argument 'RoutesBloc' doesn't conform to the bound 'BlocBase<S>' of the type variable 'B' on 'BlocBuilder'

I'm getting this error and I have no clue where it's coming from.
class Routes extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<RoutesBloc, RoutesEvent>( // <-- It occurs here
builder: (context, state) {
return Text('...');
},
);
}
}
Full error:
lib/screens/home_screen.dart:86:12: Error: Type argument 'RoutesBloc' doesn't conform to the bound 'BlocBase' of the type variable 'B' on 'BlocBuilder'.
'RoutesBloc' is from '/blocs/routes/routes_bloc.dart' ('lib/blocs/routes/routes_bloc.dart').
'BlocBase' is from 'package:bloc/src/bloc.dart' ('../../AppData/Local/Pub/Cache/hosted/pub.dartlang.org/bloc-7.0.0/lib/src/bloc.dart').
Try changing type arguments so that they conform to the bounds.
return BlocBuilder<RoutesBloc, RoutesEvent>(
^
I use a multiplocprovider in my main.dart like this:
MultiBlocProvider(
providers: [
...,
BlocProvider<RoutesBloc>(
create: (_) => RoutesBloc(
apiRepository: ApiRepository.create(),
)..add(RoutesLoaded()),
),
],
child: AppView(),
)
routes_state.dart:
abstract class RoutesState extends Equatable {
const RoutesState();
#override
List<Object> get props => [];
}
class RoutesLoadInProgress extends RoutesState {}
class RoutesLoadSuccess extends RoutesState {
final List<BoulderingRoute> routes;
const RoutesLoadSuccess([this.routes = const []]);
#override
List<Object> get props => [routes];
}
class RoutesLoadFailure extends RoutesState {}
routes_event.dart:
abstract class RoutesEvent extends Equatable {
const RoutesEvent();
#override
List<Object> get props => [];
}
class RoutesLoaded extends RoutesEvent {}
class RouteAdded extends RoutesEvent {
final BoulderingRoute route;
const RouteAdded({this.route}) : assert(route != null);
#override
List<Object> get props => [route];
}
class RouteUpdated extends RoutesEvent {
final BoulderingRoute route;
const RouteUpdated({this.route}) : assert(route != null);
#override
List<Object> get props => [route];
}
class RouteDeleted extends RoutesEvent {
final BoulderingRoute route;
const RouteDeleted({this.route}) : assert(route != null);
#override
List<Object> get props => [route];
}
routes_bloc.dart:
class RoutesBloc extends Bloc<RoutesEvent, RoutesState> {
final ApiRepository _apiRepository;
RoutesBloc({ApiRepository apiRepository})
: assert(apiRepository != null),
this._apiRepository = apiRepository,
super(RoutesLoadInProgress());
#override
Stream<RoutesState> mapEventToState(
RoutesEvent event,
) async* {
print(event);
if (event is RoutesLoaded) {
yield* _mapRoutesLoadedToState();
}
}
Stream<RoutesState> _mapRoutesLoadedToState() async* {
try {
print('start');
final List<BoulderingRoute> routes =
await _apiRepository.fetchBoulderingRoutes();
yield RoutesLoadSuccess(routes);
} catch (_) {
yield RoutesLoadFailure();
}
}
}
I firstly thought that there must be something wrong with my RoutesBloc but changing the blocbuilder to a bloc that I'm successfully using at another place ends up with the same error.
Does someone know where this is coming from?
It should be return BlocBuilder<RoutesBloc, RoutesState>
Check this: https://pub.dev/packages/flutter_bloc#blocbuilder
BlocBuilder<BlocA, BlocAState>(
builder: (context, state) {
// return widget here based on BlocA's state
}
)

Using a bloc with Navigator 2.0

Hi I am trying to use a bloc instead of ChangeNotifierDelegate in my RouterDelegate class. Unfortunately the bloc is not being called when a route is changed through my routebloc, not sure why. I have tried wrapping the delegate in a BlocProvider, but it made no difference (I currently have it injected above in the main file.)
runApp(MyApp());
class _MyApp AppState extends State<MyApp> {
MyAppRouterDelegate _routerDelegate = MyAppRouterDelegate();
MyAppRouteInformationParser _routeInformationParser = MyAppRouteInformationParser();
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
lazy: false,
create: (context) => getIt<AuthBloc>()//..add(AppStarted()),
),
BlocProvider(
lazy: false,
create: (context) => getIt<RouterBloc>(),
),
],
child: MaterialApp.router(
title: 'MyApp',
theme: globalAppThemeData,
routerDelegate: _routerDelegate,
routeInformationParser: _routeInformationParser,
),
);
}
}
In my RouterDelegate I have .....
lass MyAppRouterDelegate extends RouterDelegate<MyAppConfiguration>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<MyAppConfiguration> {
final GlobalKey<NavigatorState> _navigatorKey;
String currentPage = '';
String selectedItem = '';
#override
GlobalKey<NavigatorState> get navigatorKey => _navigatorKey;
MyAppRouterDelegate() : _navigatorKey = GlobalKey<NavigatorState>();
#override
MyAppConfiguration get currentConfiguration {
currentPage = currentConfiguration.screen;
selectedItem = currentConfiguration.selectedItemId;
if (currentPage == UNKNOWN) {
return MyAppConfiguration.unknown();
} else if (currentPage == SPLASH) {
return MyAppConfiguration.splash();
} else if (currentPage == LOGIN) {
return MyAppConfiguration.login();
} else {
return MyAppConfiguration.unknown();
}
}
#override
Widget build(BuildContext context) {
List<Page> pages = [SplashPage(SPLASH)];
return BlocBuilder<RouterBloc, RouterState>(
builder: (context, state) {
if (state is ChangedRoute) {
pages.clear();
pages = state.pages;
}
return Navigator(
key: navigatorKey,
pages: pages,
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
context.read<AuthBloc>().add(AuthEventLoggedOut());
return true;
},
);
},
);
}
#override
Future<void> setNewRoutePath(MyAppConfiguration configuration) async {
if (configuration.unknown) {
currentPage = UNKNOWN;
selectedItem = configuration.selectedItemId;
} else if (configuration.isSplashPage) {
currentPage = SPLASH;
selectedItem = configuration.selectedItemId;
} else if (configuration.isLoginPage) {
currentPage = LOGIN;
selectedItem = configuration.selectedItemId;
} else if (configuration.isSignUpPage)
currentPage = SIGNUP;
selectedItem = configuration.selectedItemId;
} else {
print(Constants.failureCouldNotSetRoute);
}
}
_clear() {
currentPage = UNKNOWN;
selectedItem = '';
}
}
In my app configuration...
class MyAppInformationParser
extends RouteInformationParser<MyAppConfiguration> {
#override
Future<MyAppConfiguration> parseRouteInformation(RouteInformation? routeInformation) async {
final uri = Uri.parse(routeInformation!.location!);
if (uri.pathSegments.length == 0) {
return MyAppConfiguration.splash();
} else if (uri.pathSegments.length == 1) {
final first = uri.pathSegments[1].toLowerCase();
if (first == LOGIN) {
return MyAppConfiguration.login();
} else {
return MyAppConfiguration.unknown();
}
} else {
return MyAppConfiguration.unknown();
}
}
#override
RouteInformation restoreRouteInformation(MyAppConfiguration configuration) {
if (configuration.isUnknownPage) {
return RouteInformation(location: '/unknown');
} else if (configuration.isSplashPage) {
return RouteInformation(location: '/splash');
} else if (configuration.isLoginPage) {
return RouteInformation(location: '/login');
} else {
return RouteInformation(location: '/unknown');
}
}
}
My auth bloc ...
#injectable
class AuthBloc extends Bloc<AuthEvent, AuthState> {
IAuthFacade authRepo;
RouterBloc routerBloc;
AuthBloc(this.authRepo, this.routerBloc) : super(Uninitialized());
#override
Stream<AuthState> mapEventToState(
AuthEvent event,
) async* {
if (event is AppStarted) {
yield AuthenticationLoading();
Option<CurrentUser> user = await authRepo.getSignedInUser();
yield user.fold(() {
routerBloc.add(RouterEventNewPage(pages: [LoginPage(LOGIN)]));
return Unauthenticated();
}, (user) {
routerBloc.add(RouterEventNewPage(pages: [HomePage(HOME)]));
return Authenticated(user);
});
}
if (event is AuthEventLoggedOut) {
authRepo.signOut();
///TODO: clear hive here??
}
}
}
abstract class AuthEvent extends Equatable {
#override
List<Object> get props => [];
}
//
class AppStarted extends AuthEvent {}
//
class AuthEventLoggedOut extends AuthEvent {}
abstract class AuthState extends Equatable {
#override
List<Object> get props => [];
}
//
class Uninitialized extends AuthState {}
//
class Authenticated extends AuthState {
final CurrentUser user;
Authenticated(this.user);
}
//
class Unauthenticated extends AuthState {}
//
class AuthenticationLoading extends AuthState {}
My Router Bloc...
#injectable
class RouterBloc extends Bloc<RouterEvent, RouterState> {
RouterBloc() : super(RouterInitial());
#override
Stream<RouterState> mapEventToState(
RouterEvent event,
) async* {
if (event is RouterEventNewPage) {
yield ChangingRoute();
yield ChangedRoute(pages: event.pages);
}
}
}
abstract class RouterEvent extends Equatable {
const RouterEvent();
#override
List<Object> get props => [];
}
class RouterEventNewPage extends RouterEvent {
final List<Page> pages;
RouterEventNewPage({required this.pages});
#override
List<Object> get props => [pages];
}
abstract class RouterState extends Equatable {
const RouterState();
#override
List<Object> get props => [];
}
class RouterInitial extends RouterState {}
class ChangingRoute extends RouterState {}
class ChangedRoute extends RouterState {
final List<Page> pages;
ChangedRoute({required this.pages});
#override
List<Object> get props => [pages];
}
The app runs through the Navigator in the build function of the delegate first, it navigates to the splash screen perfectly, then after my animation finishes in the splash screen it calls the auth bloc to check if user is authorised, this works perfectly which then calls the routerbloc. The router bloc adds the new login screen (as the user is logged out). However, the bloc inside the build function of the MyAppRouterDelegate is not firing again.
Any help provided would be very much appreciated.
When it runs through the MyAppRouterDelegates build function the first time I do receive the error
"
════════ Exception caught by scheduler library ═════════════════════════════════
The following StackOverflowError was thrown during a scheduler callback:
Stack Overflow
When the exception was thrown, this was the stack
#0 CrokettRouterDelegate.currentConfiguration
package:crokett/routes/crokett_router_delegate.dart:20
"
But I don't receive any more information on the error.
Don't you need a notifyListeners() somewhere in your blocBuilder after you update the page stack?
I am interested to know if you got it working.

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