Fair logic syntactic sugar design and implementation

Syntactic sugar (English: Syntactic Sugar) refers to a syntax added to a computer language that has no effect on the functionality of the language, but is more convenient for programmers to use. Syntactic sugar makes programs more concise and more readable. Fair syntax sugar is not to provide an easy-to-use interface for Dart syntax, but to make Fair more convenient to build in layout and logic mixing scenarios.

Thinking in the Scenario of Mixing Layout and Logic

If you need to translate the following code:

  Widget getTitleWidget() {
    if (_count == 1) {
      return Text(
        'Title 1',
        style: TextStyle(color: Colors.white),
      );
    } else {
      return Text(
        'Title Other',
        style: TextStyle(color: Colors.red),
      );
    }
  }

What do we need to do with the above code? The feasible solutions should be: 1. Assign the above code to the logic module and put it on the JS side for processing? 2. Observe that what this method returns is a Widget, most of the method is related to the layout, and only one judgment needs to be processed for logic. Is it possible to do some conditional judgment processing when building a Widget Tree on the Dart side?

plan 1:

It is necessary to fully provide the mapping of JS and Dart Widgets and the ability to build Widgets on the JS side, which becomes a solution similar to MXFlutter and Kraken. The difference is that they need to be written by developers themselves, and Fair can be generated by tools.

Scenario 2:

The layout parsing engine needs to be told which branch to use when building. Fixed-format judgments, loops, etc. can also be processed on the Dart side, and the related logic presentation needs to be in the DSL file. After thinking, we decided to adopt the second solution to solve the scene problem of mixed layout and logic, and to complete the calling and splicing of layout sub-methods on the Dart side.

Scenario 3:

If you have a better design, please contact us.

The entire logical processing unit in the final Fair, as shown in the following figure:

1629600918721.jpg

In the Fair logic processing unit, syntactic sugar supports branches and loops commonly used in layouts. The design of Fair is different from the two dynamic schemes of kraken and mxflutter. One of Fair's vision is to allow the same code to switch between Flutter native and dynamic at will. When developing and versioning requirements, we use native code release to continue to maintain Flutter's performance advantages; in hot update scenarios, we can dynamically update Widgets by delivering dynamic files. Therefore, when designing syntactic sugar, Fair needs to consider that Flutter can run normally in both native and dynamic scenarios.

This article focuses on:

How to achieve "two-state" result consistency

Dynamic data binding and logic processing

Fair syntax sugar support status

(We will introduce the List Map logic supported by Fair as an example, and there are also examples of syntactic sugar content in Fair example)

"Two-state" consistency

Since Flutter's original ecology and dynamic carriers are different, it is necessary to do "two-state" consistency processing to ensure that the results of running in the two states are consistent, and it is necessary to provide developers with a consistent coding interface. As shown in the figure below, the running process after adding syntactic sugar:

1629602514752.jpg

Original ecology

In the original ecological scenario, we only need to define the List Map method as a static method, support it in the method, and convert the target syntax. This part is relatively easy to implement. For example, we uniformly define the syntactic sugar method into the Sugar class, and the code is as follows:

class Sugar {
    static List<T> map<T, E>(List<E> data, {T Function(E item) builder}) {
  	return data.mapEach((index, item) => builder(item));
    }
}

dynamic

In a dynamic scenario, when the Fair framework is initialized, the registration of the global syntactic sugar module will be completed, and these syntactic sugars can be dynamically accessed when the FairWidget carrier is running, and then the data binding and logical processing will be introduced in Section 2 to finally generate the target. layout. Next, we introduce the registration and dynamic access of syntactic sugar.

Syntactic sugar method registration

FairApp({  ....
})  : placeholderBuilder = placeholder ?? _defaultHolder,
      super(key: key, child: child) {
  // 完成全局的模块的注册,其中就包括语法糖模块
  setup(profile, delegate, generated, modules);
}

// 语法糖动态注册
FairWidgetBinding provider = () {
  return {
    'Sugar.map': (props) {
      var items = pa1(props);
      assert(items is List, 'failed to generate list of Sugar.map');
      return ((items as List).asIteratorOf<Widget>() ?? items).toList();
    },
};

As shown in the above code, setup is the entry for registering global syntactic sugar; provider is the provided syntactic sugar set, and more syntactic sugar methods can be defined in the provider.

Syntactic sugar for method access

How to display the content of List Item to each Cell in the list? Below we give an example of displaying the list numbers:

Dart side, as follows:

class _State extends State<MapPage> {
  var _list = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 100];
  ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: ListView(
          children: Sugar.map(_list, builder: (item) {
            return Container(
              child: Row(...),
            );
          }),
        ));
  }
}

The converted DSL code is as follows:

{
    ...
    "body": {
      "className": "ListView",
      "na": {
        "children": {
          "className": "Sugar.map", // 语法糖名称
          "pa": [
            "^(_list)" // 目标List
          ],
          "na": {
            "builder": { // 每一个list item展示的卡片布局
              "className": "Container",
              "na": {
                "child": {
                  "className": "Row",
                  "na": {
                    "children": [
                      {
                        "className": "Expanded",
                        "na": {
                          "child": {
                            "className": "Container",
                            "na": {
                              "child": {
                                "className": "Row",
                                "na": {
                                  "children": [...]
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "methodMap": {}
}

When building the target layout, you only need to take the list data from the pa and iteratively build the layout DSL in the builder.

Data binding and logic processing in dynamic scenarios

When designing the syntactic sugar module, we only access the original data from the JS domain, and then the entire layout composition related work is done in the Flutter domain. The loop iteration logic of List Map, compared with the one-time layout construction such as Sugar.ifEqual, the loop logic such as Map and MapEach needs special processing during layout analysis. The iterative function is processed when parsed as follows:

List<Widget> _buildSugarMap(
    Function mapEach, Map map, Map methodMap, BuildContext context) {
  var source = pa0(map);
  var children = [];
  // 根据脚本取出List在JS域的数据
  if (source is String) {
    var r = proxyMirror.evaluate(context, bound, source);
    if (r.data != null) {
      source = r.data;
    }
  }
  if (!(source is List)) {
    throw Exception('Sugar.mapEach has no valid source array');
  }

  //根据List 内容实现迭代解析
  if (source != null && source is List) {
    // Domain 内完成目标数据的内容替换
    children = Domain(source).forEach(($, _) {
      return convert(context, map['na']['builder'], methodMap, domain: $);
    });
  }
  // 组装成整体目标布局
  var params = {
    'pa': [source, children]
  };
  return mapEach.call(params);
}

According to the above code and annotation content, developers only need to use Sugar.map(_list, builder: (item) {}) syntax sugar to complete the binding of List content and target Item data. However, currently the Map and MapEach syntactic sugars can only simply handle variable names and contents with fixed names such as item and index. In the future, we will keep the layout data binding logic processing consistent with the overall one-time processing.

Fair syntax sugar support status

At present, the Fair framework has built-in 5 syntactic sugars such as if, ifRange, and Map by default, which will be gradually expanded as needed. Of course, developers can also submit common layout logic to extend the Fair syntactic sugar set.

Here, the content related to the syntax sugar of the Fair framework is introduced. In fact, the appearance of the Fair syntax sugar module is, in the final analysis, the product of the trade-off between the syntax support of the Fair compilation tool and the ease of use for developers. Syntactic sugar will change the development grammar in the mixed scene, and this part will also be weakened with the enhancement of parsing tool capabilities and data processing capabilities during construction.

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324163081&siteId=291194637