mydomain
No ADS
No ADS

FlutterArtist FilterModel MultiOptFilterCriterion ex1

  1. Cấu trúc của ví dụ
  2. Employee03aShelf
  3. Employee03aFilterModel
  4. Employee03aBlock
  5. Employee03aFilterPanel
In this article, we will explore how to configure a filter with parent-child criteria, such as the relationship between "Company" and "Department."
The core discussion revolves around the Demo03a example in FlutterArtist Demo. In this case, Employee03aFilterModel includes three search criteria: Employee Name, Company, and Department — where Company and Department share a hierarchical relationship. Specifically, when the selected Company changes, the department list in the second Dropdown must automatically reload based on that selection.
Chú ý: Nếu bạn mới bắt đầu với FlutterArtist Filter, hãy xem ví dụ dưới đây trước thay vì ví dụ này.
  • Download FlutterArtist Demo
No ADS

1. Cấu trúc của ví dụ

Đầu tiên, hãy xem cấu trúc của ví dụ:

2. Employee03aShelf

Trong ví dụ này cấu trúc của Shelf khá đơn giản, chỉ bao gồm một FilterModel và một Block:
employee03a_shelf.dart
class Employee03aShelf extends Shelf {
  @override
  ShelfStructure defineShelfStructure() {
    return ShelfStructure(
      filterModels: {
        Employee03aFilterModel.filterName: Employee03aFilterModel(),
      },
      blocks: [
        Employee03aBlock(
          name: Employee03aBlock.blkName,
          description: null,
          config: BlockConfig(),
          filterModelName: Employee03aFilterModel.filterName,
          formModel: null,
          childBlocks: [],
        ),
      ],
    );
  }

  Employee03aFilterModel findEmployee03aFilterModel() {
    return findFilterModel(Employee03aFilterModel.filterName)
        as Employee03aFilterModel;
  }

  Employee03aBlock findEmployee03aBlock() {
    return findBlock(Employee03aBlock.blkName) as Employee03aBlock;
  }
}

3. Employee03aFilterModel

First, let's look at how the filter structure is defined. Pay close attention to the "company" and "department" criteria:
Employee03aFilterModel.defineFilterModelStructure()
@override
FilterModelStructure defineFilterModelStructure() {
  return FilterModelStructure(
    criteriaStructure: FilterCriteriaStructure(
      simpleCriterionDefs: [
        SimpleFilterCriterionDef<String>(criterionBaseName: "searchText"),
      ],
      multiOptCriterionDefs: [
        // Multi Options Single Selection Criterion.
        MultiOptFilterCriterionDef<CompanyInfo>.singleSelection(
          criterionBaseName: "company",
          fieldName: 'companyId',
          toFieldValue: (CompanyInfo? rawValue) {
            return SimpleVal.ofInt(rawValue?.id);
          },
          children: [
            // Multi Options Single Selection Criterion.
            MultiOptFilterCriterionDef<DepartmentInfo>.singleSelection(
              criterionBaseName: "department",
              fieldName: 'departmentId',
              toFieldValue: (DepartmentInfo? rawValue) {
                return SimpleVal.ofInt(rawValue?.id);
              },
            ),
          ],
        ),
      ],
    ),
    conditionStructure: FilterConditionStructure(
      connector: FilterConnector.and,
      conditionDefs: [
        FilterConditionDef.simple(
          tildeCriterionName: "searchText~",
          operator: FilterOperator.containsIgnoreCase,
        ),
        FilterConditionDef.simple(
          tildeCriterionName: "company~",
          operator: FilterOperator.equalTo,
        ),
        FilterConditionDef.simple(
          tildeCriterionName: "department~",
          operator: FilterOperator.equalTo,
        ),
      ],
    ),
  );
}
No ADS
How to define a multi-option criterion where the user can select at most one option.
singleSelection
MultiOptFilterCriterionDef<DepartmentInfo>.singleSelection(
  criterionBaseName: "department",
)
How to define a multi-option criterion that allows the user to select multiple options. 
multiSelection
MultiOptFilterCriterionDef<DepartmentInfo>.multiSelection(
  criterionBaseName: "department",
)
Based on the defined criteria and condition structures, three TildeFilterCriterion(s) are created. Each corresponds 1-1 with an input field on the FilterPanel.
FilterModel.performLoadMultiOptTildeCriterionXData()
This method is invoked sequentially for each MultiOptTildeFilterCriterion to fetch its data. The rule is that root criteria are loaded first, followed by child criteria, and finally the leaf criteria.
performLoadMultiOptTildeCriterionXData()
@override
Future<XData?> performLoadMultiOptTildeCriterionXData({
  required String multiOptTildeCriterionName,
  required String multiOptCriterionBaseName,
  required Object? parentMultiOptTildeCriterionValue,
  required SelectionType selectionType,
  required EmptyFilterInput? filterInput,
}) async {
  if (multiOptTildeCriterionName == "company~") {
    // ApiResult<PageData<CompanyInfo>>
    ApiResult<CompanyInfoPage> result = await _companyProvider.queryAll();
    // IMPORTANT: Throw ApiError.
    result.throwIfError();
    // IMPORTANT: Generics should be declared explicitly.
    var companyListXData = ListXData<int, CompanyInfo>.fromPageData(
      pageData: result.data,
      getItemId: (item) => item.id,
    );
    return companyListXData;
  } else if (multiOptTildeCriterionName == "department~") {
    //
    // Because "department" is child of "company".
    // So [parentMultiOptTildeCriterionValue] value is definitely non-null,
    // this guaranteed by the library.
    //
    CompanyInfo companyInfo =
        parentMultiOptTildeCriterionValue as CompanyInfo;
    // ApiResult<PageData<DepartmentInfo>>
    ApiResult<DepartmentInfoPage> result = await _departmentProvider
        .queryAllByCompanyId(companyId: companyInfo.id);
    // IMPORTANT: Throw ApiError if API Error.
    result.throwIfError();
    // IMPORTANT: Generics should be declared explicitly.
    return ListXData<int, DepartmentInfo>.fromPageData(
      pageData: result.data,
      getItemId: (item) => item.id,
    );
  }
  return null;
}
  • FlutterArtist ApiResult
  • FlutterArtist XData
