Do not use BuildContexts across async gaps - Flutter - flutter

I use SnackBar after, the await.
So, It's showing error like this:
Do not use BuildContexts across async gaps
I used if (!mounted) this line to remove the error. It removed the problem but, SnackBar is not showing. When complete the task
My code here:
Future removeMethod() async {
String res = await DatabaseMethods().idReject(widget.uid);
if (res == "success") {
if (!mounted) return;
showSnackBar(context, "Job done!");
} else {
if (!mounted) return;
showSnackBar(context, "Error!!");
}
}
showSnackBar is customWidget. code of it:
void showSnackBar(BuildContext context, String title) {
final snackBar = SnackBar(
content: CustomText(
text: title,
size: 16,
),
backgroundColor: darkblueColor,
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
What can I do about this?. I want to show snackbar after await.

This basic example works based on your snippets. Here a snackbar is called after the result of a future is returned (e.g. a database call). If this helps?
import 'dart:math';
import 'package:flutter/material.dart';
class SnackBarAfterFutureResult extends StatelessWidget {
const SnackBarAfterFutureResult({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: OutlinedButton(
onPressed: () async {
await removeMethod()
.then((value) => showSnackBar(context, value));
},
child: const Text('Database call')),
),
);
}
}
void showSnackBar(BuildContext context, bool result) {
String text = "Job done!";
if (!result) {
text = "Error!!";
}
final snackBar = SnackBar(content: Text(text), backgroundColor: Colors.blue);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
Future<bool> removeMethod() async {
//e.g. getting result from database call
bool isSuccess = false;
await Future.delayed(const Duration(seconds: 2), () {
isSuccess = Random().nextBool();
});
return isSuccess;
}

Related

How to show success message and catch error during flutter firestore data update

I have this function
Future updateMember(Member member) async {
final User? user = Auth().currentUser;
final docMember =
FirebaseFirestore.instance.collection('users').doc(user?.uid);
member.id = docMember.id;
final json = member.toJson();
final response = await docMember
.update(json)
.then((value) => {print("done")})
.catchError((e) => (e));
return response;
}
Then I want to catch the error here and success message here
final response = updateMember(member);
if (response.then((value) => 'done') == true) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('success'),
backgroundColor: Colors.green,
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(catchError(onError)),
backgroundColor: Colors.red,
),
);
}
Please I need help on how I can implement this
For this kind of purposes you could use a BLoC pattern, that divide an ui layer and domain layer (communication with server), you can read more on official documentation of bloc library: Bloc library
It might be complicated to a novice in flutter, so, in your case, you can also implement state managment inside a single widget by your own.
Define stream and subscribe to it.
late StreamController _controller;
late StreamSubscription _subscriber;
#override
void initState() {
_controller = StreamController<http.Response>();
_subscriber = _controller.stream.listen((event) {
});
super.initState();
}
In controller's stream we will add all server responses and work with those by _subscriber;
Add to stream value to work with
final response = await docMember
.update(json)
.then((value) => {print("done")})
.catchError((e) => (e));
_controller.add(response);
Whenever you get response from server, you should call _controller.add(response), to add to our stream a new value.
Handle responses in stream
#override
void initState() {
_controller = StreamController<http.Response>();
_subscriber = _controller.stream.listen((event) {
if (event.statusCode < 200 || event.statusCode > 299)
{
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('error'),
backgroundColor: Colors.red,
),
);
}
else
{
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('success'),
backgroundColor: Colors.green,
),
);
}
});
final response = await docMember
.update(json)
.then((value) => {print("done")})
.catchError((e) => (e));
_controller.add(response);
super.initState();
}
In stream you'l check if code is "OK", then show succes message, otherwise - error.
All code snipped is showed below:
class ParentWidget extends StatefulWidget {
ParentWidget({Key? key}) : super(key: key);
#override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
late StreamController<http.Response> _controller;
late StreamSubscription _subscriber;
#override
void initState() {
_controller = StreamController<http.Response>();
_subscriber = _controller.stream.listen((event) {
if (event.statusCode < 200 || event.statusCode > 299)
{
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('success'),
backgroundColor: Colors.green,
),
);
}
else
{
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('error'),
backgroundColor: Colors.red,
),
);
}
});
final response = await docMember
.update(json)
.then((value) => {print("done")})
.catchError((e) => (e));
_controller.add(response);
super.initState();
}
#override
void dispose() {
_subscriber.cancel();
_controller.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Center(
child: Text("Any widget")
);
}
}
This solves the problem
updateMember(member)
.whenComplete(
() => ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('succes'),
backgroundColor: Colors.green,
),
))
.onError((error, stackTrace) =>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(error.toString()),
backgroundColor: Colors.red,
),
));
});

