Save Image from Image picker to firebase Storage in flutter - firebase

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);

Related

set a username & profile picture firebase login and then get it for a profile later on

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.

Flutter Firebase Password less Authentication auth.isSignInWithEmailLink(link) returns false

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;
}

How to solve SnackBar did not work and black screen in flutter after add StreamBuilder

I am doing login and sign up using firebase. I have two error. First is the error message from firebase that did not show at the snack bar. The second is after I add StreamBuilder my apps become black screen.
Does anyone know how to solve this question? Thanks in advance.
Here is the code:
Main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (ctx, userSnapshot) {
if (userSnapshot.hasData) {
return HomePage();
}
return AuthScreen();
}),
);
}
}
AuthScreen.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,
bool isLogin,
BuildContext ctx,
) async {
UserCredential authResult;
try {
setState(() {
_isLoading = true;
});
if (isLogin) {
authResult = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} else {
authResult = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
await FirebaseFirestore.instance
.collection('users')
.doc(authResult.user.uid)
.set({
'username': username,
'email': email,
});
}
} on PlatformException catch (err) {
var message = 'An error occurred, pelase check your credentials!';
if (err.message != null) {
message = err.message;
}
ScaffoldMessenger.of(ctx).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Theme.of(ctx).errorColor,
),
);
setState(() {
_isLoading = false;
});
} catch (err) {
print(err);
setState(() {
_isLoading = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).primaryColor,
body: AuthForm(
_submitAuthForm,
_isLoading,
),
);
}
}
Auth_form.dart
class AuthForm extends StatefulWidget {
AuthForm(
this.submitFn,
this.isLoading,
);
final bool isLoading;
final void Function(
String email,
String password,
String userName,
bool isLogin,
BuildContext ctx,
) submitFn;
#override
_AuthFormState createState() => _AuthFormState();
}
class _AuthFormState extends State<AuthForm> {
final _formKey = GlobalKey<FormState>();
var _isLogin = true;
var _userEmail = '';
var _userName = '';
var _userPassword = '';
void _trySubmit() {
final isValid = _formKey.currentState.validate();
FocusScope.of(context).unfocus();
if (isValid) {
_formKey.currentState.save();
widget.submitFn(_userEmail.trim(), _userPassword.trim(), _userName.trim(),
_isLogin, context);
}
}
#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: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
key: ValueKey('email'),
validator: (value) {
if (value.isEmpty || !value.contains('#')) {
return 'Please enter a valid email address.';
}
return null;
},
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Email address',
),
onSaved: (value) {
_userEmail = value;
},
),
if (!_isLogin)
TextFormField(
key: ValueKey('username'),
validator: (value) {
if (value.isEmpty || value.length < 4) {
return 'Please enter at least 4 characters';
}
return null;
},
decoration: InputDecoration(labelText: 'Username'),
onSaved: (value) {
_userName = value;
},
),
TextFormField(
key: ValueKey('password'),
validator: (value) {
if (value.isEmpty || value.length < 7) {
return 'Password must be at least 7 characters long.';
}
return null;
},
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
onSaved: (value) {
_userPassword = value;
},
),
SizedBox(height: 12),
if (widget.isLoading) CircularProgressIndicator(),
if (!widget.isLoading)
RaisedButton(
child: Text(_isLogin ? 'Login' : 'Signup'),
onPressed: _trySubmit,
),
if (!widget.isLoading)
FlatButton(
textColor: Theme.of(context).primaryColor,
child: Text(_isLogin
? 'Create new account'
: 'I already have an account'),
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
)
],
),
),
),
),
),
);
}
}
home.dart
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Container();
}
}
To show a SnackBar first define a key:
final key = GlobalKey<ScaffoldMessengerState>();
Then, add a ScaffoldMessenger to your widget tree:
ScaffoldMessenger(
key: key, // assign the key property to your previously created key
child: Scaffold(...),
)
Now you can show the SnackBar:
key.currentState.showSnackBar(SnackBar(...))
Please add code for your home page, so we can inspect it and see why it shows a black screen, you probably forgot to wrap it in a Scaffold. You should return a Scaffold in the home page and the black screen will go away.

