FlutterArtist Filter Example
Filtering is a fundamental feature in any application, and FlutterArtist is no exception. In this article, we will implement a simple example: displaying a list of songs and filtering them by two criteria: "Song Title" and "Album".
Through this example, you will master the core concepts directly related to filtering:
- FilterModel
- FilterPanel
- FilterCriteria

This article is based on the Dem02a example, with the full source code available in the FlutterArtist Demo project, which you can download at the link below:
- Download FlutterArtist Demo
Before starting with this example, you should read the article below to better understand the concept of FilterModel in FlutterArtist and its design philosophy.
1. Structure of the example
Before we begin coding, we need to look at the overall structure of this example, which will be explained in detail in the following sections.

No ADS
The filter in this example allows users to search for songs by title and album.

2. Song02aShelf
Song02aShelf has a straightforward structure, consisting of a FilterModel and a Block. FilterModel(s) are declared independently from Block(s) because a single FilterModel can be shared across multiple Block(s) or Scalar(s).
song02a_shelf.dart
class Song02aShelf extends Shelf {
@override
ShelfStructure defineShelfStructure() {
return ShelfStructure(
filterModels: {
Song02aFilterModel.filterName: Song02aFilterModel(), //
},
blocks: [
Song02aBlock(
name: Song02aBlock.blkName,
description: null,
config: BlockConfig(),
filterModelName: Song02aFilterModel.filterName,
formModel: null,
childBlocks: [],
),
],
);
}
Song02aBlock findSong02aBlock() {
return findBlock(Song02aBlock.blkName) as Song02aBlock;
}
Song02aFilterModel findSong02aFilterModel() {
return findFilterModel(Song02aFilterModel.filterName) as Song02aFilterModel;
}
}3. Song02aFilterModel
In FlutterArtist, FilterModel is the data model that manages the data and state of criteria. FilterPanel is the UI that displays this model, allowing users to enter or select criteria. All UI interactions are synchronized back into the FilterModel.
TildeCriterion (or TildeFilterCriterion) is a term introduced to address issues related to filter modeling in FlutterArtist, and it is explained in the article below:
defineFilterModelStructure()
You must implement this method to define the filter structure.
song02a_filter_model.dart (*)
class Song02aFilterModel
extends
FilterModel<
EmptyFilterInput, //
Song02aFilterCriteria
> {
static const String filterName = "song02a-filter-model";
@override
FilterModelStructure defineFilterModelStructure() {
return FilterModelStructure(
criteriaStructure: FilterCriteriaStructure(
simpleCriterionDefs: [
SimpleFilterCriterionDef<String>(criterionBaseName: "searchText"),
],
multiOptCriterionDefs: [
// Multi Options Single Selection Criterion.
MultiOptFilterCriterionDef<AlbumInfo>.singleSelection(
criterionBaseName: "album",
fieldName: 'albumId',
toFieldValue: (AlbumInfo? rawValue) {
return SimpleVal.ofInt(rawValue?.id);
},
),
],
),
conditionStructure: FilterConditionStructure(
connector: FilterConnector.and,
conditionDefs: [
FilterConditionDef.simple(
tildeCriterionName: "searchText~",
operator: FilterOperator.containsIgnoreCase,
),
FilterConditionDef.simple(
tildeCriterionName: "album~",
operator: FilterOperator.equalTo,
),
],
),
);
}
...
}No ADS
SimpleFilterCriterionDef | Define a simple criterion, a blueprint for creating simple TildeCriterion(s). A simple TildeCriterion corresponds one-to-one to a simple input field on the FilterPanel such as FormBuilderTextField, FormBuilderCheckbox... |
MultiOptFilterCriterionDef | ![]() Define a criterion with multiple options that the user can select one or more of. A TildeCriterion created from this design corresponds one-to-one to a multi-option input field on the FilterPanel, such as:
These types of criteria can be independent of each other or have a parent-child relationship. ![]()
|
Based on the conditional structure you define, the TildeFilterCriterion(s) will be created, each TildeFilterCriterion corresponding one-to-one to an input field on the FilterPanel.

