Flutter + Dio + Fish_Redux

At present, there is only a single introduction on the Internet, there is no integrated demo, so LET me do it. Give me some stars!!

Code address: github.com/liangwei010…

The interface to be implemented this time is as follows:

Before doing a management terminal interface (spring boot + vue) data, below url is: http://101.132.124.171:8000/about

What do you get out of it?

You can get a rough idea of the directory structure, the encapsulation of Dio base requests, the use of the Fish_Redux state, and some messy pits. I almost feel for stones to cross the river, these days also stepped on some pits.

The selection

Because the team is limited, there is not much effort to maintain a set of IOS and Android code. After discussion with the team members, they decided to give up React Native and choose Flutter instead. Because the team members were React Native and had no experience with Flutter, they almost started from 0. Then they thought that Flutter might be the future trend and its performance was good, so they started to do Flutter.

Take the environment

Here you can skip ten thousand words and click on the website of Flutter Chinese.

The project structure

  • The API is used to store and back-end interface requests
  • The apiModel is used to store the collection of Model objects corresponding to the JSON returned by the back end
  • Asset is used to store resources such as images
  • Component is a collection of individual components that can be extracted
  • Page is a collection of individual pages
  • Utils is a collection of functions’ methods and classes
  • Pubspec.yaml is the place to manage packages

Dependency packages required by Demo

dependencies:
  json_annotation: ^2.4. 0# Dio for json serialization and deserialization2.17.# fish_redux: ^ for HTTP requests0.22.Dev_dependencies: flutter_test: SDK: build_runner: ^1.52.Json_serializable: ^3.0. 0Json serialization and deserializationCopy the code

Why use state management

  1. To decouple the View(interface) from the VM(logic).
  2. For maintainability of code.
  3. For future scalability.

Use fish_redux foreplay

  1. In the selection for state management, bloc and Redux, followed by Ali’s Fish_Redux. One is to trust Ali’s team, two is to look at their state management compared to others (state management comparison), and three is that it has the most stars.
  2. FishReduxTemplate is available with vscode and Android studio, but Android studio takes a long time to refresh the file after it has been created.
  3. At first, I didn’t quite understand what he meant. After contacting and reading the document, I found that his state was similar to Vuex.
  4. I read an explanation on Zhihu (I don’t know why, I found Zhihu after going over the wall, it is very good, why the search engine of a certain degree does not come out), I think the following are very important: View, Effect and Reducer components three elements. (Nuggets explanation), (Zhihu explanation)

Use the pit of Flutter

  1. Fish_redux go to Github to find the official example, I am not running. Because my Version of Flutter is 1.74. The class name of the Action is the same as that of the official Action. Solution: Downgrade to v1.5.4- Hotfix.2 and below.
Error: 'Action' is imported from both 'package:flutter/src/widgets/actions.dart' and 'package:fish_redux/src/redux/basic.dart'.
Copy the code
  1. Fish_redux “effect” file was created.
The function '_onAction' has type 'void Function(Action, Context<DemoState>)' that isn't of expected type 'dynamic Function(dynamic, Context<DemoState>)'. This means its parameter or return type does not match what is expected.
Copy the code
  1. When calling API in the beginning mode, I encountered a big pit, because I had done JAVA, C# and Vue mostly before, but not enough on mobile terminal. Then, after using Dio encapsulation, I started to connect to background API, and an error was reported. BaseUrl: BaseUrl: BaseUrl: BaseUrl: BaseUrl: BaseUrl: I guess the underlying VM of the DART is Linux. I decided to change the API of the remote IP, and it happened immediately.

    DioErrorType {/// DEFAULT error: DioErrorType {// DioErrorTypetype, usually occurs before connecting the server.
      DEFAULT,
    }
    Copy the code
  2. Not much documentation, lots of guesswork and online examples. Some are not very understanding, no one explained what, or a little painful.

Fish_redux several important concepts

  • Action defines an Action that can carry information and send it to the Store. In other words, Store changes must be triggered by the Action. Fish Redux has the following convention: The Action contains two fields, type and payload. Dart defines a Type enumeration class and an ActionCreator class to help constrain the type of payload.

  • Both functions are used to process data. Reducer is a pure function that changes Store data in response to actions. Effect is used to handle intents from the view, such as click events, making asynchronous requests, and other side effects.

  • Page can be thought of as a container that configures aggregate State, Effect, Reduce, View, Dependencies, and so on.

  • Adapter (optional), this is not easy to understand.

  • View decoupled from the pure page.

Start by creating a simple page

When you create a new one using the saltwater plugin, the file shown above will appear.

  • Action, which defines some actions for view or effect.
import 'package:fish_redux/fish_redux.dart';
import '.. /.. /.. /apiModel/user.dart';

//TODO replace with your own action
enum BusinessAction { query }

class BusinessActionCreator {

  static Action updateAction(List<User> userList){
    print('The value of dispatch is here after effect requested it');
    returnAction(BusinessAction.query, payload: userList); }}Copy the code
  • An effect is an event that makes an asynchronous request and so on
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
import '.. /.. /.. /api/user.dart';

