Build a Chat App with Flutter and Firebase Realtime Database

by Didin J. on May 13, 2025 Build a Chat App with Flutter and Firebase Realtime Database

Build a real-time Flutter chat app using Firebase Realtime Database with anonymous auth. Works on Android & iOS. Full source code and tutorial

Introduction

In this tutorial, you will learn how to build a real-time chat application using Flutter and Firebase Realtime Database. This app will allow users to send and receive messages instantly with a simple and intuitive interface.

We'll use Firebase for backend services such as real-time database and authentication, and Flutter for building the cross-platform mobile interface.

By the end of this tutorial, you'll have a working chat app and complete source code to share on GitHub.

Prerequisites

Before you begin, make sure you have the following ready:

  • Flutter SDK installed: Install Flutter https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.29.3-stable.zip
  • An IDE like VS Code or Android Studio
  • A Firebase account: https://firebase.google.com
  • Basic knowledge of Dart and Flutter


Step 1: Create a New Flutter Project

Using Flutter CLI

Open your terminal and run the following command to create a new Flutter project:

flutter create flutter_chat_app
cd flutter_chat_app

Then, open the project in your preferred IDE. 

Using Android Studio

Open Android Studio, then click the Create New Flutter Project button.

Build a Chat App with Flutter and Firebase Realtime Database - Android Studio Start

Make sure the Flutter SDK path points to your downloaded and extracted Flutter SDK. Click the Next button. Fill out all the required forms.

Build a Chat App with Flutter and Firebase Realtime Database - Android Studio New Project

Click the Next button then you will have a new Flutter project open in Android Studio.

Build a Chat App with Flutter and Firebase Realtime Database - Android Studio IDE


Step 2: Set Up Firebase for the Project

2.1 Create Firebase Project

  • Go to Firebase Console https://console.firebase.google.com
  • Click Add project, give it a name (e.g., FlutterChatApp)
  • Disable Google Analytics (optional), then click Create project

2.2 Register Your Android App

  • Choose Android (you can add iOS later)
  • Enter your app’s Android package name (e.g., com.djamware.flutter_chat_app)
  • Download the google-services.json file
  • Place it inside the android/app/ folder

2.3 Register Your iOS App

  • Click Add App > iOS (if not already added).
  • Enter:
    • iOS bundle ID: com.djamware.flutterChatApp (must match your actual app ID in Xcode)
    • App nickname: Optional
  • Click Register App.
  • Download the GoogleService-Info.plist.
  • Move GoogleService-Info.plist into:
        ios/Runner/GoogleService-Info.plist
  • In Xcode:
    • Right-click the Runner folder
    • Select Add Files to "Runner"...
    • Add the GoogleService-Info.plist

✅ Make sure it’s added to all targets.

2.4 Enable Firebase Realtime Database

  • In Firebase Console, go to Build > Realtime Database
  • Click Create Database
  • Select a location and start in Test Mode
// Sample rules for testing (not recommended for production)
{
  "rules": {
    ".read": "true",
    ".write": "true"
  }
}


Step 3: Add Firebase Dependencies

Open pubspec.yaml and add the following dependencies:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.8
  firebase_core: ^2.30.0
  firebase_database: ^10.4.0
  firebase_auth: ^5.5.0

Then run:

flutter pub get

Or click the Pub get button.


Step 4: Initialize Firebase in Flutter

4.1 Modify android/build.gradle.kts

Add the following at the bottom:

buildscript {
    repositories {
        google()
        mavenCentral()
    }

    dependencies {
        classpath("com.google.gms:google-services:4.3.15")
    }
}

4.2 Modify android/app/build.gradle

Add this line at the bottom:

plugins {
    id("com.android.application")
    id("kotlin-android")
    // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
    id("dev.flutter.flutter-gradle-plugin")
    id("com.google.gms.google-services")
}

4.3 Update iOS Project Files

ios/Podfile

Uncomment and ensure this line is set:

platform :ios, '13.0'

✅ Firebase now requires iOS 13+.

4.4 Initialize Firebase in Swift

Edit ios/Runner/AppDelegate.swift:

import Flutter
import UIKit
import FirebaseCore

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    FirebaseApp.configure()
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

4.7 Modify main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Chat App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ChatScreen(),
    );
  }
}

We'll create the ChatScreen in a later step.


Step 5: Anonymous Authentication and UI Layout

5.1 Enable Anonymous Authentication in Firebase

  • Go to Firebase Console
  • Select your project
  • Navigate to Build > Authentication
  • Click the Sign-in method tab
  • Enable Anonymous and click Save

5.2 Sign in Anonymously in Flutter

Now let’s modify main.dart to handle anonymous login and show the chat screen.

Update your main.dart:

import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'chat_screen.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  await FirebaseAuth.instance.signInAnonymously(); // Anonymous login
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Chat App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ChatScreen(),
    );
  }
}

5.3 Create the Chat Screen UI

Create a new file: lib/chat_screen.dart

import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_auth/firebase_auth.dart';

class ChatScreen extends StatefulWidget {
  const ChatScreen({super.key});

