FAC (Flutter Architecture Components)

FAC’s Flutter Architecture Components address Flutter cross-component refreshing and centralized state management, separation of concerns, and building high-performance Flutter pages.

Source code: Github

Feature

Compared to Providers:

  • FACProvides a powerful decoupling deviceViewModel;
  • A more concise way of associating states;
  • Centralized state management by page or APP latitudeViewModelStore;
  • More efficient refresh mechanism (supportedLiveStateTo state isolation,memoFilter parameter list)
  • State updates can be triggered in initState, such as loading data, initializing page data, and so on

FAC architecture component model

  • Simple abstract model

Data can be centrally managed in the VM, and UI and data can be associated through LiveState.

  • Analogy AAC

Yes, FAC looks like the AAC (Android Architecture Components) framework. We built LiveState, an observable data object with lifecycle listening, for state and UI controller binding. And implement ViewModelStoreProxyWidget used to store and uninstall the ViewModel objects — of course in business development, we do not need to pay attention to its existence, the architectural layer into place;

All of this is a boon for developers familiar with Android’s native AAC components; Because of the large and cumbersome providers, the complex Redux framework, learning FAC is cost free!

Usage scenarios

  • Component states are shared. States between components can be observed
  • Cross-component communication/refresh
  • Gets the target component state at any code location and refreshes
  • A component needs to change global state
  • Build large complex business and multi-page nesting scenarios

Install the FAC

Modify pubspec yaml

Dependencies: pikach_fac: ^ 1.0.3Copy the code

use

Three steps for enterprise-level complex business scenarios, code layering, and state management:

  • 1. Define the ViewModel and Model data types
