I'm trying to implement google sign in/ sign out, but my sign out's not working. I get an error signOut() was called on null. When I print out user after the user signs in, I do get all the proper information, but when I print out in my signout function, it says it's null. Custom firebase user does work. Here's my auth.dart file:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
abstract class BaseAuth {
Future<String> signIn(String email, String password);
Future<String> signUp(String email, String password);
Future<FirebaseUser> getCurrentUser();
Future<void> sendEmailVerification();
Future<void> signOut();
Future<bool> isEmailVerified();
Future<String> signInWithGoogle();
}
class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
FirebaseUser user;
Future<String> signIn(String email, String password) async {
user = (await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password)).user;
return user.email;
}
Future<String> signUp(String email, String password) async {
FirebaseUser user = (await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password)).user;
return user.uid;
}
Future<FirebaseUser> getCurrentUser() async {
user = await _firebaseAuth.currentUser();
return user;
}
signOut() async {
//print("signed in user: ${authService.user}");
await _firebaseAuth.signOut();
}
Future<void> sendEmailVerification() async {
FirebaseUser user = await _firebaseAuth.currentUser();
user.sendEmailVerification();
}
Future<bool> isEmailVerified() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user.isEmailVerified;
}
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,
);
user = (await _firebaseAuth.signInWithCredential(credential)).user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _firebaseAuth.currentUser();
assert(user.uid == currentUser.uid);
return 'signInWithGoogle succeeded: $user';
}
}
Something else strange is if I boot up the app and am already logged in (as google), i hit signout and it appears to work. Nothing in my console, but it goes back to login screen. Then if I log back in as google and signout, error starts happening. Another weird thing is if I happened to already be logged in and I get it to log out after one click, without changing anything if I restart my app in android studio it takes me back to the screen where I'm supposedly already logged in. This is only happening on Google Sign Out (not firebase log out).
Any idea what I might be doing wrong? Thanks
pubspec.yaml
dependencies:
firebase_auth: ^0.14.0+5
firebase_database: ^3.0.7
google_sign_in: ^4.0.7
firebase_storage:
image_picker:
cloud_firestore:
shared_preferences:
fluttertoast:
cached_network_image:
intl:
one page where I use it: (I have many, but implemented similar to this and it doesn't work in any of my pages)
import 'package:flutter/material.dart';
import 'package:pet_helper/chat.dart';
import 'package:pet_helper/lost_and_found.dart';
import 'package:pet_helper/pet_adoption.dart';
import 'authentication.dart';
class HomePage extends StatefulWidget {
HomePage({Key key, this.auth, this.userId, this.onSignedOut})
: super(key: key);
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
#override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
int _currentIndex = 0;
#override
void initState() {
super.initState();
}
final List<Widget> _children = [
new LostAndFoundPage(),
new PetAdoptionPage(),
new ChatPage(),
];
_signOut() async {
try {
await widget.auth.signOut();
widget.onSignedOut();
} catch (e) {
print(e);
}
}
onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
automaticallyImplyLeading:false,
title: new Text('Pet Helper'),
actions: <Widget>[
new FlatButton(
child: new Text('Logout',
style: new TextStyle(fontSize: 17.0, color: Colors.white)),
onPressed: _signOut)
],
),
body: _children[_currentIndex], // new
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped, // new
currentIndex: _currentIndex, // new
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Lost & Found'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.pets),
title: Text('Pet Adoption'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text('Chat'))
],
));
}
}
and for completion, here's my login page:
import 'package:flutter/material.dart';
import 'package:pet_helper/home_page.dart';
import 'authentication.dart';
class LoginSignUpPage extends StatefulWidget {
LoginSignUpPage({this.auth, this.onSignedIn});
final BaseAuth auth;
final VoidCallback onSignedIn;
#override
State<StatefulWidget> createState() => new _LoginSignUpPageState();
}
enum FormMode { LOGIN, SIGNUP }
class _LoginSignUpPageState extends State<LoginSignUpPage> {
final _formKey = new GlobalKey<FormState>();
String _email;
String _password;
String _errorMessage;
// Initial form is login form
FormMode _formMode = FormMode.LOGIN;
bool _isIos;
bool _isLoading;
// Check if form is valid before perform login or signup
bool _validateAndSave() {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
// Perform login or signup
void _validateAndSubmit() async {
setState(() {
_errorMessage = "";
_isLoading = true;
});
if (_validateAndSave()) {
String userId = "";
try {
if (_formMode == FormMode.LOGIN) {
userId = await widget.auth.signIn(_email, _password);
print('Signed in: $userId');
} else {
userId = await widget.auth.signUp(_email, _password);
widget.auth.sendEmailVerification();
_showVerifyEmailSentDialog();
print('Signed up user: $userId');
}
setState(() {
_isLoading = false;
});
if (userId.length > 0 && userId != null && _formMode == FormMode.LOGIN) {
widget.onSignedIn();
}
} catch (e) {
print('Error: $e');
setState(() {
_isLoading = false;
if (_isIos) {
_errorMessage = e.details;
} else
_errorMessage = 'Incorrect user or password';
});
}
}
}
#override
void initState() {
_errorMessage = "";
_isLoading = false;
super.initState();
}
void _changeFormToSignUp() {
_formKey.currentState.reset();
_errorMessage = "";
setState(() {
_formMode = FormMode.SIGNUP;
});
}
void _changeFormToLogin() {
_formKey.currentState.reset();
_errorMessage = "";
setState(() {
_formMode = FormMode.LOGIN;
});
}
#override
Widget build(BuildContext context) {
_isIos = Theme.of(context).platform == TargetPlatform.iOS;
return new Scaffold(
appBar: new AppBar(
title: new Text('Pet Helper'),
),
body: Stack(
children: <Widget>[
_showBody(),
_showCircularProgress(),
],
));
}
Widget _showCircularProgress(){
if (_isLoading) {
return Center(child: CircularProgressIndicator());
} return Container(height: 0.0, width: 0.0,);
}
void _showVerifyEmailSentDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Verify your account"),
content: new Text("Link to verify account has been sent to your email"),
actions: <Widget>[
new FlatButton(
child: new Text("Dismiss"),
onPressed: () {
_changeFormToLogin();
Navigator.of(context).pop();
},
),
],
);
},
);
}
Widget _showBody(){
return new Container(
padding: EdgeInsets.all(16.0),
child: new Form(
key: _formKey,
child: new ListView(
shrinkWrap: true,
children: <Widget>[
_showLogo(),
_showEmailInput(),
_showPasswordInput(),
_showErrorMessage(),
_showPrimaryButton(),
_showSecondaryButton(),
_googleSignInButton(),
],
),
));
}
Widget _showErrorMessage() {
if (_errorMessage.length > 0 && _errorMessage != null) {
return new Text(
_errorMessage,
style: TextStyle(
fontSize: 13.0,
color: Colors.red,
height: 1.0,
fontWeight: FontWeight.w300),
);
} else {
return new Container(
height: 0.0,
);
}
}
Widget _showLogo() {
return new Hero(
tag: 'hero',
child: Padding(
padding: EdgeInsets.fromLTRB(0.0, 30.0, 0.0, 0.0),
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 120.0,
child: Image.asset('assets/babies.png'),
),
),
);
}
Widget _showEmailInput() {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 25.0, 0.0, 0.0),
child: new TextFormField(
maxLines: 1,
keyboardType: TextInputType.emailAddress,
autofocus: false,
decoration: new InputDecoration(
hintText: 'Email',
icon: new Icon(
Icons.mail,
color: Colors.grey,
)),
validator: (String value) {
if (value.isEmpty) {
_isLoading = false;
return 'Email can\'t be empty';
}
else{
return null;
}
},
onSaved: (value) => _email = value.trim(),
),
);
}
Widget _showPasswordInput() {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0),
child: new TextFormField(
maxLines: 1,
obscureText: true,
autofocus: false,
decoration: new InputDecoration(
hintText: 'Password',
icon: new Icon(
Icons.lock,
color: Colors.grey,
)),
validator: (String value) {
if (value.isEmpty) {
_isLoading = false;
return 'Password can\'t be empty';
}
else{
return null;
}
},
onSaved: (value) => _password = value.trim(),
),
);
}
Widget _showSecondaryButton() {
return new FlatButton(
child: _formMode == FormMode.LOGIN
? new Text('Create an account',
style: new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300))
: new Text('Have an account? Sign in',
style:
new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300)),
onPressed: _formMode == FormMode.LOGIN
? _changeFormToSignUp
: _changeFormToLogin,
);
}
Widget _showPrimaryButton() {
return new Padding(
padding: EdgeInsets.fromLTRB(0.0, 35.0, 0.0, 0.0),
child: SizedBox(
height: 40.0,
child: new RaisedButton(
elevation: 5.0,
shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(30.0)),
color: Colors.blue,
child: _formMode == FormMode.LOGIN
? new Text('Login',
style: new TextStyle(fontSize: 20.0, color: Colors.white))
: new Text('Create account',
style: new TextStyle(fontSize: 20.0, color: Colors.white)),
onPressed: _validateAndSubmit,
),
));
}
void submitGoogleLogin() async{
setState(() {
_errorMessage = "";
_isLoading = true;
});
String userId = "";
userId = await widget.auth.signInWithGoogle().whenComplete(() {
widget.onSignedIn();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return new HomePage();
},
),
);
});
print('Signed in: $userId');
}
Widget _googleSignInButton() {
return OutlineButton(
splashColor: Colors.grey,
onPressed: submitGoogleLogin,
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("assets/google_logo.png"), height: 30.0),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
'Sign in with Google',
style: TextStyle(
fontSize: 15,
color: Colors.grey,
),
),
)
],
),
),
);
}
}
Oh and root page changes the state of the user:
import 'package:flutter/material.dart';
import 'login_signup_page.dart';
import 'authentication.dart';
import 'home_page.dart';
class RootPage extends StatefulWidget {
RootPage({this.auth});
final BaseAuth auth;
#override
State<StatefulWidget> createState() => new _RootPageState();
}
enum AuthStatus {
NOT_DETERMINED,
NOT_LOGGED_IN,
LOGGED_IN,
}
class _RootPageState extends State<RootPage> {
AuthStatus authStatus = AuthStatus.NOT_DETERMINED;
String _userId = "";
#override
void initState() {
super.initState();
widget.auth.getCurrentUser().then((user) {
setState(() {
if (user != null) {
_userId = user?.uid;
}
authStatus =
user?.uid == null ? AuthStatus.NOT_LOGGED_IN : AuthStatus.LOGGED_IN;
});
});
}
void _onLoggedIn() {
widget.auth.getCurrentUser().then((user){
setState(() {
_userId = user.uid.toString();
});
});
setState(() {
authStatus = AuthStatus.LOGGED_IN;
});
}
void _onSignedOut() {
setState(() {
authStatus = AuthStatus.NOT_LOGGED_IN;
_userId = "";
});
}
Widget _buildWaitingScreen() {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: CircularProgressIndicator(),
),
);
}
#override
Widget build(BuildContext context) {
switch (authStatus) {
case AuthStatus.NOT_DETERMINED:
return _buildWaitingScreen();
break;
case AuthStatus.NOT_LOGGED_IN:
return new LoginSignUpPage(
auth: widget.auth,
onSignedIn: _onLoggedIn,
);
break;
case AuthStatus.LOGGED_IN:
if (_userId.length > 0 && _userId != null) {
return new HomePage(
userId: _userId,
auth: widget.auth,
onSignedOut: _onSignedOut,
);
} else return _buildWaitingScreen();
break;
default:
return _buildWaitingScreen();
}
}
}
initializing root page
Main.dart
void main() async{
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Pet Helper',
debugShowCheckedModeBanner: false,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new RootPage(auth: new Auth()));
}
}
abstract class BaseAuth {
Future<String> signIn(String email, String password);
Future<String> signUp(String email, String password);
Future<FirebaseUser> getCurrentUser();
Future<void> sendEmailVerification();
Future<void> signOut();
Future<bool> isEmailVerified();
Future<String> signInWithGoogle();
void signOutGoogle();
}
class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
FirebaseUser user;
Future<String> signIn(String email, String password) async {
user = (await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password)).user;
return user.email;
}
Future<String> signUp(String email, String password) async {
FirebaseUser user = (await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password)).user;
return user.uid;
}
Future<FirebaseUser> getCurrentUser() async {
user = await _firebaseAuth.currentUser();
return user;
}
signOut() async {
print("signed in user: $user");
await _firebaseAuth.signOut();
}
Future<void> sendEmailVerification() async {
FirebaseUser user = await _firebaseAuth.currentUser();
user.sendEmailVerification();
}
Future<bool> isEmailVerified() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user.isEmailVerified;
}
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,
);
user = (await _firebaseAuth.signInWithCredential(credential)).user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _firebaseAuth.currentUser();
assert(user.uid == currentUser.uid);
return 'signInWithGoogle succeeded: $user';
}
}
final Auth authService = Auth(); // add this to the bottom outside the class
Using a global variable
So let me explain. Each time you call Auth(), it creates a new instance of that class. So in one instance, you might sign the user in. In another instance, you might sign the user out. The variables will contain different values for the variables. So if you use a different instance for signing users out than you did for signing them in, the user variable will be null, therefore not allowing you to sign them out and printing null.
Solution
The solution is a global variable to access the Auth class once. Ex. var authService = Auth();
Put the variable outside the Auth class so it can be accessed in any class, anywhere
Thanks everyone for you answers and support. I found the problem. When signing in google, widget.onSignedIn redirects me to home page (it's in root page). I was redirecting to homepage in the google sign in button after calling on SignedIn which is how I was losing the scope of the user variable. Thanks everyone!
in my case I was getting the Error:"Null check operator used on null value" whenever I call the signout function through the button which was wraped with GetBuilder.
so the solution was that I had to initiate the controller before I call it like:
GetBuilder<LoginController>(
init: LoginController(),// **I was missing this part**
builder: (controller) {
return Column(...)});
Related
ok, its late and I can't quite figure this out for some reason. I am coming from swift and newer to flutter. my code is below, I can't get my username to register in my json or anything else and I can't figure out how to pull it out later either, any help would be appreciated, I can pull out all the data but I haven't figured out how to assign the displayName or profile picture on a later screen
class WelcomeScreen extends StatefulWidget {
const WelcomeScreen({Key? key}) : super(key: key);
static const String id = 'welcome_screen';
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> with SingleTickerProviderStateMixin{
FirebaseAuth auth = FirebaseAuth.instance;
final _auth = FirebaseAuth.instance;
var _isLoading = false;
late AnimationController controller;
late Animation animation;
void _completeLogin() {
Navigator.pushReplacement<void, void>(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) => Home(),
),
);
}
void _submitAuthForm(
String email,
String username,
String password,
File imageFile,
bool isLogin,
BuildContext ctx,
) async {
UserCredential authResult;
try {
setState(() {
_isLoading = true;
});
if (isLogin) {
authResult = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
print('$username');
} else {
authResult = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
print('$username');
final ref = FirebaseStorage.instance
.ref()
.child('user_image')
.child(authResult.user!.uid + 'jpg');
await ref.putFile(imageFile);
final url = await ref.getDownloadURL();
await FirebaseFirestore.instance
.collection('users')
.doc(authResult.user!.uid)
.set({
'username': username,
'email': email,
'image_url': url,
});
}
} on FirebaseAuthException catch (error) {
var message = 'An error occurred, please check your credentials!';
if (error.message != null) {
message = error.message!;
}
ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(content: Text(message)));
setState(() {
_isLoading = false;
});
}
}
#override
void initState() {
super.initState();
controller =
AnimationController (
duration: Duration(seconds: 1),
vsync: this,
);
animation = ColorTween(begin: Colors.transparent, end: Colors.white.withOpacity(0.5)).animate(controller);
controller.forward();
controller.addListener(() {
setState(() {
});
});
}
#override
Widget build(BuildContext context) {
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
} else {
_completeLogin();
print('1');
}
});
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("images/bar1.jpg"),
fit: BoxFit.fill,
)
),
child: Scaffold(
backgroundColor: Colors.white.withOpacity(0.5),
body: Container(
child: Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Image.asset('assets/images/logo.png'),
AuthForm(
_submitAuthForm,
_isLoading,
),
],
),
),
),
),
),
);
}
}
and here is my auth form
FocusNode myFocusNode = FocusNode();
class AuthForm extends StatefulWidget {
const AuthForm(this.submitFn, this.isLoading, {Key? key}) : super(key: key);
final bool isLoading;
final void Function(
String email,
String userName,
String password,
File image,
bool isLogin,
BuildContext ctx,
) submitFn;
#override
_AuthFormState createState() => _AuthFormState();
}
class _AuthFormState extends State<AuthForm> {
final _formKey = GlobalKey<FormState>();
var _isLogin = true;
String _userEmail = '';
String _userName = '';
String _userPassword = '';
File _userImageFile = File('');
void _pickedImage(File image) {
_userImageFile = image;
}
void _trySubmit() {
final isValid = _formKey.currentState!.validate();
FocusScope.of(context).unfocus();
if (_userImageFile.path.isEmpty && !_isLogin) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please pick an image'),
),
);
return;
}
if (isValid) {
_formKey.currentState!.save();
widget.submitFn(
_userEmail.trim(),
_userName.trim(),
_userPassword.trim(),
_userImageFile,
_isLogin,
context,
);
}
}
#override
Widget build(BuildContext context) {
return Center(
child: Card(
margin: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: AnimatedContainer(
duration: const Duration(milliseconds: 350),
curve: Curves.easeIn,
constraints: BoxConstraints(
minHeight: _isLogin ? 224 : 390,
maxHeight: _isLogin ? 268 : 456,
),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (!_isLogin)
UserImagePicker(_pickedImage),
TextFormField(
cursorColor: Colors.red,
key: const ValueKey('email'),
autocorrect: false,
textCapitalization: TextCapitalization.none,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Email address',
),
validator: (val) {
if (EmailValidator.validate(val!) == false) {
return 'Please enter a valid email address.';
}
return null;
},
onSaved: (val) {
_userEmail = val!;
},
),
if (!_isLogin)
TextFormField(
cursorColor: Colors.red,
key: const ValueKey('username'),
decoration: const InputDecoration(
labelText: 'Username',
),
validator: (val) {
if (val!.isEmpty || val.length < 4) {
return 'Username must be at least 4 characters.';
}
return null;
},
onSaved: (val) {
_userName = val!;
},
),
TextFormField(
cursorColor: Colors.red,
key: const ValueKey('password'),
decoration: const InputDecoration(
labelText: 'Password',
),
obscureText: true,
validator: (val) {
if (val!.isEmpty || val.length < 6) {
return 'Check your password, it must be 6 character or longer.';
}
return null;
},
onSaved: (val) {
_userPassword = val!;
},
),
const SizedBox(height: 12),
widget.isLoading
? const CircularProgressIndicator()
: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red),
),
child: Text(_isLogin ? 'Log in' : 'Sign up'),
onPressed: _trySubmit,
),
TextButton(
child: Text(_isLogin
? 'Create new account'
: 'I already have an account'),
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
),
if (_isLogin)
TextButton(
child: Text(
'Forgot Password?'
),
onPressed: () {
Navigator.pushNamed(context, ForgotPassword.id);
},
),
],
),
),
),
),
),
),
);
}
}
answered it my self by writing to a category in firebase then used the below code to pull it out later
import 'package:flutter/material.dart';
// Import the firebase_core and cloud_firestore plugin
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AddUser extends StatelessWidget {
final String fullName;
final String company;
final int age;
AddUser(this.fullName, this.company, this.age);
#override
Widget build(BuildContext context) {
// Create a CollectionReference called users that references the firestore collection
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> addUser() {
// Call the user's CollectionReference to add a new user
return users
.add({
'full_name': fullName, // John Doe
'company': company, // Stokes and Sons
'age': age // 42
})
.then((value) => print("User Added"))
.catchError((error) => print("Failed to add user: $error"));
}
return TextButton(
onPressed: addUser,
child: Text(
"Add User",
),
);
}
}
and this code to query it
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}
You can pass them as a constructor
Example
class UserDetail extends StatelessWidget {
final File image;
final String display;
UserDetail({Key key, required this.image, required this.display}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(child:Row(
children: <Widget>[
Image.file(image: File(image))),
Text(display),
], ),);
}
}
You can read Passing data between screens in Flutter
For more info.
I'm trying to build an app with Flutter and Firebase password-less authentication. I'm able to receive the email from Firebase but when I like on the link
auth.isSignInWithEmailLink(link) says false.
My pubspec.yaml firebase dependencies
firebase_core: ^1.0.3
firebase_auth: ^1.0.2
firebase_dynamic_links: ^2.0.2
Here is the full Login code
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
class LoginPage extends StatefulWidget {
static String tag = "login page";
#override
LoginPageState createState() => new LoginPageState();
}
class LoginPageState extends State<LoginPage> with WidgetsBindingObserver {
String _email;
final _formKey = GlobalKey<FormState>();
final _scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
this.initDynamicLinks();
WidgetsBinding.instance.addObserver(this);
}
void initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
if (deepLink != null) {
print('DeepLink: ' + deepLink.path);
await _signInWithEmailAndLink(deepLink.toString());
}
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
}
#override
Widget build(BuildContext context) {
final snackBarEmailSent = SnackBar(content: Text('Email Sent!'));
final snackBarEmailNotSent = SnackBar(
content: Text('Email Not Sent. Error.'),
);
final email = TextFormField(
keyboardType: TextInputType.emailAddress,
autofocus: false,
validator: (value) {
if (value.isEmpty) return "Email cannot be empty";
return null;
},
onSaved: (value) => _email = value,
decoration: InputDecoration(
hintText: 'Email',
prefixIcon: Icon(Icons.mail),
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
),
);
final loginButton = Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
child: Text("Send Verification Email"),
onPressed: (() async => await validateAndSave()
? ScaffoldMessenger.of(context).showSnackBar(snackBarEmailSent)
: ScaffoldMessenger.of(context)
.showSnackBar(snackBarEmailNotSent))),
);
final loginForm = Form(
key: _formKey,
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24, right: 24),
children: <Widget>[
SizedBox(height: 50),
email,
SizedBox(height: 40),
loginButton
],
),
);
return Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
body: Center(child: loginForm));
}
Future<bool> validateAndSave() async {
final FormState form = _formKey.currentState;
if (form.validate()) {
form.save();
bool sent = await _sendSignInWithEmailLink();
return sent;
}
return false;
}
Future<bool> _sendSignInWithEmailLink() async {
final FirebaseAuth auth = FirebaseAuth.instance;
try {
auth.sendSignInLinkToEmail(
email: _email,
actionCodeSettings: ActionCodeSettings(
url: 'https://<MyApp>.page.link/<key>',
handleCodeInApp: true,
androidInstallApp: true,
androidMinimumVersion: "12"));
} catch (e) {
_showDialog(e.toString());
return false;
}
print(_email + "<< sent");
return true;
}
Future<void> _signInWithEmailAndLink(link) async {
final FirebaseAuth auth = FirebaseAuth.instance;
// final PendingDynamicLinkData data =
// await FirebaseDynamicLinks.instance.getInitialLink();
// final Uri deepLink = data?.link;
print('Link: ' + link);
bool validLink = auth.isSignInWithEmailLink(link);
print('Is Valid Link: ' + validLink.toString());
if (validLink) {
try {
print('email:' + _email);
print('link:' + link);
await auth.signInWithEmailLink(email: _email, emailLink: link);
} catch (e) {
print('Error' + e);
_showDialog(e.toString());
}
}
}
void _showDialog(String error) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Error"),
content: new Text("Please Try Again.Error code: " + error),
actions: <Widget>[
new TextButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
Let me know what I'm missing here.
Thank you!
Try the plugin app_links along with firebase_dynamic_links.
Sample Code
Future<bool> retrieveDynamicLinkAndSignIn() async {
try {
await Future.delayed(const Duration(seconds: 2));
final _appLinks = AppLinks(
onAppLink: (Uri uri, String url) {
appLogs('onAppLink[$url][$uri]');
},
);
final deepLink = await _appLinks.getInitialAppLink();
appLogs('appLinks.uri[$deepLink]');
if (deepLink != null) {
bool validLink =
_firebaseAuth.isSignInWithEmailLink(deepLink.toString());
if (validLink) {
String email = SPService.instance.getString('email') ?? '';
appLogs('retrieveDynamicLinkAndSignIn[$email]');
if (email.isEmpty) {
return false;
}
SPService.instance.setString('email', '');
final firebase_auth.UserCredential userCredential =
await _firebaseAuth.signInWithEmailLink(
email: email,
emailLink: deepLink.toString(),
);
if (userCredential.user != null) {
await _setUserFromFirebaseUser();
logLogin(SignUp.email, true);
return true;
} else {
logLogin(SignUp.email, false);
AppToast.info(strings.noCredentialsWereFound);
}
} else {
appLogs('Link is not valid');
AppToast.info(strings.noCredentialsWereFound);
}
} else {
appLogs('retrieveDynamicLinkAndSignIn.deepLink[$deepLink]');
}
} catch (e, s) {
AppToast.error(null, e, s);
}
return false;
}
I get endless loading Icon when selecting an existing account
I can create an account just fine for the first time and login, but when i close android studio then restart main.dart & emulator then try to sign in to that existing account, i get infinite loading icon :
Here's the loginPage.dart code:-
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:telegramchatapp/Pages/HomePage.dart';
import 'package:telegramchatapp/Widgets/ProgressWidget.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LoginScreen extends StatefulWidget {
LoginScreen({Key key}) : super(key : key);
#override
LoginScreenState createState() => LoginScreenState();
}
class LoginScreenState extends State<LoginScreen> {
final GoogleSignIn googleSignIn = GoogleSignIn();
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
SharedPreferences preferences;
bool isLoggedIn = false;
bool isLoading = false;
FirebaseUser currentUser;
#override
void initState() {
// TODO: implement initState
super.initState();
isSignedIn();
}
void isSignedIn() async{
this.setState(() {
isLoggedIn = true;
});
preferences = await SharedPreferences.getInstance();
isLoggedIn = await googleSignIn.isSignedIn();
if(isLoggedIn){
Navigator.push(context, MaterialPageRoute(builder: (context) => HomeScreen(currentUserId: preferences.getString("id"))));
}
this.setState(() {
isLoading = false;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.blueAccent, Colors.black12],
),
),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
"Solef App",
style: TextStyle(fontSize: 82.0, color: Colors.white, fontFamily: "Signatra"),
),
GestureDetector(
onTap: controlSignIn,
child: Center(
child: Column(
children: <Widget>[
Container(
width: 270.0,
height: 65.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/images/google_signin_button.png"),
),
),
),
Padding(
padding: EdgeInsets.all(1.0),
child: isLoading ? circularProgress() : Container(),
),
],
),
),
)
],
),
),
);
}
Future<Null> controlSignIn() async {
preferences = await SharedPreferences.getInstance();
this.setState(() {
isLoading = true;
});
/*GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: [
'email',
'https://www.googleapis.com/auth/contacts.readonly',
],
);*/
GoogleSignInAccount googleUser = await googleSignIn.signIn();
GoogleSignInAuthentication googleAuthentication = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(idToken: googleAuthentication.idToken, accessToken: googleAuthentication.accessToken);
FirebaseUser firebaseUser = (await firebaseAuth.signInWithCredential(credential)).user;
//SignIn Success
if(firebaseUser != null){
//check if already signup
final QuerySnapshot resultQuery = await Firestore.instance.collection("users").where("id", isEqualTo: firebaseUser.uid).getDocuments();
final List<DocumentSnapshot> documentSnapshots = resultQuery.documents;
//save data to firestore - if new user
if(documentSnapshots.length == 0){
Firestore.instance.collection("users").document(firebaseUser.uid).setData(
{
"nickname": firebaseUser.displayName,
"photoUrl": firebaseUser.photoUrl,
"id": firebaseUser.uid,
"aboutMe": "I'm using SOLEF chat app.",
"createdAt": DateTime.now().millisecondsSinceEpoch.toString(),
"chatingWith": null,
});
//write data to Local
currentUser = firebaseUser;
await preferences.setString("id", currentUser.uid);
await preferences.setString("nickname", currentUser.displayName);
await preferences.setString("photoUrl", currentUser.photoUrl);
}else{
//write data to Local
currentUser = firebaseUser;
await preferences.setString("id", documentSnapshots[0]["id"]);
await preferences.setString("nickname", documentSnapshots[0]["nickname"]);
await preferences.setString("photoUrl", documentSnapshots[0]["photoUrl"]);
await preferences.setString("aboutMe", documentSnapshots[0]["aboutMe"]);
}
Fluttertoast.showToast(msg: "SignIn Successful.");
this.setState(() {
isLoading = false;
});
Navigator.push(context, MaterialPageRoute(builder: (context) => HomeScreen(currentUserId: firebaseUser.uid)));
}
//SignIn Not Success - Failed
else{
Fluttertoast.showToast(msg: "Try Again, SignIn Failed.");
this.setState(() {
isLoading = false;
});
}
}
}
Here's the Homepage.dart:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import '../main.dart';
class HomeScreen extends StatefulWidget {
final String currentUserId;
HomeScreen({Key key, #required this.currentUserId}) : super(key : key);
#override
State createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return RaisedButton.icon(onPressed: logoutUser, icon: Icon(Icons.close), label: Text("Sign Out"));
}
final GoogleSignIn googleSignIn = GoogleSignIn();
Future<Null> logoutUser() async{
await FirebaseAuth.instance.signOut();
await googleSignIn.disconnect();
await googleSignIn.signOut();
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => MyApp()), (Route<dynamic> route ) => false);
}
}
class UserResult extends StatelessWidget
{
#override
Widget build(BuildContext context) {
}
}
The problem is you're not using the try catch block.
For example, you can do something like this:
Future signInAnon() async {
setState(() {
_isLoading = true;
});
FirebaseAuth auth = FirebaseAuth.instance;
try {
await auth.signInAnonymously();
} on FirebaseAuthException catch (e) {
setState(() {
_isLoading = false;
});
showSimpleNotification(
const Text(
'Sign In Error',
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
contentPadding: const EdgeInsets.all(12),
background: Colors.red,
);
} catch (e) {
setState(() {
_isLoading = false;
});
}
}
Then, on listening to the user signing, you can do this:
class AppWrapper extends StatefulWidget {
const AppWrapper({Key? key}) : super(key: key);
#override
_AppWrapperState createState() => _AppWrapperState();
}
class _AppWrapperState extends State<AppWrapper> {
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, AsyncSnapshot<User?> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return errorPage();
} else if (snapshot.hasData) {
return const HomePage();
} else {
return const SigningPage();
}
},
);
}
I've built a simple login page that on clicking the button opens the menu to choose a google account and then navigates to the homepage of the app.
The button gets clicked but the login menu doesn't pop up. It throws an error saying :
E/flutter ( 4974): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Navigator operation requested with a context that does not include a Navigator. E/flutter ( 4974): The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
I tried adding a new context to the tree but then a different error came up:
E/flutter (22865): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: NoSuchMethodError: The method 'findAncestorStateOfType' was called on null. E/flutter (22865): Receiver: null E/flutter (22865): Tried calling: findAncestorStateOfType()
What should I do?
Here's my code:
import 'package:flutter/material.dart';
import 'package:sorted/screens/sortedMain.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
class LoginMain extends StatefulWidget {
LoginMain({Key key}) : super(key: key);
#override
_LoginMainState createState() => _LoginMainState();
}
class _LoginMainState extends State<LoginMain> {
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
FirebaseUser _user;
bool isUserSignedIn = false;
#override
void initState() {
super.initState();
checkIfUserIsSignedIn();
}
void checkIfUserIsSignedIn() async {
var userSignedIn = await _googleSignIn.isSignedIn();
setState(() {
isUserSignedIn = userSignedIn;
});
}
Future<FirebaseUser> _handleSignIn() async {
FirebaseUser user;
bool userSignedIn = await _googleSignIn.isSignedIn();
setState(() {
isUserSignedIn = userSignedIn;
});
if (isUserSignedIn) {
user = await _auth.currentUser();
} else {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
user = (await _auth.signInWithCredential(credential)).user;
userSignedIn = await _googleSignIn.isSignedIn();
setState(() {
isUserSignedIn = userSignedIn;
});
}
_user = user;
return user;
}
void onGoogleSignIn(BuildContext context) async {
FirebaseUser user = await _handleSignIn().then((value) => null);
BuildContext context;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SortedMain(_googleSignIn, user)),
);
setState(() {
isUserSignedIn == null ? true : false;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Sorted.'),
backgroundColor: Color(0xff0A3D62),
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
const Color(0xFF5761B2),
const Color(0xFF1FC588),
])),
),
),
drawer: null,
body: Container(
padding: EdgeInsets.only(top: 30),
width: 360,
height: 600,
color: const Color(0xFF273748),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 300,
height: 300,
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(20.0),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.white, Colors.grey[400]]),
),
child: MaterialButton(
height: 50.0,
minWidth: 150.0,
color: Colors.green,
splashColor: Colors.teal,
textColor: Colors.white,
child: new Icon(Icons.arrow_forward),
onPressed: () {
BuildContext context;
onGoogleSignIn(context);
},
)),
],
)),
),
),
);
}
}
Try using the code below for setting up your firebase class
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
class AuthProvider {
final FirebaseAuth _auth = FirebaseAuth.instance;
User _userFromFirebaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid) : null;
}
Future<bool> loginWithGoogle() async {
try {
GoogleSignIn googleSignIn = GoogleSignIn();
GoogleSignInAccount account = await googleSignIn.signIn();
if (account == null) return false;
AuthResult res =
await _auth.signInWithCredential(GoogleAuthProvider.getCredential(
idToken: (await account.authentication).idToken,
accessToken: (await account.authentication).accessToken,
));
if (res.user == null) return false;
return true;
} catch (e) {
print("Error logging with google");
return false;
}
}
Future<void> logOut() async {
try {
await _auth.signOut();
} catch (e) {
print("error logging out");
}
}
}
class User {
final String uid;
User({ this.uid });
}
and try restricting your Navigator with an if statement so it can't navigate without the completion of the login process.
CustomButtom(
onpressed : () async {
bool res = await AuthProvider().loginWithGoogle();
if (res) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => SortedMain()));
} else
print("Login failed");
},
),
I am trying to upload image in firebase storage getting the image from image picker plugin by accessing camera. Image is not uploading. I also add I change the firebase rules so only authenticated users can upload the image. Git hub Repo. I used the image uploading logic defined at the auth_screen.dart Line No 48 to 59[I commented out for time being]. I also add as i add these line my other firebase fuctions which are running prefectly before. getting the errors.
auth_screen.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
// import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/services.dart';
import '../widgets/auth/auth_form.dart';
class AuthScreen extends StatefulWidget {
#override
_AuthScreenState createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
final _auth = FirebaseAuth.instance;
var _isLoading = false;
void _submitAuthForm(
String email,
String password,
String userName,
File userImage,
bool isLogin,
BuildContext ctx,
) async {
dynamic authResult;
try {
setState(() {
_isLoading = true;
});
if (isLogin) {
authResult = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} else {
print(email);
print(userName);
print(userImage.path);
authResult = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
// final FirebaseStorage storage = FirebaseStorage(
// app: FirebaseStorage.instance.app,
// storageBucket: 'gs://chatapp-1b780.appspot.com',
// );
// final StorageReference ref2 =
// storage.ref().child('userimage').child('${authResult.user.id}.jpg');
// final StorageUploadTask uploadTask = ref2.putFile(userImage);
// uploadTask.onComplete
// .then((value) => print(value))
// .catchError((error) => print(error));
// print(uploadTask.lastSnapshot.error.toString());
// ///...
// final ref = FirebaseStorage.instance
// .ref()
// .child('user_image')
// .child(authResult.user.id + '.jpg');
// await ref.putFile(userImage).onComplete;
///
await FirebaseFirestore.instance
.collection('users')
.doc(authResult.user.uid)
.set({
'username': userName,
'email': email,
});
}
} on PlatformException catch (error) {
var message = 'An error occured,Please check your credentials';
if (error.message != null) {
setState(() {
_isLoading = false;
});
message = error.message;
}
print(message);
} catch (error) {
setState(() {
_isLoading = false;
});
Scaffold.of(ctx).showSnackBar(
SnackBar(
content: Text(error.toString()),
backgroundColor: Theme.of(ctx).errorColor,
),
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).primaryColor,
body: AuthForm(_submitAuthForm, _isLoading),
);
}
}
image being picked using image picker from auth/auth_form.dart to user_image_picker.dart where i added the argument so the image is passed down.
auth/authform.dart
import 'package:flutter/material.dart';
import 'dart:io';
import '../pickers/user_image_picker.dart';
class AuthForm extends StatefulWidget {
final bool isLoading;
final void Function(String email, String password, String userName,
File userImage, bool isLogin, BuildContext ctx) submitFn;
AuthForm(this.submitFn, this.isLoading);
#override
_AuthFormState createState() => _AuthFormState();
}
class _AuthFormState extends State<AuthForm> {
final _formKey = GlobalKey<FormState>();
var _isLogin = true;
String _userEmail = '';
String _userName = '';
String _userPassword = '';
File _userImageFile;
void _pickedImage(File image) {
_userImageFile = image;
}
void _trysubmit() {
final isValid = _formKey.currentState.validate();
FocusScope.of(context).unfocus();
if (_userImageFile == null && !_isLogin) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Please Pick an Image'),
backgroundColor: Theme.of(context).errorColor,
),
);
return;
}
if (isValid) {
_formKey.currentState.save();
print(_userEmail);
print(_userPassword);
widget.submitFn(_userEmail.trim(), _userPassword.trim(), _userName.trim(),
_userImageFile, _isLogin, context);
print(_userEmail);
print(_userPassword);
}
}
#override
Widget build(BuildContext context) {
return Center(
child: Card(
margin: EdgeInsets.all(20),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(16),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (!_isLogin)
UserImagePicker(
imagePickFn: _pickedImage,
),
TextFormField(
key: ValueKey('emailAdress'),
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Email address',
),
validator: (value) {
if (value.isEmpty || !value.contains('#')) {
return 'Please return a valid email address';
}
return null;
},
onSaved: (newValue) {
_userEmail = newValue;
},
),
if (!_isLogin)
TextFormField(
key: ValueKey('userName'),
decoration: InputDecoration(labelText: 'Username'),
validator: (value) {
if (value.isEmpty || value.length < 4) {
return 'Please Enter at least 4 characters';
}
return null;
},
onSaved: (newValue) {
_userName = newValue;
},
),
TextFormField(
key: ValueKey('password'),
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (value) {
if (value.isEmpty || value.length < 7) {
return 'Please Enter at least 7 characters';
}
return null;
},
onSaved: (newValue) {
_userPassword = newValue;
},
),
SizedBox(
height: 12,
),
if (widget.isLoading) CircularProgressIndicator(),
if (!widget.isLoading)
RaisedButton(
onPressed: _trysubmit,
child: Text((_isLogin) ? 'Login' : 'SignUp'),
),
if (!widget.isLoading)
FlatButton(
textColor: Theme.of(context).primaryColor,
child: Text(_isLogin
? 'Create new account'
: 'I already have an account'),
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
),
],
),
),
),
),
),
),
);
}
}
user_image_picker.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class UserImagePicker extends StatefulWidget {
UserImagePicker({this.imagePickFn});
final void Function(File pickedImage) imagePickFn;
#override
_UserImagePickerState createState() => _UserImagePickerState();
}
class _UserImagePickerState extends State<UserImagePicker> {
File _image;
final picker = ImagePicker();
Future<void> getImage() async {
final pickedFile = await ImagePicker().getImage(source: ImageSource.camera);
setState(() {
_image = File(pickedFile.path);
});
widget.imagePickFn(_image);
}
#override
Widget build(BuildContext context) {
return Column(
children: [
CircleAvatar(
radius: 40,
backgroundColor: Colors.grey,
backgroundImage: _image != null ? FileImage(_image) : null,
),
FlatButton.icon(
onPressed: getImage,
icon: Icon(Icons.image),
label: Text('Add Image'),
textColor: Theme.of(context).primaryColor,
),
],
);
}
}
Since you asked me to show you how to upload images to Firebase Storage, I will show you the whole procedure including using the Image Picker plugin. This is how you should use Image Picker:
class PickMyImage{
static Future<File> getImage() async {
final image = await ImagePicker().getImage(
source: ImageSource.gallery,
maxHeight: 1500,
maxWidth: 1500,
);
if (image != null) {
return File(image.path);
}
return null;
}
}
This is how you can get and upload that image to firebase storage:
final File _myImage=await PickMyImage.getImage();
if(_myImage!=null){
final StorageReference firebaseStorageRef = FirebaseStorage.instance
.ref()
.child("user/${_auth.currentUser().uid}/i"); //i is the name of the image
StorageUploadTask uploadTask =
firebaseStorageRef.putFile(_myImage);
StorageTaskSnapshot storageSnapshot = await uploadTask.onComplete;
var downloadUrl = await storageSnapshot.ref.getDownloadURL();
if (uploadTask.isComplete) {
final String url = downloadUrl.toString();
print(url);
//You might want to set this as the _auth.currentUser().photourl
} else {
//error uploading
}
}
The issue i got because i am returning the ImagePicker() function of the file then gettting the image from camera then passing the file to the Fire Storage bucket.
In user_image_picker.dart
I written this
final pickedFile = await ImagePicker().getImage(source: ImageSource.camera);
Instead of i have to write this
final pickedFile = await picker.getImage(source: ImageSource.camera);