Error appears only the first call, once the screen opens again, the error disappears,

As per the screenshot once I open the screen it gives me the error "The getter 'email' was called on null." but if I just click back button, then open the screen again, it works well without any error,
the purpose of the screen is the account owner see the notes that was sent to him from the admin account
This is the error appears only one time after opening the app
Here the same screen without any error
Here is the codes in dart where the tap to screen exists
import 'package:flutter/cupertino.dart' show CupertinoIcons;
import 'package:flutter/material.dart';
import 'package:notification_permissions/notification_permissions.dart';
import 'package:provider/provider.dart';
import 'package:rate_my_app/rate_my_app.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../../app.dart';
import '../../common/config.dart';
import '../../common/constants.dart';
import '../../common/tools.dart';
import '../../generated/l10n.dart';
import '../../models/index.dart' show AppModel, User, UserModel, WishListModel;
import '../../routes/flux_navigate.dart';
import '../../screens/blogs/post_screen.dart';
import '../../services/index.dart';
import '../../widgets/common/webview.dart';
import '../custom/smartchat.dart';
import '../index.dart';
import '../users/user_point.dart';
import 'currencies.dart';
import 'language.dart';
import 'notification.dart';
import '../../common/config.dart' as config;
import 'package:cespohm/screens/bywaleed/booking_admin_screen.dart';
import 'package:cespohm/screens/bywaleed/docor_note_tap.dart';
import 'package:cespohm/screens/bywaleed/user_search_new_screen.dart';
import 'package:cespohm/screens/bywaleed/doctor_notes_user_screen.dart';
class SettingScreen extends StatefulWidget {
final List<dynamic> settings;
final String background;
final User user;
final VoidCallback onLogout;
final bool showChat;
SettingScreen({
this.user,
this.onLogout,
this.settings,
this.background,
this.showChat,
});
#override
_SettingScreenState createState() {
return _SettingScreenState();
}
}
class _SettingScreenState extends State<SettingScreen>
with
TickerProviderStateMixin,
WidgetsBindingObserver,
AutomaticKeepAliveClientMixin<SettingScreen> {
#override
bool get wantKeepAlive => true;
final bannerHigh = 150.0;
bool enabledNotification = true;
final RateMyApp _rateMyApp = RateMyApp(
// rate app on store
minDays: 7,
minLaunches: 10,
remindDays: 7,
remindLaunches: 10,
googlePlayIdentifier: kStoreIdentifier['android'],
appStoreIdentifier: kStoreIdentifier['ios']);
void showRateMyApp() {
_rateMyApp.showRateDialog(
context,
title: S.of(context).rateTheApp,
// The dialog title.
message: S.of(context).rateThisAppDescription,
// The dialog message.
rateButton: S.of(context).rate.toUpperCase(),
// The dialog "rate" button text.
noButton: S.of(context).noThanks.toUpperCase(),
// The dialog "no" button text.
laterButton: S.of(context).maybeLater.toUpperCase(),
// The dialog "later" button text.
listener: (button) {
// The button click listener (useful if you want to cancel the click event).
switch (button) {
case RateMyAppDialogButton.rate:
break;
case RateMyAppDialogButton.later:
break;
case RateMyAppDialogButton.no:
break;
}
return true; // Return false if you want to cancel the click event.
},
// Set to false if you want to show the native Apple app rating dialog on iOS.
dialogStyle: const DialogStyle(),
// Custom dialog styles.
// Called when the user dismissed the dialog (either by taping outside or by pressing the "back" button).
// actionsBuilder: (_) => [], // This one allows you to use your own buttons.
);
}
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
await checkNotificationPermission();
});
_rateMyApp.init().then((_) {
// state of rating the app
if (_rateMyApp.shouldOpenDialog) {
showRateMyApp();
}
});
}
// #override
// void dispose() {
// Utils.setStatusBarWhiteForeground(false);
// super.dispose();
// }
Future<void> checkNotificationPermission() async {
if (!isAndroid || isIos) {
return;
}
try {
await NotificationPermissions.getNotificationPermissionStatus()
.then((status) {
if (mounted) {
setState(() {
enabledNotification = status == PermissionStatus.granted;
});
}
});
} catch (err) {
printLog('[Settings Screen] : ${err.toString()}');
}
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
checkNotificationPermission();
}
}
/// Render the Admin Vendor Menu
Widget renderVendorAdmin() {
if (!(widget.user != null ? widget.user.isVendor ?? false : false)) {
return Container();
}
return Card(
color: Theme.of(context).backgroundColor,
margin: const EdgeInsets.only(bottom: 2.0),
elevation: 0,
child: ListTile(
onTap: () {
final String langCode =
Provider.of<AppModel>(context, listen: false).langCode;
if (unsupportedLanguages.contains(langCode)) {
final snackBar = SnackBar(
content: Text(
S.of(context).thisFeatureDoesNotSupportTheCurrentLanguage),
duration: const Duration(seconds: 1),
);
Scaffold.of(context).showSnackBar(snackBar);
return;
}
FluxNavigate.push(
MaterialPageRoute(
builder: (context) =>
Services().widget.getAdminVendorScreen(context, widget.user),
),
forceRootNavigator: true,
);
},
leading: Icon(
Icons.dashboard,
size: 24,
color: Theme.of(context).accentColor,
),
title: Text(
S.of(context).vendorAdmin,
style: const TextStyle(fontSize: 16),
),
trailing: Icon(
Icons.arrow_forward_ios,
size: 18,
color: Theme.of(context).accentColor,
),
),
);
}
/// Render the custom profile link via Webview
/// Example show some special profile on the woocommerce site: wallet, wishlist...
Widget renderWebViewProfile() {
if (widget.user == null) {
return Container();
}
var base64Str = Utils.encodeCookie(widget.user.cookie);
var profileURL = '${serverConfig["url"]}/my-account?cookie=$base64Str';
return Card(
color: Theme.of(context).backgroundColor,
margin: const EdgeInsets.only(bottom: 2.0),
elevation: 0,
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => WebView(
url: profileURL, title: S.of(context).updateUserInfor),
),
);
},
leading: Icon(
CupertinoIcons.profile_circled,
size: 24,
color: Theme.of(context).accentColor,
),
title: Text(
S.of(context).updateUserInfor,
style: const TextStyle(fontSize: 16),
),
trailing: Icon(
Icons.arrow_forward_ios,
color: Theme.of(context).accentColor,
size: 18,
),
),
);
}
Widget renderItem(value) {
IconData icon;
String title;
Widget trailing;
Function() onTap;
bool isMultiVendor = kFluxStoreMV.contains(serverConfig['type']);
switch (value) {
case 'bookingAdmin':
if (widget.user == null ) {
return Container();
}
else if(widget.user.email == config.adminEmail || widget.user.email == config.adminEmailTwo && widget.user != null )
{
icon = FontAwesomeIcons.keyboard;
title = S.of(context).checkout;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => const BookingAdminScreen()),
);
}
else {
return Container();
}
break;
case 'addDoctorNote':
if (widget.user == null ) {
return Container();
}
else if(widget.user.isVendor && widget.user != null )
{
icon = FontAwesomeIcons.edit;
title = S.of(context).continueToShipping;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => const UserSearchNewScreen()),
);
}
else {
return Container();
}
break;
/// here is the tap to the screen where there is the error
case 'yourDoctorNotes':
if (widget.user == null || widget.user.email == config.adminEmail || widget.user.email == config.adminEmailTwo ) {
return Container();
}
else if(!widget.user.isVendor)
{
icon = FontAwesomeIcons.notesMedical;
title = S.of(context).french;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => DoctorNoteUserScreen(senderUser: widget.user,)),
);
}
else {
return Container();
}
break;
case 'chat':
here is the dart code of the screen where is the error
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_provider.dart';
import 'package:cespohm/models/entities/user.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:provider/provider.dart';
import '../../generated/l10n.dart' as word;
import '../../models/index.dart' show AppModel, Store, User, UserModel ;
final _fireStore = FirebaseFirestore.instance;
firebase_auth.User loggedInUser;
class DoctorNoteUserScreen extends StatelessWidget {
final User senderUser;
DoctorNoteUserScreen({
Key key,
this.senderUser,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final userSearchProvider = Provider.of<UserSearchProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text(
word.S.of(context).french,
style: TextStyle(
color: Colors.white, fontSize: 18.0, fontWeight: FontWeight.w400),
),
leading: Center(
child: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onTap: () => Navigator.pop(context),
),
),
),
body: StreamBuilder<List<UserSearchModel>>(
stream: userSearchProvider.userstwo,
builder: (context, snapshot) {
if (!snapshot.hasData ) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).primaryColor),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(7.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
SizedBox(
height: 40.0,
),
Text(word.S.of(context).french,
style: TextStyle(fontSize: 18.0)),
SizedBox(
height: 15.0,
),
Divider(
thickness: 1.0,
)
],
),
),
Container(
child: ListTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 15.0,
),
Container(
child: Text(snapshot.data[index].doctorNote!=null?snapshot.data[index].doctorNote: 'No Notes Yet',
style: TextStyle(fontSize: 20.0)),
),
],
),
),
),
],
),
);
});
}
}),
);
}
}
here is the dart code of the service
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_provider.dart';
import 'package:cespohm/models/user_model.dart';
class UserSearchService {
final FirebaseFirestore _db = FirebaseFirestore.instance;
final userModel = UserModel();
//Get Entries
Stream<List<UserSearchModel>> getEntries(){
return _db
.collection('users')
.orderBy('firstName', descending: false)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => UserSearchModel.fromJson(doc.data()))
.toList());
}
/// here is the firebase collection where the error exists
Stream<List<UserSearchModel>> getEntriesTwo(){
return _db
.collection('users').where('email', isEqualTo: userModel.user.email)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => UserSearchModel.fromJson(doc.data()))
.toList());
}
}
here is the model
import 'package:cloud_firestore/cloud_firestore.dart';
class UserSearchModel {
final String date;
final String firstName;
final String lastName;
final String phone;
final String email;
final String searchKey;
final String doctorNote;
final String bYDoctor;
final String uID;
UserSearchModel(
{this.date,
this.firstName,
this.lastName,
this.phone,
this.email,
this.searchKey,
this.doctorNote,
this.bYDoctor,
this.uID});
// creating a Trip object from a firebase snapshot
UserSearchModel.fromSnapshot(DocumentSnapshot snapshot) :
date = snapshot['date'],
firstName = snapshot['firstName'].toDate(),
lastName = snapshot['lastName'].toDate(),
phone = snapshot['phone'],
email = snapshot['email'],
searchKey = snapshot['searchKey'],
doctorNote = snapshot['doctorNote'],
bYDoctor = snapshot['bYDoctor'],
uID = snapshot.id;
factory UserSearchModel.fromJson(Map<String, dynamic> json) {
return UserSearchModel(
date: json['createdAt'],
firstName: json['firstName'],
lastName: json['lastName'],
phone: json['phone'],
email: json['email'],
searchKey: json['searchKey'],
doctorNote: json['doctorNote'],
bYDoctor: json['bYDoctor'],
uID: json['uID']);
}
Map<String, dynamic> toMap() {
return {
'createdAt': date,
'firstName': firstName,
'lastName': lastName,
'phone': phone,
'email': email,
'searchKey': searchKey,
'doctorNote': doctorNote,
'bYDoctor': bYDoctor,
'uID': uID,
};
}
}
Here is the provider
import 'package:flutter/material.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/user_model.dart';
import 'package:cespohm/services/bywaleed/user_search_new_service.dart';
import 'package:uuid/uuid.dart';
class UserSearchProvider with ChangeNotifier {
final userSearchService = UserSearchService();
final userModel = UserModel();
DateTime _date;
String _firstName;
String _lastName;
String _phone;
String _email;
String _searchKey;
String _doctorNote;
String _bYDoctor;
String _uID;
var uuid = Uuid();
//Getters
DateTime get date => _date;
String get firstName => _firstName;
String get lastName => _lastName;
String get phone => _phone;
String get email => _email;
String get searchKey => _searchKey;
String get doctorNote => _doctorNote;
String get byDoctor => _bYDoctor;
String get uID => _uID;
Stream<List<UserSearchModel>> get users => userSearchService.getEntries();
Stream<List<UserSearchModel>> get userstwo => userSearchService.getEntriesTwo();
//Setters
set changeDate(DateTime date) {
_date = date;
notifyListeners();
}
set changeFirstName(String firstName) {
_firstName = firstName;
notifyListeners();
}
set changeLastName(String lastName) {
_lastName = lastName;
notifyListeners();
}
set changePhone(String phone) {
_phone = phone;
notifyListeners();
}
set changeEmail(String email) {
_email = email;
notifyListeners();
}
set changeSearchKey(String searchKey) {
_searchKey = searchKey;
notifyListeners();
}
set changeDoctorNote(String doctorNote) {
_doctorNote = doctorNote;
notifyListeners();
}
set changeBYDoctor(String bYDoctor) {
_bYDoctor = bYDoctor;
notifyListeners();
}
set changeuID(String uID) {
_uID = uID;
notifyListeners();
}
//Functions
loadAll(UserSearchModel userSearchModel) {
if (userSearchModel != null) {
_date = DateTime.parse(userSearchModel.date);
_firstName = userSearchModel.firstName;
_lastName = userSearchModel.lastName;
_phone = userSearchModel.phone;
_email = userSearchModel.email;
_searchKey = userSearchModel.searchKey;
_doctorNote = userSearchModel.doctorNote;
_bYDoctor = userModel.user.email;
_uID = userSearchModel.uID;
} else {
_date = DateTime.now();
_firstName = null;
_lastName = null;
_phone = null;
_email = null;
_searchKey = null;
_doctorNote = null;
_bYDoctor = null;
_uID = null;
}
}
saveEntry() {
if (_email == null) {
//Add
var newUserModel = UserSearchModel(
date: _date.toIso8601String(),
firstName: _firstName,
lastName: _lastName,
phone: _phone,
email: _email,
searchKey: _searchKey,
doctorNote: _doctorNote,
bYDoctor: _bYDoctor,
uID: _uID);
print(newUserModel.email);
userSearchService.setEntry(newUserModel);
} else {
//Edit
var updatedEntry = UserSearchModel(
date: _date.toIso8601String(),
firstName: _firstName,
lastName: _lastName,
phone: _phone,
email: _email,
searchKey: _searchKey,
doctorNote: _doctorNote,
bYDoctor: _bYDoctor,
uID: _uID);
userSearchService.setEntry(updatedEntry);
}
}
removeEntry(String entryId) {
userSearchService.removeEntry(entryId);
}
}
You need to make sure your User object is initialised. This error appears because user.email is accessed, but your User object is null. Make sure your code considers loading time for the user object if it comes from the API, and has appropriate checks for the case when user == null.

flutter firebase google sign out not working

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(...)});

Resources