Reputation: 1514
I have an app using firebase, I handle sign in or log in in RootPage then it passes to my FirstPage, if I directly logout without go any route from FirstPage it is working well and leading me to my RootPage unsigned version but if I go any route from my FirstPage and go back there then try to signOut it just give below error:
D/FirebaseAuth(10720): Notifying id token listeners about a sign-out event.
D/FirebaseAuth(10720): Notifying auth state listeners about a sign-out event.
I/flutter (10720): NoSuchMethodError: The method 'call' was called on null.
I/flutter (10720): Receiver: null
I/flutter (10720): Tried calling: call()
I have a movetohome function to go back my FirstPage from any page and I think there is something missing at it, my function is below;
_moveToHome(BuildContext context) {
Route route = MaterialPageRoute(builder: (context) => MyFirstPage(auth: FirebaseAuth.instance,userId: userId,));
Navigator.push(context, route);
}
and you can find firebase related parts of my pages;
main.dart:
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
const MyApp();
@override
Widget build(BuildContext context) {
return DynamicTheme(
defaultBrightness: Brightness.light,
data: (brightness) => ThemeData(
pageTransitionsTheme: PageTransitionsTheme(
builders: {
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
}
),
primarySwatch: Colors.blueGrey,
accentColor: Colors.blueGrey,
fontFamily: 'AvenirNext',
brightness: brightness,
),
themedWidgetBuilder: (context, theme) {
return MaterialApp(
theme: theme,
home: new RootPage(auth: new Auth()),
routes: <String, WidgetBuilder>{
MyFirstPage.routeName: (context) => new MyFirstPage(auth: new Auth()),
DetailPage.routeName: (context) => new DetailPage(auth: new Auth()),
GenrePage.routeName: (context) => new GenrePage(auth: new Auth())
},
);
});
}
}
RootPage:
import 'package:flutter/material.dart';
import 'package:randommoviefinal/screens/login_singup_page.dart';
import 'package:randommoviefinal/services/authentication.dart';
import 'package:randommoviefinal/screens/HomePage.dart';
enum AuthStatus {
NOT_DETERMINED,
NOT_LOGGED_IN,
LOGGED_IN,
}
class RootPage extends StatefulWidget {
RootPage({this.auth});
final BaseAuth auth;
@override
State<StatefulWidget> createState() => new _RootPageState();
}
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 loginCallback() {
widget.auth.getCurrentUser().then((user) {
setState(() {
_userId = user.uid.toString();
});
});
setState(() {
authStatus = AuthStatus.LOGGED_IN;
});
}
void logoutCallback() {
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,
loginCallback: loginCallback,
);
break;
case AuthStatus.LOGGED_IN:
if (_userId.length > 0 && _userId != null) {
return new MyFirstPage(
userId: _userId,
auth: widget.auth,
logoutCallback: logoutCallback,
);
} else
return buildWaitingScreen();
break;
default:
return buildWaitingScreen();
}
}
}
MyFirstPage:
class MyFirstPage extends StatefulWidget {
MyFirstPage({Key key, this.auth, this.userId, this.logoutCallback})
: super(key: key);
final auth;
final VoidCallback logoutCallback;
final String userId;
static String routeName = "/MyFirstPage";
@override
_MyFirstPageState createState() => new _MyFirstPageState();
}
class _MyFirstPageState extends State<MyFirstPage> {
String userId;
List<Movies> _moviesList;
final FirebaseDatabase _database = FirebaseDatabase.instance;
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
Query _moviesQuery;
StreamSubscription<Event> _onMoviesAddedSubscription;
StreamSubscription<Event> _onMoviesChangedSubscription;
@override
void initState() {
// TODO: implement initState
super.initState();
_moviesList = new List();
_moviesQuery = _database
.reference()
.child("movies")
.orderByChild("userId")
.equalTo(widget.userId);
_onMoviesAddedSubscription = _moviesQuery.onChildAdded.listen(onEntryAdded);
_onMoviesChangedSubscription =
_moviesQuery.onChildChanged.listen(onEntryChanged);
}
void dispose() {
_onMoviesAddedSubscription.cancel();
_onMoviesChangedSubscription.cancel();
super.dispose();
}
onEntryChanged(Event event) {
var oldEntry = _moviesList.singleWhere((entry) {
return entry.key == event.snapshot.key;
});
setState(() {
_moviesList[_moviesList.indexOf(oldEntry)] =
Movies.fromSnapshot(event.snapshot);
});
}
onEntryAdded(Event event) {
setState(() {
_moviesList.add(Movies.fromSnapshot(event.snapshot));
});
}
signOut() async {
try {
await widget.auth.signOut();
widget.logoutCallback();
} catch (e) {
print(e);
}
}
updateMovies(Movies movies) {
//Toggle completed
movies.watched = !movies.watched;
if (movies != null) {
_database.reference().child("movies").child(movies.key).set(movies.toJson());
}
}
deleteMovies(String moviesId, int index) {
_database.reference().child("movies").child(moviesId).remove().then((_) {
print("Delete $moviesId successful");
setState(() {
_moviesList.removeAt(index);
});
});
}
My another page for example:
class DetailPage extends StatefulWidget {
MovieDetailPage({Key key, this.title, this.auth, this.userId})
: super(key: key);
final auth;
final String userId;
static String routeName = "/MyHomePage";
final String title;
@override
_DetailPageState createState() => _DetailPageState();}
class _DetailPageState extends State<MovieDetailPage> {
String userId;
final FirebaseDatabase _database = FirebaseDatabase.instance;
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
StreamSubscription<Event> _onMoviesAddedSubscription;
StreamSubscription<Event> _onMoviesChangedSubscription;
Query _moviesQuery;
List<Movies> _moviesList;
_moveToHome(BuildContext context) {
Route route = MaterialPageRoute(builder: (context) => MyFirstPage(auth: FirebaseAuth.instance,userId: userId,));
Navigator.push(context, route);}
@override
void initState() {
// TODO: implement initState
super.initState();
_moviesList = new List();
_moviesQuery = _database
.reference()
.child("movies")
.orderByChild("userId")
.equalTo(widget.userId);
_onMoviesAddedSubscription = _moviesQuery.onChildAdded.listen(onEntryAdded);
_onMoviesChangedSubscription =
_moviesQuery.onChildChanged.listen(onEntryChanged);
widget.auth.getCurrentUser().then((user) {
if (user != null) {
userId = user?.uid;
}});
}
void dispose() {
_onMoviesAddedSubscription.cancel();
_onMoviesChangedSubscription.cancel();
super.dispose();
}
onEntryChanged(Event event) {
var oldEntry = _moviesList.singleWhere((entry) {
return entry.key == event.snapshot.key;
});
setState(() {
_moviesList[_moviesList.indexOf(oldEntry)] =
Movies.fromSnapshot(event.snapshot);
});
}
onEntryAdded(Event event) {
setState(() {
_moviesList.add(Movies.fromSnapshot(event.snapshot));
});
}
updateMovies(Movies movies) {
//Toggle completed
movies.watched = !movies.watched;
if (movies != null) {
_database.reference().child("movies").child(movies.key).set(movies.toJson());
}
}
deleteMovies(String moviesId, int index) {
_database.reference().child("movies").child(moviesId).remove().then((_) {
print("Delete $moviesId successful");
setState(() {
_moviesList.removeAt(index);
});
});
}
...
IconButton(
icon: Icon(Icons.home),
iconSize: 35,
onPressed: () {
_moveToHome(context);
},
),
...
and authentication.dart
import 'dart:async';
import 'package:firebase_auth/firebase_auth.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();
}
class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
Future<String> signIn(String email, String password) async {
AuthResult result = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
return user.uid;
}
Future<String> signUp(String email, String password) async {
AuthResult result = await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
return user.uid;
}
Future<FirebaseUser> getCurrentUser() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user;
}
Future<void> signOut() async {
return _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;
}
}
Upvotes: 0
Views: 711
Reputation: 2654
I think FirebaseAuth
is losing current user when you leave your home page try moving
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
class Auth implements BaseAuth {
//Rest of class
to right outside your class this way it has global scope just make sure it not in the abstract class that's right above it
Upvotes: 1