Author: Idle fish technology – Ji Feng

On March 5th, Xiyu announced that it has opened source Fish Redux on GitHub. Fish Redux is an assembly of Flutter application framework based on Redux data management, especially suitable for building large and complex applications. Its most notable features are its functional programming model, predictable state management, pluggable component architecture, and optimal performance. In the following sections, we will cover the features and usage of Fish Redux in detail, based on an exclusive InfoQ interview with the Idle Fish Flutter team and Fish Redux’s open source documentation.

Open source background

At the beginning of xianyu’s access to Flutter, our landing plan hoped to verify the completeness of Flutter by trying several of the most complex main links. However, our details were relatively complicated in general, mainly reflected in two aspects:

  • The page needs centralized state management, which means that different components of the page share a single data source, and all components of the page need to be notified of changes in the data source.
  • There are many FORMS of UI display on the page (such as ordinary details, free fish currency details, community details, auction details, etc.) with a large workload, so UI components need to be reused as much as possible, that is to say, component segmentation needs to be better.

When we tried to use the existing frameworks on the market (Redux and Bloc provided by Google), we found that no framework can solve both centralized state management and UI compontization, because the two problems are contradictory (centralized vs. divide-and-conquer). So we wanted a framework to solve our problems, and Fish Redux was born.

Fish Redux itself has gone through several iterations. The version you can see now has gone through three major iterations. In fact, it has also gone through a lot of discussion and thinking by the team.

The first version is based on the transformation of Flutter_redux in the community. The core is to provide the componentalization of UI code. Of course, the problem is very obvious.

The second version made significant modifications to the problems of the first version, which solved the divide and conquer problem of UI code and logic code. However, according to redux standard, it broke redux principle, which was unacceptable for the team that kept improving.

Therefore, in the third version of reconstruction, we established the overall architectural principles and layering requirements. On the one hand, we implemented the Redux on flutter side according to the code of ReduxJS, and completely retained the principles of Redux. On the other hand, to address the componentization problem, it provides the encapsulation of components on top of Redux, and innovatively provides the ability to divide and conquer business code through the architecture design of this layer.

At this point, we completed the basic design of Fish Redux, but in the subsequent application, we found the code performance problems after business assembly. To solve this problem, we provided the corresponding Adapter capability again, which ensured the big cell problem in the long list scenario. Fish Redux has been online for more than 3 months now, and we look forward to more input from fish Redux to the community.

Fish Redux technology analysis

Hierarchical architecture diagram

Architecture diagram: The main body is divided into two layers from bottom to top. Each layer is used to solve the problems and contradictions of different layers. The following layers are developed successively.

Redux

Redux is a data management framework from the front-end community, which may be a little strange to Native developers. Let’s give a brief introduction.

What does Redux do?

Redux is a framework for data management that is predictable and easy to debug. Redux is in charge of adding, deleting, modifying, and reviewing data.

How was Redux designed and implemented?

Redux is a functional data management framework. Traditional OOP does data management by defining beans, and each Bean exposes some public-API to manipulate internal data (congestion model).

Functional approach is a more abstract latitude, the definition of data is some structs (anemia model), and the methods of data operation are unified into Reducer with the same function signature (T, Action) => T.

FP:Struct (anemia model) + Reducer = OOP (congestion model)

Redux also adds the Middleware (AOP) schema and Subscribe mechanism commonly used in FP to make the framework extremely flexible and extensible.

For anemia model and congestion model, please refer to:

En.wikipedia.org/wiki/Plain_…

The disadvantage of story

The Redux core is all about data management, not about what scenarios to use it in, which is both its strength and its weakness.

There are two specific problems in our actual use of Redux:

  • The contradiction between Redux centralization and Component divide-and-conquer;
  • Reducer of Redux requires layers of manual assembly, resulting in complexity and error-prone.

Fish Redux improvements

Fish Redux does centralized observable data management with Redux. However, the disadvantages of traditional Redux at the usage level have been improved by better and higher abstractions in the scenario of end-to-end flutter page latitude development.

A component needs to define a data (Struct) and a Reducer. At the same time, there is a parent dependent child relationship between components. Through this dependency relationship, we solved the contradiction between [centralization] and [divide and conquer], and at the same time, the manual Combine of Reducer was automatically completed by the framework, greatly simplifying the difficulty of using Redux. We get the ideal concentration and divide and conquer code.

Follow community standards

The concepts of State, Action, Reducer, Store, and Middleware are exactly the same as community ReduxJS. We will keep all the advantages of Redux in its original form.

For a closer look at Redux, see: github.com/reduxjs/red…

Component

A component is a partial presentation and encapsulation of functionality. Based on the principle of Redux, we divided functions into Reducer and non-reducer functions.

Therefore, we have obtained three parts, View, Effect and Reducer, which are called the three elements of the component, responsible for the display of the component, the behavior of non-modified data and the behavior of modified data respectively.

This is a split for now, as well as for the future. In Redux’s case, it’s data management and more. In the future of UI-Automation looks to be UI expression and others.

The expression of THE UI is entering the era of black box for programmers, and developers will focus more on the behavior of non-modifying data, the behavior of modifying data.

Components divide and conquer views as well as data. By layer by layer, we slice complex pages and data into independent modules. This will facilitate collaborative development within the team.

On the View

A View is simply a function signature: (T,Dispatch,ViewService) => Widget It contains three main pieces of information

  • Views are completely data-driven.
  • Events/callbacks generated by the view are “intents” issued through Dispatch without concrete implementation.
  • Need to use component dependencies, etc., through ViewService standardization call. For example, a typical function that conforms to the View signature.

