FlutterArtist FilterModelStructure ex1
In this article, we will define a sample criteria structure where some criteria have parent-child relationships. From this template, the system automatically generates related TildeCriterion(s).
Specifically, company and department are two Criterion(s) with a parent-child relationship defined in the Template. Upon implementation, they generate two independent pairs: company~1/department~1 and company~2/department~2.
Independent mechanism:
- If you change the selection on InputField("company~1"), it will cause InputField("department~1") to change its data (reload) without affecting InputField("department~2").
In this article, we'll look at how to define a ModelFilterStructure. The result will be a Field-Based JSON like this:
Field-Based JSON
{
"connector": "AND",
"conditions": [
{
"field": "searchText",
"operator": "containsIgnoreCase",
"value": null
},
{
"connector": "OR",
"conditions": [
{
"field": "departmentId",
"operator": "equalTo",
"value": 4
},
{
"field": "departmentId",
"operator": "equalTo",
"value": 3
}
]
}
]
}
Look at the FilterPanel.
- "company~1" and "department~1" are two parent-child TildeCriterion(s).
- Similarly, "company~2" and "department~2" are two parent-child TildeCriterion(s) and are independent of the "company~1/department~1" pair.

If you're new to FlutterArtist Filters, you should read the introduction to FilterModel to understand the overall design concept.And let's start with the simplest Filter example:
- Download FlutterArtist Demo
2. registerFilterModelStructure
First, let's look at the complete code of the defineFilterModelStructure() method, then we will analyze each part of its content.
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.group(
groupName: 'G2',
connector: FilterConnector.or,
conditionDefs: [
FilterConditionDef.simple(
tildeCriterionName: "department~1",
operator: FilterOperator.equalTo,
),
FilterConditionDef.simple(
tildeCriterionName: "department~2",
operator: FilterOperator.equalTo,
),
],
),
],
),
);
}FilterModelStructure
simpleCriterionDefs | Define simple criteria and their data types. If the data types are not simple types, you need to provide a conversion function. |
multiOptCriterionDefs | Định nghĩa các tiêu chí có nhiều lựa chọn, kiểu dữ liệu và mối quan hệ của chúng. Nếu kiểu dữ liệu không phải là kiểu đơn giản bạn cần cung cấp một hàm chuyển đổi. MultiOptFilterCriterionDef |
No ADS
In the Debug Filter Model Viewer, you will see the criteria and their relationships like this:

FilterConditionStructure
Let's look at the code that defines the conditional structure.
FilterConditionStructure (*)
FilterConditionStructure(
connector: FilterConnector.and,
conditionDefs: [
FilterConditionDef.simple(
tildeCriterionName: "searchText~",
operator: FilterOperator.containsIgnoreCase,
),
FilterConditionDef.group(
groupName: 'G2',
connector: FilterConnector.or,
conditionDefs: [
FilterConditionDef.simple(
tildeCriterionName: "department~1",
operator: FilterOperator.equalTo,
),
FilterConditionDef.simple(
tildeCriterionName: "department~2",
operator: FilterOperator.equalTo,
),
],
),
],
)Note that although company~1 and company~2 do not directly appear in the condition structure (because the server only needs to filter by departmentId), they are still automatically generated to facilitate data filtering on the interface.

See another similar example where "department~1" and "department~2" are children of "company~".
No ADS
On the FilterPanel, you need UI Components with the same names as the TildeFilterCriterion(s). When the user enters or selects a value on the interface (FilterPanel), it will be updated in the FilterModel.

The current values can be observed in the Debug Filter Model Viewer.

