FutureBuilder does not get into builder? - flutter

I want to get two parameters from createUser response.
I have a raisedbutton and i want to use FutureBuilder when onPressed.
future property works well, but i can not get in builder property. I tried everything but i could not find solution.
...
RaisedButton(
elevation: 5.0,
onPressed: (){
FutureBuilder(
future: createUser(usernameController.text, emailController.text, passwordController.text, '1', 1),
builder: (context, snapshot) {
if (snapshot.hasData) {
saveLogin(snapshot.data.userUnique);
nextScreen(
context,
UserDetail(
userUnique: snapshot.data.userUnique,
));
} else {
return MyHomePage();
}
return MyHomePage();
});
},
...
Future createUser(String username, String password, String email,
String logintype, int userUnique) async {
String url =
"http://api.xx.com/api/register?username=$username&password=$password&email=$email&logintype=$logintype&userUnique=$userUnique";
final response =
await http.get(url, headers: {'Content-Type': 'application/json'});
return registerFromJson(utf8.decode(response.bodyBytes));

I changed FutureBuilder to async / await method
thanks for your help #pskink
onPressed: () async {
var xx = await createUser( usernameController.text, emailController.text, passwordController.text, '1',userUnique());
print(xx.responseCode.toString());
print(xx.userUnique.toString());
saveLogin(userUnique());
},

Related

Anyway to enable 'remember me' function using sharedPreferences in flutter?

Currently, I have enabled 'keep logging in' function if the user log in once successfully. However, I still want to make a 'remember me' checkbox to save the success login information for user. Can anyone please help me with this?
Need: a checkbox that enables the user to store email and password if the user logged in once successfully.
Code is shown below:
signIn(String email, pass) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String url = ServerDetails.ip +
':' +
ServerDetails.port +
ServerDetails.api +
'login';
Map<String, String> headers = {"Content-type": "application/json"};
var data = jsonEncode({
'email': email,
'password': pass,
'token': FirebaseNotifications.fcmtoken
});
var jsonResponse = null;
var response = await http.post(url, headers: headers, body: data);
if (response.statusCode == 200) {
jsonResponse = json.decode(response.body);
if (jsonResponse != null) {
setState(() {
_isLoading = false;
});
sharedPreferences.setString("token", jsonResponse['token']);
sharedPreferences.setString(
"token_expire_date", jsonResponse['token_expire_date']);
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext context) => MainPage()),
(Route<dynamic> route) => false);
}
} else {
setState(() {
_isLoading = false;
});
Widget okButton = FlatButton(
child: Text("OK"),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => MainPage()));
});
setState(() {
AlertDialog alert = AlertDialog(
title: Text("Error message"),
content: Text("Oops! The password is wrong or the email is invalid."),
actions: [
okButton,
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
});
print(response.headers);
print(response.body);
}
}
Of course, you can create a simple checkbox for remember me. In the login button, you can check if this checkbox is checked. If it is, you can set email & password in shared_preferences.
Next time, when the user comes again you can get these fields automatically from shared_preferences.
Here is an example.

How to get a String from a Future<String> in Flutter?

I have a strange problem. I need the value of a Future<String> as String to display an image with CachedNetworkImage. Because I have to wait for a downloadUrl which comes from Firebase Storage I found no way to use it as normal String.
Here is my Widget where I need the value
Container(child: Obx(
() {
return CachedNetworkImage(
imageUrl: _getImage(), // Here I need the value as String
height: Get.height,
fit: BoxFit.cover);
},
))
And this is my _getImage() function
Future<String> _getImage() async {
var url = return await MyStorage().getDownloadUrl(url);
return url;
}
The getDownloadUrl() only returns a String with the download url from Firebase Storage
Future<String> getDownloadUrl(ref) async {
final StorageReference storage = FirebaseStorage().ref().child(ref);
final url = await storage.getDownloadURL();
return url.toString();
}
Because I definitely have to wait for the getDownloadUrl() I have no chance to return the value as String. But I need it as String. Otherwise I get an error.
How would you solve this problem?
A FutureBuilder will build the UI depending on the state of the Future. All you have to do is check if it has the data, then build the image.
Future<String> myFutureString() async {
await Future.delayed(Duration(seconds: 1));
return 'Hello';
}
#override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: myFutureString(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Container(
child: Text(snapshot.data),
);
}
return CircularProgressIndicator();
},
);
}

flutter A build function returned null

I am trying to use FutureBuilder but its showing error of A build function returned null
My code
class _EventsState extends State<Events> {
#override
Future<List> doSomeAsyncStuff() async {
final storage = new FlutterSecureStorage();
String value = await storage.read(key: 'token');
print(value);
String url = 'http://sublimeapi.netcodesolution.com/api/NewsAndEvents/';
String token = value;
final response = await http.get(url, headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
print('Token : ${token}');
var eventData = json.decode(response.body);
print(eventData["Data"]);
List _events = eventData["Data"];
return _events;
}
#override
Widget build(BuildContext context) {
double statusBarHeight = MediaQuery
.of(context)
.padding
.top;
return Expanded(
child: FutureBuilder(
future: doSomeAsyncStuff(),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
List<Widget> children;
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
print('working');
print(snapshot.data);
return Container(
child: Column(
children: <Widget>[
Text('working')
],
),
);
}
}
}),
);
}
As you can see in code I am fetching data from API and its working fine. In code i print the value of _events in setState its also printing the value like this
I/flutter (32627): [{Id: 38, Description: Two days Workshop on Prevention of Suicide organized for GPs of Thar., ImagePath: /journals/2e8a55f3-6612-4b23-a0ea-e91022c159a8.pdf, CreatedBy: 4447, CreatedOn: 2019-09-18T14:56:13.357, Active: false, Type: Events, Attachment: null, AttachmentType: Events}
I need to print the Description value of this data in future widget but don't know why its showing error
The Error says it clearly! It returned null.
So you have to return something! Do something like this,
Future<List> doSomeAsyncStuff() async {
final storage = new FlutterSecureStorage();
String value = await storage.read(key: 'token');
print(value);
String url = 'http://sublimeapi.netcodesolution.com/api/NewsAndEvents/';
String token = value;
final response = await http.get(url, headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
print('Token : ${token}');
var eventData = json.decode(response.body);
print(eventData["Data"]);
List _events = eventData["Data"];
return _events;
}
and also, we missed another case here.
Scaffold(
appbar: AppBar(
title: const Text('Sample Future Builder'),
),
body: Expanded(
child: FutureBuilder(
future: doSomeAsyncStuff(),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
print('working');
print(snapshot.data);
return Container(
child: Column(
children: <Widget>[
Text('working')
],
),
);
}
}
return Center(child: Text("Not Loaded Yet!!"),)
}
),
),
);
Hope that solves your issue!
Tip: Move all your widgets under Scaffold. It would be the best practice. Refer this