How to move classes and functions to a separate file in Flutter/Dart?

Good day! I am new to Flutter/Dart. And the more I experiment, the bigger my main file gets. Obviously, I need a separate file in which I will store all the classes and functions that I will refer to in the future.
I have a separate screen with what I need. Here is its code:
//Internet route
class InternetRoute extends StatefulWidget {
const InternetRoute({Key? key}) : super(key: key);
#override
State<InternetRoute> createState() => _InternetRouteState();
}
class _InternetRouteState extends State<InternetRoute> {
bool ActiveConnection = false;
String T = "";
Future CheckUserConnection() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
setState(() {
ActiveConnection = true;
T = "Turn off the data and repress again";
});
}
} on SocketException catch (_) {
setState(() {
ActiveConnection = false;
T = "Turn On the data and repress again";
showInternetDialog(context);
});
}
}
#override
void initState() {
CheckUserConnection();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("GeeksforGeeks"),
),
body: Column(
children: [
Text("Active Connection? $ActiveConnection"),
const Divider(),
Text(T),
OutlinedButton(
onPressed: () {
CheckUserConnection();
},
child: const Text("Check"))
],
),
);
}
}
//Alert Dialog about Internet connection
showInternetDialog(BuildContext context) {
// set up the button
Widget okButton = Center(
child: TextButton(
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
),
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
// title: Text("My title"),
content: Text("Internet connection required"),
actions: [
okButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
I want to create a my classes.dart file that will gradually populate with the most commonly used things. In particular, I need class _InternetRouteState and showInternetDialog.
How to transfer them to a new file? I completely copied the code of that screen. Is it correct? Would that be enough to then refer to them in main.dart (after import)? Will all their variables be visible to my screens as well?
Edit 1. I don't know how I can move CheckUserConnection to my file. I mean I took the piece of code I needed and wrapped it in the CheckUserConnection class (in my separate file), but it doesn't work. What am I doing wrong?
class CheckUserConnection {
bool ActiveConnection = false;
String T = "";
Future CheckUserConnection() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
setState(() {
ActiveConnection = true;
T = "Turn off the data and repress again";
});
}
} on SocketException catch (_) {
setState(() {
ActiveConnection = false;
T = "Turn On the data and repress again";
// showInternetDialog(context); //temporary
});
}
}
}
The Problems tab shows the following errors:
Constructors can't have a return type.
The modifier 'async' can't be applied to the body of a constructor.
The await expression can only be used in an async function.
The method 'setState' isn't defined for the type 'CheckUserConnection'.
The method 'setState' isn't defined for the type 'CheckUserConnection'.
Create a new dart file. Name it internet_dialog_handler.dart. Add this to the file
class InternetDialogHandler{
//Alert Dialog about Internet connection
showInternetDialog(BuildContext context) {
// set up the button
Widget okButton = Center(
child: TextButton(
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
),
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
// title: Text("My title"),
content: Text("Internet connection required"),
actions: [
okButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
In internetRoute use this
//Internet route
class InternetRoute extends StatefulWidget {
const InternetRoute({Key? key}) : super(key: key);
#override
State<InternetRoute> createState() => _InternetRouteState();
}
class _InternetRouteState extends State<InternetRoute> {
bool ActiveConnection = false;
String T = "";
InternetDialogHandler _internetDialogHandler = InternetDialogHandler();
Future CheckUserConnection() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
setState(() {
ActiveConnection = true;
T = "Turn off the data and repress again";
});
}
} on SocketException catch (_) {
setState(() {
ActiveConnection = false;
T = "Turn On the data and repress again";
//Use the variable here to access the method in that class
_internetDialogHandler.showInternetDialog(context);
});
}
}
#override
void initState() {
CheckUserConnection();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("GeeksforGeeks"),
),
body: Column(
children: [
Text("Active Connection? $ActiveConnection"),
const Divider(),
Text(T),
OutlinedButton(
onPressed: () {
CheckUserConnection();
},
child: const Text("Check"))
],
),
);
}
}
EDIT
class CheckUserConnection {
Future checkInternetAvailability() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
return true;
}
} on SocketException catch (_) {
return false;
}
}
}
Use a different name for the method. Same name is used to defined the constructor of the class. Also make it work independent. Just return a true or false. Now to use this define a variable of type checkUserConnection
CheckUserConnection _checkUserConnection = CheckUserConnection();
bool _internetAvailable = await _checkUserConnection.checkInternetAvailability();
if(_internetAvailable)
{
//do something here;
} else{
//handle no internet here
}

