FlutterArtist FilterInput Example 1
In real-world apps, we often need to set filter states externally — such as restoring a search from history or opening a specific filter via deep linking. Instead of forcing users to act manually, FilterInput allows you to "command" the filter through code, ensuring perfect synchronization.
FilterInput acts as a raw "blueprint". It represents input hints, allowing you to set data and current values for criteria without user interaction. Imagine simply passing the string "VINFAST", and the filter automatically knows to select VinFast Company, reload its departments, and display results instantly.
FilterInput đại diện cho một đầu vào của bộ lọc, cho phép cung cấp gợi ý để thiết lập dữ liệu và giá trị hiện thời cho cho tiêu chí thông qua code thay vì các thao tác của người dùng.

2. Employee40aShelf
This example is quite simple; the Shelf only consists of a Block and a FilterModel.
employee40a_shelf.dart
class Employee40aShelf extends Shelf {
@override
ShelfStructure defineShelfStructure() {
return ShelfStructure(
filterModels: {
Employee40aFilterModel.filterName: Employee40aFilterModel(),
},
blocks: [
Employee40aBlock(
name: Employee40aBlock.blkName,
description: null,
config: BlockConfig(),
filterModelName: Employee40aFilterModel.filterName,
formModel: null,
childBlocks: [],
),
],
);
}
Employee40aFilterModel findEmployee40aFilterModel() {
return findFilterModel(Employee40aFilterModel.filterName)
as Employee40aFilterModel;
}
Employee40aBlock findEmployee40aBlock() {
return findBlock(Employee40aBlock.blkName) as Employee40aBlock;
}
}3. Employee40aFilterInput
The image below illustrates how the raw values of FilterInput are converted into values to update the criteria of FilterModel.

The Employee40aFilterInput class extends FilterInput, containing raw attributes (usually Strings or IDs) to "map" into complex criteria within the FilterModel.

employee40a_filter_input.dart
class Employee40aFilterInput extends FilterInput {
final String? searchText;
final String? companyCode;
final List<String> departmentCodes;
Employee40aFilterInput({
required this.searchText,
required this.companyCode,
required this.departmentCodes,
});
@override
String toString() {
return "searchText: $searchText, "
"companyCode: $companyCode, "
"departmentCodes: $departmentCodes";
}
}To illustrate this visually, we have 3 buttons that perform data queries on Employee40aBlock with 3 different filterInput(s) values.

