Source codeClick here to view the full source code of this example
Solidart is a simple state management library for Flutter and inspired by SolidJS.
Dependency
This is the flutter_solidart version used in this example:
This example leverages the power of the ProviderScope widget using providers.
File structure
Let’s look at the file structure:
Directorycontrollers
todos.dart
Directorydomain
todo.dart
Directorypages
todos.dart
Directorywidgets
todo_item.dart
todos_body.dart
todos_list.dart
toolbar.dart
main.dart
Models
Let’s start with the models:
TodosController
The TodosController is where the business logic lives.
It keep the whole todos list state and allows us to add, remove or toggle a Todo.
The TodosController can have an initialValue, that is the initial list of todos.
As you can see the TodosController can get an initialTodos list. This is going to be its initial state.
When the constructor is runned, the todos signal is populated with the provided initialTodos.
The thing to note here is that todos is a ListSignal.
A ListSignal automatically notifies its listeners when items change.
The controller exposes completedTodos and incompleteTodos derived signals.
They will automatically react to the _todos signal and provide a read only signal.
The add method uses the update function of a Signal to append the new todo to the current list of todos.
In a similar way the remove and toggle methods update the signal value.
In the remove method we remove from the list the todo with the id provided.
In the toggle method we loop through each todo and toggle the completed state of the todo with the given id.
TodosPage
The TodosPage uses a ProviderScope widget to provide a TodosController to descendants.
TodosBody
Let’s continue to the TodosBody.
The TodosBody is the body of our feature, it has a text input on top where you can write a new todo, a Toolbar and the TodoList.
The important parts here are two.
We’re retrieving the todosController with the syntax todosControllerProvider.of(context).
This is how we access providers from descendants. You can safely run this method in the initState, in the build method or even inside a callback like onPressed.
We’re creating a new ProviderScope widget. Yes, you can (you must) create many ProviderScope widgets inside your app, this the ideal usage.
Here we’re creating a signal with an initial value of TodosFilter.all. This signals keeps the state of the current selected tab.
In the TextFormField when the field is submitted we add the new todo simply using:
Toolbar
The toolbar shows 3 tabs (or filters).
All the todos
The incomplete todos list
The completed todos list
Each tab contain the number of todos present in the current tab.
To get the total number of todos we’ve created new Computed signals.
They subscribes to the signals used in the function and update only when the selected value changes.
Then we have used a SignalBuilder to rebuild every time the count signal changes.
Finally, when a tab is tapped, we update the activeTodoFilter signal to set the new active tab.
TodosList
The TodosList renders all the todos based on the current activeFilter.
In order to react to the active filter it uses the SignalBuilder widget that subscribes to any signal and rebuilds every time the values change.
Testing
Here we’re going to separate the widgets tests from the business logic tests.
We’re going to write unit tests just for the TodosController and then we’re going to write widgets tests for the whole feature.
Unit tests
Check that the TodosController emits the initialTodos as a value
Test that we are able to add a new Todo.
Test that we are able to remove an existing Todo by its id.
Test that we are able to toggle a Todo in order to mark it as completed.
Widget tests
I’m going to use an helper function in all the tests to easily mock the TodosController, this is the KEYPOINT of how to mock providers, here it is the source code:
Check that the TodosController emits the initialTodos as a value
Test that we are able to add a new Todo.
Test that we are able to remove an existing Todo by its id.
Test that we are able to toggle a Todo in order to mark it as completed.