Please read properly and then comment, hope this help full for software community.
I unable to auto detect firebase phone auth sms, when i test in android it's work without doing anything, but when i tried it in flutter sms not detected automatically, require manually type sms code, I did not find any documentation to do this process automatically.
I wan't to know how to detect automatically that sms code from Firebase phone auth
Future<void> verifyPhone() async {
starTimer();
phoneNumber = contryCode+mobile_controller.text;
final PhoneCodeAutoRetrievalTimeout autoReRetrive = (String verId) {
this.verificationId = verId;
};
final PhoneCodeSent smsCodesent = (String verId, [int forceCodeResent]) {
this.verificationId = verId;
pageController.animateToPage(2, duration: Duration(milliseconds: 300), curve: Curves.easeInBack);
/*smsCodeDialog(context).then((value) {
print("VERIFY PHONE SIGN IN");
});
*/
};
final PhoneVerificationCompleted phoneVerificationCompleted =
(PhoneAuthCredential creditional) {
print("VERIFY PHONE AUTH SUCESS");
this.mPhoneCreditional=creditional;
//_controller.animateToPage(3, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
loginIn(creditional);
};
final PhoneVerificationFailed phoneVerificationFailed =
(FirebaseAuthException excepton) {
Fluttertoast.showToast(
msg: excepton.message,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0
);
print("VERIFY PHONE AUTH FAILED " + excepton.message);
};
FirebaseAuth mAuth=await FirebaseAuth.instance;
mAuth.setLanguageCode("en");
await mAuth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(seconds: 45),
verificationCompleted: phoneVerificationCompleted,
verificationFailed: phoneVerificationFailed,
codeSent: smsCodesent,
codeAutoRetrievalTimeout: autoReRetrive);
}
Make sure you have added the debugging, release, sha1, sha256 keys to your firebase project.
Make sure you have enabled appcheck enabled on your firebase project.
Make sure you have enabled the Android Device Verification api on your google cloud console.
The below code is slightly modified from the example code given in firebase auth plugin, for my projects the detecion is working well.
Some devices may fail to detect the otp, but most of the devices will login automatically.
The code which detects the sms is highlighted in sendOtpFn()
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:projdebug/Widgets/Widgets.dart';
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => LoginPageState();
}
class LoginPageState extends State<LoginPage> {
final FirebaseAuth _auth = FirebaseAuth.instance;
final _phoneNumberController = TextEditingController();
final _smsController = TextEditingController();
String _message = '';
late String _verificationId;
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
setState(() {});
}),
body: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
alignment: Alignment.center,
child: const Text(
'Test sign in with phone number',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
TextFormField(
controller: _phoneNumberController,
decoration: const InputDecoration(
labelText: 'Phone number (+x xxx-xxx-xxxx)',
),
validator: (String? value) {
if (value!.isEmpty) {
return 'Phone number (+x xxx-xxx-xxxx)';
}
return null;
},
),
Container(
padding: const EdgeInsets.symmetric(vertical: 16),
alignment: Alignment.center,
child: Button(
icon: Icons.send,
buttonColor: Colors.deepOrangeAccent[700]!,
text: 'Send otp',
onPressed: sendOtpFn,
),
),
TextField(
controller: _smsController,
decoration:
const InputDecoration(labelText: 'Verification code'),
),
Container(
padding: const EdgeInsets.only(top: 16),
alignment: Alignment.center,
child: Button(
icon: Icons.done,
buttonColor: Colors.deepOrangeAccent[400]!,
onPressed: manualSignInFn,
text: 'Confirm otp',
),
),
CrossFade(
show: _auth.currentUser?.uid != null,
child: Button(
icon: Icons.logout,
buttonColor: Colors.deepOrangeAccent[400]!,
onPressed: () async{
await _auth.signOut();
setState(() {});
},
text: 'LogOut',
),
),
Visibility(
visible: _message.isNotEmpty,
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
_message,
style: const TextStyle(color: Colors.red),
),
),
)
],
),
),
),
);
}
// Example code of how to verify phone number
Future<void> sendOtpFn() async {
setState(() {
_message = '';
});
///This is the code snippet which [detects the code from sms automatically]
PhoneVerificationCompleted verificationCompleted =
(PhoneAuthCredential phoneAuthCredential) async {
setState(() {
_message = phoneAuthCredential.smsCode ?? "";
_smsController.text = _message;
});
await _auth.signInWithCredential(phoneAuthCredential);
Widgets.showToast(
'Phone number automatically verified and user signed in: $phoneAuthCredential');
setState(() {});
};
PhoneVerificationFailed verificationFailed =
(FirebaseAuthException authException) {
setState(() {
_message =
'Phone number verification failed. Code: ${authException.code}. '
'Message: ${authException.message}';
});
};
PhoneCodeSent codeSent =
(String verificationId, [int? forceResendingToken]) async {
Widgets.showToast('Please check your phone for the verification code.');
_verificationId = verificationId;
};
PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
_verificationId = verificationId;
};
try {
await _auth.verifyPhoneNumber(
phoneNumber: _phoneNumberController.text,
timeout: const Duration(seconds: 120),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
} catch (e) {
Widgets.showToast('Time out to Verify Phone Number: $e');
}
}
///Code which will be executed only when a confirm otp button is pressed, ie both phoneNumber & otp is typed by the user manually.
Future<void> manualSignInFn() async {
try {
final PhoneAuthCredential credential = PhoneAuthProvider.credential(
verificationId: _verificationId,
smsCode: _smsController.text,
);
final User user = (await _auth.signInWithCredential(credential)).user!;
Widgets.showToast('Successfully signed in UID: ${user.uid}');
} catch (e) {
print(e);
Widgets.showToast('Failed to sign in');
}
}
}
Related
I'm posting this even though I already solved the issue, because I spent more than an hour trying to figure out what was causing it, and might help someone else.
I have a simple application that connects to a server through a TCP socket. It works fine inside the debugger with the device emulator, but when I deploy it on a real device it fails to connect immediately.
Further investigations led me to finding out that the socket was actually throwing the following exception:
SocketException: Connection failed (OS Error: Operation not permitted, errno = 1), address = 192.168.1.46, port = 40001
Code sample
Connect button opens a socket on _host:_port
Send Radar Distance button sends a message formatted like {"distance": _distance, "angle": _angle} on the socket.
Status and Message fields show info about the socket status and eventually useful infos.
main.dart
import 'package:flutter/material.dart';
import 'views/view_test.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const ViewTest(),
);
}
}
views/view_test.dart
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
const List<String> materialTypes = <String>['PLASTIC', 'GLASS'];
class ViewTest extends StatefulWidget {
const ViewTest({Key? key}) : super(key: key);
#override
State<ViewTest> createState() => _ViewTestState();
}
class _ViewTestState extends State<ViewTest> {
String _distance = "";
String _angle = "";
String _status = "";
String _message = "";
Socket? _socket;
final String _host = "192.168.1.46";
final int _port = 4001;
Future<void> _sendMessage(String message) async {
print("Sent message $message");
_socket!.write(message);
await Future.delayed(const Duration(seconds: 1));
}
void _connect(String ip, int port) async {
Socket? sock;
try {
sock =
await Socket.connect(ip, port, timeout: const Duration(seconds: 3));
_socket = sock;
setState(() {
_status = "Connected to $ip:$port.";
_message = "";
});
// listen for responses from the server
_socket!.listen(
// handle data from the server
(Uint8List data) {
final serverResponse = String.fromCharCodes(data);
setState(() {
_message = serverResponse;
});
print(serverResponse);
},
// handle errors
onError: (error) {
setState(() {
_status = "Disconnected.";
_message = "Error: $error";
});
print("Error: $error");
_socket!.destroy();
_socket = null;
},
// handle server ending connection
onDone: () {
setState(() {
_status = "Disconnected.";
_message = 'Server left.';
});
print('Server left.');
_socket!.destroy();
_socket = null;
},
);
} catch (e) {
setState(() {
_status = "Connection failed.";
_message = e.toString();
});
print("Error: ${e.toString()}");
}
}
void _disconnect() {
if (_socket != null) _socket!.destroy();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Center(child: Text("Radar Test")),
),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
setState(() {
_status = "";
_message = "";
});
_disconnect();
_connect(_host, _port);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_socket == null ? 'Connect' : 'Reconnect',
style: const TextStyle(fontSize: 22.0),
),
),
),
const SizedBox(height: 50.0),
TextField(
onChanged: (text) {
_distance = text;
},
keyboardType:
const TextInputType.numberWithOptions(decimal: false),
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(
RegExp(r'[0-9]+'), // this regex allows only decimal numbers
)
],
decoration: const InputDecoration(
hintText: '100',
border: UnderlineInputBorder(),
labelText: 'Distance',
),
),
TextField(
onChanged: (text) {
_angle = text;
},
keyboardType:
const TextInputType.numberWithOptions(decimal: false),
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(
RegExp(r'[0-9]+'), // this regex allows only decimal numbers
)
],
decoration: const InputDecoration(
hintText: '90',
border: UnderlineInputBorder(),
labelText: 'Angle',
),
),
const SizedBox(height: 50.0),
Text("Status: $_status"),
Text("Message: $_message"),
],
),
),
),
floatingActionButton: ElevatedButton(
onPressed: _socket == null
? null
: () {
// test
_sendMessage(
'{"distance": ${_distance.isEmpty ? 100 : int.parse(_distance)}, "angle": ${_angle.isEmpty ? 90 : int.parse(_angle)}}');
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Send Radar Distance',
style: TextStyle(fontSize: 22.0),
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
Turns out I had to give the application Internet permissions, as stated in Flutter docs about Networking. So I added the following line to AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
NB: AndroidManifest.xml manifest is located in the following location:
android > app > src > main > AndroidManifest.xml
I am developing a mobile app for a Germany client. I am based in Sri Lanka. The OTP works well in my country but, not for other countries. I tried to enter the German phone number from Sri Lanka, it works fine. He gets the OTP code when I am trying to send OTP to his phone from Sri Lanka. But, when he tries in Germany, it does not work.
Then after that, I connected to a German VPN and tried, I am also failed to get OTP. Then, I connected with other countries' VPN and tried. It does not work. It only works when I am connected to Sri Lankan's network.
I am confused, Do I need to turn on anything in Firebase to send OTP globally. Please try to provide your answer to me.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(App());
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("Login"),),
body: const MyApp(),
),
);
}
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var otpController = TextEditingController();
var numController = TextEditingController();
FirebaseAuth auth = FirebaseAuth.instance;
String verificationId = "";
void signInWithPhoneAuthCredential(
PhoneAuthCredential phoneAuthCredential) async {
try {
final authCredential =
await auth.signInWithCredential(phoneAuthCredential);
if (authCredential.user != null) {
print("Welcome");
Text("Welcome");
}
} on FirebaseAuthException catch (e) {
print("catch");
}
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(10),
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Phone Number',
hintText: 'Enter valid number'
),
controller: numController,
),
),
Padding(
padding: EdgeInsets.all(10),
child: TextField(
obscureText: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter valid password'
),
controller: otpController,
),
),
TextButton(onPressed: () {
fetchotp();
}, child: const Text("Fetch OTP"),),
TextButton(onPressed: () {
verify();
}, child: const Text("Send"))
],
),
);
}
Future<void> verify() async {
PhoneAuthCredential phoneAuthCredential =
PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: otpController.text);
signInWithPhoneAuthCredential(phoneAuthCredential);
}
Future<void> fetchotp() async {
await auth.verifyPhoneNumber(
phoneNumber: '+94769008291',
verificationCompleted: (PhoneAuthCredential credential) async {
await auth.signInWithCredential(credential);
},
verificationFailed: (FirebaseAuthException e) {
if (e.code == 'invalid-phone-number') {
print('The provided phone number is not valid.');
}
},
codeSent: (String verificationId, int? resendToken) async {
this.verificationId = verificationId;
},
codeAutoRetrievalTimeout: (String verificationId) {
},
);
}
}
I suggest that you reach out to the Firebase team via their Support Contact page. This should work in Germany given that it is what the documentation suggests.
This question already has answers here:
Undefined class 'FirebaseUser'
(6 answers)
Closed 1 year ago.
I'm beginner to this flutter. Currently i am trying to do email authentication for sign-up. I already tried few solution from website/github/youtube, but it turns to the same error which is on FirebaseUser. Here i attach my code segment.
class _RegisterEmailSectionState extends State<_RegisterEmailSection> {
void _register() async {
final FirebaseUser user = (await
_auth.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
)
).user;
if (user != null) {
setState(() {
_success = true;
_userEmail = user.email;
});
} else {
setState(() {
_success = true;
});
}
}
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _success;
String _userEmail;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
controller: _emailController,
decoration: const InputDecoration(labelText: 'Email'),
validator: (String value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(labelText:
'Password'),
validator: (String value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Container(
padding: const EdgeInsets.symmetric(vertical: 16.0),
alignment: Alignment.center,
child: RaisedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
_register();
}
},
child: const Text('Submit'),
),
),
Container(
alignment: Alignment.center,
child: Text(_success == null
? ''
: (_success
? 'Successfully registered ' + _userEmail
: 'Registration failed')),
)
],
),
),
);
}
}
Is there something that I miss out? I don't know why FirebaseUser error in my code. I assume that it is error due to different version of firebase. I found someone from the flutter community and they said try to changed it to be Authresult but the same error come out.
Can anyone help me on this issue? Thank you in advance!
Change
final FirebaseUser user = ...
to
final User user = ...
Here is the class you trying to get here. It has the type of User.
I'm trying to do Firebase authentication and Google sign in using Flutter but I'm getting this error message:
'package:flutter/src/painting/_network_image_io.dart': Failed assertion: line 23 pos 14: 'url != null': is not true.
I can't understand what is wrong, can you help me?
login.dart code snippet
-> calls signInWithGoogle() method from sign_in.dart and return FirstScreen
Widget _signInButton() {
return OutlineButton(
splashColor: Colors.grey,
onPressed: () {
signInWithGoogle().whenComplete(() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return FirstScreen();
},
),
);
});
},
sign_in.dart code snippet:
-> Here I authenticate the user and try to get logged user name, email and image
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
String name;
String email;
String imageUrl;
Future<String> signInWithGoogle() async {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
// Checking if email and name is null
assert(user.email != null);
assert(user.displayName != null);
assert(user.photoUrl != null);
name = user.displayName;
email = user.email;
imageUrl = user.photoUrl;
// Only taking the first part of the name, i.e., First Name
if (name.contains(" ")) {
name = name.substring(0, name.indexOf(" "));
}
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
log('data: $user');
return 'signInWithGoogle succeeded: $user';
}
void signOutGoogle() async {
await googleSignIn.signOut();
print("User Sign Out");
}
FirstScreen code snippet:
-> Here I try to show user data (name, email and image) based on what I got on previously code
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(
imageUrl,
),
radius: 60,
backgroundColor: Colors.transparent,
),
SizedBox(height: 40),
Text(
'NAME',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.black54),
),
Text(
name,
style: TextStyle(
fontSize: 25,
color: Colors.deepPurple,
fontWeight: FontWeight.bold),
),
SizedBox(height: 20),
Text(
'EMAIL',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.black54),
),
Text(
email,
style: TextStyle(
fontSize: 25,
color: Colors.deepPurple,
fontWeight: FontWeight.bold),
),
SizedBox(height: 40)
EDIT: aditional info -> console log shows "Unhandled Exception: PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null)"
After wasting a day to find my way around the Google SignIn package with various fixes and troubleshooting, I realised there is web package Google has made for web sign in which uses the OAuthClient verification and configuration as per their changing policy with Google Sign in.
Sadly it isn't straight forward and quick as it was before. But following the steps mentioned in the pub.dev.
https://pub.dev/packages/google_sign_in_web
Attaching my main.dart working POC snippets here.
import 'dart:async';
import 'dart:convert' show json;
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:http/http.dart' as http;
GoogleSignIn _googleSignIn = GoogleSignIn(
// Optional clientId
// clientId: '479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com',
scopes: <String>[
'email',
'https://www.googleapis.com/auth/contacts.readonly',
],
);
void main() {
runApp(
const MaterialApp(
title: 'Google Sign In',
home: SignInDemo(),
),
);
}
class SignInDemo extends StatefulWidget {
const SignInDemo({Key? key}) : super(key: key);
#override
State createState() => SignInDemoState();
}
class SignInDemoState extends State<SignInDemo> {
GoogleSignInAccount? _currentUser;
String _contactText = '';
#override
void initState() {
super.initState();
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount? account) {
setState(() {
_currentUser = account;
});
if (_currentUser != null) {
_handleGetContact(_currentUser!);
}
});
_googleSignIn.signInSilently();
}
Future<void> _handleGetContact(GoogleSignInAccount user) async {
setState(() {
_contactText = 'Loading contact info...';
});
final http.Response response = await http.get(
Uri.parse('https://people.googleapis.com/v1/people/me/connections'
'?requestMask.includeField=person.names'),
headers: await user.authHeaders,
);
if (response.statusCode != 200) {
setState(() {
_contactText = 'People API gave a ${response.statusCode} '
'response. Check logs for details.';
});
print('People API ${response.statusCode} response: ${response.body}');
return;
}
final Map<String, dynamic> data =
json.decode(response.body) as Map<String, dynamic>;
final String? namedContact = _pickFirstNamedContact(data);
setState(() {
if (namedContact != null) {
_contactText = 'I see you know $namedContact!';
} else {
_contactText = 'No contacts to display.';
}
});
}
String? _pickFirstNamedContact(Map<String, dynamic> data) {
final List<dynamic>? connections = data['connections'] as List<dynamic>?;
final Map<String, dynamic>? contact = connections?.firstWhere(
(dynamic contact) => (contact as Map<Object?, dynamic>)['names'] != null,
orElse: () => null,
) as Map<String, dynamic>?;
if (contact != null) {
final List<dynamic> names = contact['names'] as List<dynamic>;
final Map<String, dynamic>? name = names.firstWhere(
(dynamic name) =>
(name as Map<Object?, dynamic>)['displayName'] != null,
orElse: () => null,
) as Map<String, dynamic>?;
if (name != null) {
return name['displayName'] as String?;
}
}
return null;
}
Future<void> _handleSignIn() async {
try {
await _googleSignIn.signIn();
} catch (error) {
print(error);
}
}
Future<void> _handleSignOut() => _googleSignIn.disconnect();
Widget _buildBody() {
final GoogleSignInAccount? user = _currentUser;
if (user != null) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
ListTile(
leading: GoogleUserCircleAvatar(
identity: user,
),
title: Text(user.displayName ?? ''),
subtitle: Text(user.email),
),
const Text('Signed in successfully.'),
Text(_contactText),
ElevatedButton(
onPressed: _handleSignOut,
child: const Text('SIGN OUT'),
),
ElevatedButton(
child: const Text('REFRESH'),
onPressed: () => _handleGetContact(user),
),
],
);
} else {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
const Text('You are not currently signed in.'),
ElevatedButton(
onPressed: _handleSignIn,
child: const Text('SIGN IN'),
),
],
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Google Sign In'),
),
body: ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: _buildBody(),
));
}
}
I found that the error I was getting was due to the lack of some OAuth Credentials settings. I found the solution here
Good day, I am creating an application that requires a user to log in using Google. I have gone through a tutorial (using Firebase) and have successfully managed to get the login system to work.
My issue: The user can bypass the login by clicking on the "Sign in with Google" then clicking on the side of the screen to exit the log in.
Assumption: In the login-page.dart file: My _signInButton() function onPressed() is not correct in asserting that a user is logged in.
Help needed: Rewriting/reworking my current code to send the user back to the home screen if they do not sign in. Only allow user to proceed if user has been authenticated.
login-page.dart file:
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(image: AssetImage("images/localhourlogo.png"), height: 80.0),
SizedBox(height: 50),
_signInButton(),
],
),
),
),
);
}
Widget _signInButton() {
return OutlineButton(
splashColor: Colors.grey,
onPressed: () { //****problem area?*****
signInWithGoogle().whenComplete(() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return MyTabs();
},
),
);
}
);
},
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)),
highlightElevation: 0,
borderSide: BorderSide(color: Colors.grey),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(image: AssetImage("images/google_logo.png"), height: 35.0),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
'Sign in with Google',
style: TextStyle(
fontSize: 20,
color: Colors.grey,
),
),
)
],
),
),
);
}
}
sign-in.dart file:
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
Future<String> signInWithGoogle() async {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
return 'signInWithGoogle succeeded: $user';
}
void signOutGoogle(context) async{
await googleSignIn.signOut();
Navigator.popUntil(context, ModalRoute.withName("/"));
print("User Sign Out");
}
Extra information:
Here is the tutorial I followed: https://medium.com/flutter-community/flutter-implementing-google-sign-in-71888bca24ed
Thank you very much for any and all help.
You need to refactor your code a bit. Returning an string at signInWithGoogle() is a bad manner to check if the login was successful or not. But, for now I will let the code equal and only change it a bit to fix the error. In your actual code, you always return a string saying 'succeeded', and you are not checking if something went bad. For that reason, I added a try catch bloc.
try {
GoogleSignInAccount user = await googleSignIn.signIn();
//The rest of your code
return 'Succeed';
} catch (error) {
return 'Error';
}
And at login-page.dart I change the onPressed() at _signInButton():
onPressed: () async{
String result = await signInWithtGoogle();
if(result == 'Succeed'){
//Send the user to the tab page like you did
}else{
//Reload login page
}
}