3. Employee59aFilterModel
Since both company~1 and company~2 are generated from the company template, their loading logic is identical. The "magic" lies in the parentMultiOptTildeCriterionValue parameter.
performLoadMultiOptTildeCriterionXData()
@override
Future<XData?> performLoadMultiOptTildeCriterionXData({
required String multiOptTildeCriterionName,
required String multiOptCriterionBaseName,
required Object? parentMultiOptTildeCriterionValue,
required SelectionType selectionType,
required EmptyFilterInput? filterInput,
}) async {
if (multiOptCriterionBaseName == "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 (multiOptCriterionBaseName == "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;
}4. Employee59aFilterCriteria
In case you want to send the fieldBasedJSON to the server for querying and filtering data, you simply need to implement a basic FilterCriteria class.
employee59a_filter_criteria.dart
class Employee59aFilterCriteria extends FilterCriteria {
Employee59aFilterCriteria();
..
}Next, implement the abstract method createNewFilterCriteria() within the FilterModel. This method initializes a new FilterCriteria object, and the library automatically assigns the value from the FilterModel's fieldBasedJSON property to the corresponding property in FilterCriteria. (Through the Library-private setters mechanism).
Employee59aFilterModel.createNewFilterCriteria()
@override
Employee59aFilterCriteria createNewFilterCriteria({
required Map<String, dynamic> tildeCriteriaMap,
}) {
return Employee59aFilterCriteria();
}The FilterCriteria object created above is passed as a parameter to the Block.performQuery() and Scalar.performQuery() methods.
Employee59aBlock.performQuery()
@override
Future<ApiResult<PageData<EmployeeInfo>?>> performQuery({
required Object? parentBlockCurrentItem,
required Employee59aFilterCriteria filterCriteria,
required SortableCriteria sortableCriteria,
required Pageable pageable,
}) async {
return await employeeRestProvider.queryWithFieldBasedJSON(
pageable: pageable,
fieldBasedJSON: filterCriteria.fieldBasedJSON,
);
}Finally, utilizing the fieldBasedJSON in the actual API call:
Future<ApiResult<EmployeeInfoPage>> queryWithFieldBasedJSON({
required Pageable pageable,
required String fieldBasedJSON,
}) async {
Map<String, dynamic>? queryParameters = {
"currentPage": pageable.page,
"pageSize": pageable.pageSize,
"fieldBasedJSON": fieldBasedJSON,
};
// /rest/advancedGet/employeeInfoPage
ApiResult<EmployeeInfoPage> result = await flutterArtistDio.jsonGet(
"/rest/advancedGet/employeeInfoPage",
queryParameters: queryParameters,
converter: EmployeeInfoPage.fromJson,
);
return result;
}
- FlutterArtist Debug Filter Criteria Viewer
No ADS
FlutterArtist
- Basic concepts in Flutter Artist
- FlutterArtist Block ex1
- FlutterArtist Filter Example
- FlutterArtist Form ex1
- The idea of designing filter models in FlutterArtist
- FlutterArtist FormModel.patchFormFields() Ex1
- FlutterArtist BlockQuickItemUpdateAction Ex1
- FlutterArtist BlockNumberPagination Ex1
- FlutterArtist BlockQuickMultiItemCreationAction Ex1
- FlutterArtist ListView Infinite Scroll Pagination Example
- FlutterArtist Pagination
- FlutterArtist Sort DropdownSortPanel Example
- FlutterArtist Dio
- FlutterArtist BackgroundWebDownloadAction ex1
- FlutterArtist Block External Shelf Event ex1
- FlutterArtist Master-detail Blocks ex1
- FlutterArtist Scalar ex1
- FlutterArtist Pagination Davi table Infinite Scroll Ex1
- FlutterArtist Filter FormBuilderField ex1
- FlutterArtist Form Parent-child MultiOptFormProp ex1
- FlutterArtist Manual Sorting ReorderableGridView Example
- FlutterArtist Manual Sorting ReorderableListView
- FlutterArtist Scalar External Shelf Event ex1
- FlutterArtist Code Flow Viewer
- FlutterArtist Log Viewer
- FlutterArtist config
- FlutterArtist StorageStructure
- FlutterArtist Debug Storage Viewer
- FlutterArtist DebugMenu
- FlutterArtist Debug UI Components Viewer
- FlutterArtist Debug Shelf Structure Viewer
- FlutterArtist Context Provider Views
- FlutterArtist FilterModelStructure ex1
- FlutterArtist FilterModelStructure ex3
- FlutterArtist Internal Shelf Event ex1
- FlutterArtist Deferring External Shelf Events (Ex1)
- FlutterArtist DropdownSortPanel
Show More


