How to setState() of a text field after handling an error in Flutter? - dart

I have a function called LoginWithFb(). The function has a try catch block:
void loginWithFb() async {
try {
var auth = AuthProvider.of(context).auth;
print('Signing up with fb...');
setState(() {
_showProgressIndicator = true;
});
FirebaseUser user = await auth.signInWithFBAcc();
uId = user?.uid;
if (uId != null) {
print('Signed in: $uId');
widget.onSignedIn(user);
} else {
print('fb login cancelled');
}
// _showAlert(context);
setState(() {
_showProgressIndicator = false;
});
} catch (exception) {
print(exception.toString());
setState(() {
_showProgressIndicator = false;
});
}
When the error is caught, I want to display message to the user. The message has to be in a text field and not via a dialog. At the moment I have an empty Text('') widget in my build method. I want to write text to the text widget when the error is caught..

Just use local variable for storing message and show it via Text widget
String message = "";
void loginWithFb() async {
try {
...
} catch (exception) {
print(exception.toString());
setState(() {
message = "Some error happens";
_showProgressIndicator = false;
});
}
In widget:
Text(message);

Related

Why can't catch exceptions with "catchError" in Dart?

Test:
void testAs() async {
try {
String b = await test();
print(b);
} catch (e) {
print("1 await error");
}
test().then((value) => print(value)).catchError(() {
print("2 then error");
});
}
Future<String> test() {
List<String> bb = ["2222"];
return Future.value(bb[1]);
}
1 await error
RangeError (index): Invalid value: Only valid value is 0: 1
Why is it ineffective?
If I want to deal with "future" through "then", how should I catch the exception and not let it throw out.
Thank you friends,the last problem has been solved,the problem can be solved by adding async and await flags to the test() method.
But there is a new problem, Now I use the correct code and find that it can only be printed once.why can't it print "then success",then the program ends
,modify as follows:
void testAs() async {
try {
await test();
print("await success");
} catch (e) {
print("await error");
}
test().then((value) => print("then success")).catchError((e) {
print("then error");
});
}
Future<String> test() async{
List<String> bb = ["2222"];
return await Future.value(bb[0]);
}
print:await success

How to apply alert dialog after response

public async Task<ObservableCollection<CustomerModel>> GetCustomer(string customerNumber, string department)
{
try
{
progressBar.Visibility = ViewStates.Visible;
progressBar.Progress = 0;
listofItems = new ObservableCollection<CustomerModel>();
string url = _client.BaseAddress + "/getcustomers(Number='" + customerNumber + "',department='" +
department + "')";
var response = await _client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
progressBar.Visibility = ViewStates.Invisible;
progressBar.Progress = 100;
string returnjson = await response.Content.ReadAsStringAsync();
ReplyCustomerModel replyCustomerModel =
JsonConvert.DeserializeObject<ReplyCustomerModel>(returnjson);
if (replyCustomerModel != null)
{
listofItems = replyCustomerModel.Customers;
}
}
else
{
AlertDialog.Builder alertDiag = new AlertDialog.Builder();
alertDiag.SetTitle("Butikscanner App");
alertDiag.SetMessage("User Does not exist");
alertDiag.SetPositiveButton("OK",
(senderAlert, args) => { });
alertDiag.SetNegativeButton("Cancel", (senderAlert, args) => { alertDiag.Dispose(); });
Dialog diag = alertDiag.Create();
diag.Show();
}
return listofItems;
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
Actually that is what i am doing if my response is false i am trying to show alert dialog that user does not exist I am working my project in MVVM light
Actually that is what i am doing if my response is false i am trying to show alert dialog that user does not exist I am working my project in MVVM light
Actually that is what i am doing if my response is false i am trying to show alert dialog that user does not exist I am working my project in MVVM light
Usually, API calls are made in a background thread using async-await if that is the case with you as well then I would suggest that you do is call the dialog's show method on the UIThread. For this, you will need an activity context i.e. An activity's reference.
There are two way for you to do it either directly call this method as an action something like this:
private void ShowDialog()
{
AlertDialog.Builder alertDiag = new AlertDialog.Builder();
alertDiag.SetTitle("Butikscanner App");
alertDiag.SetMessage("User Does not exist");
alertDiag.SetPositiveButton("OK",(senderAlert, args) => { });
alertDiag.SetNegativeButton("Cancel", (senderAlert, args) => { alertDiag.Dispose(); });
Dialog diag = alertDiag.Create();
diag.Show();
}
assuming above is how your method is defined you can run it on UI thread like:
activity.RunOnUIThread(ShowDialog);
But in your scenario, I do not personally think that this is a smart thing to do because the only line of code that is supposed to be on UIThread(at least i think so) is the dialog.Show();
What you should do rather would be use a lamba expression for a anonymous method something like:
private void ShowDialog(Activity activity)
{
AlertDialog.Builder alertDiag = new AlertDialog.Builder();
alertDiag.SetTitle("Butikscanner App");
alertDiag.SetMessage("User Does not exist");
alertDiag.SetPositiveButton("OK",(senderAlert, args) => { });
alertDiag.SetNegativeButton("Cancel", (senderAlert, args) => { alertDiag.Dispose(); });
Dialog diag = alertDiag.Create();
activity.RunOnUIThread(()=>
{diag.Show();});
}

future functions keep repeating

am trying to read and write in files in my flutter app .. like this:
Future<String> get _localPath async {
print('hi');
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
File f = File('$path/mypollshash.txt');
if (f.existsSync()) {
print('exists');
String contents = await f.readAsString();
content = contents;
fetchHash();
} else {
print('not exists');
fetch();
}
return f;
}
Future checkfileexist() async {
try {
final file = await _localFile;
String contents = await file.readAsString();
content = contents;
} catch (e) {
//return 'nothing';
}
}
Future<File> writehash(String hash) async {
final file = await _localFile;
return file.writeAsString('$hash', mode: FileMode.write);
}
Future<File> get _localjson async {
final path = await _localPath;
return File('$path/mypolls.json');
}
Future<File> writejson(String json) async {
final file = await _localjson;
return file.writeAsString('$json', mode: FileMode.write);
}
readjson() async {
try {
final file = await _localjson;
String contents = await file.readAsString();
content = contents;
setState(() {
polls = pollsFromJson(content);
isloading = false;
});
writejson(pollsToJson(polls));
writehash(polls.hash);
print('here');
// return contents;
} catch (e) {
fetch();
print('there');
print(e);
// If we encounter an error, return 0
//return 'nothing';
}
}
fetch() async {
String data =
await DefaultAssetBundle.of(context).loadString("assets/mypolls.json");
setState(() {
polls = pollsFromJson(data);
isloading = false;
});
writejson(pollsToJson(polls));
writehash(polls.hash);
}
fetchHash() async {
String data = await DefaultAssetBundle.of(context)
.loadString("assets/pollshash.json");
print(content);
final pollshash = pollshashFromJson(data);
if (content == pollshash.hash) {
print('take from the saved json');
readjson();
} else {
print('call api');
fetch();
}
}
and then am calling it here:
#override
void initState() {
super.initState();
checkfileexist();
}
this works fine .. but the method will keep called even when i go to another page and will get this printed over and over again:
I/flutter (17060): hi
I/flutter (17060): here
I/flutter (17060): exists
I/flutter (17060): d1f4bd60f52991d100adafa416f48b52
I/flutter (17060): take from the saved json
I want this to be called only once .. how to do this?
InitState is not called once it's normal. Instead you can do it with multiple solutions.
Create an attributes in your component to memorize if you already did your checks like this
class MyComponentState ... {
bool hasChecked = false;
bool isFileExists = false;
#override
initState() {
super.initState();
if(!hasChecked) {
this.hasChecked = true;
this.isFileExists = checkfileexist();
}
}
}

Flutter and Firebase: How can i receive data from function?

i use the following function to fetch userData from Firestore:
Future<String>getRegisterUserData({String userID}) async {
Firestore.instance.collection("Users").document(userID).get().then(
(datasnapshot) {
if (datasnapshot.exists) {
return datasnapshot.data['Email'];
} else {
return "Loading...";
}
},
);
}
I execute this function on my UserProfilePage like this:
_email = widget.firestore.getRegisterUserData(widget.userID).toString();
But i always get the print statement: Instance of 'Future' and not the saved email-address...
i also try this:
Future<String> getRegisterUserData({String userID}) async {
String email;
Firestore.instance.collection("Users").document(userID).get().then(
(datasnapshot) {
if (datasnapshot.exists) {
email = datasnapshot.data['Email'];
} else {
email = "Loading...";
}
},
);
return email;
}
The Print Statement is always the same...
So where is my mistake? I want to display the Value of 'Email' on the UserProfilePage in a variable, or is my firestore function incorrect?
Thank you for our help
Add await keyword. But at a different place.
tempEmail = await widget.firestore.getRegisterUserData(widget.userID);
setState(() {
_email = tempEmail;
});
// we don't need toString as getRegisterUserData itself is returning Future<String>
Note for using await: As we are using await the method/function which contains this should have a async in its signature.
Or you can use then block
widget.firestore.getRegisterUserData(widget.userID).then((email) {
setState(() {
_email = email;
});
});
Explanation: widget.firestore.getRegisterUserData(widget.userID) is of type Future<String>. that's why it is printed as Instance of 'Future'. We have to convert the Future to String by await or by then block
Using SharedPreferences:
Future<String> getEmail() async {
final prefs = await SharedPreferences.getInstance();
String email = prefs.getString('email');
if (email != null) {
return email;
} else {
email = await widget.firestore.getRegisterUserData(widget.userID);
prefs.setString('email', email); //update shared preferences
return email;
}
}
// usage (may be in initState)
widget.getEmail().then((email) {
setState(() {
_email = email;
})
})
Updated
Based on your information, you need a FutureBuilder in order to wait the response to build your widget:
return FutureBuilder(
future: getRegisterUserData(userID: "1234"),
builder: (context, asyncsnapshot){
return asyncsnapshot.hasData && asyncsnapshot.data != null ? TextFormField(
initialValue: asyncsnapshot.data,
) : CircularProgressIndicator();
}
);

Make function to return custom stream

Is it possible to create a function that returns a custom stream and handles it like this?
user.logIn('owner', '1234')
.listen(
success (Object user) {
print(user);
},
error: (Object user, Object error) {
print(error);
}
);
Something like:
class LoginResult {
bool success = false;
String username;
}
Stream<LoginResult> onLogin() async* {
while(...) {
yield new LoginResult()
..success = isSuccess
..userName = 'someUser';
}
}
or
StreamController<LoginResult> onLoginController = new StreamController<LoginResult>();
// might not be necessary if you only need one listener at most
Stream<LoginResult> _onLogin = onLoginController.stream.asBroadcastStream();
Stream<LoginResult> get onLogin => _onLogin
...
onLoginController.add(new LoginResult()
..success = isSuccess
..userName = 'someUser');
Then you can use it like

Resources