No ADS
filterInput1 is used when Button1 is pressed:
var filterInput1 = Employee40aFilterInput(
searchText: null,
companyCode: "VINFAST",
departmentCodes: ["VINFAST-FIN", "VINFAST-SALES", "VINFAST-HR"],
);
await block.query(filterInput: filterInput1);filterInput2 is used when Button2 is pressed:
var filterInput2 = Employee40aFilterInput(
searchText: 'g',
companyCode: "VINHOMES",
departmentCodes: ["VINHOMES-FIN", "VINHOMES-HR"],
);
await block.query(filterInput: filterInput2);filterInput3 is used when Button3 is pressed:
var filterInput3 = Employee40aFilterInput(
searchText: '',
companyCode: "VINGROUP",
departmentCodes: ["VINGROUP-FIN"],
);
await filterModel.queryAll(filterInput: filterInput3);4. Employee40aFilterModel
Employee40aFilterModel is where the "magic" happens; it takes raw data from FilterInput and transforms it into fully functional business objects. Let's first look at the structure of this FilterModel:
defineFilterModelStructure()
@override
FilterModelStructure defineFilterModelStructure() {
return FilterModelStructure(
criteriaStructure: FilterCriteriaStructure(
simpleCriterionDefs: [
SimpleFilterCriterionDef<String>(criterionBaseName: "searchText"),
],
multiOptCriterionDefs: [
// Multi Option Single Selection Criterion.
MultiOptFilterCriterionDef<CompanyInfo>.singleSelection(
criterionBaseName: "company",
fieldName: 'companyId',
toFieldValue: (CompanyInfo? rawValue) {
return SimpleVal.ofInt(rawValue?.id);
},
children: [
// Multi Option Multi Selection Criterion.
MultiOptFilterCriterionDef<DepartmentInfo>.multiSelection(
criterionBaseName: "departments",
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: "departments~",
operator: FilterOperator.inCollection,
),
],
),
);
}FilterModel.performLoadMultiOptTildeCriterionXData()
This method is called multiple times to load data for each MultiOptTildeFilterCriterion.

performLoadMultiOptTildeCriterionXData()
@override
Future<XData?> performLoadMultiOptTildeCriterionXData({
required String multiOptTildeCriterionName,
required String multiOptCriterionBaseName,
required Object? parentMultiOptTildeCriterionValue,
required SelectionType selectionType,
required Employee40aFilterInput? filterInput,
}) async {
// Company:
if (multiOptTildeCriterionName == "company~") {
// ApiResult<PageData<CompanyInfo>>
ApiResult<CompanyInfoPage> result = await _companyRestProvider.queryAll();
// IMPORTANT: Throw ApiError if Error.
result.throwIfError();
//
return ListXData<int, CompanyInfo>.fromPageData(
pageData: result.data,
getItemId: (item) => item.id,
);
}
// Department:
else if (multiOptTildeCriterionName == "departments~") {
// Because "departments" is child of "company"
// Therefore, the parentMultiOptTildeCriterionValue is guaranteed to be non-null.
CompanyInfo company = parentMultiOptTildeCriterionValue as CompanyInfo;
// ApiResult<PageData<DepartmentInfo>>
ApiResult<DepartmentInfoPage> result = await _departmentRestProvider
.queryAllByCompanyId(companyId: company.id);
// IMPORTANT: Throw ApiError if Error.
result.throwIfError();
//
return ListXData<int, DepartmentInfo>.fromPageData(
pageData: result.data,
getItemId: (item) => item.id,
);
}
return null;
}FilterModel.extractUpdateValuesForSimpleTildeCriteria()
This method provides values to update the SimpleTildeFilterCriterion(s) of the FilterModel based on the values provided by FilterInput.
extractUpdateValuesForSimpleTildeCriteria()
@override
Map<String, SimpleValueWrap?>? extractUpdateValuesForSimpleTildeCriteria({
required Employee40aFilterInput filterInput,
}) {
return {"searchText~": SimpleValueWrap.of(filterInput.searchText)};
}
No ADS
FilterModel.extractUpdateValueForMultiOptTildeCriterion()
This method is called to determine the actual value for a MultiOptTildeFilterCriterion of the FilterModel based on the raw value provided by FilterInput. This method will be called multiple times if your FilterModel has multiple MultiOptTildeFilterCriterion(s).
extractUpdateValueForMultiOptTildeCriterion()
@override
OptValueWrap? extractUpdateValueForMultiOptTildeCriterion({
required String multiOptTildeCriterionName,
required String multiOptCriterionBaseName,
required Object? parentMultiOptTildeCriterionValue,
required SelectionType selectionType,
required XData multiOptTildeCriterionXData,
required Employee40aFilterInput filterInput,
}) {
// Company:
if (multiOptTildeCriterionName == "company~") {
var listXData =
multiOptTildeCriterionXData as ListXData<int, CompanyInfo>;
CompanyInfo? companyInfo = listXData.data.firstWhere(
(c) => c.code == filterInput.companyCode,
);
//
return OptValueWrap.single(companyInfo);
}
// Department:
else if (multiOptTildeCriterionName == "departments~") {
var listXData =
multiOptTildeCriterionXData as ListXData<int, DepartmentInfo>;
List<DepartmentInfo>? departmentInfos = listXData.data
.where((d) => filterInput.departmentCodes.contains(d.code))
.toList();
//
return OptValueWrap.multi(departmentInfos);
}
return null;
}No ADS
FilterModel.createNewFilterCriteria()
createNewFilterCriteria()
@override
Employee40aFilterCriteria createNewFilterCriteria({
required Map<String, dynamic> tildeCriteriaMap,
}) {
return Employee40aFilterCriteria(
searchText: tildeCriteriaMap["searchText~"], // String?
company: tildeCriteriaMap["company~"], // CompanyInfo?
departments: tildeCriteriaMap["department~"], // DepartmentInfo?
);
}5. Employee40aFilterPanel
Filter user interface:

employee40a_filter_panel.dart
class Employee40aFilterPanel extends FilterPanel<Employee40aFilterModel> {
const Employee40aFilterPanel({required super.filterModel, super.key});
@override
Widget buildContent(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildFilterBar(context),
const Divider(),
FaFormBuilderTextField(
name: "searchText~",
labelText: "Search Text",
maxLines: 1,
floatingLabelBehavior: FloatingLabelBehavior.always,
onChanged: _onSearchTextChanged,
),
const SizedBox(height: 10),
FaFormBuilderDeselectableDropdown<CompanyInfo>(
key: const Key("filter-company"),
name: "company~",
labelText: "Company",
items: filterModel.getMultiOptTildeCriterionData("company~") ?? [],
getItemText: (item) => item.name,
onChanged: _onSelectCompany,
floatingLabelBehavior: FloatingLabelBehavior.always,
),
const SizedBox(height: 5),
FaFormBuilderMultiDropdown<int, DepartmentInfo>.topLabel(
key: const Key("filter-department"),
name: "departments~",
dropdownTitle: 'Select departments',
labelText: "Department",
items:
filterModel.getMultiOptTildeCriterionData("departments~") ?? [],
getItemText: (item) => item.name,
onChanged: _onChangeDepartment,
),
],
);
}
Future<void> _onSearchTextChanged(String? text) async {
FlutterArtist.codeFlowLogger.addMethodCall(
ownerClassInstance: this,
currentStackTrace: StackTrace.current,
parameters: {},
);
//
await filterModel.queryAll();
}
Future<void> _onSelectCompany(CompanyInfo? company) async {
FlutterArtist.codeFlowLogger.addMethodCall(
ownerClassInstance: this,
currentStackTrace: StackTrace.current,
parameters: {"company": company},
);
//
await filterModel.queryAll();
}
Future<void> _onChangeDepartment(List<DepartmentInfo>? departments) async {
FlutterArtist.codeFlowLogger.addMethodCall(
ownerClassInstance: this,
currentStackTrace: StackTrace.current,
parameters: {"departments": departments},
);
//
await filterModel.queryAll();
}
}6. Employee40aBlock
Employee40aBlock.performQuery()
@override
Future<ApiResult<PageData<EmployeeInfo>?>> performQuery({
required Object? parentBlockCurrentItem,
required Employee40aFilterCriteria filterCriteria,
required SortableCriteria sortableCriteria,
required Pageable pageable,
}) async {
return await employeeRestProvider.queryAdvanced(
pageable: pageable,
searchText: filterCriteria.searchText,
companyId: filterCriteria.company?.id,
departmentIdsAsString: filterCriteria.departmentIdsAsString,
);
}No ADS
FlutterArtist
- Basic concepts in Flutter Artist
- FlutterArtist Block ex1
- FlutterArtist Filter Example
- FlutterArtist FilterModel MultiOptFilterCriterion ex1
- FlutterArtist FilterInput Example 1
- FlutterArtist Form ex1
- The idea of designing filter models in FlutterArtist
- FlutterArtist FormModel.patchFormFields() Ex1
- FlutterArtist BlockQuickItemUpdateAction Example
- FlutterArtist BlockNumberPagination Ex1
- FlutterArtist GridView Infinite Scroll Example
- FlutterArtist BlockQuickMultiItemCreationAction Example
- FlutterArtist ListView Infinite Scroll Pagination Example
- FlutterArtist Pagination
- FlutterArtist Sort DropdownSortPanel Example
- FlutterArtist Dio
- FlutterArtist BlockBackendAction Example
- FlutterArtist BackgroundWebDownloadAction Example
- FlutterArtist Block External Shelf Event ex1
- FlutterArtist Filter FormBuilderMultiDropDown Ex1
- FlutterArtist Master-detail Blocks ex1
- FlutterArtist Scalar ex1
- FlutterArtist Pagination Davi table Infinite Scroll Ex1
- FlutterArtist Filter Tree FormBuilderField ex1
- FlutterArtist Filter FormBuilderRadioGroup 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

