The team tried to overwrite the project with Flutter, but ran into a bit of annoyance. Previously, Android and IOS generally had ready-made libraries for multiple specifications of the product. I haven’t found the library of Flutter yet, so I had to do it myself. Let’s start with one of our commodity data structures:

{
  "attributes": []."goods_id": 1636,
  "goods_name": "Pearl milk Tea"."image": "http://********/static/goods_images/Rlp6d1536542627.png"."multi_spec": [{"image": "http://********/static/goods_images/EQqf01536544612.png"."product_code": ""."sales_amount": 45,
      "spec_info_list": [{"spec_name": "Specifications"."spec_name_id": 336,
          "spec_value": "Mug"."spec_value_id": 1019}, {"spec_name": "Color"."spec_name_id": 337,
          "spec_value": "Red"."spec_value_id": 1021}, {"spec_name": "Weight"."spec_name_id": 338,
          "spec_value": "Half jins of"."spec_value_id": 1023}]."specification_id": 1918,
      "stock": 1,"unit": "Cup"."unit_price": 7}, {"image": "http://********/static/goods_images/Fe30S1536544637.png"."product_code": ""."sales_amount": 16."spec_info_list": [{"spec_name": "Specifications"."spec_name_id": 336,
          "spec_value": "The cup"."spec_value_id": 1020}, {"spec_name": "Color"."spec_name_id": 337,
          "spec_value": "Green"."spec_value_id": 1022}, {"spec_name": "Weight"."spec_name_id": 338,
          "spec_value": "A kilo"."spec_value_id": 1024}]."specification_id": 1919,
      "stock": 1,"unit": "Cup"."unit_price": 6}, {"image": "http://********/static/goods_images/Fe30S1536544637.png"."product_code": ""."sales_amount": 16."spec_info_list": [{"spec_name": "Specifications"."spec_name_id": 336,
          "spec_value": "The cup"."spec_value_id": 1020}, {"spec_name": "Color"."spec_name_id": 337,
          "spec_value": "Red"."spec_value_id": 1021}, {"spec_name": "Weight"."spec_name_id": 338,
          "spec_value": "A kilo"."spec_value_id": 1024}]."specification_id": 1919,
      "stock": 1,"unit": "Cup"."unit_price": 6}],"pack_cost": 0."product_code": null,
  "sales_amount": 61,
  "stock"A: - 2,"unit": null,
  "unit_price": null
}
Copy the code

Under the multi_spec field is all the specifications of the product, and under the spec_info_list field are the specifications that make up the collocation. We model the specification values of specification collocation and composition of specification collocation respectively:

Class ShopGoodsMultiSpec {String image; String unit; int specificationId; List<ShopGoodsMultiSpecSpecInfo> specInfoList; int salesAmount; String productCode; int stock; double unitPrice; /// the constructor is omitted..... } / / / specifications and collocation of the specifications of the value, the corresponding spec_info_list element class ShopGoodsMultiSpecSpecInfo {int specValueId; int specNameId; String specName; String specValue; / / / reload the = = operator, in order to prevent the same specification values of two ShopGoodsMultiSpecSpecInfo object / / / to differ, Specvalue [spec_info.specname]. Contains (spec_info) is useful, otherwise there is a problem // @override // int gethashCode => "ShopGoodsMultiSpecSpecInfo_$specValueId".hashCode;
    
    bool operator ==(o){
        ShopGoodsMultiSpecSpecInfo obj = o;
	returnobj.specValueId == specValueId; } /// the constructor is omitted..... }Copy the code

Paste the tool code:

class SpecSkuUtil { List<String> allSpecKey = []; Map<String, ShopGoodsMultiSpec> allSpec = {}; Map<String, List<ShopGoodsMultiSpecSpecInfo>> allSpecValue = {}; Map<String, int> selected = {}; SpecSkuUtil(list <ShopGoodsMultiSpec> multiSpec) {for (ShopGoodsMultiSpec spec in multiSpec) {
      List valueIds = [];
      for (ShopGoodsMultiSpecSpecInfo spec_info in spec.specInfoList) {
        valueIds.add(spec_info.specValueId);
        if(! allSpecValue.containsKey(spec_info.specName)) { allSpecValue[spec_info.specName] = [spec_info]; }else if(! allSpecValue[spec_info.specName].contains(spec_info)) { allSpecValue[spec_info.specName].add(spec_info); } } valueIds.sort((a, b) => a.compareTo(b)); valueIds = valueIds.map((id) {return id.toString();
      }).toList();
      allSpec[valueIds.join("-")] = spec;
      _createCollocations(valueIds);
    }
  }

  void _createCollocations(List strList) {
    void build(List candidate, String prefix, int index) {
      if(! allSpecKey.contains(prefix)) { allSpecKey.add(prefix); }for(int i = index; i < candidate.length; i++) { List tmp = new List().. addAll(candidate); build(tmp, (prefix =="" ? "" : prefix + "-") + tmp.removeAt(i), i);
      }
    }

    build(strList, "", 0); {} / / / return all specifications: List < specification value object >} Map < String, List < ShopGoodsMultiSpecSpecInfo > >getAllSpecValue() {
    returnallSpecValue; } /// Set the selected {specification name: specification value id} voidsetSelectedIds(Map<String, int> selected) { this.selected = selected; Bool checkEnable(Map<String, int> candidate) {Map<String, int> tmpMap = Map.from(selected); tmpMap.addAll(candidate); List tmp = mapValue2List(tmpMap); tmp.sort((a, b) => a.compareTo(b));return allSpecKey.contains(tmp.join("-")); } /// Get ShopGoodsMultiSpecgetSpec() {
    List tmp = mapValue2List(selected);
    tmp.sort((a, b) => a.compareTo(b));
    return allSpec[tmp.join("-")]; } list list mapValue2List(map <String, int> map) {list TMP = new list (); ForEach ((key, value) {forEach(key, value) {forEach(key, value) {forEach(key, value) {forEach(key, value)if (value != null) {
        tmp.add(value);
      }
    });
    returntmp; }}Copy the code

The core principle is that any unordered combination of non-reusable elements of the specification values under each specification collocation can represent that specification collocation. Such as:

The combination a,b,c can produce is [a, b,c, AB, AC, BC, ABC]Copy the code

So, if we collect any unordered combination of non-reusable elements of the specification values under each specification collocation in a list (called allSpecKey), all we need to do to verify that a specification value is optional is to combine the selected specification value with the specification value to be selected. Then determine whether the combination is in allSpecKey to determine whether the specification value to be selected is optional.

The checkEnable method is called when rendering each specification button component, and the getSpec method can pick up a specification object after selecting one. If the selection is not complete, null is returned, just to identify whether the selection is complete. Of course, setSelectedIds must be executed every time a specification value is selected to set the selected specification Map.