Building a Collaborative Whiteboard App Using Firebase and Flutter | by Tamilnambi | Dec, 2024

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.
  1. Basic knowledge of Flutter and Firebase.
  2. 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:

  1. HomePage: Provides navigation to Teacher and Student screens.
  2. TeacherScreen: Allows teachers to draw on the whiteboard and send updates to Firebase.
  3. 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.

Leave a Reply