Instance of 'Future<dynamic>' Flutter - flutter

I am trying to pass the Json data to the next screen after login.
Get Instance of 'Future' Flutter is displaying in print statement
onPressed()
var Data = getData(Username,Password);
print(Data);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ActivePage(jsondata:Data);
},
));
getData() print the exact json response
getData(username,password) async {
Auth auth = Auth();
var Data = await auth.signup(username, password);
print(Data);
}

Change your getData method to given code below:
getData(username,password) async {
Auth auth = Auth();
var Data = await auth.signup(username, password);
print(Data);
return Data;
}
As I can see, looks like you forget to return Data from getData method.

Related

shared_preferences values returning null in flutter

I am using shared_preferences to store a bool value locally but I think I am doing something wrong.
So first of all, here is my initState:
#override
initState(){
super.initState();
checkIfUserHasData();
getBoolValuesSF();
}
on checkIfUserHasData, Im calling another function at the end (addBoolToSF)
Future<void> checkIfUserHasData ()async {
var collection = FirebaseFirestore.instance.
collection('users').doc(userID).collection('personalInfo');
var querySnapshots = await collection.get();
for (var snapshot in querySnapshots.docs) {
documentID = snapshot.id;
}
await FirebaseFirestore.instance
.collection('users')
.doc(userID)
.collection('personalInfo').doc(documentID)
.get().then((value) {
if (!mounted) return;
setState(() {
gender = value.get('gender');
profileImageUrl = value.get('url');
print(profileImageUrl);
print(gender);
});
});
if (gender != null){
if (!mounted) return;
setState((){
isUserNew = false;
});
if(gender == "Male"){
setState(() => genderIsMale = true);
addBoolToSF();
}else{
setState(() => genderIsMale = false);
addBoolToSF();
}
}else {
return;
}
}
Then addBoolToSF:
addBoolToSF() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('genderType', genderIsMale);
}
Lastely getBoolValuesSF:
getBoolValuesSF() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
bool _genderType = ((prefs.getBool('genderType') ?? true)) ;
genderType = _genderType;
});
}
When the genderType value is obtained I then decide which image to be the background image on the screen:
CachedNetworkImage(
placeholder: (context, url) =>
CircularProgressIndicator(),
imageUrl: genderType ? // : //
With all of that said, here is what is happening when the gender is changed on the firebase firestore:
The first time I navigate or refresh the screen nothing is changed and I get this error:
type 'Null' is not a subtype of type 'bool'
The second time I refresh or navigate to the screen, I do get the correct image on place but I get the same error message again
type 'Null' is not a subtype of type 'bool'
I have tried several ways to solve this issue but i dont seem to get it right.
Edit: I have noticed that when I removed the last part for CachedNetworkImage, I get no error so I think the problem might be on this part
In case like that when you need to wait for a future to build some UI, the go to way is to use a FutureBuilder
You use it like this
FutureBuilder<bool>(
future: getBoolValuesSF,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
// build your UI here based on snapshot value
},
)
checkIfUserHasData() and getBoolValuesSF() both are future method. you can create another async method and put it inside initState.
#override
initState(){
super.initState();
newMthod();
}
newMthod() async{
await checkIfUserHasData();
await getBoolValuesSF();
}

How to extract values from onCall firebase function and load them in future builder

i have a onCall cloud function which is returning
resp.status(200).send(JSON.stringify(entities));
In my flutter app, i have created this future to get values from it.
Future<void> dataDriven(String filename) async {
HttpsCallable callable =
FirebaseFunctions.instance.httpsCallable('fruitsType');
final results = await callable;
final datE = results.call(<String, dynamic>{
'filename': 'filename',
});
final dataF = await datE.then((value) => value.data);
print (dataF);
}
It is successfully printing the response which is as per expectation. but my snapshot is always returning null. It is not even reaching hasData stage. Please help.
Response;
[{"name":"banana","type":"fruit","count":0,"color":"yellow"},{{"name":"apple","type":"fruit","count":2,"color":"red"}]
FutureBuilder(
future: dataDriven('fruits.txt'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: Text('An error has occurred!'),
);
} else {
final data = snapshot.data;
return Text(data.toString());
}
It looks like there are some issues that need to be fixed (See comments in code).
// Set the correct return type (not void because you are returning data)
Future<String> dataDriven(String filename) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('fruitsType');
// You can just call the function here with await
final result = await callable.call({
// Remove the quotes on the filename value
'filename': filename,
});
// Don't forget to return the data
return result;
}
I suggest reading up on the documentation about calling cloud functions from a flutter app and basic dart syntax.