bottom sheet called by an OnTap is not displayed properly and kill the simulator

On a page, I have a button. I am using the OnTap to display a bottom sheet. But the widget does not display on the screen. I don't know why. I have tried different option and even tried using other widget to check if they were displaying properly. It was working. But it is not with the bottom sheet. If you can explain to me what I am missing, it would be appreciated. Thank you.
I have tried also this, see code below, but it is not working properly because I can not use setstate.
ERROR : lost connection. It is killing the simulator.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:permission_handler/permission_handler.dart';
class Voice_Recording extends StatefulWidget {
const Voice_Recording({Key key}) : super(key: key);
#override
_Voice_RecordingState createState() => _Voice_RecordingState();
}
class _Voice_RecordingState extends State<Voice_Recording> {
final recorder = FlutterSoundRecorder();
#override
void initState(){
super.initState();
initRecorder();
}
#override
void dispose(){
recorder.closeRecorder();
super.dispose();
}
Future initRecorder() async{
final status = await Permission.microphone.request();
if(status != PermissionStatus.granted){
throw 'Microphone permission not granted!';
}
await recorder.openRecorder();
recorder.setSubscriptionDuration(Duration(milliseconds: 500 ));
}
Future record() async{
await recorder.startRecorder(toFile:'audio');
}
Future stop() async{
final path = await recorder.stopRecorder();
final audioFile = File(path);
print('recorded audio File :$audioFile');
}
#override
Widget build(BuildContext context) {
return BottomSheet(
builder: (context){
return Column(
children: [
StreamBuilder<RecordingDisposition>(
stream: recorder.onProgress,
builder: (context,snapshot){
final duration=snapshot.hasData?
snapshot.data.duration: Duration.zero;
String twoDigits(int n) => n.toString().padLeft(60);
final twoDigitsMinutes= twoDigits(duration.inMinutes.remainder(60));
final twoDigitsSeconds = twoDigits(duration.inSeconds.remainder(60));
return Text('$twoDigitsMinutes:$twoDigitsSeconds',
style: TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
),);
},
),
Center(child:
ElevatedButton(child:Icon(recorder.isRecording? Icons.stop:Icons.mic,size:80),
onPressed: () async{
if(recorder.isRecording){
await stop();
}else{
await record();
}
setState(() {
});
},)
),
]);
});
}
}
New code. not working perfectly due to lost of SetState()
showDialogVoiceRecording(BuildContext context, {String myText = 'Record and speak !'}) {
showModalBottomSheet<void>(
context: context,
isDismissible: false,
builder: (BuildContext context) {
context = context;
return Column(
children: [
StreamBuilder<RecordingDisposition>(
stream: recorder.onProgress,
builder: (context,snapshot){
final duration=snapshot.hasData?
snapshot.data.duration: Duration.zero;
String twoDigits(int n) => n.toString().padLeft(60);
final twoDigitsMinutes= twoDigits(duration.inMinutes.remainder(60));
final twoDigitsSeconds = twoDigits(duration.inSeconds.remainder(60));
return Text('$twoDigitsMinutes:$twoDigitsSeconds',
style: TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
),);
},
),
Center(child:
ElevatedButton(child:Icon(recorder.isRecording? Icons.stop:Icons.mic,size:80),
onPressed: () async{
if(recorder.isRecording){
await stop();
}else{
await record();
}
/* setState(() {
});*/
},)
),
],
);
}
);
}

Flutter display pop-up once based on value