About the Effect

Effect is the standard definition of non-modifying data behavior. It is a function signature: (Context, Action) => Object

  • Receive the “intent” from the View, including the corresponding lifecycle callback, and then execute it.
  • Its processing may be an asynchronous function, and the data may be modified in the process, so we don’t advocate holding data, but retrieving the latest data through context.
  • It does not modify data. If it needs to be modified, an Action should be sent to the Reducer.
  • Its return value is limited to bool or Future, corresponding to the flow that supports synchronous functions and coroutines.

Such as good coroutine support:

About the Reducer

Reducer is a function signature that fully conforms to Redux specification :(T,Action) => T Some Reducer that meets the signature:

At the same time, we register the Dependencies of components and adapters in an explicit configuration called Dependencies.

Component = View + Effect + Reducer Dependencies(Optional)

A typical assembly:

With Component abstraction, we get full divide-and-conquer, multi-latitude reuse, and better decoupling.

Adapter

Adapter is also a partial presentation and encapsulation of functionality. It is designed for ListView high performance scenarios and is a variation of the Component implementation.

It aims to solve three problems with the Component model in the flutter-ListView scenario:

1) Putting a “big-cell” in a Component does not benefit from ListView code performance optimization;

2) Component cannot distinguish appear | disappear and init | the dispose;

3) The coupling between the life cycle of Effect and View does not meet intuitive expectations in the ListView scenario.

In a nutshell, we want a logical ScrollView, a performance ListView, an abstraction of local presentation and functionality encapsulation. To make such a separate layer of abstraction, we see the actual effect. Instead of using the Component framework for the page, we use the Component+Adapter framework for the baseline performance comparison.

  • Reducer is long-lived, Effect is medium-lived, View is short-lived.

We make comparisons through continuous tests, taking an Android machine as an example:

  • The FPS of our details page before using the frame, the baseline is 52FPS;
  • With frames and only Component abstractions, FPS drops to 40 and suffers the “big-cell” trap;
  • With frames and Adapter abstractions, the FPS is up to 53, back above the baseline, with a slight improvement.

Directory

The recommended directory structure would look like this

sample_page
-- action.dart
-- page.dart
-- view.dart
-- effect.dart
-- reducer.dart
-- state.dart
components
sample_component
-- action.dart
-- component.dart
-- view.dart
-- effect.dart
-- reducer.dart
-- state.dartCopy the code

The upper layer is responsible for assembly, the lower layer is responsible for implementation, and a plug-in will be provided for us to fill in quickly.

An example of assembly using the detail scenario of idle fish:

Components are completely independent from each other, and components from containers.

Communication Mechanism

  • Component | communications within the adapter
  • Communication within | adapter between components

Simple description: Use self-first broadcast with a segment of priority processing.

Emitted actions are handled first by themselves, otherwise they are broadcast to other components and Redux. Finally, we completed all communication demands within and between components (parent to child, child to parent, brother to brother, etc.) through a simple and intuitive dispatch.

Refresh Mechanism

Data refresh

  • Partial data modification automatically triggers shallow copies of upper-layer data and is transparent to upper-layer business codes.
  • A copy of layers of data:

    • On the one hand, strict follow for Redux data modification.
    • On the other hand, it is also strict follow for data-driven display.

View refresh

Flat notifications to all components, which use shouldUpdate to determine if they need to refresh.

Advantages of Fish Redux

Centralized management of data

Centralized observable data management through Redux. We retained all the original advantages of Redux, and at the same time, the Reducer merge was automatically completed by the frame agent, greatly simplifying the complexity of using Redux.

Component divide-and-conquer management

Components divide and conquer both views and data. By layer by layer, we slice complex pages and data into independent modules. This will facilitate collaborative development within the team.

View, Reducer, and Effect isolation

Split the component into three stateless, independent functions. Because they are stateless, they are easier to write, debug, test, and maintain. At the same time, it brings more possibilities for combination, reuse and innovation.

Declarative configuration assembly

Components and adapters are assembled through free declarative configuration. Includes its View, Reducer, Effect, and its dependent children.

Good scalability

The core framework maintains its core three-tier focus, does nothing outside the core focus, and maintains flexible extensibility to the upper layers.

  • The framework doesn’t even have a single line of printed code, but the standard Middleware allows you to watch data flow and components change.

  • In addition to the core three layers of the framework, the Dart language features can be used to add mixins to Component or Adapter to flexibly combine them to enhance the customization and capabilities of their upper layers.
  • The integration of frameworks with other middleware, such as automatic exposure and high availability, is transparent to each other and is freely assembled by the top layer.

Small, simple and complete

  • It is very small, containing just over 1,000 lines of code;
  • It is simple to use, complete a few small functions, complete assembly, can run;
  • It’s complete.

About the future

After open source, Idle Fish intends to maintain Fish Redux in the following ways:

  • Through a series of subsequent external publicity, attract more developers to join or use. Currently, the application framework of Flutter ecology is still blank and has the opportunity to become a de facto standard.
  • Follow up with a series of idle Fish Flutter mobile middleware matrices to do open source;
  • Further, a series of supporting development aid debugging tools are provided to improve the efficiency and experience of upper Flutter development.

Fish Redux has been deeply applied in multiple scenarios in Alibaba Xianyu technology team. Finally, Talk is cheap, Show me the code, today we officially open source on GitHub, more content, please go to GitHub to learn.

GitHub address: github.com/alibaba/fis…