MIgrating to bloc >= 7.2.0 with multiple enevts and shared preferences - flutter

I am working on a login page that was created with the pre >=7.2.0 bloc version and I am having issues migrating this AuthBloc because it has multiple events and shared preferences within.
class AuthBloc extends Bloc<AuthEvent, AuthStates> {
AuthBloc() : super(Initialization());
Stream<AuthStates> mapEventToState(AuthEvent event) async* {
yield WaitingAuth();
switch (event.runtimeType) {
case InitEvent:
SharedPreferences prefs = await SharedPreferences.getInstance();
bool login = prefs.getBool('login');
if (login == null || !login) {
prefs.clear();
yield Initialization();
break;
} else {
String token = prefs.getString('token');
String tokenJWT = prefs.getString('tokenJWT');
if (token == null ||
tokenJWT == null ||
token.isEmpty ||
tokenJWT.isEmpty) {
yield Initialization();
} else {
setToken(token);
setJWTToken(tokenJWT);
final response = await Api.getAccount();
if (response is Account) {
final sensorResponse = await Api.getDevices();
if (sensorResponse is List<Sensor>) {
yield SuccessAuth(account: response, sensors: sensorResponse);
} else {
yield SuccessAuth(account: response, sensors: []);
}
} else {
yield Initialization();
}
}
}break;
default:
SentryCapture.error(
loggerName: 'AuthBloc',
environment: 'switch',
message: 'unhandled event($event)');
}
}
}
How do I go about it?

With flutter bloc >= 7.2.0 you have to use the new on< Event> API and replace your yield with emit. Here is a small example.
MyBloc() : super (MyInitialState()) {
on<MyEvent1>((event, emit) => emit(MyState1()));
on<MyEvent2>((event, emit) => emit(MyState2()));
}
For your case do the following.
AuthBloc() : super(Initialization()) {
on<AuthEvent>((event, emit) {
emit(WaitingAuth());
// Your logic
}
}

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.

Dark/light theme preferences do not save in the system. Flutter

I am trying to add dark/light/custom theme modes in my Flutter application. I've used this tutorial, but my theme preferences do not save and do not apply after the application is restarted. I am using the shared_preferences library in my project, which you can access here
I've tried importing theme_manager.dart and other files as a packages and my storage_manager file looks like this now:
import 'package:shared_preferences/shared_preferences.dart';
import 'package:calculator/main.dart';
import 'package:calculator/theme_manager.dart';
import 'package:calculator/settings.dart';
class StorageManager {
static void saveData(String key, dynamic value) async {
final prefs = await SharedPreferences.getInstance();
print(value);
if (value is int) {
prefs.setInt(key, value);
} else if (value is String) {
prefs.setString(key, value);
} else if (value is bool) {
prefs.setBool(key, value);
} else {
print("Invalid Type");
}
}
static Future<dynamic> readData(String key) async {
final prefs = await SharedPreferences.getInstance();
dynamic obj = prefs.get(key);
return obj;
}
static Future<bool> deleteData(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.remove(key);
}
}
And function in theme_manager.dart, that should change theme (at the app start), looks like this:
late ThemeData _themeData;
var _buttonsData;
ThemeData getTheme() => _themeData;
getButtons() => _buttonsData;
ThemeNotifier() {
StorageManager.readData('themeMode').then((value) {
var themeMode = value ?? 'light';
if (themeMode == 'light') {
_themeData = lightTheme;
} else if (themeMode == 'dark') {
_themeData = darkTheme;
} else {
_themeData = customTheme;
}
notifyListeners();
});
StorageManager.readData('buttonsMode').then((value) {
var buttonsMode = value ?? 'circle';
if (buttonsMode == 'circle') {
_buttonsData = circledButtons;
} else if (buttonsMode == 'rounded') {
_buttonsData = roundedButtons;
} else if (buttonsMode == 'box') {
_buttonsData = boxButtons;
}
notifyListeners();
});
}
In the original example - there is no late argument before ThemeData _themeData;, but without it, my whole code does not work. Can this be a problem? Any help would be much appreciated!
Your initialization process isn't working because you are not waiting for it. So you never get the saved theme from shared preferences. You can change your ThemeNotifier to something like that:
ThemeNotifier();
Future<void> init() async {
final themeMode = await StorageManager.readData('themeMode') ?? 'light';
final buttonsMode =
await StorageManager.readData('buttonsMode') ?? 'circle';
if (themeMode == 'light') {
_themeData = lightTheme;
} else if (themeMode == 'dark') {
_themeData = darkTheme;
} else {
_themeData = customTheme;
}
if (buttonsMode == 'circle') {
_buttonsData = circledButtons;
} else if (buttonsMode == 'rounded') {
_buttonsData = roundedButtons;
} else if (buttonsMode == 'box') {
_buttonsData = boxButtons;
}
notifyListeners();
}
Then in your main method you should await the init method
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
final themeNotifier = ThemeNotifier();
await themeNotifier.init();
return runApp(ChangeNotifierProvider<ThemeNotifier>.value(
value: themeNotifier,
child: const MyApp(),
));
}
This way ThemeNotifier will be initialized with your saved value

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

How to pass data to another screen with bloc -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;
}
}

flutter bloc package yeild doesnot work in state management

While this yield in setData method doesnot work can anyone describe this?? If no parameter is sent from setData then it works. Or anyone can explain yeild how it works in bloc.
class LoginerrorBloc extends Bloc<LoginerrorEvent, LoginerrorState> {
#override
LoginerrorState get initialState => InitialLoginerrorState();
#override
Stream<LoginerrorState> mapEventToState(
LoginerrorEvent event,
) async* {
if (event is LoginEvent) {
yield LoginLoading();
try {
final url = apiUrl + 'login';
http.Response response = await http.post(url,
body: {'contact': event.contact, 'password': event.password});
if (response.statusCode == 200) {
// var res = Authenticate.fromJson(true);
yield* setData(Login.fromJson(jsonDecode(response.body)));
} else if (response.statusCode == 401) {
var res = LoginError.fromJson(jsonDecode(response.body));
yield LoginErrorState(res);
} else {
var res = LoginError.fromJson({'message': 'Internal Server Error'});
yield LoginErrorState(res);
}
} catch (e) {
var res = NetworkError.fromJson({'message': 'Internal Server Error'});
yield NetworkErrorState(res);
}
}
}
Stream<LoginerrorState> setData(data) async* {
yield AuthenticateState();
}
}