FlutterArtist Filter FormBuilderField ex1
Như đã biết, trong FlutterArtist tất cả các trường dữ liệu được sử dụng trong FormView hoặc FilterPanel đều phải tuân thủ theo các tiêu chuẩn của thư viện flutter_form_builder. Trong đó, FormBuilderDropdown là một trường dữ liệu quen thuộc và thông dụng, nó có thể sử dụng trong FilterPanel cho phép người dùng lựa chọn một tiêu chí trong một danh sách các tiêu chí.
Hãy xem, bạn có thể lựa chọn một "Company" trong một FormBuilderDropdown giống thế này: | ![]() |
Và lựa chọn một "Company" trên một tiện ích cây (tree widget) như thế này. Câu hỏi là cách thức nào có thể mang lại trải nghiệm tốt hơn cho người dùng? | ![]() |
Về cơ bản tiện ích cây (tree widget) ở trên không phải là một trường đầu vào và nó cũng không tuân thủ các tiêu chuẩn của flutter_form_builder. Tuy nhiên nếu bạn gói tiện ích cây trong một FormBuilderField thì 2 điều trên đều được thoả mãn.

Trong ví dụ này chúng ta sẽ gói tiện ích cây trong một FormBuilderField và có thể sử dụng nó như một trường đầu vào trên FilterPanel, và có thể sử dụng nó để thay thế cho một FormBuilderDropdown quen thuộc.
2. CompanyTreeView
Để tạo một TreeView bạn có thể sử dụng gói thư viện animated_tree_view.
pubspect.yaml (*)
dependencies:
animated_tree_view: ^2.3.0CompanyTreeView
class CompanyTreeView extends StatefulWidget {
final CompanyTree? companyTree;
final CompanyTreeItem? selectedCompanyTreeItem;
final Function(CompanyTreeItem companyTreeItem) onSelectCompanyTreeItem;
final EdgeInsets padding;
const CompanyTreeView({
super.key,
required this.companyTree,
this.padding = EdgeInsets.zero,
required this.selectedCompanyTreeItem,
required this.onSelectCompanyTreeItem,
})
...
}Mã nguồn đầy đủ của CompanyTreeView sẵn có trong "FlutterArtist Demo".
- Download FlutterArtist Demo
No ADS
CompanyTree / CompanyTreeItem (Model)
@JsonSerializable()
class CompanyTree {
@JsonKey(name: 'rootCompanyTreeItems', defaultValue: [])
List<CompanyTreeItem> rootCompanyTreeItems = [];
..
}
@JsonSerializable()
class CompanyTreeItem implements Identifiable<int> {
@override
@JsonKey(name: 'id')
late int id;
@JsonKey(name: 'name')
late String name;
@JsonKey(name: 'children', defaultValue: [])
List<CompanyTreeItem> children = [];
@JsonKey(name: 'imagePath')
String? imagePath;
....
} 3. Employee04aShelf
Cấu trúc của Employee04aShelf là đơn giản, chỉ bao gồm một FilterModel và một Block:
employee04a_shelf.dart
class Employee04aShelf extends Shelf {
@override
ShelfStructure defineShelfStructure() {
return ShelfStructure(
filterModels: {
Employee04aFilterModel.filterName: Employee04aFilterModel(),
},
blocks: [
Employee04aBlock(
name: Employee04aBlock.blkName,
description: null,
config: BlockConfig(),
filterModelName: Employee04aFilterModel.filterName,
formModel: null,
childBlocks: [],
),
],
);
}
Employee04aBlock findEmployee04aBlock() {
return findBlock(Employee04aBlock.blkName) as Employee04aBlock;
}
}4. Employee04aFilterModel
Cấu trúc FilterModel:
defineFilterModelStructure()
@override
FilterModelStructure defineFilterModelStructure() {
return FilterModelStructure(
criteriaStructure: FilterCriteriaStructure(
simpleCriterionDefs: [
SimpleFilterCriterionDef<String>(criterionBaseName: "searchText"),
],
multiOptCriterionDefs: [
MultiOptFilterCriterionDef<CompanyTreeItem>.singleSelection(
criterionBaseName: "company",
fieldName: 'companyId',
toFieldValue: (CompanyTreeItem? 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,
),
],
),
);
}performLoadMultiOptTildeCriterionXData()
Phương thức performLoadMultiOptTildeCriterionXData() được sử dụng để tải dữ liệu cho một MultiOptTildeFilterCriterion. Với trường hợp này dữ liệu mong đợi mà bạn đang nghĩ tới là CompanyTree. Tuy nhiên phương thức này yêu cầu trả về một XData, vì vậy chúng ta cần gói CompanyTree trong một TreeXData.
No ADS
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<CompanyTree> result = await _companyTreeProvider
.getCompanyTree();
// Throw ApiError
result.throwIfError();
//
CompanyTree? companyTree = result.data;
if (companyTree == null) {
return null;
}
return TreeXData<int, CompanyTreeItem, CompanyTree>(
treeData: companyTree,
getItemId: (item) => item.id,
getRootTreeItems: () {
return companyTree.rootCompanyTreeItems;
},
getChildren: (CompanyTreeItem item) {
return item.children;
},
addNotFoundTreeItem: (CompanyTreeItem item) {
companyTree.addNotFoundCompany(item);
},
removeNotFoundTreeItem: (CompanyTreeItem item) {
companyTree.removeNotFoundCompany(item);
},
);
}
//
return null;
}Lý do tại sao phương thức FilterModel.performLoadMultiOptTildeCriterionXData() được thiết kế để trả về một XData được giải thích trong các bài viết dưới đây:
- FlutterArtist ListXData
- FlutterArtist XData
5. Employee04aFilterPanel
CompanyTreeView chỉ là một tiện ích (widget) thông thường, nó không phải là một trường dữ liệu và cũng không tuân theo các tiêu chuẩn của flutter_form_builder. Tuy nhiên cả 2 tiêu chuẩn trên sẽ được thoả mãn nếu bạn gói nó trong một FormBuilderField.

