real two-friend chat app (not fake replies).
That means we need a backend to sync messages between two devices.
In Flutter, the easiest way is Firebase Firestore because it gives us:
- Real-time database (messages sync instantly ).
- Free tier for small apps.
- Simple setup with Flutter.
Step 1: Setup Firebase in Your Flutter Project
- Go to Firebase Console → Add a new project.
- Enable Firestore Database (Start in Test Mode for now).
- Add your Android app inside Firebase project:
- Get your Android package name from
android/app/build.gradle
→applicationId
- Example:
com.example.chatapp
- Download the
google-services.json
and put it insideandroid/app/
.
- Get your Android package name from
- Add Firebase Flutter packages in
pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.30.0
cloud_firestore: ^5.0.0
- Run:
flutter pub get
- Update
android/build.gradle
andandroid/app/build.gradle
(Firebase docs will guide you, or I can give step-by-step).
Step 2: Initialize Firebase
In main.dart
:
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Real Chat App',
theme: ThemeData(primarySwatch: Colors.blue),
home: ChatScreen(),
);
}
}
Step 3: Build Real Chat UI with Firestore
ChatScreen
:
class ChatScreen extends StatefulWidget {
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final TextEditingController _controller = TextEditingController();
final CollectionReference messages =
FirebaseFirestore.instance.collection('messages');
void _sendMessage() {
if (_controller.text.trim().isEmpty) return;
messages.add({
'text': _controller.text.trim(),
'createdAt': FieldValue.serverTimestamp(),
'sender': "User1", // later replace with auth user
});
_controller.clear();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Real Chat")),
body: Column(
children: [
// Live messages
Expanded(
child: StreamBuilder(
stream: messages.orderBy('createdAt', descending: true).snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return const Center(child: CircularProgressIndicator());
var docs = snapshot.data!.docs;
return ListView.builder(
reverse: true,
itemCount: docs.length,
itemBuilder: (context, index) {
var msg = docs[index];
bool isMe = msg['sender'] == "User1"; // later dynamic
return Align(
alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: const EdgeInsets.all(8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isMe ? Colors.blueAccent : Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
child: Text(
msg['text'],
style: TextStyle(
color: isMe ? Colors.white : Colors.black,
),
),
),
);
},
);
},
),
),
// input box
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
color: Colors.grey[200],
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: "Type a message...",
border: InputBorder.none,
),
onSubmitted: (_) => _sendMessage(),
),
),
IconButton(
icon: const Icon(Icons.send, color: Colors.blue),
onPressed: _sendMessage,
)
],
),
)
],
),
);
}
}
Now:
- Install app on two devices (or emulator + phone).
- Whatever you type on one device instantly appears on the other device .
- You have built a real chat app!