Learn how to create a complete Android application using Flutter and Firebase from scratch
๐ Table of Contents
- Introduction
- Prerequisites
- Setting Up Your Development Environment
- Creating a New Flutter Project
- Setting Up Firebase
- Connecting Flutter to Firebase
- Building the User Interface
- Implementing Firebase Authentication
- Storing Data with Firestore
- Testing Your App
- Conclusion
๐ Introduction
Flutter has revolutionized mobile app development by allowing developers to create beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. When combined with Firebase, Google's mobile and web application development platform, you get a powerful stack that handles everything from authentication to real-time databases.
In this tutorial, we'll build a simple Task Management App that demonstrates:
- User authentication with Firebase Auth
- Real-time data storage with Firestore
- Beautiful UI with Flutter widgets
- Proper project structure and best practices
๐ Prerequisites
Before we start, make sure you have:
- Basic programming knowledge (Dart syntax is similar to JavaScript/TypeScript)
- Android Studio installed (includes Android SDK and emulator)
- Flutter SDK installed
- A Google account for Firebase
- Visual Studio Code (optional, but recommended)
Installing Flutter
If you haven't installed Flutter yet:
# Download Flutter SDK from https://flutter.dev/docs/get-started/install
# Extract and add to PATH
export PATH="$PATH:/path/to/flutter/bin"
# Verify installation
flutter doctor
๐ ๏ธ Setting Up Your Development Environment
1. Install Android Studio
- Download Android Studio
- Install the Flutter and Dart plugins
- Set up an Android virtual device (AVD)
2. Verify Your Setup
Run this command to ensure everything is working:
flutter doctor -v
You should see green checkmarks for:
- Flutter toolchain
- Android toolchain
- Connected device (emulator or physical device)
๐ฑ Creating a New Flutter Project
Let's create our Flutter project:
# Create a new Flutter project
flutter create task_manager_app
cd task_manager_app
# Open in VS Code (optional)
code .
Project Structure Overview
task_manager_app/
โโโ android/ # Android-specific code
โโโ ios/ # iOS-specific code
โโโ lib/ # Main Dart code
โ โโโ main.dart # Entry point
โ โโโ screens/ # App screens
โโโ pubspec.yaml # Dependencies
โโโ test/ # Test files
๐ฅ Setting Up Firebase
1. Create a Firebase Project
- Go to Firebase Console
- Click "Add project"
- Enter project name:
task-manager-app - Enable Google Analytics (optional)
- Click "Create project"
2. Add Android App to Firebase
- In Firebase Console, click the Android icon
- Package name: Find it in
android/app/build.gradle(usuallycom.example.task_manager_app) - Download
google-services.json - Place it in
android/app/directory
3. Enable Firebase Services
In your Firebase project, enable:
- Authentication โ Email/Password
- Firestore Database โ Start in test mode
๐ Connecting Flutter to Firebase
1. Add Firebase Dependencies
Update your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.15.0
firebase_auth: ^4.7.0
cloud_firestore: ^4.8.0
firebase_ui_auth: ^1.6.0
Run flutter pub get to install dependencies.
2. Configure Android
Add these lines to android/app/build.gradle:
apply plugin: 'com.google.gms.google-services'
android {
// ...
defaultConfig {
// ...
minSdkVersion 19
}
}
Add to android/build.gradle:
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.3.15'
}
}
๐จ Building the User Interface
Let's create a simple task management interface.
1. Create the Main App Structure
Replace lib/main.dart:
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'screens/auth_screen.dart';
import 'screens/home_screen.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Task Manager',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: AuthWrapper(),
);
}
}
class AuthWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
return user != null ? HomeScreen() : AuthScreen();
}
}
2. Create Authentication Screen
Create lib/screens/auth_screen.dart:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class AuthScreen extends StatefulWidget {
@override
_AuthScreenState createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _isLogin = true;
bool _isLoading = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isLogin ? 'Login' : 'Sign Up'),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 16),
TextField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
obscureText: true,
),
SizedBox(height: 24),
_isLoading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: _authenticate,
child: Text(_isLogin ? 'Login' : 'Sign Up'),
style: ElevatedButton.styleFrom(
minimumSize: Size(double.infinity, 50),
),
),
TextButton(
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
child: Text(_isLogin
? 'Don\'t have an account? Sign up'
: 'Already have an account? Login'),
),
],
),
),
);
}
Future<void> _authenticate() async {
setState(() => _isLoading = true);
try {
if (_isLogin) {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text,
);
} else {
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text,
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: ${e.message}')),
);
} finally {
setState(() => _isLoading = false);
}
}
}
3. Create Home Screen
Create lib/screens/home_screen.dart:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _taskController = TextEditingController();
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
@override
Widget build(BuildContext context) {
final user = _auth.currentUser;
return Scaffold(
appBar: AppBar(
title: Text('Task Manager'),
actions: [
IconButton(
icon: Icon(Icons.logout),
onPressed: _logout,
),
],
),
body: Column(
children: [
Padding(
padding: EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _taskController,
decoration: InputDecoration(
labelText: 'Add a new task',
border: OutlineInputBorder(),
),
),
),
SizedBox(width: 8),
IconButton(
icon: Icon(Icons.add),
onPressed: _addTask,
style: IconButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
),
],
),
),
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('tasks')
.where('userId', isEqualTo: user?.uid)
.orderBy('createdAt', descending: true)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
return Center(child: Text('No tasks yet. Add one!'));
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
final doc = snapshot.data!.docs[index];
final task = doc.data() as Map<String, dynamic>;
return Card(
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
title: Text(task['title']),
subtitle: Text(
'Created: ${task['createdAt'].toDate().toString().substring(0, 19)}',
),
trailing: IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => _deleteTask(doc.id),
),
leading: Checkbox(
value: task['completed'] ?? false,
onChanged: (value) => _toggleTask(doc.id, value!),
),
),
);
},
);
},
),
),
],
),
);
}
Future<void> _addTask() async {
if (_taskController.text.trim().isEmpty) return;
final user = _auth.currentUser;
if (user == null) return;
try {
await _firestore.collection('tasks').add({
'title': _taskController.text.trim(),
'completed': false,
'userId': user.uid,
'createdAt': Timestamp.now(),
});
_taskController.clear();
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error adding task: $e')),
);
}
}
Future<void> _deleteTask(String taskId) async {
try {
await _firestore.collection('tasks').doc(taskId).delete();
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error deleting task: $e')),
);
}
}
Future<void> _toggleTask(String taskId, bool completed) async {
try {
await _firestore.collection('tasks').doc(taskId).update({
'completed': completed,
});
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error updating task: $e')),
);
}
}
Future<void> _logout() async {
await _auth.signOut();
}
}
๐ Implementing Firebase Authentication
Our authentication system is already implemented! Here's what we've done:
Email/Password Authentication
- Users can sign up with email and password
- Firebase handles password hashing and security
- Automatic login state persistence
Key Features
- Input validation: Basic email format checking
- Error handling: User-friendly error messages
- Loading states: Visual feedback during authentication
- State management: Automatic UI updates on auth changes
๐ Storing Data with Firestore
Firestore is a flexible, scalable NoSQL cloud database. Here's how we're using it:
Data Structure
tasks/
[taskId]: {
title: "Complete Flutter tutorial",
completed: false,
userId: "user-uid",
createdAt: timestamp
}
Key Features Implemented
- Real-time updates: UI updates automatically when data changes
- User-specific data: Each user only sees their own tasks
- CRUD operations: Create, Read, Update, Delete functionality
- Query optimization: Efficient data fetching with where clauses
๐งช Testing Your App
Run on Emulator
# Check available devices
flutter devices
# Run on specific device
flutter run -d <device-id>
Test on Physical Device
- Enable developer options on your Android device
- Enable USB debugging
- Connect device to computer
- Run
flutter devicesto verify connection - Run
flutter run
Common Testing Scenarios
-
Authentication Flow
- Sign up with new account
- Login with existing account
- Logout and login again
-
Task Management
- Add new tasks
- Mark tasks as complete
- Delete tasks
- Verify data persistence
-
Real-time Updates
- Open app on multiple devices
- Verify changes sync across devices
๐ฏ Best Practices and Tips
Security
- Always validate user input on both client and server
- Use Firebase Security Rules for production apps
- Never store sensitive data in client-side code
Performance
- Use pagination for large datasets
- Implement proper error handling
- Optimize widget rebuilds with
constconstructors
User Experience
- Provide loading indicators
- Show meaningful error messages
- Implement proper navigation flow
๐ Next Steps
Congratulations! You've built a complete Flutter app with Firebase integration. Here are some ways to enhance your app:
-
Add More Features
- Task categories and priorities
- Due dates and notifications
- Task sharing between users
-
Improve UI/UX
- Custom animations and transitions
- Dark mode support
- Responsive design for tablets
-
Advanced Firebase
- Cloud Functions for server-side logic
- Firebase Storage for file uploads
- Firebase Analytics for user insights
-
Deployment
- Build release APK:
flutter build apk --release - Publish to Google Play Store
- Set up CI/CD pipeline
- Build release APK:
๐ Conclusion
Building mobile apps with Flutter and Firebase offers an incredible development experience. You get:
- Fast development with hot reload
- Beautiful UI with Material Design
- Real-time functionality out of the box
- Scalable backend without server management
- Cross-platform support from a single codebase
The combination of Flutter's expressive UI framework and Firebase's powerful backend services makes it possible to build production-quality applications quickly and efficiently.
Remember, this is just the beginning. The Flutter and Firebase ecosystems are constantly evolving, with new features and improvements being added regularly. Keep learning, experimenting, and building amazing apps!

Top comments (0)