Reputation: 1698
I have a large Flutter
app that is working perfectly...
Now, I am struggling how to update the Firebase firebase_auth: ^0.15.5+3
plugin that has Breaking changes (since summer 2020).
I absolutely need to keep my StreamProvider
as I check MyUser
in multiple locations of the app.
I re-created a minimal working app to focus on my specific problem.
Can you suggest how I should adjust these:
1- getCurrentUser
in AuthService
: It seems I need to use authStateChanges
but dont know how.
2- StreamProvider
in MyApp
: How to adjust this so I can keep using it in my app without changing anything.
3- Using this way: final currentUser = Provider.of<MyUser>(context)
then currentUser.uid
to get the uid String.
Please, be specific with concrete example.
Here is my puspec.yaml
name: myApp
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# **** FIREBASE ****
firebase_auth: ^0.15.5+3
provider: ^4.0.5
#firebase_core: "^0.5.2"
#firebase_auth: "^0.18.3"
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
assets:
- assets/
here is all the (working) code with previous Firebase
version 0.15
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:firebase_auth/firebase_auth.dart';
void main() {
runApp(MyApp());
}
//******************************************
class MyApp extends StatelessWidget {
@override Widget build(BuildContext context) {
//The following StreamProvider is what I am trying to recreate with new Firebase version.
return StreamProvider<MyUser>.value(
value: AuthService().getCurrentUser,
child: MaterialApp( title: 'MyApp',
home: Wrapper(),),);}
}
//******************************************
class Wrapper extends StatelessWidget {
@override Widget build(BuildContext context) {
final currentUser = Provider.of<MyUser>(context);
// return either the Home or Authenticate widget
if (currentUser == null) {return SignIn();}
else {return HomePage();}}
}
//******************************************
class MyUser {
String uid;
MyUser({this.uid ,});
}
//******************************************
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
//This method will need to be updated I guess
Stream<MyUser> get getCurrentUser {
return _auth.onAuthStateChanged.map((FirebaseUser user) => _refereeFromFirebaseUser(user));
}
MyUser _refereeFromFirebaseUser(FirebaseUser _authUser) {
return (_authUser != null) ? MyUser(uid: _authUser.uid) : null;
}
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
return user;
} catch (error) {
print(error.toString());
return null;}
}
Future signOut() async {
try {return await _auth.signOut();}
catch (error) {print(error.toString());return null;}
}
}
//******************************************
class HomePage extends StatelessWidget {
final AuthService _auth = AuthService();
@override Widget build(BuildContext context) {
final currentUser = Provider.of<MyUser>(context);
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('MyApp2'),
actions: <Widget>[FlatButton.icon(icon: Icon(Icons.person), label: Text('logout'), onPressed: () async {await _auth.signOut();},), ],),
//Following line is very important: this is what I'd like to replicate with new Firebase version
body: Text("My ID is ${currentUser.uid}"),
),);
}
}
//******************************************
class SignIn extends StatefulWidget {
@override _SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
final AuthService _auth = AuthService();
final _formKey = GlobalKey<FormState>();
String error = '';
String email ;
String password ;
@override Widget build(BuildContext context) {
return Scaffold(
body: Form(key: _formKey,
child: ListView(shrinkWrap: true, children: <Widget>[ SizedBox(height: 60.0),
emailField(), SizedBox(height: 8.0),
passwordField(), SizedBox(height: 24.0),
signInButton(),
Text(error, style: TextStyle(color: Colors.red, fontSize: 16.0),),
],),),);}
RaisedButton signInButton() {
return RaisedButton( child: Text('Sign In'),
onPressed: () async {
if(_formKey.currentState.validate()) {
dynamic result = await _auth.signInWithEmailAndPassword(email, password);
//If successful SignIn, the Provider listening in the Wrapper will automatically load the Home page.
if(result == null) {setState(() {error = 'Could not sign in with those credentials';});}}});
}
TextFormField passwordField() {
return TextFormField(
validator: (val) => val.length < 6 ? 'Enter a password 6+ chars long' : null,
onChanged: (val) {setState(() => password = val);},);
}
TextFormField emailField() {
return TextFormField(
validator: (val) => val.isEmpty ? 'Enter an email' : null,
onChanged: (val) {setState(() => email = val);},);
}
}
Upvotes: 0
Views: 371
Reputation: 1698
I was able to migrate the whole code. It is now 100% running with new firebase_auth: "^0.18.3"
.
(Big thanks to Net Ninja for his web course with the initial code)
Here is new pubspec.yaml
:
name: flutter_app
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# **** FIREBASE ****
provider: ^4.0.5
firebase_core: "^0.5.2"
firebase_auth: "^0.18.3"
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
assets:
- assets/
and here is the main.dart
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart'; //New
void main() {
WidgetsFlutterBinding.ensureInitialized();//New
runApp(Initialize());
}
//******************************************
class Initialize extends StatelessWidget {//New
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
@override Widget build(BuildContext context) {
return FutureBuilder(
future: _initialization,
builder: (context, snapshot) {
if (snapshot.hasError) {print("Error-01"); return myCircularProgress();} //to be updated
if (snapshot.connectionState == ConnectionState.done) {return MyApp();}
else {print("loading-02"); return myCircularProgress();}
},);}
Center myCircularProgress() => Center(child: SizedBox(child: CircularProgressIndicator(), height: 100.0, width: 100.0,));
}
//******************************************
class MyApp extends StatelessWidget {
@override Widget build(BuildContext context) {
return StreamProvider<MyUser>.value(
value: AuthService().getCurrentUser,
child: MaterialApp( title: 'MyApp',
home: Wrapper(),),);}
}
//******************************************
class Wrapper extends StatelessWidget {
@override Widget build(BuildContext context) {
final currentUser = Provider.of<MyUser>(context);
// return either the Home or Authenticate widget
if (currentUser == null) {return SignIn();}
else {return HomePage();}}
}
//******************************************
class MyUser {
String uid;
MyUser({this.uid ,});
}
//******************************************
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
Stream<MyUser> get getCurrentUser {
return _auth.authStateChanges().map((User user) => _refereeFromFirebaseUser(user)); // Also works with "_auth.idTokenChanges()" or "_auth.userChanges()"
}
MyUser _refereeFromFirebaseUser(User _authUser) {
return (_authUser != null) ? MyUser(uid: _authUser.uid) : null;
}
Future signInWithEmailAndPassword(String email, String password) async {
try {
//AuthResult result = await _auth.signInWithEmailAndPassword(email: email, password: password);
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return user;
} catch (error) {
print(error.toString());
return null;}
}
Future signOut() async {
try {return await _auth.signOut();}
catch (error) {print(error.toString());return null;}
}
}
//******************************************
class HomePage extends StatelessWidget {
final AuthService _auth = AuthService();
@override Widget build(BuildContext context) {
final currentUser = Provider.of<MyUser>(context);
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('MyApp v0.18'),
actions: <Widget>[FlatButton.icon(icon: Icon(Icons.person), label: Text('logout'), onPressed: () async {await _auth.signOut();},), ],),
body: Text("My ID is ${currentUser.uid}"),
),);
}
}
//******************************************
class SignIn extends StatefulWidget {
@override _SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
final AuthService _auth = AuthService();
final _formKey = GlobalKey<FormState>();
String error = '';
String email ;
String password ;
@override Widget build(BuildContext context) {
return Scaffold(
body: Form(key: _formKey,
child: ListView(shrinkWrap: true, children: <Widget>[ SizedBox(height: 60.0),
emailField(), SizedBox(height: 8.0),
passwordField(), SizedBox(height: 24.0),
signInButton(),
Text(error, style: TextStyle(color: Colors.red, fontSize: 16.0),),
],),),);}
RaisedButton signInButton() {
return RaisedButton( child: Text('Sign In'),
onPressed: () async {
if(_formKey.currentState.validate()) {
dynamic result = await _auth.signInWithEmailAndPassword(email, password);
//If successful SignIn, the Provider listening in the Wrapper will automatically load the Home page.
if(result == null) {setState(() {error = 'Could not sign in with those credentials';});}}});
}
TextFormField passwordField() {
return TextFormField(
validator: (val) => val.length < 6 ? 'Enter a password 6+ chars long' : null,
onChanged: (val) {setState(() => password = val);},);
}
TextFormField emailField() {
return TextFormField(
validator: (val) => val.isEmpty ? 'Enter an email' : null,
onChanged: (val) {setState(() => email = val);},);
}
}
Upvotes: 0
Reputation: 2215
First point: the current user is not async anymore. If you check the documentation you will see that it doesn't return a future but a User. If you want to check for auth state updates just call FirebaseAuth.instance.authStateChanges(). Another thing to notice is that now the class containing users' data is not called anymore FirebaseUser but just User.
StreamProvider is still available in the provider package. In your question it isn't clear what is the issue that this class is raising to you.
Provider.of<>(context) can still be used. However now you can also use these 2 alternatives methods:
Upvotes: 1