Challenges Faced and Solutions

I faced various technical challenges related to UI programming and display issues when writing these apps, and I am sharing these below.

Accomodating different device sizes

Adapting the sizes of headings, text, icons, and display heights for phone and tablet devices

Since I wanted to support a wide variety of mobile and tablet devices, I experimented with different device sizes in the simulator application and recorded them. In the main.dart file (which has the home screen) I reset the sizes based on the device parameters (ratios of height and width) seen using MediaQuery in the build() function and recorded this in variables declared in global.dart. This way the same sizes were made available to all other screens.

Wrapping around listing of moves into different rows on the mobile display

Since Flutter does not know the width of each character on the specific device, deciding when to wrap around the display of moves text was a challenge. I used RenderBox to read sample text in the home page and calculated the expected size of each letter. This information was available to use in the Analysis and Training screens which needed this information to decide when to wrap around. Each row of widgets had to be encapsulated in a Row widget, and a new row created when a wrap around was required. Also some special handling was required at the beginning and end of the line such as indentation. I used a recursive call each time a variation was encountered.

Dynamically resizing the menu box sizes on the home page during the first display

On the homepage, I initially showed an hourglass to allow the Render objects to calculate the device specific sizes and dynamically size the home page text. This worked on most phones, but in a couple of phones which ran on different Android variants, the transition wasn't smooth due to an intermittent timing issue.

To solve this issue, I later simplified the design and converted the home page boxes into images with predefined text. Although this sacrificed some flexibility, the initial display was faster as images automatically resize themselves to the available screen width. The database access was performed asynchronously after the initial display.

Technical Issues - Events and Screen refresh

Finding the right point to asynchronously access the database and avoiding image load jitters

I needed to establish a connection to the database and initialize some content. If I did this in the initState() function, the display was held up for a second, which was not a pleasant user experience.

To solve this I set up a PostFrameCallback function to be called after the build() (completing the initial display). I also pre-cached the images in the didChangeDependencies function to avoid a jitter when the image was first loaded.

Capturing a button click on one widget and using that to change the state of another widget

The Analysis screen was composed of different UI elements including the Chessboard, Status bar, Moves Listing, and Capture Buttons, each of which were separate widgets in Flutter. A user could either enter a new move in the chessboard, use the Capture Buttons to start a variation, or click on any move to go to that point in time. I needed an efficient way to manage the events and state across these.

I created a MoveController class to manage moves in the chessboard as well as events coming from the other screen components. A reference to the MoveController was available to each component through callback functions. This allowed a consistent handling of all the events from any area.

I also realized that the unique behavior of the moves listing (wrapping around, indentation on variations, scrollbar behavior, and interacting with the MoveController on user clicks) would be required in the training screens as well. I wrapped this into a separate PgnDisplay stateful widget to abstract this out. This made the programming of the training screens much easier and efficient.

Display Challenges

Modifying graphics to “lift” and drag the piece from its location prior to moving

The external (publicly available) flutter_chess_board package which I used for displaying the chessboard, had the capability to drag a piece, but it was not “lifted” till after the move was completed. This was a minor UI issue, but being a chess player, I wanted to address this. To fix this, I was able to add logic in the DragTarget functions onLeave(), onWillAccept(), and onAccept(), to remove a piece once it is “lifted” and put it back if the cursor went out of bounds. This required an extra refresh at the end across all the squares to make it work correctly.

I had to test this with various user actions like going into other squares, out of bounds etc., to perfect it.

Compressing a button to the smallest size possible

This was required in the moves listing section as there was a shortage of space, and I had to compress the display as much as possible. There are multiple ways to reduce a button’s size, but what worked best for me was enclosing the button widget in a ButtonTheme and using a fixed height and minWidth. Also setting the padding to 0 helped to control the size.

Keeping a consistent look and feel across the screens

To ensure a consistent set of colors, icons, fontsizes to be used, I set up a global.dart file and included it in every screen. Over time the global file grew to include authentication logic, indentation, connection routines, standard navigation logic, error handling, and it was really useful having it all in one place.

I also put all the icons in an iconsList Map, so that I could easily change the look and feel throughout the app by modifying the Map in a single location.

Support for advanced features while capturing Chess games

Recognizing branching and variations while loading a pre-written chess notation (in PGN format) for training games

This was an interesting challenge. The external (publicly available) chess.dart package that I used, had logic to read chess notation written in PGN format, but did not recognize variations/branching which are used to show alternative sequences of moves. I had to write this as a state machine with state changing with every tag (e.g. white move received, #625D5D move received, comment started, decoration started etc.), and fixed paths from one state to another. I also separated out processing a sequence of moves into a separate function, which could be recursively called each time a variation was encountered. I also incorporated $ decorations supported by ChessBase since I author my training games using that software.

Allowing the user to Undo Move, Delete Line and Navigate to any move in the game

While capturing games on the mobile phone, a user may want to undo a move, or delete a line from a certain point. Also the user may want to go to any move in any variation, and the chessboard needs to display the snapshot at that point in time.

To achieve this I enhanced the PgnDisplay widget to build a move tree using a doubly-linked list with child lists for each variation. Each node was marked with a level, and a new variation increased the level number. Using this it was possible to navigate up and down the tree. When undoing a move and deleting a line, all I had to do was mark the unused nodes inactive, and reconnect the previous and next nodes in the linked list.

Interesting Features in Google Firebase and Cloud Firestore

Getting changes in the database to reflect immediately on the users’ phones

Firebase allows “listening” to a collection which is a query into a table with a where clause. Anytime the data changes in that collection, any configured listeners are alerted. A listener can be set up using collectionReference.snapshots().listen((snapshot) {}). The listener is given access to a snapshot, from which the latest data can be extracted as a List of DocumentSnapshots.

This list can also be saved and iterated upon in other functions. If the underlying data changes, the listener is called by Firebase and provides an opportunity to update the display accordingly.

Sending emails from the app

In order to send email a captured chess game in PGN notation , I had to enable the Trigger Email package in the Firebase Console, and create a table to hold outgoing mail. It took a little trial and error to get the syntax correct, especially for an email attachment. I finally used the attachments tag, gave a filename, and the PGN text in the content tag. I chose to use my ChessProf Gmail account as the sending email server for simplicity, but this required enabling Less Secure app access on the Google account settings.

For other emails like Rest PasswordGoogle Firebase API functions. Firebase allows creating an email template, and I used the default one.