Cubit not emitting state - flutter

Folks, I need some help with this.
I have this code
void onLoad(int id) async {
final result = await ListRepository.loadProduct(id);
if (result != null) {
product = result;
WoocommerceModel? model = product!.woo;
if (model!.woocommerce) {
List? result2 = await ListRepository.loadOffsiteCoupon(model: model);
emit(ProductOffsiteCouponSuccess(result2));
emit(ProductDetailSuccess(product!));
} else {
emit(ProductDetailSuccess(product!));
}
}
}
emit(ProductDetailSuccess(product!)); works fine but emit(ProductOffsiteCouponSuccess(result2)); doesn't work.
Here's the state definition:
abstract class ProductDetailState {}
class ProductDetailLoading extends ProductDetailState {}
class ProductOffsiteCouponSuccess extends ProductDetailState {
final List? coupon;
ProductOffsiteCouponSuccess(this.coupon);
}
class ProductDetailSuccess extends ProductDetailState {
final ProductModel product;
ProductDetailSuccess(this.product);
}
I'm trying to access the data here >>
if (state is ProductOffsiteCouponSuccess) {
couponList = state.coupon;
}
In this build method >>
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => _productDetailCubit,
child: BlocBuilder<ProductDetailCubit, ProductDetailState>(
builder: (context, state) {
ProductModel? product;
List? couponList;
if (state is ProductDetailSuccess) {
product = state.product;
}
if (state is ProductOffsiteCouponSuccess) {
couponList = state.coupon;
}
return Scaffold(
body: _buildContent(product),
drawer: Drawer(),
);
},
),
);
}
}
Any idea why this doesn't work?
I get the data in the variable 'result2'with no issues, but I can't emit the success state to hold the data for later use.
Any help is appreciated.
Thanks in advance.

If you are using a specific type with coupon (like List) and it is not a String or int then you should extend it and also the abstract class with Equatable and add its variables to props even if this doesn't work then you can try toSet().toList()

Related

cubit returns a null value

I am facing very weird problem. i am using bloc with freezed, injectable and dartz. i just need to get data from SQl database and display it when a today page opened.
The code of UI is:
class TodayPage extends HookWidget {
const TodayPage();
#override
Widget build(BuildContext context) {
return BlocProvider<ScheduledNotesCubit>(
lazy:false,
create: (context) => getIt<ScheduledNotesCubit>()
..countDoneNoteOutOfAllNotes()
..retrieveData(),
child: BlocBuilder<ScheduledNotesCubit, ScheduledNotesState>(
builder: (context, state) {
return ListView.builder(
itemCount: state.maybeMap(
orElse: () {}, getNotesCount: (g) => g.noteCount),
itemBuilder: (BuildContext context, int index) {
return Text(
"${state.maybeMap(orElse: () {}, getNotes: (notes) {
return notes.getNotes[index]['content'];
})}",
);
},
);
},
),
);
}
}
The code of state is:
#freezed
class ScheduledNotesState with _$ScheduledNotesState {
const factory ScheduledNotesState.initial() = _Initial;
const factory ScheduledNotesState.getNotes({required List<Map<String, dynamic>> getNotes}) = _GetNotes;
const factory ScheduledNotesState.getNotesCount({required int noteCount}) = _GetNotesCount;
const factory ScheduledNotesState.getCountDoneNoteOutOfAllNotes({required String getCountDoneNoteOutOfAllNotes}) = _GetCountDoneNoteOutOfAllNotes;
const factory ScheduledNotesState.updateIsDoneNote({required int updateIsDoneNote}) = _UpdateIsDoneNote;
}
The code of cubit is:
#injectable
class ScheduledNotesCubit extends Cubit<ScheduledNotesState> {
ScheduledNotesCubit(this._noteRepository)
: super(const ScheduledNotesState.initial());
final NoteRepository _noteRepository;
// retrieve data
void retrieveData() async {
return emit(ScheduledNotesState.getNotes(
getNotes: await _noteRepository.retrieveData()));
}
}
This Cubit Does not return a value in listView, instead it returns null values, But When i try to do so it works!!!!!!
the updated cubit code is:
#injectable
class ScheduledNotesCubit extends Cubit<ScheduledNotesState> {
ScheduledNotesCubit(this._noteRepository)
: super(const ScheduledNotesState.initial());
final NoteRepository _noteRepository;
// retrieve data
void retrieveData() async {
var d= await _noteRepository.retrieveData(); //-->updated
var x= d[1]['content']; //-->updated
print("\n $x \n") ; // -->updated
return emit(ScheduledNotesState.getNotes(
getNotes: await _noteRepository.retrieveData()));
}
}
Can you try add lazy to false for BlocProvider, and update this code:
void retrieveData() {
_noteRepository.retrieveData().then((value) {
emit(ScheduledNotesState.getNotes(getNotes: value));
});
}
The solution is create a data class for the cubit, instead of creating a sealed classes.

