I'm trying to display a document field value from firestore and want to display it on other pages using the provider.
This is my code inside the provider file:
class UserState extends ChangeNotifier {
String userName = 'default error';
Future<void> getName() async {
await FirebaseFirestore.instance
.collection("Users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then((value) {
userName = (value.data()?[' uname'] ?? "Default userName");
print(userName);
});
notifyListeners();
}
}
Here the correct userName value is getting printed using print statement, but when I try to pass it through the provider it is showing the initialized string value default error which I provided for null safety.
This is the screen where I want to display the variable userName :
class testscreen extends StatefulWidget {
const testscreen({Key? key}) : super(key: key);
_testscreenState createState() => _testscreenState();
}
class _testscreenState extends State<testscreen> {
#override
Widget build(BuildContext context) {
Provider.of<UserState>(context, listen: false).getName();
final String name = Provider.of<UserState>(context).userName;
return Scaffold(body: Text(name));
}
}
How can I show the right value instead of initialized value for userName?What's wrong with my code?
There are some fixes in the code.
notifyListeners() are not waiting for the fetch to complete
await FirebaseFirestore.instance
.collection("Users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then((value) {
userName = (value.data()?[' uname'] ?? "Default userName");
print(userName);
notifyListeners(); š Try adding the notifyListeners() here
});
}
Remove the provider() call from within the bluid method
class testscreen extends StatefulWidget {
const testscreen({Key? key}) : super(key: key);
_testscreenState createState() => _testscreenState();
}
class _testscreenState extends State<testscreen> {
#override
void initState() {
super.initState();
Provider.of<UserState>(context, listen: false).getName();š Call getName here
}
#override
Widget build(BuildContext context) {
return Scaffold(body: Text( Provider.of<UserState>(context).userName)); š Call it this way
}
}
notifyListeners doesn't wait to finish the fetch. You can try
void getName() {
FirebaseFirestore.instance
.collection("Users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then((value) {
userName = (value.data()?[' uname'] ?? "Default userName");
print(userName);
notifyListeners();
});
}
Or
Future<void> getName() async {
final value = await FirebaseFirestore.instance
.collection("Users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.get();
userName = (value.data()?[' uname'] ?? "Default userName");
notifyListeners();
}
I will prefer using Consumer widget for this case.
You can also use initState to call the method.
class testscreen extends StatefulWidget {
const testscreen({Key? key}) : super(key: key);
_testscreenState createState() => _testscreenState();
}
class _testscreenState extends State<testscreen> {
#override
void initState() {
super.initState();
Provider.of<UserState>(context, listen: false).getName();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<UserState>(// using consumer
builder: (context, value, child) {
return Text(value.userName);
},
),
);
}
}
Also you can call the method on constructor,
class UserState extends ChangeNotifier {
String userName = 'default error';
UserState() {
getName();
}
Future<void> getName() async {
await Future.delayed(Duration(seconds: 1));
userName = "Default userName";
notifyListeners();
}
}
Related
Iām fetching data from firebase but i have this error
Unhandled Exception: Bad state: field does not exist within the DocumentSnapshotPlatform
the fields are correct and there is no difference i checked them
this is my method
static final List<ProductModels> _products = [];
Future<void> fetchData() async {
await FirebaseFirestore.instance
.collection('products')
.get()
.then((QuerySnapshot productsSnapshot) {
for (var element in productsSnapshot.docs) {
_products.insert(
0,
ProductModels(
id: element.get('id'),
title: element.get('title'),
imageUrl: element.get('imageUrl'),
productcat: element.get('productCategoryName'),
price: element.get('price'),
salePrice: element.get('salePrice'),
isOnSale: element.get('isOnSale'),
size: element.get('Size'),
color: element.get('Color'),
));
}
});
notifyListeners();
}
and i call it here
class FetchScreen extends StatefulWidget {
const FetchScreen({Key? key}) : super(key: key);
#override
State<FetchScreen> createState() => _FetchScreenState();
}
class _FetchScreenState extends State<FetchScreen> {
#override
void initState() {
Future.delayed(Duration(microseconds: 5),() async{
final productsProvider = Provider.of<ProductProvider>(context,listen: false);
await productsProvider.fetchData();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=> BtmNavBarScreen()));
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
there is no difference between the firebase and the code i checked each one
I wrote short flutter app that have a async function that get value from cloud firestore and return Future bool type according to the information from the database.
now in my main code I want to return a Widget according to the value that got return form that mention function, but I didn't succeed using the returned value right.
I tried to write an external async function that will call the first function and will put the returned value inside a shared variable. but it didn't work for me and got sometimes error about that variable isn't been initialized.
it's looked like the code is not stopping line by line and wait for my function to return the value when been called and continue on the if statement.
Someone know how can I return a widget according to the value that the function returning and fix that issue?.
I tried using block of code using then but I never use it before so it doesn't worked as well.
my fist function:
Future<bool> firstFunc(String uid) async {
DocumentSnapshot userData =
await FirebaseFirestore.instance.collection('Users').doc(uid).get();
bool value = userData["param"];
return value;
}
and this is my main code:
// imports
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: AuthenticationWrapper(),
);
}
}
class AuthenticationWrapper extends StatefulWidget {
const AuthenticationWrapper({Key? key}) : super(key: key);
#override
_AuthenticationWrapperState createState() => _AuthenticationWrapperState();
}
class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
final FirebaseAuth _auth = FirebaseAuth.instance;
late bool sharedVar;
void getValue(String uid) async {
bool value = await firstFunc(uid);
sharedVar = value;
}
#override
Widget build(BuildContext context) {
if (_auth.currentUser != null) {
getValue(_auth.currentUser!.uid);
if (sharedVar == true)
{
return BossHomePage();
}
else {
return ClientHomePage();
}
} else {
return const EnterPhonePage();
}
}
}
The reason it doesn't wait for getValue is that getValue is of type void.
Try this;
class AuthenticationWrapper extends StatefulWidget {
const AuthenticationWrapper({Key? key}) : super(key: key);
#override
_AuthenticationWrapperState createState() => _AuthenticationWrapperState();
}
class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
final FirebaseAuth _auth = FirebaseAuth.instance;
#override
Widget build(BuildContext context) {
if (_auth.currentUser != null) {
return FutureBuilder<bool>(
future: firstFunc(uid),
builder: (BuildContext context, snapshot){
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const CircularProgressIndicator();
case ConnectionState.none:
return const Text('No data');
case ConnectionState.active:
case ConnectionState.done:
if (snapshot.data == true) {
return BossHomePage();
} else {
return ClientHomePage();
}
default:
return const Text('No data');
}
},
)
} else {
return const EnterPhonePage();
}
}
}
getValue is a future method, You need to use async at some point. You can convert this to
Future<bool> getValue(String uid) async {
bool value = ....;
return value;
}
And use FutureBuilder (or initState is better SatefullWidget) for this case
late bool sharedVar;
Future<void> getValue(String uid) async {
bool value =...;
sharedVar = value;
}
bool? currentUser;
_initData()async{
getValue("");
///all others methods
}
#override
void initState() {
super.initState();
_initData();
}
More about async-await
I'm trying to use ObjectBox as the database in a flutter application. The following is the sample code.
However, while execution I was returned with the error of "_store is not initialized".
class _HomePageState extends State<HomePage> {
...
// š ADD THIS
late Stream<List<ShopOrder>> _stream;
#override
void initState() {
super.initState();
setNewCustomer();
getApplicationDocumentsDirectory().then((dir) {
_store = Store(
getObjectBoxModel(),
directory: join(dir.path, 'objectbox'),
);
setState(() {
// š ADD THIS
_stream = _store
.box<ShopOrder>()
// The simplest possible query that just gets ALL the data out of the Box
.query()
.watch(triggerImmediately: true)
// Watching the query produces a Stream<Query<ShopOrder>>
// To get the actual data inside a List<ShopOrder>, we need to call find() on the query
.map((query) => query.find());
hasBeenInitialized = true;
});
});
}
...
}```
initialize the databases in the main one and then you pass the store to the HomePage, that is why it tells you that error '_store no se inicializa'. You must declare your global store and then you pass it to each view.
late Store _stores;
void main() async {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyState createState() => _MyState();
}
class _MyState extends State<MyApp> {
bool iniciando_store = true;
#override
void initState() {
super.initState();
initPlatformState();
getApplicationDocumentsDirectory().then((directory) {
_stores = Store(
getObjectBoxModel(),
directory: join(directory.path, 'objectbox')
);
setState(() {
iniciando_store = false;
});
});
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ThemeProvider()),
],
child: Consumer<ThemeProvider>(builder: (context, theme, snapshot) {
return MaterialApp(
title: 'Object box title',
home: !iniciando_store
? MyHomePage(
title: "Home", loadingSore: iniciando_store, STORE: _stores)
: MyStatefulWidget());
}),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage(
{Key? key,
required this.title,
required this.loadingSore,
required this.STORE})
: super(key: key);
final String title;
final Store STORE;
final bool loadingSore;
#override
_MyHomePageState createState() => _MyHomePageState();
}
this is the simple way to connect with Object box
My home page is Displayed Based on the UserType value that I set in SharedPreffrence, for the first time when I run The app it works fine and the home page is displayed Successfully because it knows the values of Usertype from SharedPreference, but when I close the app and run again The value of UserType become Null, due to this my home page is not Displayed because Usertype value is null, I set SharedPreference when the user login for The first time,
As you See in my code First I pass the value of UserType from SharedPreference, and also I Call Get() method in initState(), to Call SharedPrefrence.
Here is how I set SharedPreference when the user Login for the first time.
Future loginWithEmailAndPasswords(String email, String password,BuildContext context) async {
try {
UserCredential register = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
User registredUser = register.user;
final customSnapshots= await customSnapshot.doc(registredUser.uid).get();
if(customSnapshots.exists){
SharedPreferences preferences= await SharedPreferences.getInstance() ;
preferences.setString("UserType", customSnapshots.data()['Account type'].toString());
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context)=>homecontroller(
controllUserType:preferences.getString('UserType'),
userid: customSnapshots.data()['userId'].toString(),))); }
}
Here is My HomeController Page.
class homecontroller extends StatelessWidget {
final String controllUserType;
final String userid;
const homecontroller({Key key,#required this.controllUserType,#required this.userid}):super(key: key);
#override
Widget build(BuildContext context) {
Authservice auth
final Authservice auth=Provider.of(context).auth;
return StreamBuilder(
stream:auth.authStateChanges,
builder: (context,AsyncSnapshot<String>snapshot){
if(snapshot.connectionState==ConnectionState.active){
final bool SignedIn=snapshot.hasData;
return SignedIn?HomePage(UserType:controllUserType,userID: userid,):firstview();
}else{
return CircularProgressIndicator();
}
},
);
}
}
Here is My home page
class HomePage extends StatefulWidget {
final String UserType;
final String userID;
const HomePage({Key key,#required this.UserType,#required this.userID}):super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isSignedIn= false;
String owneruerID;
dynamic uploadusertypes;
List<Widget>_children;
void initState(){
super.initState();
uploadusertypes= widget.UserType; //this value is passed from SharedPreference when the user login for the first time
owneruerID = widget.userID;//this value is passed from SharedPreference when the user login for the first time
GetData(); // here is Where i Call SharedPreference Method
_children=[
TimeLinePage(),
SearchPage(), //search(),
UploadPage(UserSID:owneruerID,uploadusertypes:uploadusertypes),
NotificationsPage(),
ProfilePage(userProfileID:widget.userID),
];
if(FirebaseAuth.instance.currentUser!=null){
setState(() {
isSignedIn= true;
});
}else{
setState(() {
isSignedIn= false;
});
}
}
#override
Widget build(BuildContext context) {
if(isSignedIn){
if(widget.UserType== 'Customer')
{
return Scaffold(
body: WillPopScope(
onWillPop: onwillpops,
child: buildHomeScreen()));
}
}
void GetData()async {
SharedPreferences preferences= await SharedPreferences.getInstance();
setState(() {
widget.UserType=preferences.getString('UserType');
});
}
}
I think first you should call GetData() before the pass shared preference values to variables ( EX - UserType). Then you could get the UserType value and then could pass that value to uploadusertypes.
Try the following code.
class HomePage extends StatefulWidget {
final String UserType;
final String userID;
const HomePage({Key key,#required this.UserType,#required this.userID}):super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isSignedIn= false;
String owneruerID;
dynamic uploadusertypes;
List<Widget>_children;
void initState(){
super.initState();
GetData(); // you should call this one first.
uploadusertypes= widget.UserType; //this value is passed from SharedPreference when the user login for the first time
owneruerID = widget.userID;//this value is passed from SharedPreference when the user login for the first time
_children=[
TimeLinePage(),
SearchPage(), //search(),
UploadPage(UserSID:owneruerID,uploadusertypes:uploadusertypes),
NotificationsPage(),
ProfilePage(userProfileID:widget.userID),
];
if(FirebaseAuth.instance.currentUser!=null){
setState(() {
isSignedIn= true;
});
}else{
setState(() {
isSignedIn= false;
});
}
}
#override
Widget build(BuildContext context) {
if(isSignedIn){
if(widget.UserType== 'Customer')
{
return Scaffold(
body: WillPopScope(
onWillPop: onwillpops,
child: buildHomeScreen()));
}
}
void GetData()async {
SharedPreferences preferences= await SharedPreferences.getInstance();
setState(() {
widget.UserType=preferences.getString('UserType');
});
}
}
Hope this will solve your problem.
what I am Doing is Just I Remove both uploadusertypes and owneruerID from InitState() and I Declared Them Inside Getdata(), it works but i don't know weather it have effect on my code or not,
uploadusertypes= widget.UserType;
owneruerID = widget.userID;
Here is what I am doing
void initState(){
GetData();
super.initState();
_children=[
TimeLinePage(),
SearchPage(), //search(),
UploadPage(UserSID:owneruerID,uploadusertypes:uploadusertypes),
NotificationsPage(),
ProfilePage(userProfileID:widget.userID),
];
_agentchildren=[
TimeLinePage(),
SearchPage(), //search(),
NotificationsPage(),
ProfilePage(userProfileID:owneruerID),
];
if(FirebaseAuth.instance.currentUser!=null){
setState(() {
isSignedIn= true;
});
}else{
setState(() {
isSignedIn= false;
});
}
uploadusertypes= widget.UserType;
owneruerID = widget.userID;
}
void GetData()async {
SharedPreferences preferences= await SharedPreferences.getInstance();
setState(() {
uploadusertypes=preferences.get('UserType');
widget.UserType=preferences.get('UserType');
});
}
I'm getting this error
E/flutter ( 9610): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: A CustRegViewModel was used after being disposed.
E/flutter ( 9610): Once you have called dispose() on a CustRegViewModel, it can no longer be used.
I have a View named CustRegView where I take a phone number form the user and send it to ViewModel named CustRegViewModel to authenticate which returns bool based on its authentication status.
class CustRegView extends StatefulWidget {
#override
_CustRegViewState createState() => _CustRegViewState();
}
class CustRegView extends StatelessWidget{
final TextEditingController _controller = TextEditingController();
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
return BaseView<CustRegViewModel>(
builder: (context, model, child) => Scaffold(
...<some code>
FlatButton (
onPressed: () async {
var registerSuccess = await model.register( _controller.text, context);
// ^^^^^ HERE, IN ABOVE LINE, I'M GETTING AN ERROR ^^^^^^
if (registerSuccess) {
Navigator.pushNamed(context, 'newScreen');
} else {
UIHelper().showErrorButtomSheet(context, model.errorMessage);
}
)
}
CustRegViewModel looks like this
class CustRegViewModel extends BaseViewModel {
final AuthService _authService = locator<AuthService>();
final DialogService _dialogService = locator<DialogService>();
dynamic newUserResult;
dynamic verifyResult;
Future<bool> register(String phoneNo, BuildContext context) async {
await verifyPhone;
return verifyResult ? true : false; // From here it returns true
}
Future<void> verifyPhone(phoneNo) async {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: updatedPhoneNo,
timeout: Duration(seconds: 50),
verificationCompleted: (AuthCredential authCred) async {...... <some code>
verificationFailed: (AuthException authException) {...... <some code>
codeSent: (String verID, [int forceCodeResend]) async {...... <some code>
codeAutoRetrievalTimeout: (String verID) {...
).catchError((error) {...... <some code>
}
}
BaseView looks like this
class BaseView<T extends BaseViewModel> extends StatefulWidget {
final Widget Function(BuildContext context, T model, Widget child) builder;
final Function(T) onModelReady;
BaseView({this.builder, this.onModelReady});
#override
_BaseViewState<T> createState() => _BaseViewState<T>();
}
class _BaseViewState<T extends BaseViewModel> extends State<BaseView<T>> {
T model = locator<T>();
#override
void initState() {
if (widget.onModelReady != null) {
widget.onModelReady(model);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
create: (context) => model,
child: Consumer<T>(builder: widget.builder),
);
}
}
BaseViewModel looks like this
class BaseViewModel extends ChangeNotifier {
ViewState _state = ViewState.Idle;
ViewState get state => _state;
void setState(ViewState viewState) {
_state = viewState;
notifyListeners();
}
}
You may have used RegisterLazySingleton instead of Factory in your depencie injection.