Filter Operator
The where() operator filters values based on a predicate function, only propagating values that pass the test.
where()
Creates a filtered ValueListenable that only notifies when values pass the predicate test.
Signature
dart
ValueListenable<T> where(
bool Function(T) selector, {
T? fallbackValue,
})Parameters:
selector- Predicate function that determines if a value should propagatefallbackValue- Optional fallback value to use as initial value if current value doesn't pass the predicate
Basic Usage
dart
void main() {
final intNotifier = ValueNotifier<int>(1);
bool onlyEven = true;
// Filter to only allow even values
final filteredNotifier = intNotifier.where((i) => onlyEven ? i.isEven : true);
filteredNotifier.listen((value, _) => print('Filtered value: $value'));
intNotifier.value = 2; // Even - passes filter
// Prints: Filtered value: 2
intNotifier.value = 3; // Odd - blocked by filter
// No output
intNotifier.value = 4; // Even - passes filter
// Prints: Filtered value: 4
// Change filter condition
onlyEven = false;
intNotifier.value = 5; // Now passes (filter disabled)
// Prints: Filtered value: 5
}How It Works
The predicate function is called for every source value. Only values where the predicate returns true are propagated to listeners.
dart
final numbers = ValueNotifier<int>(1);
final evenNumbers = numbers.where((n) => n.isEven);
evenNumbers.listen((value, _) => print('Even: $value'));
numbers.value = 2; // Prints: Even: 2
numbers.value = 3; // No output (filtered out)
numbers.value = 4; // Prints: Even: 4
numbers.value = 5; // No output (filtered out)Common Use Cases
Validation
dart
final input = ValueNotifier<String>('');
// Only propagate non-empty strings
final validInput = input.where((text) => text.isNotEmpty);
// Only propagate strings meeting length requirement
final longEnoughInput = input.where((text) => text.length >= 3);State-Based Filtering
dart
class AppState {
final bool isOnline;
final String data;
AppState(this.isOnline, this.data);
}
final appState = ValueNotifier<AppState>(AppState(true, ''));
// Only propagate when online
final onlineData = appState.where((state) => state.isOnline);Range Filtering
dart
final temperature = ValueNotifier<double>(20.0);
// Only alert on high temperatures
final highTemp = temperature.where((temp) => temp > 30.0);
highTemp.listen((temp, _) => showAlert('High temperature: $temp'));Combining Conditions
dart
final userAge = ValueNotifier<int>(0);
// Multiple conditions
final eligibleAge = userAge.where((age) {
return age >= 18 && age <= 65;
});Dynamic Predicates
The predicate can reference external state:
dart
bool onlyEven = true;
final numbers = ValueNotifier<int>(0);
// Predicate references external variable
final filtered = numbers.where((n) => onlyEven ? n.isEven : true);
// Initially filters to even numbers
numbers.value = 2; // Passes
numbers.value = 3; // Blocked
// Change filter
onlyEven = false;
// Now all numbers pass
numbers.value = 5; // PassesInitial Value Behavior
By default, if the current source value doesn't pass the predicate, it still becomes the initial value:
dart
final numbers = ValueNotifier<int>(1); // Odd number
final evenNumbers = numbers.where((n) => n.isEven);
print(evenNumbers.value); // 1 (initial value, even though it's odd!)
numbers.value = 2; // Passes filter (even)
numbers.value = 3; // Blocked (odd)
print(evenNumbers.value); // Still 2Using fallbackValue
To handle cases where the initial value doesn't pass the predicate, provide a fallbackValue:
dart
final numbers = ValueNotifier<int>(1); // Odd number
// Provide fallback for when initial value doesn't pass
final evenNumbers = numbers.where(
(n) => n.isEven,
fallbackValue: 0, // Use 0 if current value is odd
);
print(evenNumbers.value); // 0 (fallback used!)
numbers.value = 2; // Passes filter (even)
print(evenNumbers.value); // 2
numbers.value = 3; // Blocked (odd)
print(evenNumbers.value); // Still 2 (not 0 - fallback only used at creation)Practical fallbackValue Examples
Search Input with Minimum Length
dart
final searchTerm = ValueNotifier<String>('');
// Use empty string as fallback when search term is too short
final validSearchTerm = searchTerm.where(
(term) => term.length >= 3,
fallbackValue: '',
);
validSearchTerm
.debounce(Duration(milliseconds: 300))
.listen((term, _) {
if (term.isEmpty) {
clearSearchResults();
} else {
performSearch(term);
}
});Age Validation
dart
final userAge = ValueNotifier<int>(0);
// Use 0 as fallback for invalid ages
final adultAge = userAge.where(
(age) => age >= 18,
fallbackValue: 0,
);
adultAge.listen((age, _) {
if (age == 0) {
showMessage('Must be 18 or older');
} else {
enableFeature();
}
});Temperature Alerts
dart
final temperature = ValueNotifier<double>(20.0);
// Use safe temp as fallback
final dangerousTemp = temperature.where(
(temp) => temp > 35.0 || temp < 5.0,
fallbackValue: 20.0, // Normal temp
);
dangerousTemp.listen((temp, _) {
if (temp != 20.0) {
showTemperatureAlert(temp);
}
});Chaining with Other Operators
where() is commonly chained with transformation operators:
dart
final input = ValueNotifier<String>('');
input
.where((text) => text.length >= 3) // Min 3 characters
.map((text) => text.toUpperCase()) // Transform to uppercase
.debounce(Duration(milliseconds: 300)) // Debounce
.listen((text, _) => search(text));where() vs select()
| Feature | where() | select() |
|---|---|---|
| Purpose | Filter values | React to property changes |
| Notifies when | Predicate returns true | Selected value changes |
| Initial value | Always passes | Normal behavior |
| Use for | Conditional propagation | Property-specific updates |
dart
final user = ValueNotifier<User>(User(age: 16));
// where() - filters based on condition
final adults = user.where((u) => u.age >= 18);
// Won't notify for age 16, 17 updates
// select() - reacts to age changes
final age = user.select<int>((u) => u.age);
// Notifies for every age changeWhen to Use where()
Use where() when:
- ✅ You need to filter values based on conditions
- ✅ You only want to react to certain values
- ✅ You're implementing validation logic
- ✅ You need to filter based on runtime state
Real-World Example
Search input with minimum length requirement:
dart
final searchTerm = ValueNotifier<String>('');
// Without fallbackValue (backward compatible)
searchTerm
.where((term) => term.length >= 3) // At least 3 characters
.debounce(Duration(milliseconds: 300)) // Wait for typing pause
.listen((term, _) => performSearch(term));
// With fallbackValue (recommended for cleaner logic)
searchTerm
.where(
(term) => term.length >= 3,
fallbackValue: '', // Clear indicator when no search
)
.debounce(Duration(milliseconds: 300))
.listen((term, _) {
if (term.isEmpty) {
clearResults();
} else {
performSearch(term);
}
});