Sending data to other class is getting null in flutter dart - flutter

I am using model to convert data to map. When I pass value to the model from class A it returns a map and then I am passing the returned value (Map) to class B. Before sending the valuing I am printing value in class A (It is showing data) but in class B it is showing null. here is class A function.
///Dummy Data remove later
jobPostModel = JobPostModel(
jobPost: 'Web design',
);
var data = jobPostModel.toJob(jobPostModel);
print('before $data');
JobPostScreen8(data);
Here is Model class function which is returning Map
class JobPostModel {
String jobPost;
JobPostModel(
{this.jobPost});
Map toJob(JobPostModel info){
var data = Map<String, dynamic>();
data['jobPost'] = info.jobPost;
return data;
}
factory JobPostModel.fromJob(Map<String, dynamic> data){
return JobPostModel(
jobPost: data['jobPost']
);
}
}
and here is other class where i must display data but it is showing null
class JobPostScreen8 extends StatefulWidget {
Map jobPostModelMapData;
JobPostScreen8([this.jobPostModelMapData]);
#override
_JobPostScreen8State createState() => _JobPostScreen8State();
}
class _JobPostScreen8State extends State<JobPostScreen8> {
#override
void initState() {
super.initState();
print('after ${widget.jobPostModelMapData}');
}
}

The solution is I was passing directly to class B. All I need is to use Navigation.
BlocListener<JobBloc, JobState>(
listener: (context, state) {
if (state is JobSuccessfulState)
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => JobPostScreen8(state.data)));
})
here is Bloc
if (event is PreviewPostJob) {
yield JobSuccessfulState(_updateModel());
}
and state
class JobSuccessfulState extends JobState {
var data;
JobSuccessfulState([this.data]);
}

Related

why posting data to firebase using flutter bloc is not emitting?

