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