Rating: 4.88 / 5 Based on 8 reviews
Whiteboard is an irreplaceable tool in many business processes. Live whiteboards have been created to allow for the same kind of collaborative process using whiteboards online. In this blog post, we explore a possible implementation of such a whiteboard using Flutter to create a mobile application and Firestore database as a backend.
Flutter is a framework for creating cross-platform applications for mobile devices (Android and iOS), desktop (Mac and PC), and web.
Firestore is a document database offered by Google as a part of the Firebase platform. Features that make it especially well suited for use cases such as live whiteboard are low latency synchronization and the ability for clients to subscribe to notifications about data changes.
This blog post will analyze the most important aspects of creating a simple whiteboard app. The app consists of two pages, the first StartPage for providing the id of the whiteboard the user intends to connect to. Second WhiteboardPage displays the selected whiteboard and allows for adding and removing lines.
The widget responsible for rendering the lines to the screen is WhiteboardView. WhiteboardView consists of a CustomPaint widget with WhiteboardPainter. WhiteboardPainter in the paint method, iterates over all lines and draws lines joining consecutive points to the canvas.
WhiteboardView is also responsible for detecting user inputs and exposing appropriate onGestureStart, onGestureUpdate, and onGestureEnd callbacks. Gestures are detected using a GestureDetector widget and mapped to Point models after being scaled relative to whiteboard width. Using coordinated relative to screen width resolves issues related to different screen dimensions across devices. WhiteboardView has also fixed the aspect ratio to 9/16 for the same reason.
The app uses a very simple data structure in Firestore. Every whiteboard is saved in a single document consisting of a content id and a list of lines. Such a structure results from the Firestore pricing model, which is based mostly on a number of documents being read and written. Not separating the whiteboard into multiple documents has some drawbacks, the most significant of which in this case is the limit to 1 write per second to a document.
New lines are saved to the database when the drawing is finished to reduce the number of writes and Firestore costs. The new content id is also added to the list of written ids to allow for the comparison explained earlier.
Adding a new line is pretty straightforward. A new line is created in the onGestureStart method and stored in the _currentLine field. Further points are added in the onGestureUpdate method. In onGestureEnd, a line is added to the WhiteboardContent model and saved in Firestore.
Deleting lines is a bit more complex. In each onGestureStart or onGestureUpdate invocations rectangle created by current and previous pointer positions is checked for intersection with rectangles created by each two consecutive line points (or with a rectangle of size equal to double the line width centered around the point in case of only one available). If any of the rectangles created by the line section intersect, the line is removed from WhiteboardContent, and changes are saved in the database.
Firestore, by default created indexes for each array field in a document to allow for, “contains” querying. Large arrays lead to a huge number of indexes being created, which translates to higher database costs and slower write speeds. In our case “contains” query is completely useless, so it’s important to disable the creation of those indexes.
You can find the application source code on GitHub.
In this case, Firestore is a great use; however, there are also some drawbacks of Firestore usage you should be aware of.