By Carlos Lira
Why should my apps consume APIs with flutter and Redux?
Most of people only meet Redux when they bump into React, and internalize the architecture as a React thing… This is very sad because it’s f****** awesome! It’s a unidirectional data flow architecture made for any User Interface a.k.a. a UI layer, it allows you to attach functions that will do things such as send an HTTP request to an API and dispatch the response to your state changer – we’ll discuss this in a while. But it’s more useful when combined with a declarative view implementation that can infer the UI updates from state changes, such as Flutter!
Redux Middlewares
A brief explanation before we go on. Redux let you include custom functions that processes the action before it’s dispatched to the next dispatcher and eventually to the reducer to form the new state. These functions are called middleware.
They really come in handy in many scenarios, for example: You make calls to an API and the response of those calls affect your app state. What should you do? First of all, use Redux, it’ll make your life a lot easier, all the data that matters to the UI will be centralized in a state singleton and be available whenever you want. As you know, flutter uses Dart, there are two middleware that will help us out: redux_api_middleware and redux_thunk.
The original thunk middleware is a javascript implementation that you can find here. The thunk middleware that we’ll use was ported to Dart by Brian Egan. The original API middleware is also a javascript implementation that you can find here The API middleware was ported by me to Dart, check it out on my GitHub 😀
The thunk middleware lets you dispatch async calls, which is perfect for the API middleware, because HTTP requests are asynchronous. The API middleware does exactly what you’re thinking, it calls the API and dispatches the response to your reducer so you can figure out the next app state.
We’ll also add a logging middleware so we can easily see the traffic going through our middleware.
In this tutorial we’ll walk through these topics:
- Create a new Flutter project.
- Add the dependencies.
- Create the Redux related files, such as actions and reducers.
- Edit the app entry point.
- Add a API request logger.
- Create the app routes.
- Create the UI components.
In this tutorial I’ll be using the VSCode flutter extension. That being said, let’s code!
Create a new flutter project
- Invoke View > Command Palette.
- Type “flutter”, and select the Flutter: New Project.
- Enter a project name, such as myapp , and press Enter.
- Create or select the parent directory for the new project folder.
- Wait for project creation to complete and the main.dart file to appear.
Add the dependencies
Your flutter app searches for its dependencies in pubspec.yaml dependencies section. Let’s change this file to add our dependencies. We’ll need redux, flutter_redux, redux_thunk and redux_api_middleware.
As you already know, the Flutter community is growing every day and so are the number of libraries and components out there for you to use in your app. These community libraries and components potentially have vulnerabilities that you might end up adding to your app if you’re not careful.
To avoid this always do some research before using them, check how many people are using them, always use the updated version because previous versions could have already fixed vulnerabilities and, if you feel comfortable, audit the code searching for possible vulnerabilities.
That being said, at the time of writing my pubspec.yaml file looked like this:
Create the Redux structure
First of all let’s create the base folder structure for our app. Create these folders in the lib folder: models, actions, reducers and components. After that, your lib folder structure should look like this:
lib
├── actions
├── components
├── models
├── reducers
└── main.dart
The names are kind of suggestive, but it doesn’t hurt to talk a little bit about them.
The models folder is where our data mapping will be, I like to separate them by modules, for example: We have a user module, so all models that concern that module should be created inside the user folder in the models folder.
The actions folder is where our actions will be, I like to split them by modules, for example: We have a user module, so all actions that concern that module should be in a user_actions.dart file in the actions folder.
The reducers folder is where our reducers will be, I like to split them by modules, for example: We have a user module, so all reducers that concern that module should be in a user_reducer.dart file in the reducers folder.
The components folder is where our components will be, but the way I like to organize it is certainly controversial, just like all the other folders I also separate the components as modules, instead of the typical separation of presentation and containers. I’m not against it, I just prefer the modules organization. If you prefer, you can use the presentation and containers approach, but in this article I’ll be using the modules separation.
Create the user and user state models
I’ll use the approach described above, but feel free to modify it as you like. Create a user folder in the models folder, then create a user.dart and a user_state.dart in the user folder.
Now create a user_state.dart in the user folder.
Create the app state model
The app state model centralizes the entire application state in a singleton, including the user state described above. Create an app_state.dart in the models folder.
Create some RSAAs
Redux Standard API-calling Actions a.k.a. RSAAs are the type of actions that the redux_api_middleware intercepts and contain the request definition. We’ll be using a rest API sample provided by JSONPlaceholder for this tutorial. Create a user_actions.dart in the actions folder.
Create the user reducer
The RSAAs dispatch Flux Standard Actions a.k.a. FSAs that contain the dispatched type, the response payload and an error, should one occur. This FSA will be dispatched to the user reducer and the reducer will return a new state based on the FSA. Create a user_reducer.dart in the reducers folder.
Create the app reducer
The app reducer combines all the reducers so they’re accessible from the AppState singleton. Create an app_reducer.dart in the reducers folder.
Edit the main.dart
This section is pretty straight forward, our build method will return a StoreProvider from the Redux package that will manage our store and let it be accessible through StoreConnectors that we’ll talk about in a minute. Edit the main.dart file.
You’ll notice that the logging middleware, the app routes and the screens are missing. Let’s fix that.
Create the logging middleware
This will be very simple, it’s a middleware that intercepts FSAs and prints their type, payload and error. Create a logger.dart file in the lib folder
Create the app routes
This is simply a way to organize our named routes. Create a routes.dart in the lib folder.
Create the users screen
The users screen is a StatelessWidget because it doesn’t need a state of it’s own. It connects to the store and has access to all the app data, we only need to map the state into “props”, I’m not sure about that term yet, but It’s what I’m using, once we do that the builder from the StoreConnector should have a props parameter that can be used through our component, and as you can tell we’ll also map the actions so we can easily use them. Create a users_screen.dart in the components/users folder.
And finally…
Create the user details screen
The user details screen is a StatelessWidget because it also doesn’t need a state of it’s own. It dynamically calls the API middleware and dispatches states and the UI reflects it beautifully. Create a user_details_screen.dart in the components/users folder.
Time to run your app and enjoy it…
Security Tip
Most of the APIs that we consume on apps are authenticated somehow. And we don’t want to ask the user’s credentials every time. So we want to store these credentials to make our lives and the user’s life a breeze, but that’s dangerous as other apps could have access to that specific file and it become a possible attack vector. There’s a library called flutter_secure_storage that solves this problem. It uses native Keychain on IOS and KeyStore on Android. So yeah… Use it if you’re storing user’s credentials.
Conclusion
Forget the idea that Redux is a react package! Use it wherever you can, it made my life easier and I hope I did yours a little bit too 😀
I know flutter is a new and fresh piece of technology, but it’s extremely productive, easy to work with and I believe it has a bright future. I was surprised by Dart, it’s very easy to learn and fun to code, reminds me a bit of javascript.
The full project will be in my GitHub if you need it. You can get in touch with me at my LinkedIn if you need any help, criticize or just want to talk about your problems.
If you enjoy this reading, please tell me about it at the comments, and remember the most important thing: Cat gifs get claps…