How to handle "Lost connection to device." error

I created an app, which contains login and dashboard screen, i used Nodejs backend and mongodb also. i trying to add functionality when user logged in and close the app without logging out and then when user come to the app it should display where it left last time. so i used shared preference for it. I am testing it now, when i logged in and close the app using the right-most button (which shows all the currently running apps) it send me this "Lost connection to device.
".
login Code:
bool newuser;
String type ;
SharedPreferences myPrefs;
void initState() {
checkIfLoggedinalready();
}
Future login() async {
try {
Dio dio = new Dio();
var data = {
'username':"Munib khatri",
'password': "Munib123",
'date': "5/5/2021"
};
await dio
.post(localhostUrlLogin, data: json.encode(data))
.then((onResponse) async {
type = onResponse.data['User']['Type'];
if (type == 'Employee') {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => Employee()));
} else if (type == 'Manager') {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => Manager()));
}
myPrefs.setBool('login', false);
});
} catch (e) {
}
}
void checkIfLoggedinalready() async{
myPrefs = await SharedPreferences.getInstance();
newuser = (myPrefs.getBool('login') ?? true);
print(newuser);
if (newuser == false && type == 'Employee') {
Navigator.pushReplacement(
context, new MaterialPageRoute(builder: (context) => Employee()));
}
}
dashboard code:
i am doing this on drawer code where i use logout
new ListTile(
title: new Text('Log out'),
leading: Icon(Icons.logout,color:Colors.grey),
onTap: (){
myPrefs.setBool('login', true); //here i set it to true, if user is new
Navigator.pop(context);
Navigator.push(context, new MaterialPageRoute(
builder: (context)=>
Login()
)
);
},
),
please help it would be appreciated.
Lost connection to device is not a coding issue, your linked device got disconnected at that time due to data cable, If you will test this on emulator then you will not get this issue.
If you are saving user data on Shared preference then do encode your sensitive data before saving it.
var response =
await http.post(Uri.parse(parseUse), body: json.encode(data), headers: { "Content-Type": "application/json; charset=UTF-8", }).timeout(Duration(minutes: 2));
Updated Answer :
try{
Dio dio = new Dio();
var data = {
'username':"Munib khatri",
'password': "Munib123",
'date': "5/5/2021"
};
await dio
.post(localhostUrlLogin, data: json.encode(data))
.then((onResponse) async {
type = onResponse.data['User']['Type'];
if (type == 'Employee') {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => Employee()));
} else if (type == 'Manager') {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => Manager()));
}
myPrefs.setBool('login', false);
});
}on DioError catch (e){
if(e.type == DioErrorType.connectTimeout){
"You can put a alert dialog or something you prefer".
}
}
or you can also use base Option since your using DIO
var dio = Dio(); // with default Options
// Set default configs
dio.options.baseUrl = 'https://www.xx.com/api';
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout = 3000;
// or new Dio with a BaseOptions instance.
var options = BaseOptions(
baseUrl: 'https://www.xx.com/api',
connectTimeout: 5000,
receiveTimeout: 3000,
);
Dio dio = Dio(options);
response = await dio.request(
'/test',
data: {'id':12,'name':'xx'},
options: Options(method:'GET'),
);

Why Flutter "await" doesn't wait?

I'm working on a Flutter app which needs uploading image files to Firebase Storage and then saving urls to Firestore.
Below is my code:
void onSubmit() async {
final fireStore = FirebaseFirestore.instance;
//picList has one element.
List<String> picUrlList = [];
picList.forEach((element) async {
//picUrlList.add(await uploadImage(File(element)));
var url = await uploadImage(File(element));
picUrlList.add(url);
print('1');
});
await fireStore.collection('xxx').doc('yyy').update({
"picUrlList": FieldValue.delete(),
});
await fireStore.collection('xxx').doc('yyy').update({
"picUrlList": FieldValue.arrayUnion(picUrlList),
});
print('2');
print(picUrlList.length);
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => EditPost()),
(Route<dynamic> route) => false,
);
}
Future<String> uploadImage(var imageFile) async {
//var uuid = Uuid().v1();
Reference ref = FirebaseStorage.instance.ref(
user.uid + DateTime.now().millisecondsSinceEpoch.toString() + '.jpg');
await ref.putFile(imageFile);
return await ref.getDownloadURL();
}
I was expecting print('1') to be executed before print('2'). But the printout order is 2, 0, 1.
0 is the length of the list, which I hope can be full of urls just added.
I just don't know what's wrong with the code. Somebody help me.
Thanks.
Your code will be executed normally till the end and then the callbacks from forEach will start executing, so solve this you have two solutions:
Replace forEach by for loop
Use await Future.forEach(picList, (element) async { var url = await uploadImage(File(element));... });

