In this post, we’ll explore how to build a simple collaborative whiteboard application using Flutter and Firebase. This app allows teachers to draw on a whiteboard and students to view the drawing in real time. The app also supports clearing the whiteboard for both teacher and student views.
- Real-time updates: The teacher’s drawings are synchronized with the student’s view.
- Firebase Realtime Database Integration: Storing and retrieving drawing points efficiently.
- Cross-platform support: Compatible with web, Android, and iOS platforms.
- Basic knowledge of Flutter and Firebase.
- Firebase project setup. Ensure Firebase is configured for your app with the Realtime Database enabled.
To enable real-time data synchronization, initialize Firebase for your app. This step varies based on the platform.
For web platforms, use the FirebaseOptions
class to configure Firebase:
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: "<YOUR_API_KEY>",
authDomain: "<YOUR_AUTH_DOMAIN>",
databaseURL: "<YOUR_DATABASE_URL>",
projectId: "<YOUR_PROJECT_ID>",
storageBucket: "<YOUR_STORAGE_BUCKET>",
messagingSenderId: "<YOUR_MESSAGING_SENDER_ID>",
appId: "<YOUR_APP_ID>"
),
);
For non-web platforms, initialization is simpler:
await Firebase.initializeApp();
Firebase Realtime Database offers efficient data synchronization and supports real-time updates, making it ideal for collaborative applications like whiteboards.
The app consists of three main screens:
- HomePage: Provides navigation to Teacher and Student screens.
- TeacherScreen: Allows teachers to draw on the whiteboard and send updates to Firebase.
- StudentScreen: Displays real-time updates from Firebase.
The app uses Firebase’s event listeners for live updates and a custom painter for drawing operations.
Main Function
The main
function initializes Firebase and launches the app:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Firebase initialization
if (kIsWeb) {
await Firebase.initializeApp(options: <FirebaseOptions>);
} else {
await Firebase.initializeApp();
}
runApp(MyApp());
}
HomePage
The home page provides navigation options for teachers and students:
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Whiteboard App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => TeacherScreen())),
child: Text('Teacher'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => StudentScreen())),
child: Text('Student'),
),
],
),
),
);
}
}
TeacherScreen
The teacher’s screen supports drawing on the whiteboard and syncing data with Firebase. Each drawing point is normalized for consistency across device resolutions:
class TeacherScreen extends StatefulWidget {
@override
_TeacherScreenState createState() => _TeacherScreenState();
}class _TeacherScreenState extends State<TeacherScreen> {
List<Offset> points = [];
final databaseRef = FirebaseDatabase.instance.ref("whiteboard_data");
void savePointsToFirebase(Offset point) {
final newPoint = {"x": point.dx, "y": point.dy};
databaseRef.push().set(newPoint);
}
void clearWhiteboard() {
databaseRef.remove();
setState(() { points.clear(); });
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Teacher Whiteboard'),
actions: [
IconButton(
icon: Icon(Icons.clear),
onPressed: clearWhiteboard,
),
],
),
body: GestureDetector(
onPanUpdate: (details) {
setState(() {
Offset normalizedPoint = Offset(details.localPosition.dx / MediaQuery.of(context).size.width, details.localPosition.dy / MediaQuery.of(context).size.height);
points.add(normalizedPoint);
savePointsToFirebase(normalizedPoint);
});
},
child: CustomPaint(
painter: WhiteboardPainter(points),
size: Size.infinite,
),
),
);
}
}
Explanation
- GestureDetector: Captures the user’s drawing input and updates the state.
- Normalized Points: Ensure drawings appear consistent across various screen sizes.
- Firebase Database: Stores each point as a JSON object.
StudentScreen
The student’s screen listens to Firebase for real-time updates and renders the drawing points:
class StudentScreen extends StatefulWidget {
@override
_StudentScreenState createState() => _StudentScreenState();
}class _StudentScreenState extends State<StudentScreen> {
List<Offset> points = [];
late DatabaseReference databaseRef;
@override
void initState() {
super.initState();
databaseRef = FirebaseDatabase.instance.ref("whiteboard_data");
databaseRef.onChildAdded.listen((event) {
final data = event.snapshot.value as Map<dynamic, dynamic>;
setState(() {
points.add(Offset((data["x"] as num).toDouble(), (data["y"] as num).toDouble()));
});
});
databaseRef.onChildRemoved.listen((event) {
setState(() { points.clear(); });
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Student View')),
body: CustomPaint(
painter: WhiteboardPainter(points),
size: Size.infinite,
),
);
}
}
Explanation
- onChildAdded Listener: Updates the student’s view in real time as new points are added.
- onChildRemoved Listener: Clears the whiteboard when the teacher clears it.
WhiteboardPainter
This class renders the points on the whiteboard:
class WhiteboardPainter extends CustomPainter {
final List<Offset> points;WhiteboardPainter(this.points);
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != Offset.zero && points[i + 1] != Offset.zero) {
canvas.drawLine(points[i], points[i + 1], paint);
}
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
This whiteboard app demonstrates the power of Firebase Realtime Database and Flutter in creating real-time collaborative tools. The app ensures consistent platform performance by normalizing data and leveraging event listeners. Feel free to extend this app by adding features like multi-user collaboration or different drawing tools.