How to pass data to another screen with bloc -Flutter - flutter

How to transfer data from one screen to another using bloc and save , I would like to create a user profile where I have two screens, two steps to creating a profile. I created two blocs for each class, in one I have an avatar, city and name, in the other only description. I used an amplify and when I save the first screen and when I go to the second one, delete the data from the first screen. How do I save everything? without delete? after save second screen.
First screen:
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final DataRepository dataRepo;
final StorageRepository storageRepo;
final _picker = ImagePicker();
ProfileBloc(
{User? user,
required bool isCurrentUser,
required this.storageRepo,
required this.dataRepo})
: super(ProfileState(user: user, isCurrentUser: isCurrentUser)) {
// storageRepo
// .getUrlForFile(user!.avatarKey)
// .then((url) => add(ProvideImagePath(avatarPath: url)));
ImageUrlCache.instance
.getUrl(user!.avatarKey)
.then((url) => add(ProvideImagePath(avatarPath: url)));
}
#override
Stream<ProfileState> mapEventToState(ProfileEvent event) async* {
if (event is ChangeAvatarRequest) {
yield state.copyWith(isImageSourceActionSheetVisible: true);
} else if (event is OpenImagePicker) {
yield state.copyWith(isImageSourceActionSheetVisible: false);
try {
final selectedImage =
await _picker.pickImage(source: event.imageSource);
if (selectedImage == null) return;
final imageKey = await storageRepo.uploadFile(File(selectedImage.path));
final user = state.user!.copyWith(avatarKey: imageKey);
String? imageUrl;
await Future.wait<void>([
dataRepo.updateUser(user),
storageRepo.getUrlForFile(imageKey).then((value) => imageUrl = value)
]);
yield state.copyWith(avatarPath: imageUrl);
} catch (e) {
throw e;
}
} else if (event is ProvideImagePath) {
if (event.avatarPath != null)
yield state.copyWith(avatarPath: event.avatarPath);
} else if (event is ProfileCityChanged) {
yield state.copyWith(userCity: event.city);
} else if (event is ProfileNameChanged) {
yield state.copyWith(userName1: event.name);
} else if (event is SaveProfileChanges) {
// handle save changes
yield state.copyWith(formStatus: FormSubmitting());
final updatedUser2 =
state.user!.copyWith(city: state.userCity, name: state.userName1);
try {
await dataRepo.updateUser(updatedUser2);
print(updatedUser2);
yield state.copyWith(formStatus: SubmissionSuccess());
} on Exception catch (e) {
yield state.copyWith(formStatus: SubmissionFailed(e));
} catch (e) {
print(e);
}
}
}
}
Second screen:
class Profile2Bloc extends Bloc<Profile2Event, Profile2State> {
final DataRepository dataRepo;
// User? user;
// String ?get userCity => user!.city;
// String? get userName1 => user!.name;
Profile2Bloc(
{User? user, required bool isCurrentUser, required this.dataRepo})
: super(Profile2State(user: user, isCurrentUser: isCurrentUser));
#override
Stream<Profile2State> mapEventToState(Profile2Event event) async* {
if (event is ProfileDescriptionChanged) {
yield state.copyWith(userDescription: event.description);
} else if (event is SaveProfile2Changes) {
yield state.copyWith(formStatus: FormSubmitting());
final updatedUser =
state.user!.copyWith(description: state.userDescription);
try {
await dataRepo.updateUser(updatedUser);
print(updatedUser);
// print(userDescribe);
yield state.copyWith(formStatus: SubmissionSuccess());
} on Exception catch (e) {
yield state.copyWith(formStatus: SubmissionFailed(e));
} catch (e) {
print(e);
}
}
}
}
Data Repo:
Future<User> updateUser(User updatedUser) async {
try {
await Amplify.DataStore.save(updatedUser);
return updatedUser;
} catch (e) {
throw e;
}
}

Related

I can't list database data in Flutter