Dynamic variable in flutter. Any better way to do this?

[Edited] I have this application with multilevel user application where I have functions based on roles. Currently, I am saving user response in shared preferences and fetching it by getting it's instance whenever I need it. And also, I am using different screens and different widgets for each role. But there has to be a better way to do it. I am so confused with singleton pattern and making global variables in dart.
Here's my code:
void main() {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences.getInstance().then((prefs) {
var user=prefs.getString("role");
runApp(MultiProvider(
providers: [
ChangeNotifierProvider<RoleNotifier>(
create: (_) => RoleNotifier(user),
),
],
child: MyApp(),
));
});
}
void setRole(String role) async {
Provider.of<RoleNotifier>(context, listen:false).setUser(role);
await SharedPreferences.getInstance().then((prefs){
prefs.setString("role", role);
});
}
_login() async {
try {
setState(() {
_isbusy = true;
});
var data = {"username": _emailc.text, "password": _pass.text};
var response = await CallApi().postData(data, 'login');
SharedPreferences local = await SharedPreferences.getInstance();
var res = response.data;
print(res);
if (res['success']) {
local.setString('token', res['data']['token']);
if (res['data']['role'] == 'admin') {
setRole(res['data']['role']);
local.setString('info', json.encode(res['data']));
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => AdminDashBoard()));
} else if (res['data']['role'] == 'dev') {
setRole(res['data']['role']);
local.setString('post', res['data']['role']);
local.setString('info', json.encode(res['data']));
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => DevDashBoard()));
} else if (res['data']['role'] == 'user') {
setRole(res['data']['role']);
local.setString('post', res['data']['role']);
local.setString('info', json.encode(res['data']));
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => UserDashBoard()));
}
} else {
print('error');
setState(() {
_isbusy = false;
});
showSimpleFlushbar(context, "An Error Occurred!");
}
} on DioError catch (e) {
print(e);
setState(() {
_isbusy = false;
});
print(e.response.data);
print(e.response.headers);
print(e.response.request);
showSimpleFlushbar(context,
"Login Failed! Please Check your credentials and try again.");
}
}
And to access the variables:
SharedPreferences.getInstance().then((prefs) {
var data = jsonDecode(prefs.getString("info"));
setState(() {
email = data['email'];
post = data['role'];
});
});
The problem is, I have to run this on initState in every screen and there is a delay in fetching data which throws an exception for small time.
I just figured out this is working.
(Provider.of<RoleNotifier>(context).getUser()=="admin")?AdminWidget():SizedBox(),
Now I can access the data from anywhere using provider. But is there any better way to do this? I've heard a lot about singleton pattern and in my case even though it works, it seems like I am doing something wrong. Like I am listening to the value that is static immediately after login is completed.
SharedPreferences prefs;// file level global variable
main(){
SharedPreferences.getInstance().then((p)=>prefs = p);
// do whatever
runApp(MyApp());
}
Now, don't use SharedPreferences.getInstance() when needed but use the global variable
created.
Like
prefs.getString('name');
or
prefs.setString('foo','bar');
For example
class Foo extends StatelessWidget{
Widget build(context){
var name = prefs.getString('name');// don't use var prefs = await SharedPreferences.getInstance();
return Text("name is $name");
}
}
Why not create a User class and extend it with Provider?
Then based on the Consumers to build dynamic widgets you can pump out what ever you want based on the User.role for the selected user.
In your Singleton you can add a Singleton().selectedUser var and once a user logs in or what ever process they follow you can assign it to that. Use this selectedUser var for your Provider.value.
If you need example code let me know.