FlutterArtist FilterModel MultiOptFilterCriterion ex1
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
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 | ![]() |
How to define a multi-option criterion that allows the user to select multiple options. multiSelection | ![]() |
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
- Basic concepts in Flutter Artist
- FlutterArtist Block ex1
- FlutterArtist Filter Example
- FlutterArtist FilterModel MultiOptFilterCriterion ex1
- 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 ex2
- FlutterArtist FilterModelStructure ex3
- FlutterArtist Internal Shelf Event ex1
- FlutterArtist Deferring External Shelf Events (Ex1)
- FlutterArtist DropdownSortPanel
Show More