I have a problem, my database has data, but I can't list this data in the application, can you see where the problem is?
Here the database query is being implemented
#override
Stream<Either<TodoFailures, List<Todo>>> watchAll() async* {
//yield left(const InsufficientPermissions());
// users/{user ID}/notes/{todo ID}
final userDoc = await firestore.userDocument();
yield* userDoc.todoCollection
.snapshots()
.map((snapshot) => right<TodoFailures, List<Todo>>(snapshot.docs
.map((doc) => TodoModel.fromFirestore(doc).toDomain()).toList()))
.handleError((e) {
if (e is FirebaseException) {
if (e.code.contains('permission-denied') || e.code.contains("PERMISSION_DENIED")) {
return left(InsufficientPermisssons());
} else {
return left(UnexpectedFailure());
}
} else {
// ? check for the unauthenticated error
// ! log.e(e.toString()); // we can log unexpected exceptions
return left(UnexpectedFailure());
}
});
}
Below is where I capture the integrated query through the BloC
#injectable
class ObserverBloc extends Bloc<ObserverEvent, ObserverState> {
final TodoRepository todoRepository;
StreamSubscription<Either<TodoFailures, List<Todo>>>? todoStreamSubscription;
ObserverBloc({required this.todoRepository}) : super(ObserverInitial()) {
on<ObserverEvent>((event, emit) async {
emit(ObserverLoading());
await todoStreamSubscription?.cancel();
todoStreamSubscription = todoRepository
.watchAll()
.listen((failureOrTodos) => add(TodosUpdatedEvent(failureOrTodos: failureOrTodos)));
});
on<TodosUpdatedEvent>((event, emit) {
event.failureOrTodos.fold((failures) => emit(ObserverFailure(todoFailure: failures)),
(todos) => emit(ObserverSuccess(todos: todos)));
});
}
#override
Future<void> close() async {
await todoStreamSubscription?.cancel();
return super.close();
}
}
Even containing data in the database it comes empty, I need help to know where the problem is.

Exception throw flutter

am learning api integration with bloc, these exception is been thrown when data is trying to fetch, for loadingstate i assigned a progressindicator then after that state when trying to get data,these exeption is been thrown ,pls helpenter image description here
as per the console i tried to change the data type to from double to num, still same exception
try {
_emitters.add(emitter);
await handler(event as E, emitter);
} catch (error, stackTrace) {
onError(error, stackTrace);
rethrow;
} finally {
onDone();
}
networkfile.dart
class Repository {
List<FakeStore> collections = [];
Future<List<FakeStore>?> getdata() async {
String url = 'https://fakestoreapi.com/products';
final data = await http.Client().get(Uri.parse(url));
if (data.statusCode != 200) {
return null;
} else {
Iterable values = jsonDecode(data.body);
for (var value in values) {
FakeStore fakeStore = FakeStore.fromJson(value);
collections.add(fakeStore);
}
return collections;
}
}
}
bloc.dart
class FakestoreBloc extends Bloc<FakestoreEvent, FakestoreState> {
final Repository repository;
FakestoreBloc({required this.repository}) : super(FakestoreInitialstate()) {
on<FakestoreEvent>((event, emit) async {
if (event is StorelaodEvent) {
emit(Fakestorelaodingstate());
List<FakeStore>? apiresult = await repository.getdata();
if (apiresult == null) {
emit(FAkestoreErrorstate());
} else {
emit(Fakestoreloadedstate(apiresult: apiresult));
}
}
});
}
}

Flutter Firestore Query snapshot- result is always null

I have a simple flutter code to retrieve some data from Firestore. the data is retireved correctly, however passing the data from the future function making the result always null. can you advise how to adapt the code to return the list?
that is the class where the actual query is happening:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
print(businessprofileslist[0]);
});
});
} catch (e) {
print(e.toString());
return null;
}
}
}
here is the page where I am calling the function: (however the result is always null)
class _ProfilesListPageState extends State<ProfilesListPage> {
List businessprofileslist = [];
#override
void initState() {
super.initState();
fetchBusinessProfilesList();
}
fetchBusinessProfilesList() async {
dynamic result = await DatabaseManager().GetBusinessProfilesCollection();
print(result.toString());
if (result == null) {
print('enable to retieve');
} else {
print('success');
setState(() {
businessprofileslist = result;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
You're not returning anything from GetBusinessProfilesCollection but null, so the result seems somewhat expected.
I guess you want to do:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
var QuerySnapshot = await BusinessProfilesCollection.get();
querySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
});
return businessprofileslist;
} catch (e) {
print(e.toString());
return null;
}
}
}
Btw: returning null when the load fails, is just going to lead to a null pointer exception when you then do print(result.toString());. So I recommend not catching the error and just letting it bubble up. With that your code can be simplified to:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
var QuerySnapshot = await BusinessProfilesCollection.get();
return querySnapshot.docs.map((element) => element.data());
}
}
You just need to return the list
return businessprofileslist;
CODE :
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
print(businessprofileslist[0]);
});
// you just need to return the list here after filling it up
return businessprofileslist;
});
} catch (e) {
print(e.toString());
return null;
}
}
}
Code with a little improvement:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.map((doc) => doc.data()).toList();
});
}
}
Try that with calling the function in feching
fetchBusinessProfilesList()
async {
dynamic result ;
await DatabaseManager().GetBusinessProfilesCollection().then((value){
result=value;
print(result.toString());
if (result == null) {
print('enable to retieve');
} else {
print('success');
setState(() {
businessprofileslist = result;
});
}
});
}

