easy_auth 0.2.1
easy_auth: ^0.2.1 copied to clipboard
Widgets and classes that make it easy to add authentication to any Flutter app
EasyAuth
Widgets and classes that make it easy to add authentication to any Flutter app. Built on top of the package:bloc architecture, it is fully authentication framework agnostic but provides some plug-and-play mechanisms for commonly used frameworks like package:firebase_auth.
⚠️ If you like this repository, I would really appreciate some help in maintaining/improving/promoting it!
Usage #
Lets take a look at how to integrate a basic Firebase Auth state to your app. For other examples, check the examples folder.
First, we create a basic MaterialApp (or any other app you might use) and initialize the default Firebase App:
// -> main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
GestureBinding.instance!.resamplingEnabled = true;
await Firebase.initializeApp();
runApp(
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const MyApp(),
),
);
}
We then create our MyApp
widget that extends AuthenticationBasedApp
:
// -> main.dart
class MyApp extends AuthenticationBasedApp<EquatableUser> {
const MyApp({Key? key}) : super(key: key);
@override
BasicFirebaseAuth get repository => BasicFirebaseAuth();
@override
Widget buildState(BuildContext context, AuthStatus status, EquatableUser user) {
switch (status) {
case AuthStatus.uninitialized:
return const SplashScreenView();
case AuthStatus.authenticated:
return const HomeView();
case AuthStatus.newAccount:
return const HomeView.newAccount();
case AuthStatus.authenticating:
case AuthStatus.unauthenticated:
return const LoginView();
}
}
}
That's it, you're done! Now you can use EasyAuth
as you would use FirebaseAuth
and login, signout, create accounts, etc.
Notice that we passed the EquatableUser
class as a generic to AuthenticationBasedApp
, let's talk about why.
EquatableUser #
This class is used as a default representation of what a user would be. It can easily be extended to add your own parameters.
As it extends Equatable, if you want to include property as part of the == operation, you'll have to add it to the props array.
// -> custom_user.dart
class CustomUser extends EquatableUser {
const CustomUser({required this.birthday}) : super(id: '1', email: 'first@user.com');
final String birthday;
@override
List<Object?> get props => [...super.props, birthday];
}
EasyAuth methods #
EasyAuth is a utility class that lets you statically access the methods on your AuthenticationRepository
.
ElevatedButton(
child: const Text('Log in'),
onPressed: () {
final provider = EmailPasswordAuth('test@easyauth.com', 'some-password');
EasyAuth.login(context, provider: provider);
},
)
AuthenticationRepository #
AuthenticationRepository is an abstract class that defines all the methods necessary to add a custom authentication provider.
Note that you do not need to handle errors when overriding any methods in this class!
abstract class AuthenticationRepository<T extends EquatableUser> {
Future<void> login({required EasyAuthProvider provider});
Future<void> register({required T user, required String password});
Future<void> signOut();
Future<void> deleteAccount();
bool isUserNew(T user);
T get currentUser;
Stream<T> get user;
Future<AuthException?> performSafeAuth(Future<void> future, AuthAction action) async {...}
}
The only method that does not need to be re-implemented is performSafeAuth(...)
. It is used to handle any errors that might be thrown by performing an authentication action.
Here's an example of a BasicFirebaseAuth
:
class BasicFirebaseAuth extends AuthenticationRepository<EquatableUser> {
final _firebaseAuth = FirebaseAuth.instance;
@override
Stream<EquatableUser> get user => _firebaseAuth.authStateChanges().map<EquatableUser>((user) {
if (user == null) {
return EquatableUser.empty;
} else {
return EquatableUser(
id: user.uid,
name: user.displayName,
email: user.email,
createdAt: user.metadata.creationTime,
);
}
});
@override
bool isUserNew(EquatableUser user) =>
user.createdAt?.isAfter(DateTime.now().subtract(const Duration(seconds: 5))) ?? false;
@override
EquatableUser get currentUser {
final _user = _firebaseAuth.currentUser!;
return EquatableUser(id: _user.uid, name: _user.displayName, email: _user.email);
}
@override
Future<void> login({required EasyAuthProvider provider}) async {
if (provider is EmailPasswordAuth) {
await _firebaseAuth.signInWithEmailAndPassword(email: provider.email, password: provider.password);
} else if (provider is GoogleAuth) {
//sign in with google
}
}
@override
Future<void> register({required EquatableUser user, required String password}) async {
if (user.email == null) throw FirebaseAuthException(code: 'no-email-registration');
await _firebaseAuth.createUserWithEmailAndPassword(email: user.email!, password: password);
}
@override
Future<void> signOut() => _firebaseAuth.signOut();
@override
Future<void> deleteAccount() => _firebaseAuth.currentUser!.delete();
}
EasyAuthProvider #
If you need to add more authentication providers than the pre-packaged ones, simply override the EasyAuthProvider
class.
EasyAuth Widgets #
AuthenticationBasedApp
AuthenticationBasedApp is an abstract class that you need to extend to add authentication responsiveness to your app.
There are a couple of methods you need to know about:
/// Rebuilds the state of the app every time the authentication status changes.
/// This is an efficient method due to `T` extending `Equatable` and
/// therefore only rebuilding when necessary
Widget buildState(BuildContext context, AuthStatus status, T user);
Note: this method needs to be overriden.
/// Called when an exception relating to authentication gets thrown.
/// Can be overriden to provide your own custom error-handling logic (e.g. logging, custome snackbar, etc.)
void handleError(BuildContext context, AuthException exception) {...}
Note: this method has a default implementation that prints the action that was performed when the exception was thrown and displays the following Flushbar
from package:another_flushbar.
Flushbar(
icon: const Padding(padding: EdgeInsets.only(left: 14.0), child: Text('😱')),
message: exception.message,
backgroundColor: Theme.of(context).errorColor,
margin: const EdgeInsets.all(8),
borderRadius: BorderRadius.circular(8.0),
flushbarPosition: FlushbarPosition.TOP,
flushbarStyle: FlushbarStyle.FLOATING,
);
EasyAuthBuilder
EasyAuthBuilder is a Flutter widget which requires a builder
function. EasyAuthBuilder
handles building the widget in response to new authentication states. EasyAuthBuilder
is a simple wrapper around BlocBuilder
from package:bloc. The builder
function will potentially be called many times and should be a pure function that returns a widget in response to the state.
EasyAuthBuilder(
builder: (context, status, user) {
// return widget here based on the current AuthSatus and User
}
)
Examples #
- Basic Valid/Invalid Authentication - an example of how to create a basic authentication system in any Flutter app.
Dart Versions #
- Dart 2: >= 2.12
- Flutter: >=1.17.0