← Back home

Rows' Formula Composer

While building Rows' Flutter app, I was responsible for creating the formula composer. In a spreadsheet we have a way to compose formulas, it might look like a simple input, but upon further inspection, you can see that it is not:
  1. Autocomplete - show possible functions and parameters
  2. Syntax highlight - highlight the syntax of the formula
  3. Error detection - detect errors in the formula and errors
  4. Point & Compose - eg. when you open a parenthesis you go into a mode where you can click a cell to compose it into the formula, or you can use the keyboard to pick a cell.
  5. Positioning - It can be displayed on a fixed bar on top or as a floating element that when the spreadsheet is scrolled out of position it should show a label with the cell name.
I had prior experience developing one for the web app and found that statecharts were an effective way to model its state. For formula parsing, we chose ANTLR - it’s a powerful tool, easy to use, and we were already familiar with it. However, the Dart port of ANTLR was outdated and had several issues due to a lack of maintenance. Before diving into the actual work, we first contributed a PR to migrate the library to the latest Dart version and add support for non-nullable types. This was a great opportunity to contribute to open-source and support the community. With that resolved, I built a custom parser using ANTLR. I was able to reuse the grammar from the web application but needed to tweak parser rules and create new visitors and listeners to integrate with the state machine. Given a formula, the parser would generate a state machine to evaluate it. For example, a formula like =SUM(A1:B1) would be represented as: Automata With this approach, we could enable proper syntax highlighting, autocomplete, basic auto-fixes, and compute point and compose references. To ensure the formula was always in a valid state and transitioned only between valid states, we relied on state machines. The existing statecharts libraries didn’t fully meet our needs, so I built a custom one: Automata . It’s a declarative and expressive library for state machines and statecharts, heavily inspired by XState.
Here's a simple example of how to use the library:

  final machine = StateMachine.create(
    (g) => g
      ..initial<Alive>()
      ..state<Alive>(
        builder: (b) => b
          ..initial<Young>()
  
          ..on<OnBirthday, Young>(...)
          ..on<OnBirthday, MiddleAged>(...)
          ..on<OnBirthday, Old>(...)
          ..on<OnDeath, Purgatory>()
  
          ..state<Young>()
          ..state<MiddleAged>()
          ..state<Old>(),
      )
      ..state<Dead>(),
  );
      
This is just a high-level overview of the work that went into building the formula composer. There were many interesting challenges along the way, from integrating ANTLR with Dart to designing a robust state management system. It was a rewarding experience to work on, combining parsing, state machines, and UI behavior into a cohesive system.