How to implement CircularProgressIndicator in Flutter

I am trying to implement a CircularProgressIndicator when waiting user to finish their authentication process, here's the code, I am using Firebase as the auth backend
Future<void> signIn() async {
if (_formKey.currentState.validate()) {
new Loading();
_formKey.currentState.save();
try {
final user = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
if (user != null) {
final FirebaseUser user = await auth.currentUser();
print('success login');
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Home(user: user),
));
}
} catch (e) {
print(e);
}
}
}
how to implement the circularprogressindicator into the code?
CircularProgressIndicator can be implemented in UI with a condition.If you want to implement it in some area of your page you can use boolean to make it work like this:
bool showCircular = false;
Future<void> signIn() async {
if (_formKey.currentState.validate()) {
new Loading();
_formKey.currentState.save();
setState(() {
showCircular=true;
});
try {
final user = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
if (user != null) {
final FirebaseUser user = await auth.currentUser();
setState(() {
showCircular=false;
});
print('success login');
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Home(user: user),
));
}
} catch (e) {
print(e);
}
}
}
Now anywhere in your UI add this widget:
showCircular ? CircularProgressIndicator() : SizedBox(),
Recommended way is using an AlertDialog to show progress indicator. Here is an example with AlertDialog.To open Dialogs you need context so take BuildContext as a parameter and call signIn(context); from sign in button.We will call dialog using openLoadingDialog function and when sign in is completed we will pop it using Navigator.of(context).pop();
Future<void> signIn(BuildContext context) async {
if (_formKey.currentState.validate()) {
new Loading();
_formKey.currentState.save();
openLoadingDialog(context, 'Signing In...');
try {
final user = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
if (user != null) {
final FirebaseUser user = await auth.currentUser();
Navigator.of(context).pop();
print('success login');
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Home(user: user),
));
}
} catch (e) {
print(e);
}
}
}
Paste this dialog anywhere in your app.You can call it by passing context and message you want to show.
openLoadingDialog(BuildContext context, String text) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
content: Row(children: <Widget>[
SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator(
strokeWidth: 1,
valueColor: AlwaysStoppedAnimation(Colors.black)
)
),
SizedBox(width: 10),
Text(text)
]),
)
);
}

cannot navigate form login screen to bottom_tab_screen with provider

I'm trying to navigate from login screen to the bottom tab screen but nothing happen and now i have no error
it is the main
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: UserProvider()),
ChangeNotifierProvider.value(value: AppProvider()),
],
child:MaterialApp(
key: key,
title: 'Voyager',
debugShowCheckedModeBanner: false,
theme: AppTheme.getTheme(),
routes: routes,
),
);
}
my dialog which has two cases if success or fail to login or sign up
import 'package:flutter/material.dart';
class Dialogs {
static showErrorDialog(BuildContext context,
{#required String message, #required int code}) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Ok'),
)
],
title: Text('error $code'),
content: Text(message),
backgroundColor: Colors.white,
);
},
);
}
}
my login method and it depend on user api provider
signIn() async {
var res = await userProvider.login(
_userNameController.text, _passwordController.text);
if (res is FailedRequest) {
Dialogs.showErrorDialog(widget._context , message: res.message, code: res.code);
print('results ${res.toString()}');
} else {
print("Signing in success");
Navigator.pushReplacement(
widget._context, MaterialPageRoute(builder: (context) => BottomTabScreen()));
}
userProvider.isLoading = false;
}
and the api provider which use in the login
Future<dynamic> login(String email, String password) async {
final Map<String, dynamic> body = {'email': email, 'password': password};
_isLoading = true;
notifyListeners();
print('Starting request');
http.Response response = await http.post(Environment.userLogin,
body: json.encode(body), headers: Environment.requestHeader);
print('Completed request');
print('user login response : ${response.body}');
Map<String, dynamic> res = json.decode(response.body);
var results;
if (res['code'] == 200) {
// login successful
_user = User.fromJson(res['message']);
results = true;
} else {
// login failed;
results =
FailedRequest(code: 400, message: res['error'], status: false);
}
_isLoading = false;
notifyListeners();
return results;
}
finally the failed request class if request not done
import 'package:flutter/foundation.dart';
class FailedRequest {
String message;
int code;
bool status;
FailedRequest({
#required this.message,
#required this.code,
#required this.status,
});
}
The Issue seems to be with the res['error'] can you verify that the field error actually exists and is not null.
At this block can you print the value of res['error']
else {
print(res['error']);
// login failed;
results =
FailedRequest(code: 400, message: res['error'], status: false);
}