I am trying to display a pop-up dialog like (+20) based on the value coming from server. I have a variable name ageRestriction which is getting the value from server. And I want to display pop-up based on the value of this variable. (Eg: If ageRestriction has the value of "18" the pop-up will be displayed only once then later on if the value will change to "20" the pop-up will be displayed once again, so these values will be stored somewhere and the pop-up will not be displayed if the same value comes again)
I have tried to do it with shared preferences unfortunately it did not work:
// initializing shared pref
#override
void initState() async{
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
widget.ageRestriction = await prefs.getString("ageRestriction");
await prefs.setString("ageRestriction", widget.ageRestriction);
}
// displaying pop-up dialog
widget.ageRestriction.toString() == widget.ageRestriction ? null :
AwesomeDialog(
popContext: false,
context: context,
dialogType: DialogType.WARNING,
animType: AnimType.TOPSLIDE,
title: "${widget.ageRestriction} Warning",
desc: "We only sell this product to persons who are ${widget.ageRestriction} years old. Age will be verified upon delivery.",
btnOkText: "Continue",
btnOkOnPress: () async{
widget.onPressed();
Navigator.of(context).pop();
},
btnCancelOnPress: () {
Navigator.of(context).pop();
},
btnCancelText: S.current.cancel,
btnOkColor: Theme.of(context).accentColor,
btnCancelColor: Color(0xFF084457).withOpacity(0.9),
).show();
ageRestrict();
}
Store widget.ageRestriction in a variable in the state of the widget and then check in didUpdateWidget whether the value changed and if it did show the popup
Simple Demo App to show age restriction :
Future<String> getAgeRestrictionFromServer() async {
// write your own logic
await Future.delayed(Duration(seconds: 2));
return "22";
}
enum RequestState { LOADING, SUCCESS, ERROR }
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>{
// request status
RequestState requestState = RequestState.LOADING;
// disable multiple clickes
bool retryButtonEnabled = true;
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
getData();
});
}
showAgeRestrictionDialog(String age) {
return showDialog(
context: context,
child: AlertDialog(
title: Text('Age policy changed!'),
content: Text('New Age: $age'),
),
);
}
void changeRequestState(RequestState newRequestState) {
if (mounted) {
setState(() {
requestState = newRequestState;
});
}
}
Future<void> getData() async {
changeRequestState(RequestState.LOADING);
try {
// get age from server
var newAgeRestriction = await getAgeRestrictionFromServer();
// get age stored locally
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
var previousAgeRestriction =
sharedPreferences.getString('ageRestriction');
print('$previousAgeRestriction, $newAgeRestriction');
int.parse(newAgeRestriction);
if (mounted) {
// compare previous and new age
if (previousAgeRestriction != newAgeRestriction) {
// save new age
await sharedPreferences.setString(
'ageRestriction', newAgeRestriction);
// show dialog because age changed
showAgeRestrictionDialog(newAgeRestriction);
}
}
retryButtonEnabled = true;
changeRequestState(RequestState.SUCCESS);
} catch (e) {
print(e);
retryButtonEnabled = true;
changeRequestState(RequestState.ERROR);
}
}
#override
Widget build(BuildContext context) {
var child;
if (requestState == RequestState.LOADING) {
child = Center(
child: CircularProgressIndicator(),
);
} else if (requestState == RequestState.SUCCESS) {
child = Center(
child: Text('Got data from server!'),
);
} else {
child = Center(
child: FlatButton(
color: Colors.blue,
onPressed: retryButtonEnabled
? () {
setState(() {
retryButtonEnabled = false;
});
getData();
}
: null,
child: Text('Retry')),
);
}
return Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: child,
);
}
}
More info about state management here

How to check user is logged in or not with phone authentication using firebase in flutter?