class PageViewModel extends ViewModel { LiveState<PageModel> pageModel = LiveState<PageModel>(PageModel()); // define static methods, Static PageViewModel get(BuildContext Context) {return viewModel. of<PageViewModel>(BuildContext context) context, builder: () => PageViewModel(), ); } void onCountClick(BuildContext Context) {pageViewModel.get (context).pagemodel.setState ((value, _) { _.state.count++; }); }}Copy the code
  • 2. Define LiveState
LiveState<PageModel> pageModel = LiveState<PageModel>(PageModel());
Copy the code
  • 3.UI and state binding
/ / / data and UI binding relationship PageViewModel. Get (context). PageModel. BuildWithObserve ((CTX, _state) {return Padding (Padding: Const EdgeInsets. All (8.0), child: new Row(children: <Widget>[Text(' refresh, only refresh: ${_state.count}',),],); }, memo: (value) => [value.count])Copy the code

See Github for details

The data flow

Architectural Design Background

The original design of Flutter architecture is to solve the problem that Flutter can effectively manage page state and refresh efficiency in complex nested page scenarios. For simple business scenarios, friendly code layering and state management capabilities as well!

Here are the Flutter pages developed by FAC in the business:

component

1. LiveState components

LiveState: data components that can be observed; It’s called Live because it can sense the life cycle of Widget components!

LiveState can be used to build observable data objects, that is, build models. The ability to notify observers of changes to the data in a timely manner, if the lifecycle is legal; To distribute or forward data objects (MediatorLiveState can be used as a forward or merge data source);

LiveState can be used in isolation to build cross-component refresh models; But we prefer to put it in a ViewModel component, so that we can convert model objects into ViewModels and build code structures that are separate from the UI and model!

LiveState<ReportRequestModel> _reportRequest; LiveState<ReportRequestModel> get reportRequest => _reportRequest ?? = LiveState<ReportRequestModel>(ReportRequestModel());Copy the code
Other interfaces

LiveState provides other related interfaces for responsive programming development;

  • StateOwnerUsed to provide an overall change to state
class StateOwner<T> {
  T state;
} 
Copy the code
  • MediatorLiveStateUsed to merge multiple LiveState data sources, making it easier to build responsive applications

  • TransformationsFor LiveState data source changes, dart does not support reflection and may not be as useful, so it is recommended to use it directlyMediatorLiveStatealternative

2. The ViewModel components

ViewModel: Separate data and UI, focus on logical processing, build two-way binding of data and UI;

In addition, the class provides appContext for navigation and other non-element calls. This context comes from VMS!

1. Life cycle safety

The ViewModel’s declaration cycle is consistent with the current page cycle, and there is no need to worry about memory leaks or focus on recycling;

2. Decouple data from UI

To use a Provider directly, you need to manually build a ViewModel to decouple the data from the UI. The ViewModel component came in to solve this problem! The ViewModel can be used to manage the relevant data required by the UI, assume the interaction between the UI and the Model, and process the business logic. At any point on the page, we can get the corresponding ViewModel instance through viewModel. of

(context), thus establishing a two-way association between the UI and the Model! The ViewModel can handle complex interactions, business logic, asynchronous calls, and so on; All of our changes are based on the Model, which automatically correlates registered UI objects (Flutter widgets) to build a single data source model; Because the refresh of the component is automatically associated with the Model (rather than calling the setState() method in State), we would prefer to build the application by using just the StatelessWidget!

3. Build cross-component communication interfaces

The ViewModel is used to provide a custom component refresh interface such as an AppBar that needs to provide a title change interface, or an Action on the right side of the AppBar that provides a shopping cart count indicator. Typical cross-component refresh problem, which can be provided via ViewModel, developers can call on demand!

3. ViewModelStoreProxyWidget components

A similar story in the Store but ViewModelStoreProxyWidget sharing data is the current page in the related; Developers often don’t need to know it exists because we inject it in the right place through the framework.

_ViewModelStore Data store component

Similar in Android bag ViewModelStore component lifecycle – viewmodel components, ViewModelStoreProxyWidget as top page widgets, Share the page’s data with the InheritedWidget! Of course, it has the same storage mode as the native VMS. Map

() is used to store the ViewModel. In addition, we added final _factoriesByName = Map

() to accommodate complex large application scenarios; Can store multiple viewModels with the same name, so that the same component type interface problem in a single page can be solved!
,>
,>

Design principle

Proxy interception versus proxy penetration

  • Agent to intercept

The ease with which we can retrieve data sources and refresh the UI from anywhere is largely due to proxy interception. As follows, the ViewModel. Of < CountViewModel > (context) model

The proxy mechanism is simple. We use the Provider to register VMS in the right location for node interception, because the inheritWidget gets data from the generic at the closest location for interception. Benefits:

  • Prevents data mixing between the outer page and the current child page

  • You can retrieve data and refresh the UI from any location

  • The agent through

How do we get the data from the outer component after proxy interception? The ViewModel provides a proxy penetration method for retrieving the outer layer data.

The proxy interception and proxy penetration patterns are as follows

Proxy penetration demo

Shared Stroe

Pages can share multiple viewModels of the same type within a VMS using the instanceName parameter. Under a page, multiple components of the same type can be nested and methods of different components can be called.

expand

Custom component interfaces

How does a custom component provide a component refresh interface?

Use providers directly

The Provider refreshes the child components under Consumer by mounting the Provider’s Model built through ChangeNotifier on the upper-layer node. This process seems to me unusually long and overly coupled; Cannot provide an interface for external cross-component refresh.

Using the FAC

Is there a way to provide a custom component with an external refresh interface that can be invoked from anywhere in the code? For example, AppBar provides interface for title modification and shopping cart provides interface for item quantity change.

  • LoadingPage implementation

LoadingPage is a page status management component, including page load, error, empty data, display, etc.

Because with FAC you can get an instance of the ViewModel anywhere on the page, you can provide a VM interface when developing a custom Widget LoadingPage; External refreshes can be performed using this interface:

PkcPageViewModel.get(context, instance: instance).pageLiveData.setState((value, _) {
        value.pageState = PkcPageState.state_loading;
      });
Copy the code

See Github for a component implementation of LoadingPage

  • AppBar refresh and shopping cart interface

reference

  • Application Architecture Guide
  • LiveData beyond the ViewModel — Reactive patterns using doubling and MediatorLiveData
  • Understanding LiveData made simple