  @override
  ChatScreenState createState() => ChatScreenState();
}

class ChatScreenState extends State<ChatScreen> {
  final _messageController = TextEditingController();
  final _database = FirebaseDatabase.instance.ref("messages");

  @override
  void dispose() {
    _messageController.dispose();
    super.dispose();
  }

  void _sendMessage() {
    final text = _messageController.text.trim();
    if (text.isEmpty) return;

    final user = FirebaseAuth.instance.currentUser;
    final timestamp = DateTime.now().millisecondsSinceEpoch;

    _database.push().set({
      'text': text,
      'uid': user?.uid,
      'timestamp': timestamp,
    });

    _messageController.clear();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Chat App")),
      body: Column(
        children: [
          Expanded(
            child: StreamBuilder<DatabaseEvent>(
              stream: _database.orderByChild("timestamp").onValue,
              builder: (context, snapshot) {
                if (snapshot.hasData && snapshot.data!.snapshot.value != null) {
                  final messagesMap = snapshot.data!.snapshot.value as Map;
                  final messages = messagesMap.entries.toList()
                    ..sort((a, b) => (a.value['timestamp'] as int).compareTo(b.value['timestamp'] as int));

                  return ListView.builder(
                    itemCount: messages.length,
                    itemBuilder: (context, index) {
                      final msg = messages[index].value;
                      final isMe = msg['uid'] == FirebaseAuth.instance.currentUser?.uid;
                      return Align(
                        alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
                        child: Container(
                          padding: const EdgeInsets.all(12),
                          margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
                          decoration: BoxDecoration(
                            color: isMe ? Colors.blue[100] : Colors.grey[300],
                            borderRadius: BorderRadius.circular(8),
                          ),
                          child: Text(msg['text']),
                        ),
                      );
                    },
                  );
                }
                return const Center(child: Text("No messages yet."));
              },
            ),
          ),
          const Divider(height: 1),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _messageController,
                    decoration: const InputDecoration(
                      hintText: 'Type a message...',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                ElevatedButton(
                  onPressed: _sendMessage,
                  child: const Text("Send"),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

What We’ve Done

  • Users are signed in anonymously via FirebaseAuth
  • Built a real-time chat screen with:
    •   A message input
    •   Send button
    •   Message list that updates automatically using StreamBuilder and Firebase Realtime Database


Step 6: Finalizing Chat Features and Project Structure

In this step, we’ll:

  • Improve the message display with timestamps
  • Add better UI styling
  • Refactor the code into a cleaner folder structure
  • Add a simple user ID indicator
  • Optionally allow logout (even with anonymous users)

6.1 Add Timestamps to Messages

Update the message widget in chat_screen.dart to show the time:

Replace this part in your ListView.builder:

child: Container(
  padding: const EdgeInsets.all(12),
  margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
  decoration: BoxDecoration(
    color: isMe ? Colors.blue[100] : Colors.grey[300],
    borderRadius: BorderRadius.circular(8),
  ),
  child: Text(msg['text']),
),

With this improved UI block:

child: Container(
  padding: const EdgeInsets.all(10),
  margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
  decoration: BoxDecoration(
    color: isMe ? Colors.lightBlueAccent.withOpacity(0.3) : Colors.grey[300],
    borderRadius: BorderRadius.circular(8),
  ),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(
        msg['text'],
        style: const TextStyle(fontSize: 16),
      ),
      const SizedBox(height: 4),
      Text(
        _formatTimestamp(msg['timestamp']),
        style: const TextStyle(fontSize: 12, color: Colors.grey),
      ),
    ],
  ),
),

And add this helper function at the bottom of the state class:

String _formatTimestamp(dynamic timestamp) {
  if (timestamp == null) return '';
  final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
  final time = TimeOfDay.fromDateTime(date);
  return "${time.format(context)}";
}

6.2 Refactor Project Structure

Organize your app like this:

lib/
│
├── main.dart
├── screens/
│   └── chat_screen.dart
├── services/
│   └── auth_service.dart
├── widgets/
│   └── message_bubble.dart

6.2.1 Create auth_service.dart

import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  Future<User?> signInAnonymously() async {
    final result = await _auth.signInAnonymously();
    return result.user;
  }

  User? get currentUser => _auth.currentUser;

  Future<void> signOut() async {
    await _auth.signOut();
  }
}

6.2.2 Use AuthService in main.dart

Update main.dart:

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'screens/chat_screen.dart';
import 'services/auth_service.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  await AuthService().signInAnonymously();
  runApp(MyApp());
}

6.3 Extract Message UI to a Widget

Create widgets/message_bubble.dart:

import 'package:flutter/material.dart';

class MessageBubble extends StatelessWidget {
  final String text;
  final String time;
  final bool isMe;

  const MessageBubble({
    required this.text,
    required this.time,
    required this.isMe,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
      child: Container(
        padding: const EdgeInsets.all(10),
        margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
        decoration: BoxDecoration(
          color: isMe ? Colors.blue[200] : Colors.grey[300],
          borderRadius: BorderRadius.circular(8),
        ),
        child: Column(
          crossAxisAlignment:
              isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
          children: [
            Text(
              text,
              style: const TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 4),
            Text(
              time,
              style: const TextStyle(fontSize: 12, color: Colors.black54),
            ),
          ],
        ),
      ),
    );
  }
}

Update your chat_screen.dart to use this widget:

import '../widgets/message_bubble.dart';

Replace the message UI inside the ListView.builder with:

return MessageBubble(
  text: msg['text'],
  time: _formatTimestamp(msg['timestamp']),
  isMe: isMe,
);

6.4 Add Logout (Optional)

Inside chat_screen.dart, add this to AppBar:

import '../services/auth_service.dart';

appBar: AppBar(
  title: const Text("Chat App"),
  actions: [
    IconButton(
      icon: const Icon(Icons.logout),
      onPressed: () async {
        await AuthService().signOut();
        await AuthService().signInAnonymously();
        setState(() {});
      },
    ),
  ],
),

This logs out the current user and signs in anonymously again.

✅ Done!

Now your Flutter Firebase Chat App is:

  • Cleanly structured
  • Shows timestamps
  • Has reusable widgets
  • Handles user authentication (anonymously)
  • Ready for sharing on GitHub


Step 7: Build, Test, and Deploy

Now that your Flutter chat app using Firebase Realtime Database is complete, it’s time to:

  • Test the app thoroughly
  • Build for production

7.1 Test the App

Make sure everything works as expected:

  • Run flutter clean and flutter pub get
  • Use flutter run on an Android/iOS emulator or device
  • Test:
    •  Message sending
    •   Realtime updates
    •   Anonymous login
    •   Logout and re-login
    •   Auto-scroll of messages
    •   Firebase connection (check Firebase Realtime DB for new entries)
  • You can also test on the web using:
flutter run -d chrome

Make sure you’ve enabled Firebase for web in the Firebase console and updated the index.html if needed.

7.2 Build for Android

flutter build apk --release

The output will be at:

build/app/outputs/flutter-apk/app-release.apk

If you get these errors:

The android/app/build.gradle.kts like this
defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId = "com.djamware.flutter_chat_app"
        // You can update the following values to match your application needs.
        // For more information, see: https://flutter.dev/to/review-gradle-config.
        minSdk = flutter.minSdkVersion
        targetSdk = flutter.targetSdkVersion
        versionCode = flutter.versionCode
        versionName = flutter.versionName
    }
Your project is configured with Android NDK 26.3.11579264, but the following plugin(s) depend on a different Android NDK version:
- firebase_auth requires Android NDK 27.0.12077973
- firebase_core requires Android NDK 27.0.12077973
- firebase_database requires Android NDK 27.0.12077973

Fix this issue by using the highest Android NDK version (they are backward compatible).

Update android/app/build.gradle.kts and hardcode these lines:

android {
    ...
    ndkVersion = "27.0.12077973"

    ...

    defaultConfig {
        ...
        minSdk = 23
        ...
    }

    ...
}

You can now distribute or install it on Android devices.

7.3 Install iOS Dependencies

From the root of your Flutter project:

flutter clean
flutter pub get
cd ios
pod install
cd ..

7.4 Run the iOS App

Use:

flutter run -d ios

If you get an error like this:

if (app.isDefaultApp) {
      [NSException raise:kFirebaseCoreErrorDomain
                  format:@"Default app has already been configured."];

This error means that FirebaseApp.configure() is being called more than once, which causes a crash because the default Firebase app can only be initialized once in an iOS app. Open ios/Runner/AppDelegate.swift and update:

import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

On Android:

Build a Chat App with Flutter and Firebase Realtime Database - Android Device

On iOS:

Build a Chat App with Flutter and Firebase Realtime Database - iOS Device

Conclusion

In this tutorial, we successfully built a fully functional cross-platform chat application using Flutter and Firebase Realtime Database. We covered everything from setting up Firebase, implementing anonymous authentication, creating a simple and responsive chat UI, and syncing real-time messages between Android and iOS devices.

Along the way, you learned how to:

  • Integrate Firebase into both Android and iOS Flutter projects.
  • Use Firebase Anonymous Authentication to allow instant, secure access.
  • Store and retrieve real-time chat messages with Firebase Realtime Database.
  • Design a responsive Flutter UI that works seamlessly across platforms.
  • Handle platform-specific configurations for both Android (google-services.json) and iOS (GoogleService-Info.plist).

This project demonstrates how easy it is to use Firebase as a backend for scalable and real-time mobile apps, without managing servers or complex infrastructure.

Source Code

You can find the full source code on GitHub:

👉 GitHub - flutter_chat_app

We hope this tutorial helped you understand how to use Flutter with Firebase to build powerful, real-time applications. If you enjoyed this guide, feel free to share it and check out more Flutter and Firebase tutorials on Djamware.com.

That just the basic. If you need more deep learning about Flutter, Dart, or related you can take the following cheap course:

That just the basic. If you need more deep learning about Flutter, Dart, or related you can take the following cheap course:

Happy coding! 🚀