How to add an object to flutter_realm?

The following Exception is thrown when I try to add an object
Maybe I misunderstand how functions work.
I couldn't find something helpful in documentation too
Unhandled Exception: Unhandled error PlatformException(-1, Attempting to create an object of type 'Country' with an existing primary key value ''., {
RLMRealmCoreVersion = "";
RLMRealmVersion = "3.17.3";
}, null) occurred in Instance of 'CountriesBloc'.
Here is my code . I have an CountriesToRealm event and I want to save my objects in flutter_realm when I add the event . I add it when I load countries by http request
if (event is CountriesToRealm) {
for (int i = 0; i < event.countries.length; ++i) {
Country country = event.countries[i];
print(country.name);
if (country.name == null || country.name.isEmpty) {}
final query = Query('Country').equalTo('name', country.name);
final List all = await realm.objects(query);
if (all.isEmpty) {
await realm.createObject('Country', country.toMap(withId: true));
}
}
final List allcountries = await event.realm.allObjects('Country');
if (allcountries.length == event.countries.length) {
yield CountriesAddedToRealmState();
} else {
yield FailedToAddCountriesState();
}
}
Also Realm Provider
import 'package:flutter/material.dart';
import 'package:flutter_realm/flutter_realm.dart';
import 'package:uuid/uuid.dart';
class RealmProvider extends StatefulWidget {
final Widget Function(Realm) builder;
const RealmProvider({this.builder}) : super();
#override
_RealmProviderState createState() => _RealmProviderState();
}
class _RealmProviderState extends State<RealmProvider> {
Future<Realm> realm;
#override
void initState() {
super.initState();
final configuration = Configuration(inMemoryIdentifier: Uuid().v4());
realm = Realm.open(configuration);
}
#override
Widget build(BuildContext context) {
return FutureBuilder<Realm>(
future: realm,
builder: (context, snapshot) {
if (snapshot.data == null) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: CircularProgressIndicator(),
),
);
}
return widget.builder(snapshot.data);
},
);
}
}
Thank you for your help

Should I use final in models with equatable and flutter_bloc to distinguish 2 states?

