firebase - wurzelsand/flutter-memos GitHub Wiki
Codelab: Set up firebase authentication
Codelab: App with authentication and firestore database
-
Install Flutterfire CLI and Firebase CLI. Login.
dart pub global activate flutterfire_cli sudo npm install -g firebase-tools firebase login
-
Open the Firebase Console to start a new project with any name (here: "godimu"):
-
Add Email/Password as Sign-in method:
-
Create new Flutter project and add dependencies for Firebase:
> flutter create --empty my_app > cd my_app > flutter pub add firebase_core > flutter pub add firebase_auth
-
So that our apps have access to Firebase and the configuration file firebase_options.dart is generated:
> flutterfire configure "Select a Firebase project to configure your Flutter application with" > godimu (godimu) "Which platforms should your configuration support?" > android ios macos web "The files android/build.gradle & android/app/build.gradle will be updated to apply Firebase configuration and gradle build plugins. Do you want to continue?" > yes "Firebase configuration file lib/firebase_option.dart generated successfully with the following Firebase apps: ..."
-
Reload your console web page to see your newly assigned apps. Open the Android settings to configure the Support email:
-
Add a few users that you will allow to log in:
-
Edit lib/main.dart:
import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'firebase_options.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: StreamBuilder<User?>( stream: FirebaseAuth.instance.authStateChanges(), builder: (context, snapshot) { if (snapshot.hasData) { return ElevatedButton( onPressed: () { FirebaseAuth.instance.signOut(); }, child: const Text('Logout'), ); } return const AuthGate(); }, ), ), ), ); } } class AuthGate extends StatefulWidget { const AuthGate({super.key}); @override State<AuthGate> createState() => _AuthGateState(); } class _AuthGateState extends State<AuthGate> { final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _formKey = GlobalKey<FormState>(); @override Widget build(BuildContext context) { return Form( key: _formKey, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextFormField( controller: _emailController, decoration: const InputDecoration( hintText: 'Email', border: OutlineInputBorder(), ), keyboardType: TextInputType.emailAddress, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, obscureText: true, decoration: const InputDecoration( hintText: 'Password', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), ElevatedButton( onPressed: () async { try { await FirebaseAuth.instance.signInWithEmailAndPassword( email: _emailController.text, password: _passwordController.text); } on FirebaseAuthException catch (e) { if (!context.mounted) return; ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar(content: Text(e.message ?? 'Error')), ); } }, child: const Text('Login'), ), ], ), ), ); } @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } }
-
Launch the program on an Android device
-
To solve a common error on Android: Enabling Multidex Support
flutter run --debug
-
-
To host the app on Firebase:
> firebase init "Which Firebase features do you want to set up for this directory?" > Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys "Please select an option:" > Use an existing project "Select a default Firebase project for this directory:" > godimu (godimu) "What do you want to use as your public directory?" > build/web "Configure as a single-page app (rewrite all urls to /index.html)?" > Yes "Set up automatic builds and deploys with GitHub?" > No "Wrote build/web/index.html" > flutter build web > firebase deploy "Hosting URL: https://godimu.web.app"
Starting from the first Firebase codelab from Google, we create a program that runs on iOS, Android, macOS and web. We also host it on Firebase.
- Open Firebase Console.
- Add a project with an arbitrary name, here: "Attentive Bloom"
- Project name: "Attentive Bloom"
- Disable Google Analytics
- Click "Authentication" image button. Click "Get started" image button.
- Sign-in method
- Enable Email/Password
- Open terminal:
flutter create --empty attentive_bloom
cd attentive_bloom
firebase login
-
firebase projects:list
(to see attentivebloom) -
flutterfire configure
- Select attentivebloom. File firebase_options.dart is generated.
flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add flutterfire_ui
-
Open Firebase Console.
- Reload.
- Edit Support email of Android apps settings.
- Write your app. Code: see below.
- Running on MacOS will fail:
- To macos/Podfile add:
after line:
target.build_configurations.each do |config| config.build_settings.delete 'MACOSX_DEPLOYMENT_TARGET' end
flutter_additional_macos_build_settings(target)
- To Runner/DebugProfile.entitlements and Runner/Release.entitlements add
<key>com.apple.security.network.client</key> <true/>
- Maybe keychain problems.
- To macos/Podfile add:
- Open Firebase Console and choose Project Overview.
- To add an app to get started click web-icon.
- Register app: "Attentive Bloom".
- Enable Also set up Firebase Hosting.
- next => next => Deploy to Firebase Hosting
- Change to terminal:
firebase login
-
firebase init
- Choose Hosting: Configure files.
- public directory: "build/web"
- single page app: "yes"
- Github: "no"
flutter build web
firebase deploy
main.dart:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutterfire_ui/auth.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: AuthGate(),
);
}
}
class AuthGate extends StatelessWidget {
const AuthGate({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SignInScreen(
providerConfigs: [
EmailProviderConfiguration(),
],
);
}
return const HomeScreen();
},
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: const Icon(Icons.person),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<ProfileScreen>(
builder: (context) => ProfileScreen(
appBar: AppBar(
title: const Text('User Profile'),
),
actions: [
SignedOutAction((context) {
Navigator.of(context).pop();
})
],
children: const [
Text('You can customize the profile screen here'),
],
),
),
);
},
)
],
automaticallyImplyLeading: false,
),
body: Center(
child: Column(
children: [
const Icon(Icons.celebration),
Text(
'Welcome!',
style: Theme.of(context).textTheme.displaySmall,
),
const SignOutButton(),
],
),
),
);
}
}
-
Add
async
:import 'dart:async';
-
Add
actions
-parameter toSignInScreen
:actions: [ AuthStateChangeAction((context, state) { if (state is UserCreated) { state.credential.user?.sendEmailVerification(); } }), ],
-
Create stream:
Stream<User?> getEmailVerifiedUser() async* { bool finished = false; while (!finished) { final user = FirebaseAuth.instance.currentUser; await user?.reload(); if (user == null) { finished = true; yield null; } else if (user.emailVerified) { finished = true; yield user; } else { await Future.delayed(const Duration(seconds: 5)); } } }
-
Use Stream to decide wether to return
HomeScreen
orWaitingForEmailVerificationScreen
:return StreamBuilder<User?>( stream: getEmailVerifiedUser(), builder: (context, snapshot) { if (snapshot.hasData) { return const HomeScreen(); } return const WaitingForEmailVerificationScreen(); }, );
class WaitingForEmailVerificationScreen extends StatelessWidget { const WaitingForEmailVerificationScreen({super.key}); @override Widget build(BuildContext context) { return const Column( children: [ Text('Waiting for email verification'), SignOutButton(), ], ); } }
-
Build/Firestore Database: Click "Create database" image button.
- Start in test mode.
- Choose cloud location.
-
Terminal:
flutter pub add cloud_firestore
-
Add
cloud_firestore.dart
:import 'package:cloud_firestore/cloud_firestore.dart';
-
Change
actions
forSignInScreen
by creating a Firestore collectionprofile
and adding documents with fieldsemail
,uid
andpermission
:actions: [ AuthStateChangeAction((context, state) { if (state is UserCreated) { final user = state.credential.user; if (user == null) return; FirebaseFirestore.instance.collection('profile').add({ 'uid': user.uid, 'email': user.email, 'permission': false, }); user.sendEmailVerification(); } }), ],
-
Instead of returning
HomeScreen
after email verification, listen toprofile
collection:final user = snapshot.data!; return StreamBuilder( stream: FirebaseFirestore.instance .collection('profile') .snapshots(), builder: (context, snapshot) { if (snapshot.hasData) { final allProfiles = snapshot.data!.docs; final profileOfCurrentUser = allProfiles.firstWhere( (profile) => profile.data()['uid'] == user.uid); if (profileOfCurrentUser.data()['permission'] == true) { return const HomeScreen(); } } return const WaitingForPermissionScreen(); }, );
class WaitingForPermissionScreen extends StatelessWidget { const WaitingForPermissionScreen({super.key}); @override Widget build(BuildContext context) { return const Column( children: [ Text('Waiting for permission'), SignOutButton(), ], ); } }
-
Go to Rules tab of Cloud Firestore where you find something like this:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.time < timestamp.date(2023, 6, 23); } } }
Change it to:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /profile/{entry} { allow read: if request.auth.uid != null; allow write: if request.auth.uid != null && request.resource.data.uid == request.auth.uid && request.resource.data.permission == true } } }
If you want to see all users email verification status:
firebase auth:export /dev/stdout --format json | grep "email"
flutter pub add firebase_storage
import 'package:firebase_storage/firebase_storage.dart';
When you want to download a file, such as an image or video, from your storage bucket using the web platform, the process is initially terminated by an exception . This is due to CORS. To solve the problem, install gsutils.
After initialization (./google-cloud-sdk/bin/gcloud init
) create the file cors.json:
[
{
"origin": [
"*"
],
"method": [
"GET"
],
"maxAgeSeconds": 3600
}
]
gsutil cors set cors.json gs://PROJECT_NAME.appspot.com
import 'dart:convert';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html';
import 'package:flutter/foundation.dart';
final storageRef = FirebaseStorage.instance.ref();
final pictureRef = storageRef.child('images/image.jpg');
Uint8List? data = await pictureRef.getData();
if (data != null && kIsWeb) {
final String base64data = base64Encode(data);
// 'data:image/jpeg;base64,$base64data'
final anchor = AnchorElement(href: 'data:;base64,$base64data');
anchor.download = 'downloaded_image.jpg';
anchor.click();
anchor.remove();
}