Sudoku Solver in Flutter — Understanding Flutter State Management by Example — Part 2

Nikhil Heda
5 min readApr 7, 2020

Hey guys!

Welcome to Part 2, where we discuss a solution using just stateful widgets — do read Part 1 to understand the application better.

Design 1 : Use stateful widgets — Down the rabbit hole we go…

The first thing that comes to mind if we have changing state is stateful widgets. So lets start with them.

Widgets SudokuCell, KeyPadCell, Solve and Reset buttons need access to board and activeNumber. However, we cannot define the state in any of these, as it cannot be accessed by all — even if we can somehow gain access to siblings, we would be fighting the declarative framework (where, changes imply rebuild).

A better place would be to add it above the widgets that access the state. We lift the state up so that all widgets can access it, this will be a common theme across all designs. We always lift shared state up and never access it across siblings.

Refer this for more details — Lifting state up

In this design, we define the raw state in SudokuSolverPage (as its the common ancestor to widgets that need access), and pass it down (by reference) to all widgets that need this state via constructors.

Right away, this does not look so appealing right? But lets do this anyway!

Widget Tree

Widget Tree — with widget state and shared state

Things to note:

  1. final or not: If a widget is stateless, its members must be final because the class should be immutable. If a stateful widget’s state changes , must be changed inside the setState() function (this rebuilds the widget with new state).
  2. Types for board and activeNumber: board is a 2-d list of integers. activeNumber is a list of integers with just 1 number because, I need to pass activeNumber as a reference, as it is shared by SudokuCell (reads) and KeyPadCell (writes). However, Dart doesn’t support pass by reference for primitive types, so forcing activeNumber to be passed around as a reference by wrapping it under a List.

Code

Github: https://github.com/NikhilHeda/SudokuSolver/tree/design1

SudokuSolverPage

The widget state is the board — A change in this, will require a rebuild.

We convert this to a stateful widget — when “Solve!” or “Reset Board” is pressed, board changes and hence needs to be rebuilt.

Being the parent of all the widgets that need to interact with state, we start maintaining the state here — Adding board and activeNumber as members of this widget.

SudokuSolverPage widget

Solve and Reset Buttons

A click on solve and reset buttons needs a rebuild of the solver page as it changes the board.

Solve and Reset Buttons

SudokuBoard

No widget state here, and hence survives as a stateless widget. Constructor to accept board and activeNumber to pass them down the tree.

SudokuBoard widget

KeyPad

No widget state here, and hence is a stateless widget.

Constructor to accept activeNumber, as this widget doesn’t care about the board state.

KeyPad widget

SudokuCell

The widget state here is hidden, its the value at board[row][col] — we convert this to a stateful widget as a change in board[row][col], requires a rebuild of widget.

Constructor to accept board and activeNumber.

A click on sudoku cell, should change board[row][col] to the activeNumber and rebuild widget (hence, inside setState()).

SudokuCell widget

KeyPadCell

The widget state here is number

Stateless widget — Since its number is not changing, no need for rebuild.

Constructor to accept board and activeNumber.

A click on keypad cell, should change the active number to that cells number.

KeyPadCell widget

Problems

Its obvious that this is not a good design —

  1. Maintaining this code is hell — shared state is not at one place, its passed around via constructors and if the widget tree gets deep, this is not preferable.
  2. Code Noise — In our example, SudokuBoard and KeyPad have nothing to do with board and activeNumber and still we have to define these variables to pass it down — this is unwanted code.
  3. Difficult to extend — Suppose we want to highlight selected cells, we need to add another state for background colors — this causes a cascade of changes.
  4. Workaround for activeNumber — Not a good idea, we lose the semantics for final, as we will always be able to modify elements inside the data structure. Also, its not readable.

Avoid stateful widgets, if possible

  • Performance — If widget state changes, the entire tree rooted here is rebuilt, even if some widgets need not be rebuilt — KeyPad and KeyPadCells need to be rebuilt whenever the board state changes, as the entire tree rooted at SudokuSolverPage is rebuilt (on solve/reset), even though they are not affected by those changes.
  • Code noise — Lot of boiler code is required for supporting stateful widgets, like a new state class and a widget class, can be avoided if we use stateless widgets.
  • Usually Flutter has widgets that support for almost any kind of operation and we can use these widgets instead of creating stateful widgets.

Up Next!

A better solution using Inherited Widgets — Part 3

--

--