No ADS
Debug Filter Model Viewer
The Debug Filter Model Viewer is a tool that allows you to view a list of criteria defined in the FilterModel and their values. This tool is accessed via the FilterControlBar or code.
  • FlutterArtist Debug Filter Model Viewer
FilterModel.createNewFilterCriteria()
When a user performs a Block or Scalar query, this method is called to instantiate the FilterCriteria object (in this case, Employee03aFilterCriteria).
createNewFilterCriteria()
@override
Employee03aFilterCriteria createNewFilterCriteria({
  required Map<String, dynamic> tildeCriteriaMap,
}) {
  return Employee03aFilterCriteria(
    searchText: tildeCriteriaMap["searchText~"], // String?
    company: tildeCriteriaMap["company~"], // CompanyInfo?
    department: tildeCriteriaMap["department~"], // DepartmentInfo?
  );
}
No ADS
Finally, the resulting FilterCriteria object is passed as a parameter to the performQuery() method of the Block or Scalar.
// Block:
Future<ApiResult<PageData<ITEM>?>> performQuery({
  required Object? parentBlockCurrentItem,
  required FILTER_CRITERIA filterCriteria,
  required SortableCriteria sortableCriteria,
  required Pageable pageable,
});

// Scalar:
Future<ApiResult<VALUE>> performQuery({
  required Object? parentScalarValue,
  required FILTER_CRITERIA filterCriteria,
});

4. Employee03aBlock

Hãy chú ý vào phương thức performQuery() của Employee03aBlock. Tham số filterCriteria là một đối tượng Dart bao gồm các tiêu chí được sử dụng cho việc truy vấn dữ liệu.
employee03a_block.dart (*)
class Employee03aBlock
    extends
        Block<
          int, //
          EmployeeInfo,
          EmployeeData,
          EmptyFilterInput,
          Employee03aFilterCriteria,
          EmptyFormInput,
          EmptyAdditionalFormRelatedData
        > {
  static const blkName = "employee03a-block";

  final employeeRestProvider = EmployeeRestProvider();

  Employee03aBlock({
    required super.name,
    required super.description,
    required super.config,
    required super.filterModelName,
    required super.formModel,
    required super.childBlocks,
  });

  @override
  Future<ApiResult<PageData<EmployeeInfo>?>> performQuery({
    required Object? parentBlockCurrentItem,
    required Employee03aFilterCriteria filterCriteria,
    required SortableCriteria sortableCriteria,
    required Pageable pageable,
  }) async {
    return await employeeRestProvider.query(
      searchText: filterCriteria.searchText,
      companyId: filterCriteria.company?.id,
      departmentId: filterCriteria.department?.id,
      pageable: pageable,
    );
  }
  ...
}
  • FlutterArtist FilterCriteria

5. Employee03aFilterPanel

Lớp Employee03aFilterPanel tạo ra giao diện người dùng cho phép người dùng nhập vào và lựa chọn các tiêu chí cho việc lọc dữ liệu.
employee03a_filter_panel.dart
class Employee03aFilterPanel extends FilterPanel<Employee03aFilterModel> {
  const Employee03aFilterPanel({required super.filterModel, super.key});

  @override
  Widget buildContent(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        buildFilterBar(context),
        Divider(),
        FaFormBuilderTextField.topLabel(
          name: "searchText~",
          labelText: "Search Text",
          maxLines: 1,
          onChanged: (_) async {
            await _queryEmployee03a();
          },
        ),
        SizedBox(height: 10),
        FaFormBuilderDeselectableDropdown<CompanyInfo>.topLabel(
          name: "company~",
          labelText: "Company",
          items: filterModel.getMultiOptTildeCriterionData("company~") ?? [],
          getItemText: (item) => item.name,
          onChanged: (_) async {
            await _queryEmployee03a();
          },
        ),
        SizedBox(height: 10),
        FaFormBuilderDeselectableDropdown<DepartmentInfo>.topLabel(
          name: "department~",
          labelText: "Department",
          items: filterModel.getMultiOptTildeCriterionData("department~") ?? [],
          getItemText: (item) => item.name,
          onChanged: (_) async {
            await _queryEmployee03a();
          },
        ),
      ],
    );
  }

  Future<void> _queryEmployee03a() async {
    FlutterArtist.codeFlowLogger.addMethodCall(
      ownerClassInstance: this,
      currentStackTrace: StackTrace.current,
      parameters: null,
    );
    //
    await filterModel.queryAll();
  }
}
  • FlutterArtist Fa FormBuilder
No ADS

FlutterArtist

Show More
No ADS