Effect<BusinessState> buildEffect() {
  returncombineEffects(<Object, Effect<BusinessState>>{ Lifecycle.initState: _init, }); } void _init(Action action, Context<BusinessState> CTX){// Http request userapi.getUser ().then((value){ ctx.dispatch(BusinessActionCreator.updateAction(value)); }); }Copy the code
  • Page is the configuration aggregation State, Effect, Reduce, View
import 'package:fish_redux/fish_redux.dart';

import 'effect.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';

class BusinessPage extends Page<BusinessState, Map<String, dynamic>> {
  BusinessPage()
      : super(
            initState: initState,
            effect: buildEffect(),
            reducer: buildReducer(),
            view: buildView,
            dependencies: Dependencies<BusinessState>(
                adapter: null,
                slots: <String, Dependent<BusinessState>>{
                }),
            middleware: <Middleware<BusinessState>>[
            ],);
}
Copy the code
  • Reducer Where values are modified
import 'package:fish_redux/fish_redux.dart';

import 'action.dart';
import 'state.dart';

Reducer<BusinessState> buildReducer() {
  return asReducer(
    <Object, Reducer<BusinessState>>{
      BusinessAction.query: _onQuery,
    },
  );
}

BusinessState _onQuery(BusinessState state, Action action) {
  print('I'm the place where it's really worth updating');
  final BusinessState newState = state.clone();
  newState.userList = action.payload;
  return newState;
}
Copy the code
  • State is where the property is initialized
import 'package:fish_redux/fish_redux.dart';
import '.. /.. /.. /apiModel/user.dart';

class BusinessState implements Cloneable<BusinessState> {

  BusinessState({this.userList});

  List<User> userList = new List<User>();

  @override
  BusinessState clone() {
    returnBusinessState(); } // Initialize BusinessState initState(Map<String, dynamic> args) {List<User> tempList = new List<User>(); User user = new User(); user.no = 0; user.name ='Liang Ergou';
  user.email = '[email protected]';
  tempList.add(user);
  return BusinessState(userList: tempList);
}
Copy the code
  • View decoupled interface
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';

import 'action.dart';
import 'state.dart';

Widget buildView(BusinessState state, Dispatch dispatch, ViewService viewService) {

  var buildListView = ListView.builder(
    itemCount: state.userList.length,
    itemBuilder: (BuildContext context, int index) {
      return Card(
        child: ListTile(
          leading: FlutterLogo(),
          title: Text('no. : +
              state.userList[index].no.toString() +
              'name: +
              state.userList[index].name.toString() +
              'email:+ state.userList[index].email.toString()), ), ); }); // View of the middle pagereturn Scaffold(appBar: null, body: Center(child: buildListView));
}
Copy the code

The Http wrapper

Above, we use the Http request, which is packaged with Dio once, as follows:

import 'package:dio/dio.dart';
import 'dart:io';
import 'dart:async'; /* * Encapsulates restful requests * * GET, POST, DELETE, and PATCH are used to process related transactions in a unified manner: * - Process the request prefix in a unified manner. * - Unified print request information; * - Uniformly prints response information; * - Uniformly print error messages; */ class HttpUtils { /// global dio object static Dio dio; /// default options static const String API_PREFIX ='http://101.132.124.171:8080/demo-1.0/api';
  static const int CONNECT_TIMEOUT = 10000;
  static const int RECEIVE_TIMEOUT = 3000;

  /// http request methods
  static const String GET = 'get';
  static const String POST = 'post';
  static const String PUT = 'put';
  static const String PATCH = 'patch';
  static const String DELETE = 'delete';

  static Future<dynamic> request (
      String url,
      { data, method }) async {

    data = data ?? {};
    method = method ?? 'GET'; Data. forEach((key, value) {if(url.indexOf(key) ! = -1) { url = url.replaceAll(':$key', value.toString()); }}); Dio dio = createInstance(); /// Prints request information: request address, request mode, and request parametersprint('Request address: [' + dio.options.baseUrl + url + '】');
    print('Request parameters:'+ data.toString()); var result; try { Response response = await dio.request(url, data: data, options: new Options(method: method)); result = response.data; /// Prints response informationprint('Response data successful! '); } on DioError catch (e) {/// Prints information about the request failureprint('Request error:' + e.toString());
    }

    returnresult; } // create a dio instance object static diocreateInstance () {
    if(dio == null) {/// Global attributes: request prefix, connection timeout, response timeout BaseOptions Option = new BaseOptions(baseUrl: API_PREFIX, connectTimeout: CONNECT_TIMEOUT, receiveTimeout: RECEIVE_TIMEOUT, headers: {"user-agent": "dio"."api": "1.0.0"}, contentType: ContentType.JSON, // Transform the response data to a String encoded with UTF8. // The default value is [ResponseType.JSON]. responseType:  ResponseType.plain ); dio = new Dio(option); }returndio; } /// Empty the dio object staticclear() { dio = null; }}Copy the code

Json String parsing

In the above HTTP, our data is a JSON array. The data is as follows. We use jSON_serialIZABLE to convert the JSON string to a List array.

[{"no": 10,"name":"12"."email":"[email protected]"}, {"no": 11."name":"11"."email":"[email protected]"}, {"no": 12."name":"asdf"."email":"[email protected]"}, {"no": 14."name":"li"."email":"[email protected]"}, {"no": 15."name":"w"."email":"[email protected]"}, {"no": 16."name":"Beam"."email":"[email protected]"}, {"no": 17."name":"Li"."email":"[email protected]"}, {"no": 18."name":"li"."email":"[email protected]"}, {"no": 112,"name":"In"."email":"[email protected]"}, {"no": 122,"name":"1"."email":"[email protected]"}]
Copy the code

It’s a bit more complicated than Java or C# to json. Dart = user; dart = user; dart

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  User({this.no, this.name, this.email});

  int no;
  String name;
  String email;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

Copy the code

When you start creating the file, the last two lines will report an error. Then we use the following command line, which will generate a “user.g.art”, and it will be ready to use.

flutter packages pub run build_runner watch 
Copy the code

[json] List [json] List [json] List [json]

Var usersJson = json.decode(result); List<User> userList = (usersJson as List).map((i)=>User.fromJson(i)).toList();Copy the code

I’ll leave you there. Hope to help you oh, and remember to give me some stars, writing is not easy ah.