Here, I'm authenticated through the phone number with OTP code using firebase but
after login succeeded, it navigated through home page but when I click on back
it drags me login Screen.
here, the code I have tried, but it doesn't work
#override
void initState() {
super.initState();
isSignedIn();
}
void isSignedIn() async {
this.setState(() {
isLoading = true;
});
firebaseAuth.currentUser().then((user){
if(user !=null){
Navigator.of(context).pushReplacementNamed('/homepage');
}else{
verifyPhone();
}
});
this.setState(() {
isLoading = false;
});
}
Method for getting OTP code
Future<void> verifyPhone()async{
final PhoneCodeAutoRetrievalTimeout autoRetrieval=(String verId){
this.verificationId=verId;
};
final PhoneCodeSent smsCodeSent=(String verId, [int forceCodeResend]){
this.verificationId=verId;
smsCodeDialog(context).then((value){
print("Signed in");
});
};
final PhoneVerificationCompleted verificationCompleted = (AuthCredential credential) {
print("verified");
};
final PhoneVerificationFailed verfifailed=(AuthException exception){
print("${exception.message}");
};
await firebaseAuth.verifyPhoneNumber(
phoneNumber: this.phoneNo,
codeAutoRetrievalTimeout: autoRetrieval,
codeSent: smsCodeSent,
timeout: const Duration(seconds: 10),
verificationCompleted: verificationCompleted,
verificationFailed: verfifailed
);
}
here the dialog box for sign in with OTP code
Future<bool> smsCodeDialog(BuildContext context){
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
return new AlertDialog(
title: Text('Enter sms Code'),
content: TextField(
onChanged: (value){
this.smsCode=value;
},
),
contentPadding: const EdgeInsets.all(10.0),
actions: <Widget>[
new FlatButton(
child: Text("Done"),
onPressed: (){
firebaseAuth.currentUser().then((user){
if(user !=null){
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed('/homepage');
}else{
Navigator.of(context).pop();
signIn();
}
});
},
)
],
);
}
);
}
method for Sign in with phone number
signIn()async{
AuthCredential credential= PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode
);
await firebaseAuth.signInWithCredential(credential).then((user){
Navigator.of(context).pushReplacementNamed('/homepage');
print('signed in with phone number successful: user -> $user');
}).catchError((onError){
print(onError);
});
}
`
Welcome Shruti Ramnandan Sharma in Stackoverflow and Flutter dev.
Your code seems to working fine with me, I coded for you a one page dart that can test you the whole code with fixing your problem with going back to Login or VerifyPhone page.
Note: I changed your order of code in verifyPhone() method.
And Changed Navigator.of(context).pushReplacementNamed('/homepage'); to
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => HomeRoute()));
The whole code here
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
void main() => runApp(VerifyPhoneRoute());
class VerifyPhoneRoute extends StatefulWidget {
#override
_VerifyPhoneRouteState createState() {
return _VerifyPhoneRouteState();
}
}
class _VerifyPhoneRouteState extends State<VerifyPhoneRoute> {
bool isLoading = false;
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
String verificationId;
String phoneNo = "Your number here";
String smsCode;
#override
void initState() {
super.initState();
isSignedIn();
}
void isSignedIn() async {
this.setState(() {
isLoading = true;
});
firebaseAuth.currentUser().then((user) {
if (user != null) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeRoute()),
);
} else {
verifyPhone();
}
});
this.setState(() {
isLoading = false;
});
}
Future<void> verifyPhone() async {
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential credential) {
print("verified");
};
final PhoneVerificationFailed verifyFailed = (AuthException exception) {
print("${exception.message}");
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResend]) {
this.verificationId = verId;
smsCodeDialog(context).then((value) {
print("Signed in");
});
};
final PhoneCodeAutoRetrievalTimeout autoRetrieval = (String verId) {
this.verificationId = verId;
};
await firebaseAuth.verifyPhoneNumber(
phoneNumber: this.phoneNo,
codeAutoRetrievalTimeout: autoRetrieval,
codeSent: smsCodeSent,
timeout: const Duration(seconds: 10),
verificationCompleted: verificationCompleted,
verificationFailed: verifyFailed);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Inapp Plugin by dooboolab'),
),
body: Center(
child: RaisedButton(
child: Text("Verify"),
onPressed: () {
verifyPhone();
}),
),
),
);
}
Future<bool> smsCodeDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Enter sms Code'),
content: TextField(
onChanged: (value) {
this.smsCode = value;
},
),
contentPadding: const EdgeInsets.all(10.0),
actions: <Widget>[
new FlatButton(
child: Text("Done"),
onPressed: () {
firebaseAuth.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeRoute()),
);
} else {
Navigator.of(context).pop();
signIn();
}
});
},
)
],
);
});
}
signIn() async {
AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId, smsCode: smsCode);
await firebaseAuth.signInWithCredential(credential).then((user) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeRoute()),
);
print('signed in with phone number successful: user -> $user');
}).catchError((onError) {
print(onError);
});
}
}
class HomeRoute extends StatefulWidget {
#override
_HomeRouteState createState() {
return _HomeRouteState();
}
}
class _HomeRouteState extends State<HomeRoute> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Inapp Plugin by dooboolab'),
),
body: Center(
child: Text("Welcome There."),
),
),
);
}
}
This code works fine with me. So if there's any problem happened with you again, don't be hesitate to comment on this answer. And if this answered your question and solve your problem, please make it as answer.
Use method below by pass verificationID come from API firebase when code sent and code enter by user, so if method return FirebaseUser the code is correct if return null the code enter by user is not correct
Future<FirebaseUser> getUserFromCodePhone(String code, String verificationID) async {
FirebaseAuth mAuth = FirebaseAuth.instance;
AuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(
verificationId: verificationID, smsCode: code);
try {
AuthResult result = await mAuth.signInWithCredential(phoneAuthCredential);
FirebaseUser currentUser = await mAuth.currentUser();
if (currentUser != null && result.user.uid == currentUser.uid) {
return currentUser;
} else {
return null;
}
} on PlatformException catch (_) {}
return null;
}
How it work ? : when use signInWithCredential method if code passed to
AuthCredential is false then the method will throw PlatformException
so out from try block and return null