Hey everyone! Today, we're tackling the most requested Firebase integration: Auth and Firestore are still dominating for quick, scalable backends. We'll build a Notes app with email/password login and real-time Firestore sync while fixing common setup headaches along the way.
Set up and Why?
Why Firebase now? Real-time data, offline persistence, and auth in minutes - plus emerging AI tools like Firebase Genkit.
Setup:
- Go to console.firebase.google.com → Create a New Firebase project.
Click on add app → Select Flutter → Follow through the process

Enable Auth methods (Email & Password) and Firestore in the console.

Start with a new project or your counter app: flutter create firebase_notes.
pubspec.yaml:
firebase_core: ^4.3.0
firebase_auth: ^6.1.3
cloud_firestore: ^6.1.1
flutter pub get.
Initialize Firebase (main.dart):
import 'package:flutter/material.dart';
import 'package:flutter_firebase_tutorial/firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
runApp(const MyApp());
}
Generate firebase_options.dart via FlutterFire CLI: dart pub global pub global activate flutterfire_cli then flutterfire configure.
Auth screen (login_signup.dart):
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_firebase_tutorial/home.dart';
class LoginSignUp extends StatefulWidget {
const LoginSignUp({super.key});
@override
State<LoginSignUp> createState() => _LoginSignUpState();
}
class _LoginSignUpState extends State<LoginSignUp> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login SignUp Screen')),
body: Column(
children: [
Text('Login SignUp Screen'),
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 16.0),
TextFormField(controller: _passwordController, obscureText: true),
SizedBox(height: 16.0),
ElevatedButton(
child: Text("Login"),
onPressed: () async {
String email = _emailController.text.trim();
String password = _passwordController.text.trim();
try {
// Try to sign in
await FirebaseAuth.instance
.signInWithEmailAndPassword(
email: email,
password: password,
)
.then((c) {
if (!context.mounted) return;
Navigator.push(
context,
MaterialPageRoute(builder: (_) => HomeScreen()),
);
});
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
// If user not found, create a new account
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password,
);
} else {
// Handle other errors
print('Error: $e');
}
}
},
),
SizedBox(height: 16.0),
ElevatedButton(
child: Text("Sign Up"),
onPressed: () async {
String email = _emailController.text.trim();
String password = _passwordController.text.trim();
try {
// Try to sign in
await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: email,
password: password,
)
.then((v) {
if (!context.mounted) return;
Navigator.push(
context,
MaterialPageRoute(builder: (_) => HomeScreen()),
);
});
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
// If user not found, create a new account
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password,
);
} else {
// Handle other errors
print('Error: $e');
}
}
},
),
],
),
);
}
}
Firestore: Collection 'notes' per user (users/{uid}/notes).
Stream notes:
FirebaseFirestore.instance
.collection('users')
.doc(uid)
.collection('notes')
.snapshots()
Add notes:
await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.collection('notes')
.add({'title': title, 'content': content, 'timestamp': FieldValue.serverTimestamp()});
Full Code (update the field where necessary). UI: ListView.builder with StreamBuilder.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
User? user = FirebaseAuth.instance.currentUser;
final db = FirebaseFirestore.instance;
final TextEditingController _textController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home Screen')),
body: Column(
children: [
Text('Home Screen'),
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: db
.collection('texts')
.doc(user!.uid)
.collection('user_texts')
.snapshots(),
builder:
(
BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,
) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
final data = snapshot.requireData;
return ListView.builder(
itemCount: data.size,
itemBuilder: (context, index) {
return ListTile(title: Text(data.docs[index]['text']));
},
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
color: Colors.white,
child: Column(
children: [
SizedBox(height: 16.0),
TextFormField(
maxLines: 4,
controller: _textController,
decoration: InputDecoration(labelText: 'Enter some text'),
),
SizedBox(height: 16.0),
ElevatedButton(
child: Text("Submit"),
onPressed: () {
String enteredText = _textController.text;
print('Entered Text: $enteredText');
db
.collection('texts')
.doc(user!.uid)
.collection('user_texts')
.add({'text': enteredText});
_textController.clear();
Navigator.pop(context);
},
),
],
),
);
},
);
},
child: Icon(Icons.add),
),
);
}
}
Basic Firestore Rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/{document=**} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
That's all! Test the app and see how it works.
I hope you've learn something incredible. Press that follow button if you're not following me yet. Also, make sure to subscribe to the newsletter so you're notified when I publish a new article.
🔗 Let's Connect 🔗 → Github | Twitter | LinkedIn.
Join my Community 👨💻👨💻 on Discord.
Subscribe to my YouTube channel.
Happy Building! 🥰👨💻
Top comments (0)