I'm creating an app with firebase as a database. After sending data to firebase, app screen should pop out for that I had bloclistener on the screen but after sending the data to firestore database, nothing is happening, flow is stopped after coming to loaded state in bloc file why? check my code so that you will know. I can see my data in firebase but it is not popping out because flow is not coming to listener.
state:
class SampletestInitial extends SampletestState {
#override
List<Object> get props => [];
}
class SampletestLoaded extends SampletestState {
SampletestLoaded();
#override
List<Object> get props => [];
}
class SampletestError extends SampletestState {
final error;
SampletestError({required this.error});
#override
List<Object> get props => [error];
}
bloc:
class SampletestBloc extends Bloc<SampletestEvent, SampletestState> {
SampletestBloc() : super(SampletestInitial()) {
on<SampletestPostData>((event, emit) async {
emit(SampletestInitial());
try {
await Repo().sampleTesting(event.des);
emit(SampletestLoaded());
} catch (e) {
emit(SampletestError(error: e.toString()));
print(e);
}
});
}
}
Repo: ---- Firebase post data
Future<void> sampleTesting(String des) async {
final docTicket = FirebaseFirestore.instance.collection('sample').doc();
final json = {'Same': des};
await docTicket.set(json);
}
TicketScreen:
//After clicking the button ---
BlocProvider<SampletestBloc>.value(
value: BlocProvider.of<SampletestBloc>(context, listen: false)
..add(SampletestPostData(description.text)),
child: BlocListener<SampletestBloc, SampletestState>(
listener: (context, state) {
if (state is SampletestLoaded) {
Navigator.pop(context);
print("Popped out");
}
},
),
);
im not sure but i think that you have the same hash of:
AllData? data;
try to remove AllData? data; and create new data variable so you can be sure that you has a new hash code every time you call createTicket method;
final AllData data = await repo.createTicket(AllData(
Check your AllData class properties.
BLoC will not show a new state if it not unique.
You need to check whether all fields of the AllData class are specified in the props field.
And check your BlocProvider. For what you set listen: false ?
BlocProvider.of<SampletestBloc>(context, listen: false)

How do I update a Map with BLoC and equatable?

I am still a beginner with BLoC architecture. So far the UI updates when using int, bool, and other basic data types. But when it comes to Maps it really confuses me. My code basically looks like this:
my state
enum TalentStatus { initial, loading, loaded, error }
class TalentState extends Equatable {
const TalentState({
required this.talentStatus,
this.selectedService = const {},
required this.talents,
this.test = 0,
});
final TalentStatus talentStatus;
final Talents talents;
final Map<String, Service> selectedService;
final int test;
TalentState copyWith({
TalentStatus? talentStatus,
Talents? talents,
Map<String, Service>? selectedService,
int? test,
}) =>
TalentState(
selectedService: selectedService ?? this.selectedService,
talentStatus: talentStatus ?? this.talentStatus,
talents: talents ?? this.talents,
test: test ?? this.test,
);
#override
List<Object> get props => [talentStatus, talents, selectedService, test];
}
my event
abstract class TalentEvent extends Equatable {
const TalentEvent();
#override
List<Object> get props => [];
}
class TalentStarted extends TalentEvent {}
class TalentSelectService extends TalentEvent {
const TalentSelectService(
this.service,
this.talentName,
);
final Service service;
final String talentName;
}
and my bloc
class TalentBloc extends Bloc<TalentEvent, TalentState> {
TalentBloc(this._talentRepository)
: super(TalentState(
talentStatus: TalentStatus.initial, talents: Talents())) {
on<TalentSelectService>(_selectService);
}
final TalentRepository _talentRepository;
Future<void> _selectService(
TalentSelectService event,
Emitter<TalentState> emit,
) async {
state.selectedService[event.talentName] = event.service;
final selectedService = Map<String, Service>.of(state.selectedService);
emit(
state.copyWith(
selectedService: selectedService,
),
);
}
}
whenever an event TalentSelectService is called BlocBuilder doesn't trigger, what's wrong with my code?
Your Service object must be comparable. One suggestion is that it extends Equatable. Either way it have to implement (override) the == operator and hashCode
The reason your BlocBuilder doesn't trigger is (probably) that it doesn't recognize that there has been a change in the Map.

Best practice on how to write/update data from a Flutter provider

I'm fairly new to Flutter providers. I use Riverpod.
I have a Future provider that provide some data from a JSON file - in the future it will be from a API response.
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/pokemon.dart';
final pokemonProvider = FutureProvider<List<Pokemon>>((ref) async {
var response =
await rootBundle.loadString('assets/mock_data/pokemons.json');
List<dynamic> data = jsonDecode(response);
return List<Pokemon>.from(data.map((i) => Pokemon.fromMap(i)));
});
I subscribe to with ref.watch in ConsumerState widgets, e.g.:
class PokemonsPage extends ConsumerStatefulWidget {
const PokemonsPage({Key? key}) : super(key: key);
#override
ConsumerState<PokemonsPage> createState() => _PokemonsPageState();
}
class _PokemonsPageState extends ConsumerState<PokemonsPage> {
#override
Widget build(BuildContext context) {
final AsyncValue<List<Pokemon>> pokemons =
ref.watch(pokemonProvider);
return pokemons.when(
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
data: (pokemons) {
return Material(
child: ListView.builder(
itemCount: pokemons.length,
itemBuilder: (context, index) {
Pokemon pokemon = pokemons[index];
return ListTile(
title: Text(pokemon.name),
);
},
));
},
);
}
}
But in that case, what is the best practice to write/update data to the JSON file/API?
It seems providers are used for reading/providing data, not updating it, so I'm confused.
Should the same provider pokemonProvider be used for that? If yes, what is the FutureProvider method that should be used and how to call it? If not, what is the best practice?
I am new to riverpod too but I'll try to explain the approach we took.
The examples with FutureProviders calling to apis are a little bit misleading for me, because the provider only offers the content for a single api call, not access to the entire api.
To solve that, we found the Repository Pattern to be very useful. We use the provider to export a class containing the complete api (or a mock one for test purposes), and we control the state (a different object containing the different situations) to manage the responses and updates.
Your example would be something like this:
First we define our state object:
enum PokemonListStatus { none, error, loaded }
class PokemonListState {
final String? error;
final List<Pokemon> pokemons;
final PokemonListStatus status;
const PokemonListState.loaded(this.pokemons)
: error = null,
status = PokemonListStatus.loaded,
super();
const PokemonListState.error(this.error)
: pokemons = const [],
status = PokemonListStatus.error,
super();
const PokemonListState.initial()
: pokemons = const [],
error = null,
status = PokemonListStatus.none,
super();
}
Now our provider and repository class (abstract is optional, but let's take that approach so you can keep the example for testing):
final pokemonRepositoryProvider =
StateNotifierProvider<PokemonRepository, PokemonListState>((ref) {
final pokemonRepository = JsonPokemonRepository(); // Or ApiRepository
pokemonRepository.getAllPokemon();
return pokemonRepository;
});
///
/// Define abstract class. Useful for testing
///
abstract class PokemonRepository extends StateNotifier<PokemonListState> {
PokemonRepository()
: super(const PokemonListState.initial());
Future<void> getAllPokemon();
Future<void> addPokemon(Pokemon pk);
}
And the implementation for each repository:
///
/// Class to manage pokemon api
///
class ApiPokemonRepository extends PokemonRepository {
ApiPokemonRepository() : super();
Future<void> getAllPokemon() async {
try {
// ... calls to API for retrieving pokemon
// updates cached list with recently obtained data and call watchers.
state = PokemonListState.loaded( ... );
} catch (e) {
state = PokemonListState.error(e.toString());
}
}
Future<void> addPokemon(Pokemon pk) async {
try {
// ... calls to API for adding pokemon
// updates cached list and calls providers watching.
state = PokemonListState.loaded([...state.pokemons, pk]);
} catch (e) {
state = PokemonListState.error(e.toString());
}
}
}
and
///
/// Class to manage pokemon local json
///
class JsonPokemonRepository extends PokemonRepository {
JsonPokemonRepository() : super();
Future<void> getAllPokemon() async {
var response =
await rootBundle.loadString('assets/mock_data/pokemons.json');
List<dynamic> data = jsonDecode(response);
// updates cached list with recently obtained data and call watchers.
final pokemons = List<Pokemon>.from(data.map((i) => Pokemon.fromMap(i)));
state = PokemonListState.loaded(pokemons);
}
Future<void> addPokemon(Pokemon pk) async {
// ... and write json to disk for example
// updates cached list and calls providers watching.
state = PokemonListState.loaded([...state.pokemons, pk]);
}
}
Then in build, your widget with a few changes:
class PokemonsPage extends ConsumerStatefulWidget {
const PokemonsPage({Key? key}) : super(key: key);
#override
ConsumerState<PokemonsPage> createState() => _PokemonsPageState();
}
class _PokemonsPageState extends ConsumerState<PokemonsPage> {
#override
Widget build(BuildContext context) {
final statePokemons =
ref.watch(pokemonRepositoryProvider);
if (statePokemons.status == PokemonListStatus.error) {
return Text('Error: ${statePokemons.error}');
} else if (statePokemons.status == PokemonListStatus.none) {
return const CircularProgressIndicator();
} else {
final pokemons = statePokemons.pokemons;
return Material(
child: ListView.builder(
itemCount: pokemons.length,
itemBuilder: (context, index) {
Pokemon pokemon = pokemons[index];
return ListTile(
title: Text(pokemon.name),
);
},
));
}
}
}
Not sure if this is the best approach but it is working for us so far.
you can try it like this:
class Pokemon {
Pokemon(this.name);
final String name;
}
final pokemonProvider =
StateNotifierProvider<PokemonRepository, AsyncValue<List<Pokemon>>>(
(ref) => PokemonRepository(ref.read));
class PokemonRepository extends StateNotifier<AsyncValue<List<Pokemon>>> {
PokemonRepository(this._reader) : super(const AsyncValue.loading()) {
_init();
}
final Reader _reader;
Future<void> _init() async {
final List<Pokemon> pokemons;
try {
pokemons = await getApiPokemons();
} catch (e, s) {
state = AsyncValue.error(e, stackTrace: s);
return;
}
state = AsyncValue.data(pokemons);
}
Future<void> getAllPokemon() async {
state = const AsyncValue.loading();
/// do something...
state = AsyncValue.data(pokemons);
}
Future<void> addPokemon(Pokemon pk) async {}
Future<void> updatePokemon(Pokemon pk) async {}
Future<void> deletePokemon(Pokemon pk) async {}
}

Bloc builder not printing current state in flutter

I am working on a flutter project where i am calling an Api using bloc provider and emiting a state if i got the output.When i printed the output in cubit its working and after emiting that output i cant get it in bloc builder, even the state is also not printing in bloc builder.
my api fetch code is as below
Map<String, dynamic> fields = {'upload_id': widget.content.id};
ApiModel apiData = (ApiModel(
fields: fields,
token: userToken == null ? userDataGlobal['data'].token : userToken,
));
if (widget.content.isSeries) {
BlocProvider.of<SeasonCountCubit>(context).getSeasonCount(apiData);
BlocBuilder<SeasonCountCubit, SeasonCountState>(
builder: (context, state) {
print('state is $state');
if (state is SeasonCountLoaded) {
print('season Cout is ${(state as SeasonCountLoaded).data}');
setState(() {
int seasonCount = (state as SeasonCountLoaded).data;
});
}
return CircularProgressIndicator();
});
}
and my cubit is as follows
part 'season_count_state.dart';
class SeasonCountCubit extends Cubit<SeasonCountState> {
final Repository repository;
SeasonCountCubit({this.repository}) : super(SeasonCountInitial());
getSeasonCount(ApiModel fields) {
repository.getSeasonCount(fields).then((datas) {
emit(SeasonCountLoading());
print('seson count in cubit $datas');
seasonCount = datas;
emit(SeasonCountLoaded(data: datas));
});
}
}
part of 'season_count_cubit.dart';
#immutable
abstract class SeasonCountState {}
class SeasonCountInitial extends SeasonCountState {}
class SeasonCountLoading extends SeasonCountState {}
class SeasonCountLoaded extends SeasonCountState {
final int data;
SeasonCountLoaded({this.data});
}
Anyone please help me with this..

Cubit not rebuilding when emiting a new state

Whenever I call the toggleLocked event, the BlocBuilder does not rebuild the widget.
I have looked around a lot on the internet and found this explanation: https://stackoverflow.com/a/60869187/3290471
I think that somewhere I incorrectly use the equatable package which results in the fact that the BlocBuilder thinks nothing has changed (while is has).
I have read the FAQ from the Bloc libray and the three provided solution (props for equatable / not reusing the same state / using fromList) seem to not fix the problem.
My Cubit:
class LockCubit extends Cubit<LockState> {
LockCubit({#required this.repository})
: assert(repository != null),
super(LockInitial());
final LocksRepository repository;
Future<void> fetch() async {
try {
final locks = await repository.fetchLocks();
emit(LocksDisplayed().copyWith(locks));
} on Exception {
emit(LockError());
}
}
Future<void> toggleLocked(int id) async {
try {
final locks = await repository.toggleLocked(id);
emit(LocksDisplayed().copyWith(List.from(locks)));
} on Exception {
emit(LockError());
}
}
}
My states:
abstract class LockState extends Equatable {
const LockState();
#override
List<Object> get props => [];
}
class LockInitial extends LockState {
#override
String toString() => 'LocksUninitialized';
}
class LockError extends LockState {
#override
String toString() => 'LockError';
}
class LocksDisplayed extends LockState {
final List<Lock> locks;
const LocksDisplayed([this.locks = const []]);
LocksDisplayed copyWith(locks) => LocksDisplayed(locks ?? this.locks);
#override
List<Object> get props => [locks];
#override
String toString() => 'LocksDisplayed { locks: $locks }';
}
My model:
class Lock extends Equatable {
Lock({this.id, this.name, this.locked, this.displayed});
final int id;
final String name;
final bool locked;
final bool displayed;
#override
String toString() =>
'Lock { id: $id name: $name locked: $locked displayed: $displayed }';
Lock copyWith({id, name, locked, displayed}) => Lock(
id: id ?? this.id,
name: name ?? this.name,
locked: locked ?? this.locked,
displayed: displayed ?? this.displayed);
#override
List<Object> get props => [id, name, locked, displayed];
}
My repositotory:
class LocksRepository {
List<Lock> locks = [];
Future<List<Lock>> fetchLocks() async {
// This is a temporary implementation
// In the future the data should be fetched from a provider
locks = [
new Lock(
id: 0,
name: 'Voordeur',
locked: false,
),
new Lock(
id: 1,
name: 'Achterdeur',
locked: false,
)
];
return locks;
}
Future<List<Lock>> toggleLocked(int id) async {
// This is a temporary implementation
// In the future a request to change a lock should be made and then the specific lock should be retrieved back and edited.
locks[id] = locks[id].copyWith(locked: !locks[id].locked);
return locks;
}
}
I am changing a state with the following trigger:
context.read<LockCubit>().toggleLocked(focusedIndex);
I am using BlocBuilder like this to build the state:
BlocBuilder<LockCubit, LockState>(builder: (context, state) {
print('State Changed');
if (state is LockInitial) {
return Text('lockInitial');
}
if (state is LocksDisplayed) {
return Swiper(
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
Text(state.locks[index].name),
Text(state.locks[index].locked.toString())
],
);
},
onIndexChanged: onIndexChanged,
loop: true,
itemCount: state.locks.length);
}
if (state is LockError) {
return Text('lockError');
}
return Container();
});
All help would be very appreciated.
Can you check BlocProvider ? I got the same problem. If this bloc inside materialApp, you must pass BlocProvider.value not create in widget.
I am a bit confused, if this could work. But with a bloc you would use an event not a cubit (even though events are based on cubits).
So first of all I would use the standard pattern:
state
event
bloc with mapEventToState
Then, what I also do not see in your code, if you toggle your lock it would look like this in pseudo code
if (event is toggleLock) {
yield lockInProgress();
toggleLock();
yield locksDisplayed;
}
This way your state always changes from locksDisplayed to lockInProgress to locksDisplayed - just as you read in your link above