Most commercial math apps for young students rely on multiple-choice quizzes (MCQs). While MCQs are trivial to program, they fail to teach actual pencil-and-paper computation mechanics: aligning columns, carrying over values, borrowing across zero, and step-by-step long division algorithms.

Maths Kids was designed to replicate the exact mechanics of physical math workbooks inside a zero-latency, offline Flutter application.

Here is an architectural walkthrough of how the column-arithmetic calculation engines, keystroke validation, and custom input grids were implemented.


1. Core Principles: No Check Buttons, No MCQs

  1. Instant Keystroke Validation: There is no “Submit” or “Check” button. As soon as a child enters the correct digit in an active cell, the cell locks, turns green, and advances focus automatically.
  2. Natural Arithmetic Cursor Traversal:
    • Addition, Subtraction, Multiplication: Moves Right-to-Left (units $\to$ tens $\to$ hundreds $\to$ thousands), matching natural hand calculations.
    • Long Division: Moves Left-to-Right (highest place value dividend $\to$ remainder $\to$ drop down).
  3. Hold-to-Peek: Children can press and hold a “Peek” button to momentarily view expected digits if they are stuck. Releasing the button immediately hides the hint, encouraging self-correction without penalty.

2. Column Addition & Carry Propagation

Addition supports 4-digit $+$ 4-digit operations with potential 5-digit sums:

        [C3] [C2] [C1] [  ]     <-- Carry Row (Col 3, 2, 1)
          4    8    7    6      <-- Term A
   +      7    4    5    9      <-- Term B
   ────────────────────────
     [1]  [2]  [3]  [3]  [5]     <-- 5-digit Answer Row

The Carry Row

The carry cells sit directly above the addends. They accept single digits ($0$ or $1$) and remain ungraded helper fields. If the child fills them, they see visual confirmation; if they compute mentally, they can bypass them directly to the answer row.


3. Subtraction: Borrow Cells & Strikethrough Engine

Borrowing in multi-digit subtraction is where most young learners struggle. Standard textbook mechanics require:

  1. Slashing through a lender digit in column $i+1$.
  2. Writing the decremented value above it.
  3. Adding $+10$ to the borrower digit in column $i$.
       [  ]  [ 7 ] [ 6 ] [16]    <-- Borrow Row
        8     8     7     6      <-- Minuend (strikethroughs applied dynamically)
   -    3     4     8     9      <-- Subtrahend
   ────────────────────────
       [5]   [3]   [8]   [7]     <-- Answer Row

The Material 3 Tinting Bug

In Flutter’s Material 3 theme, TextField containers inherit primary seed colors, tinting borrow cells green by default even before validation. We forced clean transparent backgrounds with:

TextField(
  controller: _borrowControllers[col],
  keyboardType: TextInputType.number,
  textAlign: TextAlign.center,
  decoration: const InputDecoration(
    filled: true,
    fillColor: Colors.transparent, // Prevents M3 auto-tinting
    border: OutlineInputBorder(),
    contentPadding: EdgeInsets.zero,
  ),
  onChanged: (val) => _validateBorrow(col, val),
)

4. Multi-Stage Long Multiplication

Long multiplication ($3\text{-digit} \times 2\text{-digit}$) requires multiple distinct computational phases:

  1. Multiply top number by bottom units digit $\to$ Partial Product 1 ($P_1$).
  2. Multiply top number by bottom tens digit $\to$ Partial Product 2 ($P_2$, shifted by a fixed units zero).
  3. Sum $P_1 + P_2$ with carry cells $\to$ Final Product.

Dynamic Auto-Erasing Carry Row

Early prototypes featured two separate multiplication carry rows, which crowded phone screens. Instead, we implemented a single shared multiplication carry row that functions exactly like an eraser on physical paper:

  • As the child calculates $P_1$ ($\times\text{units}$), the top row acts as the units carry buffer.
  • Once $P_1$ turns fully green, the row is automatically cleared, relabeled to $\times\text{tens}$, and resets for $P_2$:
void _onCellChanged(int col, String val, List<bool> correct) {
  // Check if Partial Product 1 just completed
  if (identical(correct, _p1Correct) && _p1Done && !_carryClearedForP2) {
    _carryClearedForP2 = true;
    for (var controller in _multCarryControllers) {
      controller.clear();
    }
    setState(() {
      _currentMultPhase = MultiplicationPhase.tensPass;
    });
  }
}

5. Step-by-Step Long Division Bracket

Rather than reducing division to a simple quotient and remainder input, Maths Kids generates an authentic long division bracket with intermediate product and subtraction steps:

            [ 1 ] [ 4 ] [ 2 ]   R [ 3 ]  <-- Quotient + Remainder
         ┌───────────────────
       6 │   8     5     5                <-- Divisor | Dividend
         -   6                            <-- Divisor x 1
         ──────
             2     5                      <-- Remainder + Brought Down
         -   2     4                      <-- Divisor x 4
         ───────────
                   1     5                <-- Remainder + Brought Down
               -   1     2                <-- Divisor x 2
               ───────
                   [ 3 ]                  <-- Final Remainder

State Machine for Interleaved Working

The input sequence strictly enforces standard classroom order:

  1. Enter quotient digit.
  2. Enter subtraction product ($-\text{divisor} \times \text{quotient digit}$).
  3. Compute intermediate remainder.
  4. Bring down the next dividend digit.

By modeling the working rows as a structured matrix of fixed controller pools (_WorkRow data class), keyboard navigation moves seamlessly through each operation.


6. Daily Habituation: 12-Hour Reset Cycle

Times tables (from $2\times$ up to $20\times$) are unlocked sequentially. To prevent children from permanently completing the board and abandoning practice, progress resets on a rolling 12-hour epoch:

  • If $\Delta t > 12\text{ hours}$ since the child’s last session, unlocked tables reset to Table 2.
  • A single daily session allows rapid advancement through previously mastered tables, ensuring active revision of multiplication fundamentals.