fluo 3.3.2
fluo: ^3.3.2 copied to clipboard
Authentication made for Flutter. Complete UI flows in less than 5 minutes.
Fluo #
📝 Looking for the full documentation? Check out the new Fluo SDK docs →
- Getting started
- More about the SDK
- Integrating with Firebase
- Integrating with Supabase
- Integrating with any backend
- Customizing the theme
Getting started #
Add the package to your dependencies:
flutter pub add fluo
Add the FluoLocalizations.delegate to your app's localizationsDelegates:
MaterialApp(
// ...other properties...
localizationsDelegates: const [
FluoLocalizations.delegate,
// ...other delegates...
],
)
Use the Fluo SDK:
import 'package:fluo/fluo.dart';
import 'package:fluo/fluo_onboarding.dart';
FutureBuilder(
// Get your api key from https://dashboard.fluo.dev (it's free)
future: Fluo.init('YOUR_API_KEY'),
builder: (context, snapshot) {
// Check if Fluo is initialized.
if (!Fluo.isInitialized) {
return const Scaffold();
}
// Fluo is initialized. Check if the user is ready.
if (!Fluo.instance.isUserReady()) {
return FluoOnboarding(
fluoTheme: FluoTheme.native(), // or FluoTheme.web()
onUserReady: () => setState(() {}), // force build
);
}
// User is ready!
// An example `ConnectedScreen` widget is available in
// the example/lib/main.dart file.
return ConnectedScreen(
onSignOut: () async {
await Fluo.instance.clearSession();
setState(() {});
},
);
},
)
For macOS, make sure you have networking allowed by adding this key to both {your-app}/macos/Runner/DebugProfile.entitlements and {your-app}/macos/Runner/Release.entitlements:
<dict>
<!-- Add this key set to true -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
More about the SDK #
Below are the most important methods to know:
// Initialize the SDK
await Fluo.init('YOUR_API_KEY');
// Check if init is done
if (Fluo.isInitialized) {
// ...
}
// Check if user is ready (valid session + complete user attributes)
if (Fluo.instance.isUserReady()) {
// ...
}
// Only check if a session exists
if (Fluo.instance.hasSession()) {
// ...
}
// Get a certified fresh access token. "Certified fresh" means that
// if the access token has expired, Fluo will first refresh it before
// returning it. This is an important method if you selected a "custom"
// backend.
String accessToken = await Fluo.instance.getAccessToken();
// Force refresh the session. Generally, you should not need it because
// `Fluo.instance.getAccessToken()` handles refreshing the session.
await Fluo.instance.refreshSession();
// Clear the locally stored token. This is equivalent to a sign out.
await Fluo.instance.clearSession();
// User data
String userId = Fluo.instance.session.user.id; // "jzi8w7bdou4m0kq"
String email = Fluo.instance.session.user.email; // "peter.parker@marvel.com"
String mobileE164 = Fluo.instance.session.user.mobileE164; // "+14155556766"
String mobileIso2 = Fluo.instance.session.user.mobileIso2; // "US"
String firstName = Fluo.instance.session.user.firstName; // "Peter"
String lastName = Fluo.instance.session.user.lastName; // "Parker"
// If you build your own connect screen (and don't use FluoOnboarding)
// When authentication is done, these flows will automatically show the
// registration steps (you don't need to call `showRegisterFlow`).
Fluo.instance.showConnectWithEmailFlow(/* ... */)
Fluo.instance.showConnectWithMobileFlow(/* ... */)
Fluo.instance.showConnectWithGoogleFlow(/* ... */)
Fluo.instance.showConnectWithAppleFlow(/* ... */)
// Force show the registration steps. Generally, you should not need it.
Fluo.instance.showRegisterFlow(/* ... */)
Integrating with Firebase #
Select 'Firebase' for your backend. Once complete, when users are onboarded, Fluo forwards their info to:
- the Firebase Authentication service
- a
userstable created automatically in the Firestore Database (make sure the Firestore Database is initialized)
Back to your app code, to initialize correctly the Firebase session, use the Fluo.instance.session.firebaseToken as below:
// 1. Initialize the Firebase client somewhere in your code
// 2. Make sure Fluo is initialized and has a session
// 3. Use 'signInWithCustomToken' as shown below
if (Fluo.isInitialized) {
final fluoSession = Fluo.instance.session;
if (fluoSession != null) {
final firebaseToken = fluoSession.firebaseToken!;
await FirebaseAuth.instance.signInWithCustomToken(firebaseToken);
}
}
Integrating with Supabase #
Select 'Supabase' for your backend. Once complete, when users are onboarded, Fluo forwards their info to:
- the Supabase Authentication service
- a
userstable that you will create as part of the Supabase setup (no worries, it's a simple copy-paste)
Back to your app code, to initialize correctly the Supabase session, use the Fluo.instance.session.supabaseSession as below:
// 1. Initialize the Supabase client somewhere in your code
// 2. Make sure Fluo is initialized and has a session
// 3. Use 'recoverSession' as shown below
if (Fluo.isInitialized) {
final fluoSession = Fluo.instance.session;
if (fluoSession != null) {
final supabaseSession = fluoSession.supabaseSession!;
await Supabase.instance.client.auth.recoverSession(supabaseSession);
}
}
Integrating with any backend #
Select 'Custom' for your backend. The general idea is to use the JWT access token provided by Fluo to get a unique user id via the "sub" JWT claim.
Here is a full example to understand how it works:
- Wherever you need it, call
Fluo.instance.getAccessToken()to get the JWT access token generated by Fluo and send it to your backend.
import 'dart:convert';
import 'package:http/http.dart' as http;
// Example of a function that gets a user. If the user
// doesn't exist yet, it should create it first.
Future<User> getOrCreateUser() async {
final accessToken = await Fluo.instance.getAccessToken();
final response = await http.post(
Uri.parse('https://your-backend.com/api/user/me'),
// Send the JWT access token to securely authenticate
// the user and retrieve the user id.
headers: {
'authorization': 'Bearer $accessToken',
},
// Send the user data to create or update the user if
// the object does not exist yet.
body: jsonEncode(Fluo.instance.session.user),
);
return User.fromJson(jsonDecode(response.body));
}
- In your backend, decode the access token to get the JWT payload.
const jwt = require("jsonwebtoken")
// This is your JWT secret key (do not share it with anyone)
// You can find it on https://dashboard.fluo.dev/backend
const SECRET_KEY = "YOUR_SECRET_KEY"
// Following on the example, here is the corresponding endpoint.
// Note that this is simplified and does not handle all edge cases.
app.post("/api/user/me", async (req, res) => {
const accessToken = req.headers["authorization"].split(" ")[1]
// Decode the access token using your secret key
const payload = jwt.verify(accessToken, SECRET_KEY)
// 'payload.sub' contains a unique user id generated by Fluo
const userId = payload.sub
// Find the user by id
let user = await User.findOne({ id: userId }) // or { fluoId: userId }
// If the user doesn't exist, create it
if (!user) {
const { email, mobileE164, mobileIso2, firstName, lastName } = req.body
user = await User.create({
id: userId,
email: email,
mobileE164: mobileE164,
mobileIso2: mobileIso2,
firstName: firstName,
lastName: lastName,
})
}
return res.status(200).json(user)
})
- If you need to go further, here is a complete example of the payload. For example, for increased security, you might want to verify that the token has not expired.
{
"sub": "2rztxukf57pnjz9", // user id
"iat": 1744039599, // issued at
"exp": 1744043199, // expires 1 hour after being issued
"iss": "fluo.dev", // issuer
}
Customizing the theme #
Pass a FluoTheme to the FluoOnboarding component.
For iOS, Android, macOS
FluoOnboarding(
// ...other properties...
fluoTheme: FluoTheme.native(/* parameters */),
)
For web
FluoOnboarding(
// ...other properties...
fluoTheme: FluoTheme.web(/* parameters */),
)
Parameters
{
Color? primaryColor,
Color? inversePrimaryColor,
Color? accentColor,
EdgeInsets? screenPadding,
ButtonStyle? connectButtonStyle,
ButtonStyle? connectButtonStyleGoogle,
ButtonStyle? connectButtonStyleApple,
TextStyle? connectButtonTextStyle,
TextStyle? connectButtonTextStyleGoogle,
TextStyle? connectButtonTextStyleApple,
double? connectButtonIconSize,
Widget? connectButtonIconEmail,
Widget? connectButtonIconMobile,
Widget? connectButtonIconGoogle,
Widget? connectButtonIconApple,
TextStyle? legalTextStyle,
EdgeInsets? legalTextPadding,
TextStyle? modalTitleTextStyle,
TextStyle? titleStyle,
InputDecorationTheme? inputDecorationTheme,
TextStyle? inputTextStyle,
TextStyle? inputErrorStyle,
TextAlignVertical? inputTextAlignVertical,
ButtonStyle? continueButtonStyle,
Size? continueButtonProgressIndicatorSize,
Color? continueButtonProgressIndicatorColor,
double? continueButtonProgressIndicatorStrokeWidth,
EdgeInsets? countryItemPadding,
Color? countryItemHighlightColor,
TextStyle? countryTextStyle,
PinTheme? codeInputThemeDefault,
PinTheme? codeInputThemeFocused,
PinTheme? codeInputThemeSubmitted,
PinTheme? codeInputThemeFollowing,
PinTheme? codeInputThemeDisabled,
PinTheme? codeInputThemeError,
}