I am making an app in flutter an now when i try to show the user Name on the profile i got this error
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building UserPage(dirty, dependencies: [MediaQuery], state: _UserProfile#ca57c):
The getter 'auth' was called on null.
Receiver: null
Tried calling: auth
The relevant error-causing widget was
UserPage
lib/HomePage.dart:92
When the exception was thrown, this was the stack
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 _UserProfile.build
package:tariffo/UserPage.dart:131
#2 StatefulElement.build
package:flutter/…/widgets/framework.dart:4619
#3 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4502
#4 StatefulElement.performRebuild
package:flutter/…/widgets/framework.dart:4675
...
═══════════════════════════════════════════════════════════════════════════════
and i used this code
FutureBuilder(
future:
Provider.of(context).auth.getCurrentUser(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
return Text("${snapshot.data.displayName}",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 22,
));
} else {
return CircularProgressIndicator();
}
},
),
This is the code that i used for my auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'User.dart';
String email, name, photoUrl;
class Authentication {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = GoogleSignIn();
Stream<String> get onAuthStateChanged => _firebaseAuth.onAuthStateChanged.map(
(FirebaseUser user) => user?.uid,
);
// GET UID
Future<String> getCurrentUID() async {
return (await _firebaseAuth.currentUser()).uid;
}
// GET CURRENT USER
Future getCurrentUser() async {
return await _firebaseAuth.currentUser();
}
// Email & Password Sign Up
Future<String> createUserWithEmailAndPassword(
String email, String password, String name) async {
final authResult = await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
// Update the username
await updateUserName(name, authResult.user);
return authResult.user.uid;
}
Future updateUserName(String name, FirebaseUser currentUser) async {
var userUpdateInfo = UserUpdateInfo();
userUpdateInfo.displayName = name;
await currentUser.updateProfile(userUpdateInfo);
await currentUser.reload();
}
// Email & Password Sign In
Future<String> signInWithEmailAndPassword(
String email, String password) async {
return (await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password))
.user
.uid;
}
// Sign Out
signOut() {
return _firebaseAuth.signOut();
}
// Reset Password
Future sendPasswordResetEmail(String email) async {
return _firebaseAuth.sendPasswordResetEmail(email: email);
}
// Create Anonymous User
Future singInAnonymously() {
return _firebaseAuth.signInAnonymously();
}
Future convertUserWithEmail(
String email, String password, String name) async {
final currentUser = await _firebaseAuth.currentUser();
final credential =
EmailAuthProvider.getCredential(email: email, password: password);
await currentUser.linkWithCredential(credential);
await updateUserName(name, currentUser);
}
Future convertWithGoogle() async {
final currentUser = await _firebaseAuth.currentUser();
final GoogleSignInAccount account = await _googleSignIn.signIn();
final GoogleSignInAuthentication _googleAuth = await account.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: _googleAuth.idToken,
accessToken: _googleAuth.accessToken,
);
await currentUser.linkWithCredential(credential);
await updateUserName(_googleSignIn.currentUser.displayName, currentUser);
}
// GOOGLE
Future<String> signInWithGoogle() async {
final GoogleSignInAccount account = await _googleSignIn.signIn();
final GoogleSignInAuthentication _googleAuth = await account.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: _googleAuth.idToken,
accessToken: _googleAuth.accessToken,
);
return (await _firebaseAuth.signInWithCredential(credential)).user.uid;
}
// APPLE
}
class NameValidator {
static String validate(String value) {
if (value.isEmpty) {
return "Name can't be empty";
}
if (value.length < 2) {
return "Name must be at least 2 characters long";
}
if (value.length > 50) {
return "Name must be less than 50 characters long";
}
return null;
}
}
class EmailValidator {
static String validate(String value) {
if (value.isEmpty) {
return "Email can't be empty";
}
return null;
}
}
class PasswordValidator {
static String validate(String value) {
if (value.isEmpty) {
return "Password can't be empty";
}
return null;
}
}
and this is the code for User.dart
import 'package:flutter/material.dart';
class User {
final String uid;
User({this.uid});
String adress;
bool business;
Map<String, dynamic> toJson() => {
'adress': adress,
'business': business,
};
}
Now it just appear a red background when i try to acces the profile page pressing the profile icon button
provider_widget.dart
import 'package:flutter/material.dart';
import 'auth.dart';
class Provider extends InheritedWidget {
final Authentication auth;
Provider({Key key, Widget child, this.auth}) : super(key: key, child: child);
#override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
static Provider of(BuildContext context) =>
(context.inheritFromWidgetOfExactType(Provider) as Provider);
}
Related
I'm using the Provider dependencie to manage states on my screen. Currently I have created a Loading Screen that works with Lottie animation. In my Sign In page, whenever there is an error with the log in, a Snackbar is shown to the user. Althought now, when I use the splash screen, the screen doesn't return and the snackBar isn't shown.
This is a piece of the login screen:
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: RaisedButton(
onPressed: userManager.loading
? null
: () {
if (formKey.currentState!
.validate()) {
userManager.signIn(
user: User(
email:
emailController.text,
password:
passController.text),
onFail: (e) {
scaffoldKey.currentState!
.showSnackBar(SnackBar(
content: Text(
'Falha ao entrar: $e'),
backgroundColor:
Colors.red,
));
},
onSucess: () {
debugPrint(
'Sucesso ao Logar');
Navigator.of(context).pop();
});
}
},
On the onFail I get this error, whenever I have a wrong password or other datas wrong:
Ocorreu uma exceção.
_CastError (Null check operator used on a null value)
This is how I'm changing between pages:
class LoginScreen extends StatelessWidget {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final TextEditingController emailController = TextEditingController();
final TextEditingController passController = TextEditingController();
#override
Widget build(BuildContext context) {
return Consumer<UserManager>(builder: (_, userManager, child) {
if (userManager.loading) {
return SplashScreen();
} else {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
UserManager:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:loja_virtual_nnananene/helpers/firebase_errors.dart';
import 'package:loja_virtual_nnananene/models/user.dart';
class UserManager extends ChangeNotifier {
UserManager() {
_loadCurrentUser();
}
final FirebaseAuth auth = FirebaseAuth.instance;
User? user;
bool _loading = false;
bool get loading => _loading;
bool get isLoggedIn => user != null;
Future<void> signIn(
{required User user,
required Function onFail,
required Function onSucess}) async {
loading = true;
try {
final AuthResult result = await auth.signInWithEmailAndPassword(
email: user.email!, password: user.password!);
await _loadCurrentUser(firebaseUser: result.user);
onSucess();
} on PlatformException catch (e) {
onFail(getErrorString(e.code));
}
loading = false;
notifyListeners();
}
Future<void> signUp(
{required User user,
required Function onFail,
required Function onSucess}) async {
loading = true;
try {
final AuthResult result = await auth.createUserWithEmailAndPassword(
email: user.email!, password: user.password!);
user.id = result.user.uid;
this.user = user;
await user.saveData();
onSucess();
} on PlatformException catch (e) {
onFail(getErrorString(e.code));
}
loading = false;
notifyListeners();
}
void signOut() {
auth.signOut();
user = null;
notifyListeners();
}
set loading(bool value) {
_loading = value;
notifyListeners();
}
Future<void> _loadCurrentUser({FirebaseUser? firebaseUser}) async {
final FirebaseUser currentUser = firebaseUser ?? await auth.currentUser();
if (currentUser != null) {
final DocumentSnapshot docUser = await Firestore.instance
.collection('users')
.document(currentUser.uid)
.get();
user = User.fromDocument(docUser);
final docAdmin = await Firestore.instance
.collection('admins')
.document(user!.id!)
.get();
if (docAdmin.exists) {
user!.admin = true;
}
notifyListeners();
}
}
bool get adminEnabled => user != null && user!.admin;
}
Is there another way to set the splash screen thats easier?
While I wait for you to add the UserManager class implementation, I think there's a missing notifyListeners() in the signIn method.
Flutter - How to read email with Gmail Api?
I was looking into googleapis https://pub.dev/packages/googleapis but there is no documentation on how it is actually done.
Can someone point me to a an example or a tutorial?
Here is a complete example:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:zapit/models/appState.dart';
import 'package:googleapis/gmail/v1.dart' as gMail;
class GmailApiScreen extends StatefulWidget {
#override
_GmailApiScreenState createState() => _GmailApiScreenState();
}
class _GmailApiScreenState extends State<GmailApiScreen> {
gMail.GmailApi gmailApi;
List<gMail.Message> messagesList = [];
Future waitForInit;
#override
void initState() {
super.initState();
waitForInit = init();
}
init() async {
final authHeaders = await AppState.state.googleUser.authHeaders;
final authenticateClient = GoogleAuthClient(authHeaders);
gmailApi = gMail.GmailApi(authenticateClient);
gMail.ListMessagesResponse results =
await gmailApi.users.messages.list("me");
for (gMail.Message message in results.messages) {
gMail.Message messageData =
await gmailApi.users.messages.get("me", message.id);
messagesList.add(messageData);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("email")),
body: buildFutureBuilder(),
);
}
FutureBuilder buildFutureBuilder() {
return FutureBuilder(
future: waitForInit,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
List<Widget> messageTxt = [];
for (var m in messagesList) {
messageTxt.add(Text(m.snippet));
}
return Column(
children:messageTxt,
);
} else {
// Otherwise, display a loading indicator.
return Center(child: CircularProgressIndicator());
}
});
}
}
class GoogleAuthClient extends http.BaseClient {
final Map<String, String> _headers;
final http.Client _client = new http.Client();
GoogleAuthClient(this._headers);
Future<http.StreamedResponse> send(http.BaseRequest request) {
return _client.send(request..headers.addAll(_headers));
}
}
the googleUser is created with calling ensureLoggedInOnStartUp in the following code:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:googleapis/gmail/v1.dart' as gMail;
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn.standard(scopes: [gMail.GmailApi.GmailReadonlyScope]);
Future<GoogleSignInAccount> ensureLoggedInOnStartUp() async {
// That class has a currentUser if there's already a user signed in on
// this device.
try {
GoogleSignInAccount googleSignInAccount = googleSignIn.currentUser;
if (googleSignInAccount == null) {
// but if not, Google should try to sign one in whos previously signed in
// on this phone.
googleSignInAccount = await googleSignIn.signInSilently();
if (googleSignInAccount == null) return null;
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final UserCredential authResult =
await _auth.signInWithCredential(credential);
final User user = authResult.user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final User currentUser = await _auth.currentUser;
assert(user.uid == currentUser.uid);
return googleSignInAccount;
}
} catch (e) { //on PlatformException
print(e);
}
return null;
}
Future<GoogleSignInAccount> signInWithGoogle() async {
try {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
// throws FirebaseException when no internet connection
final UserCredential authResult = await _auth.signInWithCredential(credential);
final User user = authResult.user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final User currentUser = await _auth.currentUser;
assert(user.uid == currentUser.uid);
return googleSignInAccount;
} catch (e,s) {
print(e);
print(s);
return null;
}
}
void signOutGoogle() async {
await googleSignIn.signOut();
print("User Sign Out");
}
Register Screen On Pressed method given, I believe there is a problem with calling Firebase user = result.user
onPressed: () async {
if(_formKey.currentState.validate()){
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password);
FirebaseUser user = result.user;
await DatabaseService(uid: user.uid).newUserInfo(
_nameC.text,
_cityC.text,
_contactnoC.toString()
);
if(result == null) {
setState(() {
error = 'Please supply a valid email';
loading = false;
});
}}},
// Database backend
class DatabaseService {
final String uid;
DatabaseService ({this.uid});
final CollectionReference userdata2 = Firestore.instance.collection('UserData');
Future newUserInfo(String name, String city, String contactno) async {
return await userdata2.document(uid).setData({
'name' : name,
'city' : city,
'contactno' : contactno
});
}}
// authentication backend
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
DatabaseService(uid: user.uid);
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
} }
// user.dart
class User {
final String uid;
User({this.uid});
}
In the Future fetchStudentInfo() function, i would like to use the userId from my Auth class to do filtering. The userId is embedded in the URL and it will retrieve data from database. But, the issue is that the context is lacking in the function itself. However, I couldn't figure out a way to pass in the context. It would be great if any legend could help me. The solution which retrieve data from internet is found on the flutter documentation. And i wouldn't like to hard code the userId.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import '../model/student.dart';
import '../provider/auth.dart';
Future<Student> fetchStudentInfo() async {
final auth = Provider.of<Auth>(context);
final response = await http.post(
'https://intermediary-sharpe.000webhostapp.com/Student/read_one.php?userId=$auth.userId');
if (response.statusCode == 200) {
return Student.fromJson(json.decode(response.body));
} else {
throw Exception('Failed');
}
}
class ProfileScreen extends StatefulWidget {
#override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
Future<Student> student;
#override
void initState() {
// TODO: implement initState
super.initState();
student = fetchStudentInfo();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<Student>(
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.studentId);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return CircularProgressIndicator();
},
future: student,
),
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
import '../model/http_exception.dart';
class Auth with ChangeNotifier {
String _token;
DateTime _expiryDate;
String userId;
Timer _authTimer;
bool get isAuthenticated {
return token != null;
}
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
Future<void> _authenticate(
String email, String password, String urlSegment) async {
final url =
'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegment?key=AIzaSyCkNZysDY4PGpScw2jUlBpd0mvpGjgSEag';
try {
final response = await http.post(
url,
body: json.encode(
{
'email': email,
'password': password,
'returnSecureToken': true,
},
),
);
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['idToken'];
userId = responseData['localId'];
_expiryDate = DateTime.now().add(
Duration(
seconds: int.parse(
responseData['expiresIn'],
),
),
);
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'userId': userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
//Auto Login Function
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('userData')) {
return false;
}
final extractedUserData =
json.decode(prefs.getString('userData')) as Map<String, Object>;
final expiryDate = DateTime.parse(extractedUserData['expiryDate']);
if (expiryDate.isBefore(DateTime.now())) {
return false;
}
_token = extractedUserData['token'];
userId = extractedUserData['userId'];
_expiryDate = expiryDate;
notifyListeners();
_autoLogout();
return true;
}
//SignUp function
Future<void> signUp(String email, String password) async {
return _authenticate(email, password, 'signUp');
}
//Login Function
Future<void> login(String email, String password) async {
return _authenticate(email, password, 'signInWithPassword');
}
//Logout Function
Future<void> logout() async {
_token = null;
userId = null;
_expiryDate = null;
if (_authTimer != null) {
_authTimer.cancel();
_authTimer = null;
}
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.clear();
}
//Auto Logout function
void _autoLogout() {
if (_authTimer != null) {
_authTimer.cancel();
}
final timeToExpiry = _expiryDate.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timeToExpiry), logout);
}
//PHP related functions
}
Thank you in advance.
I agree with #lyio, you need to modify the function to pass the context, however after passing context, you cannot call it from initState as stated in documentation of initState
BuildContext.dependOnInheritedWidgetOfExactType from this method. However, didChangeDependencies will be called immediately following this method, and BuildContext.dependOnInheritedWidgetOfExactType can be used there.
Getting provider with Provider.of(context) under the hood is using the inherited widget, so cannot be called using context from initState
So implement instead of initState use didChangeDependencies to call your fetchStudentsInfo(context) method
Wouldn't the easiest solution be to pass the context into fetchStudentInfo?
You would change fetchStudentInfo() to fetchStudentInfo(BuildContext context). And then, when you call the method you pass in the required context. That way, you have the appropriate context available.
If you are not using the `fetchStudentInfo()` outside of the state class, then just move that method into the state class and the issue will be resolved.
Since Any state class has a context getter defined by default./
I just realized how improper this answer was.
Update:
According to the answer by #dlohani, didChangeDependencies should be used in stead of initState.
So what you can do is following:
Pass BuildContext as parameter in the fetchStudentInfo method
Override didChangeDependencies in state class & call fetchStudentInfo from here instead of initState
I want to use value returned by Future method and depending on that I want push new routes into the navigator.
I am new with flutter so I don't know how to get values from Future functions. I have tried something like -
final value = await signInWithGoogle();
Future Function Code -
try {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount
.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult result = await _auth.signInWithCredential(credential);
final FirebaseUser user = result.user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
if(result.additionalUserInfo.isNewUser){
Navigator.push(context, MaterialPageRoute(builder: (context) => GetInfo()));
}
else {
name = user.displayName;
email = user.email;
imageUrl = user.photoUrl;
return 1;
}
}
catch(e){
print(e.message);
return -1;
}
Try the below code
// 1
// FirebaseAuthUtils
class FirebaseAuthUtils {
final GoogleSignIn _googleSignIn = GoogleSignIn();
Future<User> signInWithGoogle(BuildContext context) async {
User user;
FirebaseUser firebaseuser;
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
try {
firebaseuser = await _auth.signInWithCredential(credential);
assert(!firebaseuser.isAnonymous);
assert(await firebaseuser.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(firebaseuser.uid == currentUser.uid);
} on PlatformException catch (e) {
UtilsImporter().commanUtils.showToast(e.message, context);
}
if (firebaseuser != null) {
user = new User(firebaseuser.displayName, firebaseuser.email, firebaseuser.photoUrl);
}
print('===Google Login: ' + user._fullname);
return user;
}
}
// 2
// User Model
class User {
String _fullname;
String _emailAddress;
String _profilPic;
User(this._fullname, this._emailAddress, this._profilPic);
String get name => _fullname;
set name(String value) {
_fullname = value;
}
String get emailaddress => _emailAddress;
set image(String value) {
_emailAddress = value;
}
String get profilepic => _profilPic;
set profilepic(String value) {
_profilPic = value;
}
}
// 3
// Home Page
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return HomeState();
}
}
class HomeState extends State<Home> {
FirebaseAuthUtils firebaseAuthUtils = new FirebaseAuthUtils();
#override
Widget build(BuildContext context) {
//getting screen size
}
#override
void initState() {
super.initState();
var user = firebaseAuthUtils.signInWithGoogle;
print("User : $user");
}
}
int _value;
#override
void initState() async {
super.initState();
int newValue = await signInWithGoogle();
setState(() {
_value = newValue;
});
}