How to access the state outside BlocConsumer and BlocCubit? - flutter

I am facing this weird issue, where though I have set the state, but the value when accessed in init state is showing null !!
user_cubit.dart
UserCubit() : super(UserInitialState()) {
emit(UserMainLoadingState());
_firestore
.collection("users")
.doc(_currentUser?.uid)
.snapshots()
.listen((event) {
event.exists
? {
emit(UserExists(
userModel: UserModel.fromjson(event.data()!, event.id)))
}
: {print("There is no such user"), emit(UserNotExists())};
});
}
user_state.dart
class UserState extends Equatable {
final UserModel? userModel;
const UserState({this.userModel});
#override
List<Object?> get props => [userModel];
}
class UserExists extends UserState {
UserExists({required UserModel userModel}) : super(userModel: userModel) {
print("I am inside the UserExists state and the user is :$userModel");
}
}
myWidget.dart
#override
void initState() {
_userState = const UserState();
print("I am inside the initState The value of userstate is ${_userState.userModel}"); // This prints null , but why ?
if (_userState.userModel != null) {
print("user is ${_userState.userModel.toString()}");
}
super.initState();
}
Console log:
I/flutter ( 5029): I am inside the UserExists state and the user is :avatar boy
I/flutter ( 5029): fullName krrrt
I/flutter ( 5029): dob 19/1/2022
I/flutter ( 5029): email rd#xddf.co
I/flutter ( 5029): phone 12222222255
I/flutter ( 5029): I am inside the initState The value of userstate is null
Though the userState's userModel has value, why can't i access that in the initState.
// Ignore this, this is for stackoverflow.
// Ignore this, this is for stackoverflow.
// Ignore this, this is for stackoverflow.
// Ignore this, this is for stackoverflow.
// Ignore this, this is for stackoverflow.

To access the state of a BLoC or Cubit in Flutter, you can use the context.read<Bloc>(). This method returns a Bloc instance that you can use to access the current state.
initState() {
super.initState();
_userState = const context.read<UserCubit>().state();
print("I am inside the initState The value of userstate is ${_userState.userModel}"); // This prints null , but why ?
if (_userState.userModel != null) {
print("user is ${_userState.userModel.toString()}");
}
}

Related

state is always null in flutter_bloc