FilterCriteriaStructure
FilterCriteriaStructure(
simpleCriterionDefs: [
SimpleFilterCriterionDef<String>(criterionBaseName: "searchText"),
],
multiOptCriterionDefs: [
// Multi Options Single Selection Criterion.
MultiOptFilterCriterionDef<AlbumInfo>.singleSelection(
criterionBaseName: "album",
fieldName: 'albumId',
toFieldValue: (AlbumInfo? rawValue) {
return SimpleVal.ofInt(rawValue?.id);
},
),
],
)You can view the values of the TildeFilterCriterion(s) in real time on the Debug Filter Model Viewer.

- FlutterArtist Debug Filter Model Viewer
FilterModel.performLoadMultiOptTildeCriterionXData()
The performLoadMultiOptTildeCriterionXData() method is used to load data for the MultiOptTildeFilterCriterion(s).

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 == "album~") {
ApiResult<AlbumInfoPage> result = await _albumRestProvider.allAlbums();
// Throw ApiError:
result.throwIfError();
// IMPORTANT: Generics should be declared explicitly.
return ListXData<int, AlbumInfo>.fromPageData(
pageData: result.data,
getItemId: (item) => item.id,
);
}
return null;
}
- FlutterArtist ApiResult
- FlutterArtist XData
FilterModel.specifyDefaultValueForMultiOptTildeCriterion()
This method is used to specify a default value for MultiOptTildeFilterCriterion after its data has been successfully loaded. Note: This method is used one or more times throughout the lifecycle of the FilterModel (depending on the configuration).
- Flutter FilterModelStructure (***)
specifyDefaultValueForMultiOptTildeCriterion()
@override
OptValueWrap? specifyDefaultValueForMultiOptTildeCriterion({
required String multiOptTildeCriterionName,
required String multiOptCriterionBaseName,
required Object? parentMultiOptTildeCriterionValue,
required SelectionType selectionType,
required XData multiOptTildeCriterionXData,
}) {
if (multiOptTildeCriterionName == "album~") {
var listXData = multiOptTildeCriterionXData as ListXData<int, AlbumInfo>;
AlbumInfo? album = listXData.data.firstOrNull;
return OptValueWrap.single(album);
}
return null;
}FilterModel.createNewFilterCriteria()
The createNewFilterCriteria() method is called automatically to create a FilterCriteria object. Here, we extract values from tildeCriteriaMap and assign them to FilterCriteria properties.

No ADS
FilterModel stores the values of the TildeCriteria(s) in a Map<String,dynamic> object, which is passed as a parameter to the createNewFilterCriteria() method.

createNewFilterCriteria()
@override
Song02aFilterCriteria createNewFilterCriteria({
required Map<String, dynamic> tildeCriteriaMap,
}) {
return Song02aFilterCriteria(
album: tildeCriteriaMap["album~"],
searchText: tildeCriteriaMap["searchText~"],
);
}The FilterCriteria object obtained from the previous step will be passed as a parameter to the Block.performQuery() or Scalar.performQuery() method.
// 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,
});
- FlutterArtist Debug Filter Model Viewer
4. Song02aFilterCriteria
Essentially, from the FilterCriteria object you will get the fieldBasedJSON and can send it to the server for querying and filtering data.
Block.performQuery()
@override
Future<ApiResult<PageData<SongInfo>?>> performQuery({
required Object? parentBlockCurrentItem,
required Song02aFilterCriteria filterCriteria,
required SortableCriteria sortableCriteria,
required Pageable pageable,
}) async {
return await songRestProvider.someAdvQuery(
pageable: pageable,
fieldBasedJSON: filterCriteria.fieldBasedJSON,
);
}
No ADS
However, for simple applications, sending a fieldBasedJSON might complicate the server-side logic. Instead, you can opt for a more straightforward solution by passing criterion values directly into the FilterCriteria object.
Song02aFilterCriteria
class Song02aFilterCriteria extends FilterCriteria {
final String? searchText;
final AlbumInfo? album;
Song02aFilterCriteria({required this.album, required this.searchText});
..
} Song02aBlock.performQuery()
@override
Future<ApiResult<PageData<SongInfo>?>> performQuery({
required Object? parentBlockCurrentItem,
required Song02aFilterCriteria filterCriteria,
required SortableCriteria sortableCriteria,
required Pageable pageable,
}) async {
return await songRestProvider.querySearch(
pageable: pageable,
searchText: filterCriteria.searchText,
albumId: filterCriteria.album?.id,
);
}
...Filter Criteria Viewer
The Debug Filter Criteria Viewer is a tool that shows you the latest FilterCriteria object extracted from the FilterModel and used to query the Block most recently. This tool can be accessed via a BlockControlBar button or code.