I'm creating an app where you login and go to a page where you have a list of your restaurants, you have also a form where you can add a new restaurant.
This part works.
The problem is that when i click add the restaurant is added in firestore correctly, but the list doesn't refresh. I usually yield 2 states, a LoadingState and a LoadedRestaurantsListState, but with the last version of flutter_bloc this trick doesn't work, seems like just the last state yielded is received, but the previous was LoadedRestaurantsListState, so they are equals and the blocbuilder ignores the second one. So I've to use the equatable's props to distinguish the 2 states, but in the equatable documentation is written: "Note: Equatable is designed to only work with immutable objects so all member variables must be final".
So I've to make all the model's fields final, but if I do it how can i modify just one o two fields when I need it to?
What is the best practice?
If someone has examples, or videos, etc it would be very appreciated.
Thanks in advance
Without props
FirebaseBloc.dart
Stream<FirebaseState> mapEventToState(
FirebaseEvent event,
) async* {
print("event firebase ${event.runtimeType.toString()}");
if (event is CreateRestaurantFirebaseEvent) {
yield LoadingState();
await _databaseService.createRestaurant(event.restaurant, event.user);
List<Restaurant> restaurantsList = await _databaseService
.loadRestaurantsList(event.user.restaurantsIDsList);
yield LoadedRestaurantsListState(restaurantsList);
}
if (event is LoadRestaurantsListEvent) {
List<Restaurant> restaurantsList =
await _databaseService.loadRestaurantsList(event.restaurantsIDs);
yield LoadedRestaurantsListState(restaurantsList);
}
FirebaseState.dart
class LoadingState extends FirebaseState {
#override
List<Object> get props => [];
}
class LoadedRestaurantsListState extends FirebaseState {
List<Restaurant> restaurantsList;
LoadedRestaurantsListState(this.restaurantsList);
#override
List<Object> get props => [];
}
view.dart
class RestaurantSelectionScreen extends StatefulWidget {
final User user;
RestaurantSelectionScreen({
#required this.user,
});
#override
_RestaurantSelectionScreenState createState() =>
_RestaurantSelectionScreenState();
}
class _RestaurantSelectionScreenState extends State<RestaurantSelectionScreen> {
FirebaseBloc _firebaseBloc;
#override
void initState() {
super.initState();
_firebaseBloc = FirebaseBloc();
_firebaseBloc.add(LoadRestaurantsListEvent(widget.user.restaurantsIDsList));
}
#override
Widget build(BuildContext context) {
return BlocProvider<FirebaseBloc>(
create: (context) => _firebaseBloc,
child: Scaffold(
body: SingleChildScrollView(
child: Center(
child: BlocBuilder(
cubit: _firebaseBloc,
builder: (context, state) {
print("state ${state.runtimeType.toString()}");
if (state is InitialFirebaseState) {
return CircularProgressIndicator();
} else if (state is LoadedRestaurantsListState) {
return buildUI(state);
} else if (state is LoadingState) {
return CircularProgressIndicator();
} else {
return _CreateRestaurantFormWidget(widget.user);
}
},
),
),
),
),
);
}

flutter_bloc many Event to many BlocBuilder

Recently I am learning flutter_bloc, and I refer to the project flutter_weather.
What I am puzzled is that if a Bloc class has many Events, and most of the Events will have values returned by State, and there are many BlocBuilders in the project, what should I do if I want a BlocBuilder to only respond to a certain Event?
The method I can think of is to divide this Bloc into multiple Blocs, or treat each value to be returned as an attribute of Bloc, BlocBuilder uses the buildwhen method to determine whether to rebuild.
But both of these methods are not good for me. Is there any good method? It is best to have projects on github for reference.
For example:
This is Event:
abstract class WeatherEvent extends Equatable {
const WeatherEvent();
}
class WeatherRequested extends WeatherEvent {
final String city;
const WeatherRequested({#required this.city}) : assert(city != null);
#override
List<Object> get props => [city];
}
class WeatherRefreshRequested extends WeatherEvent {
final String city;
const WeatherRefreshRequested({#required this.city}) : assert(city != null);
#override
List<Object> get props => [city];
}
This is State:
abstract class WeatherState extends Equatable {
const WeatherState();
#override
List<Object> get props => [];
}
class WeatherInitial extends WeatherState {}
class WeatherLoadInProgress extends WeatherState {}
class WeatherLoadSuccess extends WeatherState {
final Weather weather;
const WeatherLoadSuccess({#required this.weather}) : assert(weather != null);
#override
List<Object> get props => [weather];
}
class WeatherLoadFailure extends WeatherState {}
This is Bloc:
class WeatherBloc extends Bloc<WeatherEvent, WeatherState> {
final WeatherRepository weatherRepository;
WeatherBloc({#required this.weatherRepository})
: assert(weatherRepository != null),
super(WeatherInitial());
#override
Stream<WeatherState> mapEventToState(WeatherEvent event) async* {
if (event is WeatherRequested) {
yield* _mapWeatherRequestedToState(event);
} else if (event is WeatherRefreshRequested) {
yield* _mapWeatherRefreshRequestedToState(event);
}
}
Stream<WeatherState> _mapWeatherRequestedToState(
WeatherRequested event,
) async* {
yield WeatherLoadInProgress();
try {
final Weather weather = await weatherRepository.getWeather(event.city);
yield WeatherLoadSuccess(weather: weather);
} catch (_) {
yield WeatherLoadFailure();
}
}
Stream<WeatherState> _mapWeatherRefreshRequestedToState(
WeatherRefreshRequested event,
) async* {
try {
final Weather weather = await weatherRepository.getWeather(event.city);
yield WeatherLoadSuccess(weather: weather);
} catch (_) {}
}
}
This is BlocConsumer:
// BlocBuilder1
BlocBuilder<WeatherBloc, WeatherState>(
builder: (context, state) {
if (state is WeatherLoadInProgress) {
return Center(child: CircularProgressIndicator());
}
if (state is WeatherLoadSuccess) {
final weather = state.weather;
return Center(child: Text("WeatherRequested "))
}
)
// BlocBuilder2
BlocBuilder<WeatherBloc, WeatherState>(
builder: (context, state) {
if (state is WeatherLoadInProgress) {
return Center(child: CircularProgressIndicator());
}
if (state is WeatherLoadSuccess) {
final weather = state.weather;
return Center(child: Text("WeatherRefreshRequested"))
}
)
The problem is that I want BlocBuilder1 only to work when the type of Event is WeatherRequested and BlocBuilder2 only works when the type of Event is WeatherRefreshRequested. One of my ideas is that each Event has its own State, and then judge the type of State in buildwhen.
Is there any good method?
if you want to build you widget to respond for certain states you should use
BlocConsumer and tell that bloc in buildWhen to tell it what state it should build/rebuild you widget on.
BlocConsumer<QuizBloc, QuizState>(
buildWhen: (previous, current) {
if (current is QuizPoints)
return true;
else
return false;
},
listener: (context, state) {},
builder: (context, state) {
if (state is QuizPoints)
return Container(
child: Center(
child: Countup(
begin: 0,
end: state.points,
duration: Duration(seconds: 2),
separator: ',',
),
),
);
else
return Container();
},
);

Is there a way for multiple FutureBuilders to use the same future from a ChangeNotifier?

I have a class (that extends ChangeNotifier - Provider package) that has a function that returns a Future. What I'm trying to do is have it so that multiple futureBuilders in my UI code can receive values from this function but without having to call that function once per FutureBuilder.
However, I the function itself gets run again and again with every FutureBuilder I use. I know there must be a way to expose the Future itself through the Provider package but I can't seem to figure out how.
Here is the class that extends ChangeNotifier and has the future in it:
class ApiService extends ChangeNotifier {
CurrencyTicker _data;
CurrencyTicker get getdata => _data;
set setdata(CurrencyTicker data) {
_data = data;
}
Future<CurrencyTicker> fetchBaseData() async {
final response =
await http.get(API_URL_HERE); // url removed for stackoverflow
if (response.statusCode == 200) {
print('1 call logged');
setdata = CurrencyTicker.fromJson(json.decode(response.body));
return CurrencyTicker.fromJson(json.decode(response.body));
} else {
throw Exception('Request failed: ' + response.statusCode.toString());
}
}
}
Here is the UI code (it's just a FutureBuilder):
class MyBody extends StatelessWidget {
const MyBody({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final provider = Provider.of<ApiService>(context);
return Center(
child: FutureBuilder(
future: provider.fetchBaseData(),
initialData: CurrencyTicker().price,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(snapshot.data.company),
],
);
} else {
return LinearProgressIndicator();
}
},
),
);
}
}
I haven't included the MultiProvider thats at the "top level" in the widget tree since I don't see why I'd have to. I haven't included the Model for the CurrencyTicker class. I can provide both of these if necessary.
Would appreciate any input at all here
You don't want to do the HTTP call directly inside the build method of your consumers.
Instead of exposing a method on your ChangeNotifier, you should expose a property:
class MyNotifier with ChangeNotifier {
Future<Foo> foo;
}
That foo is a variable that stores the result of your last fetchData call.
Depending on your needs, you can then:
call fetchData in the constructor, if immediately needed
class MyNotifier with ChangeNotifier {
MyNotifier() {
foo = fetchData();
}
Future<Foo> foo;
}
lazy load it using a custom getter:
class MyNotifier with ChangeNotifier {
Future<Foo> _foo;
Future<Foo> get foo => _foo ??= fetchData();
}