No ADS
FormBuilderField(CompanyTreeView)
FormBuilderField<CompanyTreeItem>(
name: "company~",
builder: (FormFieldState<CompanyTreeItem> field) {
return CompanyTreeView(
companyTree: filterModel.getMultiOptTildeCriterionData(
"company~",
),
selectedCompanyTreeItem: field.value,
onSelectCompanyTreeItem: (CompanyTreeItem item) {
// IMPORTANT (If you wrap a CompanyTreeView in a FormBuilderField):
field.didChange(item);
//
_onSelectCompanyTreeItem(item);
},
);
},
),Code đầy đủ của Employee04aFilterPanel:
employee04a_filter_panel.dart
class Employee04aFilterPanel extends FilterPanel<Employee04aFilterModel> {
const Employee04aFilterPanel({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 filterModel.queryAll();
},
),
SizedBox(height: 10),
Expanded(
child: FormBuilderField<CompanyTreeItem>(
name: "company~",
builder: (FormFieldState<CompanyTreeItem> field) {
return CompanyTreeView(
companyTree: filterModel.getMultiOptTildeCriterionData(
"company~",
),
selectedCompanyTreeItem: field.value,
onSelectCompanyTreeItem: (CompanyTreeItem item) {
// IMPORTANT (If you wrap a CompanyTreeView in a FormBuilderField):
field.didChange(item);
//
_onSelectCompanyTreeItem(item);
},
);
},
),
),
],
);
}
Future<void> _onSelectCompanyTreeItem(CompanyTreeItem treeItem) async {
FlutterArtist.codeFlowLogger.addMethodCall(
ownerClassInstance: this,
currentStackTrace: StackTrace.current,
parameters: {"treeItem": treeItem},
);
//
await filterModel.queryAll();
}
}6. Employee04aBlock
Employee04aBlock.performQuery()
@override
Future<ApiResult<PageData<EmployeeInfo>?>> performQuery({
required Object? parentBlockCurrentItem,
required Employee04aFilterCriteria filterCriteria,
required SortableCriteria sortableCriteria,
required Pageable pageable,
}) async {
return await employeeRestProvider.query(
companyId: filterCriteria.companyTreeItem?.id,
searchText: filterCriteria.searchText,
pageable: pageable,
);
}No ADS
FlutterArtist
- Basic concepts in Flutter Artist
- FlutterArtist Block ex1
- FlutterArtist Form ex1
- 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 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 Context Provider Views
- FlutterArtist Internal Shelf Event ex1
- FlutterArtist Deferring External Shelf Events (Ex1)
- FlutterArtist DropdownSortPanel
Show More