I am facing this weird issue, where though I have set the state, but the value when accessed in init state is showing null !!
user_cubit.dart
UserCubit() : super(UserInitialState()) {
emit(UserMainLoadingState());
_firestore
.collection("users")
.doc(_currentUser?.uid)
.snapshots()
.listen((event) {
event.exists
? {
emit(UserExists(
userModel: UserModel.fromjson(event.data()!, event.id)))
}
: {print("There is no such user"), emit(UserNotExists())};
});
}
user_state.dart
class UserState extends Equatable {
final UserModel? userModel;
const UserState({this.userModel});
#override
List<Object?> get props => [userModel];
}
class UserExists extends UserState {
UserExists({required UserModel userModel}) : super(userModel: userModel) { // Here the state is received
print("I am inside the UserExists state and the user is :$userModel");
}
}
myWidget.dart
#override
void initState() {
_userState = const UserState();
print("I am inside the initState The value of userstate is ${_userState.userModel}"); // This prints null , though i have set the state and console logs the state and it is confirmed that state exists why isn't this not null
if (_userState.userModel != null) {
print("user is ${_userState.userModel.toString()}");
}
super.initState();
}
Console log:
I/flutter ( 5029): I am inside the UserExists state and the user is :avatar boy
I/flutter ( 5029): fullName krrrt
I/flutter ( 5029): dob 19/1/2022
I/flutter ( 5029): email rd#xddf.co
I/flutter ( 5029): phone 12222222255
I/flutter ( 5029): I am inside the initState The value of userstate is null
Though the userState's userModel has value, why can't i access that in the `initState.
My tries :
I have used BlocListener, still the state remains null, I'm hell confused.
body: BlocListener<UserCubit, UserState>(
listener: (context, state) {
print("I am inside the listener"); // this line is never printed
if (state.userModel != null) {
print("State is ${state.runtimeType}"); // But if the state has some value, why isn't this being printed !!!??
}
},
child: <My widget>
It seems that the issue is that you are initializing the _userState variable in the initState method with const UserState(), which sets the userModel value to null. This is happening before the UserExists state is emitted from the UserCubit, which sets the userModel value to a valid UserModel object.
You should instead initialize _userState with the current state of the UserCubit by calling the state getter.
#override
void initState() {
_userCubit = context.bloc<UserCubit>();
_userState = _userCubit.state;
print("I am inside the initState The value of userstate is ${_userState.userModel}");
if (_userState.userModel != null) {
print("user is ${_userState.userModel.toString()}");
}
super.initState();
}
It would also be best practice to unsubscribe the listener after the widget is disposed, in the dispose method of your stateful widget.
#override
void dispose() {
_userCubit.close();
super.dispose();
}
Note that the above code assumes that you are using the bloc package to manage the state of your app.

Flutter: How to synchronize synchron static method with asynchron non-static method?

When I start the app, it should check if it is possible to use biometric authentication (fingerprint/face id). I have a class that checks this and the login page need the result of it. I have the following code:
class LocalAuthenticationUtil with ChangeNotifier {
static LocalAuthentication _auth = LocalAuthentication();
static List<BiometricType> biometricTypes;
static bool haveBiometrics = true;
bool _biometricAuthenticated = true;
static LocalAuthenticationUtil _instance;
static LocalAuthenticationUtil getInstance() {
if (_instance == null) {
_instance = LocalAuthenticationUtil();
print("GetInstance CanCheckBiometrics before");
_instance._canCheckBiometrics();
print("GetInstance CanCheckBiometrics after");
if (haveBiometrics) {
_instance.addListener(() {
_instance.authenticate();
});
_instance.authenticate();
}
}
return _instance;
}
Future<void> _canCheckBiometrics() async {
print("CanCheckBiometrics before");
haveBiometrics = await _auth.canCheckBiometrics;
print("CanCheckBiometrics after");
if (haveBiometrics) {
biometricTypes = await _auth.getAvailableBiometrics();
}
}
set biometricAuthenticated(bool value) {
if (_biometricAuthenticated != value) {
_biometricAuthenticated = value;
notifyListeners();
}
}
When the code runs this is the result:
I/flutter (23495): GetInstance CanCheckBiometrics before
I/flutter (23495): CanCheckBiometrics before
I/flutter (23495): GetInstance CanCheckBiometrics after
I/flutter (23495): CanCheckBiometrics after
While the order that I want to happen is:
I/flutter (23495): GetInstance CanCheckBiometrics before
I/flutter (23495): CanCheckBiometrics before
I/flutter (23495): CanCheckBiometrics after
I/flutter (23495): GetInstance CanCheckBiometrics after
You're not awaiting _instance._canCheckBiometrics();
Dart executes synchonously until it hits an await, at which point the function immediately returns, but "remembers where it was", so it can continue where it left off when the awaited Future completes:
Here, when you call _instance._canCheckBiometrics(), it immediately runs the first print statement, then hits the await _auth.canCheckBiometrics and immediately returns a Future representing the result of _instance._canCheckBiometrics().
Simply replace _instance._canCheckBiometrics() with await _instance._canCheckBiometrics() and it should work.
BTW, you can create an analysis_options.yaml file to customise your linter warnings for your project. One in particular, called unawaited_futures warns you when you have a Future-returning function in an async context that doesn't have an await. This is usually an error, but you can suppress it manually if you're certain. This rule often helps catch bugs like this.
To use the linter, check out: https://dart.dev/guides/language/analysis-options#enabling-linter-rules
cameron1024 is right.
What you need to do is to create a StatefulWidget which will redirect the user once the check is completed.
class AuthWidget extends StatefulWidget {
AuthWidget({Key key}) : super(key: key);
#override
_AuthWidgetState createState() => _AuthWidgetState();
}
class _AuthWidgetState extends State<AuthWidget> {
#override
void initState() {
super.initState();
checkBiometrics(); // perform the check asynchronously and then use Navigator.of(context).push/replace
}
#override
Widget build(BuildContext context) {
// Display a loader while checking
return CircularProgressIndicator();
}
}

BlocProvider inside initstate that receive value from blocbuilder

i curious how to call BlocProvider inside inistate that retrieve value from BlocBuilder.
usually inside widget i call Blocbuilder to retrieve a string and then the string value i can pass to Bloc provider so will run fetch data.
i try in inistate like below :
void initState(){
super.initState();
BlocBuilder<idoutletbloc,String>(
builder: (context,idoutlet)=>BlocProvider.of<tablelistbloc>(context).add(idoutlet);
);}
but it said "The return is void isn't Widget", as defined by anonymous closure.
how i can retrieve a value from idoutletbloc and then i can add BlocProvider.of(context).add(idoutlet) ??
i can't find it anywhere
here my bloc code
this is my bloc string value
class idoutletbloc extends Bloc<String, String>{
#override
String get initialState => '';
#override
Stream<String> mapEventToState(String idoutlet) async* {
yield idoutlet.toString();
}
}
my bloc that fetch data that need to receive a value
class viewtableactivebloc extends Bloc<String, List<ViewTableActives>>{
#override
List<ViewTableActives> get initialState => [];
#override
Stream<List<ViewTableActives>> mapEventToState(String event) async*{
List<ViewTableActives> viewtableactive =[];
try{
final response = await http.post(BaseUrl.ViewTableActive,
body: {
"ID_Outlet": event,
});
final data = jsonDecode(response.body);
if (data.length != 0) {
print("----------START Print View Table Active----------");
data.forEach((api) {
viewtableactive.add(
ViewTableActives(
api['ID_Transactions'],
api['ID_Customer'],
)
);
print("Print View Table Actibe : "
"ID_Transactions : "+api['ID_Transactions']+", "
"ID_Customer : "+api['ID_Customer']+", "
);
});
print("----------END Print View Table Active----------");
print("viewtableactivebloc : sukses");
} else {
viewtableactive =[];
print('data kosong');
}
}
catch(e){
print("Error ViewTableActive :");
print(e);
}
yield viewtableactive;
}}
You can access the current state of the bloc like:
BlocProvider.of(context).state;
or via the shorthand
context.bloc().state;

how to fix E/flutter (15862): Unhandled Exception: Bad state: No element when push data to firebase in flutter?

i trying to send data to firebase i did every thing
this my code
my code
the modal
import 'package:firebase_database/firebase_database.dart';
class Student {
String _id;
String _name;
String _age;
String _city;
String _department;
String _description;
Student(this._id, this._name, this._age, this._city, this._department, this._description);
Student.map(dynamic obj) {
this._id = obj['id'];
this._name = obj['name'];
this._age = obj['age'];
this._city = obj['city'];
this._department = obj['department'];
this._description = obj['_description'];
}
String get id => _id;
String get name => _name;
String get age => _age;
String get city => _city;
String get department => _department;
String get description => _description;
Student.fromSnapShot(DataSnapshot snapshot){
_id = snapshot.value['id'];
_name = snapshot.value['name'];
_age = snapshot.value['age'];
_city = snapshot.value['city'];
_department = snapshot.value['department'];
_description = snapshot.value['_description'];
}
}
and
final studentRef = FirebaseDatabase.instance.reference().child('student');
....
class ListViewStudentState extends State<ListViewStudent> {
List<Student> _students;
StreamSubscription<Event> _onStudentAddedSub;
}
StreamSubscription<Event> _onStudentChangedSub;
#override
void initState() {
_students = List();
_onStudentAddedSub = studentRef.onChildAdded.listen(_onStudentAdded);
_onStudentChangedSub = studentRef.onChildChanged.listen(_onStudentChanged);
super.initState();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
_onStudentAddedSub.cancel();
_onStudentChangedSub.cancel();
}
...
void _onStudentAdded(Event event) {
setState(() {
_students.add(Student.fromSnapShot(event.snapshot));
});
}
void createNewStudent(BuildContext context) async{
await Navigator.push(context, MaterialPageRoute(builder: (context)=> StudentScreen(Student('', '', '', '', '', ''))));
}
....
}
and the StudentScreen widget code:
class StudentScreen extends StatefulWidget {
final Student student;
StudentScreen(this.student);
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return StudenScreenState();
}
}
class StudenScreenState extends State<StudentScreen> {
TextEditingController _nameController;
TextEditingController _ageController;
TextEditingController _cityController;
TextEditingController _deptController;
TextEditingController _noteController;
#override
void initState() {
_nameController = TextEditingController(text: widget.student.name);
_ageController = TextEditingController(text: widget.student.age);
_cityController = TextEditingController(text: widget.student.city);
_deptController = TextEditingController(text: widget.student.department);
_noteController = TextEditingController(text: widget.student.description);
super.initState();
}
....
FlatButton(
onPressed:(){
if(widget.student.id != null){
studentRef.child(widget.student.id).set({
'name': _nameController.text,
'age': _ageController.text,
'city': _cityController.text,
'department': _deptController.text,
'_description': _noteController.text,
}).then((_){
Navigator.pop(context);
});
}else {
studentRef.push().set({
'name': _nameController.text,
'age': _ageController.text,
'city': _cityController.text,
'department': _deptController.text,
'_description': _noteController.text,
}).then((_){
Navigator.pop(context);
});
}
},
child: (widget.student.id != null)? Text('Update'): Text('Add'),
...
}
it's gives me this error when try to push the data
E/flutter (15862): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Bad state: No element
E/flutter (15862): #0 ListMixin.singleWhere (dart:collection/list.dart:185:5)
E/flutter (15862): #1 ListViewStudentState._onStudentChanged (package:flutter_app/ui/listview_student.dart:82:37)
It's just app for test and learning about student
there the StudentList widget for show list of the student and StudentScreen for Add or Edit student
so it must doing push if i pressed on this Flatbutton but it's gives the error above when i do it,
i don't know what i must to do
i searched for long time about the problem and i didn't found anything
and it's gives this error too when run the app
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
E/flutter (15862): #0 new Student.fromSnapShot (package:flutter_app/_modal/student.dart:28:25)
E/flutter (15862): #1 ListViewStudentState._onStudentAdded. (package:flutter_app/ui/listview_student.dart:78:29)
E/flutter (15862): #2 State.setState (package:flutter/src/widgets/framework.dart:1148:30)
E/flutter (15862): #3 ListViewStudentState._onStudentAdded (package:flutter_app/ui/listview_student.dart:77:5)
can help please!
StateError of Bad State: No Element is thrown whenever Dart tries to access an element inside a List object which is empty.
Example:
List<Foo> foos = [];
foos.first;
=> Bad state: No element
Do the above and you'll get a "Bad State: No element" exception thrown.
To avoid this you have to check whether your list is empty before trying to access any element.
if (foos != null && foos.isNotEmpty)
foos.first;
If you're using a List method such as .firstWhere or .singleWhere you can specify an orElse clause which will be used when there are no elements or no elements found.
foos.firstWhere((e) => e.id == 1, orElse: () => null);
The above will return null whenever there are no elements or when no element matches the "where" criteria.
Null Safe
With the release of Null safe Dart in Flutter 2, a version of .firstWhere called .firstWhereOrNull which can return either the Object type of your List (if found), or null, is available in an Iterable extension class inside package:collection/collection.dart.
So the above would become:
import 'package:collection/collection.dart'; // don't forget this at top of file
Foo? firstFoo = foos.firstWhereOrNull((e) => e.id == 1);
Where, if a list item has id == 1 it will be returned, else, null is returned.

How to handle navigation after Firebase google login using provider?

I'm creating a simple app where user can be authenticated using firebase google_sigin. I have created AuthChecker widget which checks the authentication and returns login page, where user can login with google. After when the login in complete I want to go back to AuthChecker and show homepage.
Following code below is my implementation which gives context error
From the main.dart file the AuthChecker widget is called:
class AuthChecker extends StatefulWidget {
#override
_AuthCheckerState createState() => _AuthCheckerState();
}
class _AuthCheckerState extends State<AuthChecker> {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getCurrentUser(),
builder: (context, snapshot){
if(snapshot.connectionState == ConnectionState.done){
//got response
FirebaseUser user = snapshot.data;
LoginStateProvider prov = Provider.of<LoginStateProvider>(context);
if(user == null){
//not loggedin pls login
return LoginPage();
}else{
//already logged in
print("Already logged in");
prov.updateUserState(user);
return Home();
}
}
},
);
}
}
The login page contains the signin button:
Widget signInButton(BuildContext context) {
return OutlineButton(
onPressed: () {
signInWithGoogle().whenComplete(() {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context)=>AuthChecker();
),
ModalRoute.withName('/auth_check')
);
},
}
The provider class looks like:
class LoginStateProvider with ChangeNotifier{
String uid;
String name;
String email;
String profilePhoto;
LoginStateProvider({
this.uid,
this.name,
this.email,
this.profilePhoto,
});
void updateUserState(FirebaseUser user){
this.uid = user.uid;
this.name = user.displayName;
this.email = user.email;
this.profilePhoto = user.photoUrl;
}
}
I got following error:
I/flutter ( 9551): * Ensure the Provider<LoginStateProvider> is an ancestor to this FutureBuilder<FirebaseUser>
I/flutter ( 9551): Widget
I/flutter ( 9551): * Provide types to Provider<LoginStateProvider>
I/flutter ( 9551): * Provide types to Consumer<LoginStateProvider>
I/flutter ( 9551): * Provide types to Provider.of<LoginStateProvider>()
I/flutter ( 9551): * Always use package imports. Ex: `import 'package:my_app/my_code.dart';
I/flutter ( 9551): * Ensure the correct `context` is being used.
I/flutter ( 9551):
I/flutter ( 9551): If none of these solutions work, please file a bug at:
I/flutter ( 9551): https://github.com/rrousselGit/provider/issues
you could try and do this:
Widget signInButton(BuildContext context) {
return OutlineButton(
onPressed: () {
signInWithGoogle().whenComplete(() {
FirebaseUser user = snapshot.data;
if (user == null) {
//Route to login
} else {
//route to somewhere
}
},
}
and create a splash screen with your logo and a async function that runs at initState that checks if user == null and route to login if needed or to homepage if already logged in.
there's a nice example here: Firebase Auth state check in Flutter
I'm not sure if it helps or if that was really your question, sorry if I misunderstood u