- FlutterArtist Debug Filter Criteria Viewer
5. Song02aBlock
song02a_block.dart
class Song02aBlock
extends
Block<
int, //
SongInfo,
SongData,
EmptyFilterInput,
Song02aFilterCriteria,
EmptyFormInput,
EmptyAdditionalFormRelatedData
> {
static const String blkName = "song02a-block";
final songRestProvider = SongRestProvider();
Song02aBlock({
required super.name,
required super.description,
required super.config,
required super.filterModelName,
required super.formModel,
required super.childBlocks,
});
@override
Future<ApiResult<PageData<SongInfo>?>> performQuery({
required Object? parentBlockCurrentItem,
required Song02aFilterCriteria filterCriteria,
required SortableCriteria sortableCriteria,
required Pageable pageable,
}) async {
return await songRestProvider.querySearch(
pageable: pageable,
searchText: filterCriteria.searchText,
albumId: filterCriteria.album?.id,
);
}
...
}
Let's look at how to create FilterCriteria and how it's used in the Block.performQuery() method.
Song02aFilterModel | Song02aBlock |
| |
6. Song02aFilterPanel
No ADS
Song02aFilterPanel is a class extended from FilterPanel, used to render the filter data model onto the user interface.
Important Note: Instead of creating a separate FilterPanel class, you can use FilterPanelBuilder directly. However, this is not recommended as it becomes harder to maintain as the project grows.

song02a_filter_panel.dart
class Song02aFilterPanel extends FilterPanel<Song02aFilterModel> {
const Song02aFilterPanel({
super.key,
required super.filterModel,
});
@override
Widget buildContent(BuildContext context) {
return CustomAppContainer(
padding: EdgeInsets.all(5),
child: Column(
children: [
buildFilterBar(context),
SizedBox(height: 5),
FaFormBuilderTextField.topLabel(
name: "searchText",
labelText: "Search Text",
maxLines: 1,
onChanged: (_) {
_querySong02a();
},
),
SizedBox(height: 5),
FaFormBuilderDeselectableDropdown<AlbumInfo>.topLabel(
name: 'album',
labelText: "Album",
items: filterModel.getMultiOptCriterionData("album") ?? [],
getItemText: (AlbumInfo item) {
return item.name;
},
onChanged: (_) {
_querySong02a();
},
),
SizedBox(height: 5),
TextButton(
onPressed: _querySong02a,
child: Text("Search"),
),
],
),
);
}
Future<void> _querySong02a() async {
FlutterArtist.codeFlowLogger.addMethodCall(
ownerClassInstance: this,
currentStackTrace: StackTrace.current,
parameters: null,
);
// Query all blocks and scalars associated with this filterModel.
await filterModel.queryAll();
}
}
FlutterArtist uses the fluttter_fa_form_builder library package with widgets like FaFormBuilderTextField, FaFormBuilderDropdown, etc., replacing TextField, Dropdown, etc., to help you build and handle form logic more simply.
The most important parameter of a FaFormBuilderXxx utility in FilterPanel is "name", which is mapped one-to-one with a TildeFilterCriterion of the same name as the FilterModel. Data synchronization between FilterPanel and FilterModel is completely automatic and transparent by the library.
- FlutterArtist Fa FormBuilder
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