flutter how to yield to a stream of bloc?

Hi I'm new to flutter and dart. I'm following a lesson on internet which is practicing to use bloc to control states. First lesson is after showing appStart animation, turn to a login page.
the lesson was using 'mapEventToState':
class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
final UserRepository? _userRepository;
AuthenticationBloc({UserRepository? userRepository})
: assert(userRepository != null),
_userRepository = userRepository, super(Uninitialized());
#override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event,
) async* {
if (event is AppStarted) {
yield* _mapAppStartedToState();
} else if (event is LoggedIn) {
yield* _mapLoggedInToState();
} else if (event is LoggedOut) {
yield* _mapLoggedOutToState();
}
Stream<AuthenticationState> _mapAppStartedToState() async* {
log('_mapAppStartedToState is running.');
try {
final bool? isSigned = await _userRepository?.isSignedIn();
if (isSigned != null) {
if (isSigned) {
final String? name = await _userRepository?.getUser();
yield Authenticated(name);
}
else {
yield Unauthenticated();
}
}
} catch (_) {
yield Unauthenticated();
}
}
Stream<AuthenticationState> _mapLoggedInToState() async* {
log('_mapLoggedInToState is running.');
yield Authenticated(await _userRepository?.getUser());
}
Stream<AuthenticationState> _mapLoggedOutToState() async* {
log('_mapLoggedOutToState is running.');
yield Unauthenticated();
_userRepository?.signOut();
}
}
turns out 'mapEventToState' was removed.
According to this page(https://github.com/felangel/bloc/issues/2526), I try to use on< event > instead:
#override
AuthenticationBloc({UserRepository? userRepository})
: assert(userRepository != null, 'userRepository == null'),
_userRepository = userRepository,
super(Uninitialized()) {
log('AuthenticationBloc is running.');
on<AppStarted>(_appStarted);
on<LoggedIn>(_loggedIn);
on<LoggedOut>(_loggedOut);
}
Stream<AuthenticationState> _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) async* {
log('_appStarted is running.');
yield* _mapAppStartedToState();
}
But it didn't work. Even log('_appStarted is running.'); didn't show at console.
I tried to change type and aync*. It would show console log if _appStarted isn't aync.
void _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) {
log('_appStarted is running.');
// yield* _mapAppStartedToState();
}
However, it can't yield to stream as _appStarted isn't aync. Makes me confused.
Please let me know if I got some misunderstand about bloc and stream. Happy to see any solution or advise.
You no longer need your one function per event, because you already have it:
void _appStarted(AuthenticationEvent event, Emitter<AuthenticationState> emit) {
log('_appStarted is running.');
try {
final bool? isSigned = await _userRepository?.isSignedIn();
if (isSigned != null) {
if (isSigned) {
final String? name = await _userRepository?.getUser();
emit(Authenticated(name));
}
else {
emit(Unauthenticated());
}
}
} catch (_) {
emit(Unauthenticated());
}
}
If you want to delegate this to another function, just remove the stream return value and pass the emitter.

How to use yield to return an error (from catch error)

My service _session try to log in and return true if succeed or an error message from catchError if fail. I would like to yield this message and so, call yield from the catch block, but it's not possible, so I did:
Will this work as I expected or is there another way to do this?
#override
Stream<DgState> mapEventToState(DgEvent event) async* {
if (event is LoginDgEvent) {
yield LoadingState();
String errMessage;
bool hasLogged = await _session
.login(event.userCredential.login, event.userCredential.password)
.catchError((err) {
errMessage = err;
});
yield LoginState(hasLogged ? 'Ok': errMessage);
}
}
You can create a event for update state.
#override
Stream<DgState> mapEventToState(DgEvent event) async* {
//Event for update state
if(event is LoginUpdateStateEvent){
yield event.state;
}
if (event is LoginDolceGustoEvent) {
yield LoadingState();
String errMessage;
bool hasLogged = await _session
.login(event.userCredential.login, event.userCredential.password)
.catchError((err) {
//dispatch
dispatch(LoginUpdateStateEvent(state:LoginErrorState(errMessage)));
});
yield LoginState(hasLogged ? 'Ok': errMessage);
}
}