the redux packages adds all the necessary components to use Redux in Dart, that is the Store, the Reducer, and the middleware
flutter_redux
this is a Flutter-specific packages which provides additional components on top of the redux library which is useful for implementing Redux in Flutter, such as StoreProvider, StoreBuilder, StoreConnector.
Key Concepts
App State is an immutable Object that lives at the top of your Widget hierarchy within a Store.
The Store is passed down to all ancestors via an InheritedWidget called a StoreProvider.
The State object is immutable. To create a new State, you must dispatch an Action.
The Action will be picked up by a Reducer, which is a function that builds and returns a new State based on the previous State and the Action that was dispatched.
Reducers are pure functions.
When there is a new app State, all widgets connected to the Store using StoreConnector will be rebuilt.
The widgets that use StoreConnector are called container widgets. They are only responsible for converting the latest App State to a ViewModel.
The Widgets that display data are called presentation widgets. Think of a Text widget or FloatingActionButton.
To read data from the State, use selector functions. These act like against your "State Database".
To handle fetching data from out Database or Web Service, we use a Middleware.
App State Singleton
In Redux, the idea is to store your Application State in a root level singleton.
Lifting out App State all the way to the top of our App so that all descendants have access to it.
To accomplish this, you create a Redux Store and hand it to a StoreProvider. All descendants of the StoreProvider can access the store using a StoreProvider.of(context).store or by using StoreConnector widget.
Updating App State
In order to update the App State in a Redux app, must dispatch an Action.
This Action will then be intercepted by your Reducer function, which is responsible for updating the App State using the data contained within the Action.
Reducer functions are pure functions. They are only responsible for taking in the last state and the dispatched action, and returning a new App State.
This means Reducer functions should not make any API calls or have side effects such as logging. For this purpose, use middleware.
Dispatching Actions and updating the App State in this rigorous way allows you to easily determine:
What Action cased a State change.
What Reducer is responsible for handling that change.
Why the State was broken in response to an Action.
When a View needs to update in response to a State change.
Updating UI
Whenever you the App State changes, in response to an Action, you most likely want to update your UI in some way.
To do so, connect to the StoreProvider using a StoreConnector widget. The job of the StoreConnector widget is simple: Take the latest state of the store and convert it into a ViewModel. Then, build a widget tree using this viewModel.
Whenever the App State changes, the StoreConnector will rebuild the ViewModel and Widget tree.
In order to make it easier to test your widgets and share functionality, it is recommended you have two types of widgets:
container widgets -- These use StoreConnector widgets to build up a ViewModel for your presentation widgets.
presentation widgets -- StatelessWidgets that are given all the data they need are responsible for building the UI.
This allows you to more easily test your presentation widgets, because you only need to pass in the data they require in each test for rendering, and then write assertions against the rendered output.
It also allows you to reuse container widgets.
Selector Functions
Selector functions are simple functions that provide a single point of access to your App State. For a full explanation of why they are useful, please refer to the reselect package.
Selector Functions是在做資料處理。
Fetching and Storing Data using Middleware
In order to fetch data from the Web or a Database, we need to make an async call. Since Reducer functions are pure, we must instead use a Middleware.
Middleware are run in response to Actions that are dispatched, and execute before the Reducer. This allows you to intercept an Action and fetch data in response.
Testing
Generally, this app conforms the "Testing Pyramid": Lots of Unit tests, few Widget tests, and fewer integration tests.
Unit tests
Reducer functions are very easy to unit test since they are pure functions.
Middleware functions that call out to APIs can be tested using Mock implementations. This is done using the Mockito library.
selector functions are also easy to test since they are pure.
Widget tests
container widgets can be tested to ensure they generate the correct ViewModel.
presentation widgets can be tested by passing in fake data and making assertions against the widget rendered with that data.
Redux
Redux in Flutter
There is two very useful packages we can use.
redux
the
redux
packages adds all the necessary components to use Redux in Dart, that is theStore
, theReducer
, and themiddleware
flutter_redux
this is a Flutter-specific packages which provides additional components on top of the
redux
library which is useful for implementing Redux in Flutter, such asStoreProvider
,StoreBuilder
,StoreConnector
.Key Concepts
App
State
is an immutable Object that lives at the top of your Widget hierarchy within aStore
.The
Store
is passed down to all ancestors via anInheritedWidget
called aStoreProvider
.The
State
object is immutable. To create a newState
, you must dispatch anAction
.The
Action
will be picked up by aReducer
, which is a function that builds and returns a newState
based on the previousState
and theAction
that was dispatched.Reducers
are pure functions.When there is a new app
State
, all widgets connected to theStore
usingStoreConnector
will be rebuilt.The widgets that use
StoreConnector
are calledcontainer
widgets. They are only responsible for converting the latest App State to aViewModel
.The Widgets that display data are called
presentation
widgets. Think of a Text widget or FloatingActionButton.To read data from the
State
, useselector
functions. These act like against your "State Database".To handle fetching data from out Database or Web Service, we use a
Middleware
.App State Singleton
In Redux, the idea is to store your Application State in a root level singleton.
Lifting out App State all the way to the top of our App so that all descendants have access to it.
To accomplish this, you create a Redux
Store
and hand it to aStoreProvider
. All descendants of theStoreProvider
can access the store using aStoreProvider.of(context).store
or by usingStoreConnector
widget.Updating App State
In order to update the App State in a Redux app, must dispatch an
Action
.This
Action
will then be intercepted by yourReducer
function, which is responsible for updating the App State using the data contained within theAction
.Reducer
functions are pure functions. They are only responsible for taking in the last state and the dispatched action, and returning a new App State.This means
Reducer
functions should not make any API calls or have side effects such as logging. For this purpose, usemiddleware
.Dispatching
Actions
and updating the App State in this rigorous way allows you to easily determine:Updating UI
Whenever you the App State changes, in response to an
Action
, you most likely want to update your UI in some way.To do so, connect to the
StoreProvider
using aStoreConnector
widget. The job of theStoreConnector
widget is simple: Take the latest state of the store and convert it into aViewModel
. Then, build a widget tree using thisviewModel
.Whenever the App State changes, the
StoreConnector
will rebuild theViewModel
andWidget
tree.In order to make it easier to test your widgets and share functionality, it is recommended you have two types of widgets:
container
widgets -- These useStoreConnector
widgets to build up aViewModel
for yourpresentation
widgets.presentation
widgets --StatelessWidgets
that are given all the data they need are responsible for building the UI.This allows you to more easily test your
presentation
widgets, because you only need to pass in the data they require in each test for rendering, and then write assertions against the rendered output.It also allows you to reuse
container
widgets.Selector Functions
Selector
functions are simple functions that provide a single point of access to your App State. For a full explanation of why they are useful, please refer to the reselect package.Selector Functions是在做資料處理。
Fetching and Storing Data using Middleware
In order to fetch data from the Web or a Database, we need to make an async call. Since
Reducer
functions are pure, we must instead use aMiddleware
.Middleware
are run in response to Actions that are dispatched, and execute before theReducer
. This allows you to intercept anAction
and fetch data in response.Testing
Generally, this app conforms the "Testing Pyramid": Lots of Unit tests, few Widget tests, and fewer integration tests.
Reducer
functions are very easy to unit test since they are pure functions.Middleware
functions that call out to APIs can be tested using Mock implementations. This is done using the Mockito library.selector
functions are also easy to test since they are pure.container
widgets can be tested to ensure they generate the correctViewModel
.presentation
widgets can be tested by passing in fake data and making assertions against the widget rendered with that data.Reference