flutter_watcher

Last updated:

0 purchases

flutter_watcher Image
flutter_watcher Images
Add to Cart

Description:

flutter watcher

Watcher State Management #
Flutter Watcher is an alternative to ValueNotifier. It provides convenient syntax, built-in asynchronous safety, automatic tracking of changes within complex data structures, and value caching capabilities. Additionally, Flutter Watcher includes rich widgets to handle not only Watcher objects but any Listenable type.

Quick Sample
The Watcher Class
ValueNotifier vs. Watcher
Watcher Types

Native-like Watchers
Extension-based Watchers
Quick Initializationn


Watcher Widgets

WatchValue
Watch
WatchAll
Widgets Extensions


CachedWatcher

Simple Usage
Advanced Usage (Custom Types)
Initialization Properties


Utilities
Full Counter Example
FAQ
Contributions & Issues
License
Support

Quick Sample #
// create watcher instance
final counter = 0.watcher;

// use it in the WatchValue widget.
counter.watchValue(
builder: (value) => Text('Counter: $value'),
),

// manpulate the counter value anywhere
onPress: () => counter.increment()
copied to clipboard
The Watcher Class #
Initialization #
final myWatcher = Watcher<int>(0);
copied to clipboard

This is the basic initialization of watcher, please see watcher types, and quick initialization for a recommended approach.

Modifying the Value #
myWatcher.value = newValue; // Updates the value and notifies listeners
copied to clipboard
Checking for Disposal #
print(myWatcher.isDisposed) // whether the watcher instnace is disposed or not.
copied to clipboard
check the Utilities for more.
ValueNotifier vs. Watcher #

Asynchronous Safety: Watcher enhances safety in asynchronous contexts with a safeMode and isDisposed check, preventing errors when a notifier is updated after being disposed. This is a common issue with ValueNotifier in async scenarios that Watcher effectively addresses.
Internal Changes Notification: Unlike ValueNotifier, Watcher automatically notifies listeners of internal changes in complex data types like lists and maps, thanks to custom types like ListWatcher and MapWatcher. This ensures UI components can react dynamically to state changes.
Simplified Syntax: Watcher offers a more concise syntax, reducing boilerplate and improving code readability . For instance, what requires a verbose ValueListenableBuilder setup with ValueNotifier can be more succinctly done using .watchValue(builder: (value) {}) with Watcher. for more see quick initialization and widgets extensions.
Versatile Reactivity: Watcher provides specialized widgets (WatchValue, Watch, WatchAll) that cater to various reactive scenarios, supporting not only Watcher instances but any ValueListenable or Listenable object. For better control, these widgets include optional parameters like watchWhen (to conditionally trigger rebuilds based on value changes) and threshold (to limit rebuild frequency and optimize performance).

Watcher Types #
While you can basically create any watcher type using the default initializer Watcher<T>() , This package comes with a suite of Watcher types, each designed for specific data structures and primitives, providing a seamless and intuitive way to manage state that aligns closely with Dart's native types.
Native-like Watchers #
These watchers act like their native counterparts. directly implement their corresponding native types

ListWatcher: Implements List<T>, supporting all list operations.
MapWatcher: Implements Map<K, V>, with full Map functionality.
SetWatcher: Behaves like a native Set, supporting all set operations.
Additional types include DateWatcher, DurationWatcher, and UriWatcher for managing DateTime, Duration, and Uri types respectively.

Extension-based Watchers #
Due to the constraints of the Dart programming language, not all watchers can directly implement their corresponding native types. However, This package provides a set of extension-based watchers to bridge this gap.

IntWatcher: Facilitates integer operations.
StringWatcher: Enables string manipulations.
BoolWatcher: Simplifies boolean state management.
Others include NumWatcher for numeric values, DoubleWatcher for floating-point operations, and ColorWatcher for color management.

Quick Initialization #
The Watcher Package comes with a cool extension called .watcher, The .watcher is assigned to any object/value in dart and automatically detects the type of the watcher for you, for example 12.watcher creates an IntWatcher, false.watcher, creates a BoolWatcher, and for custom types it creates the default Watcher<T>.
Watcher Widgets #
Widgets designed to reacts to Watcher value changes. These widgets, WatchValue, Watch, and WatchAll, along with their corresponding extensions, no only reacts to Watcher instances, but with any ValueListenable or Listenable instance. Below, we'll dive into each widget and understand when to use them.
WatchValue #
WatchValue is designed to rebuild its child widget in response to changes in a specific ValueListenable value, including Watcher instances. It's particularly useful for displaying or reacting to single data changes in your UI.
Parameters:

watcher: The ValueListenable (or Watcher) instance you want to listen to.
builder: Builds the widget in response to changes
threshold (optional): Limits rebuild frequency to improve performance.
watchWhen (optional): Conditionally triggers rebuilds based on value changes.

Usage
WatchValue<int>(
watcher: counter, // Your Watcher<int> instance
builder: (context, value) => Text('Counter: $value'),
watchWhem: (prev, curr) => prev != curr, // optional
threshold: Duration(milliseconds: 100), // optional
),
copied to clipboard
Watch #
Extends UI reactivity to any Listenable object, accommodating broader changes beyond value updates, such as animations or scroll positions.
Parameters:

watcher: Any Listenable object, not limited to ValueListenable or Watcher.
builder: Builds the widget in response to changes. Does not receive value since listenable might not has value.
threshold (optional): Limits rebuild frequency to improve performance.
watchWhen (optional): Conditionally triggers rebuilds based on value changes.

Usage
Watch(
watcher: scrollController, // Any Listenable object including watcher
builder: (context) => MyCustomScrollView(scrollController.offset),
watchWhen: () => scrollController.offset > 100,
threshold: Duration(milliseconds: 300),
),
copied to clipboard
WatchAll #
WatchAll is designed for scenarios where your UI depends on multiple Listenable objects. It rebuilds its child widget when any of the provided listenable change.
Parameters:

watchers: A list of Listenable objects including watchers to observe.
builder: Builds the widget in response to changes. Does not receive value since listenable might not has value.
threshold (optional): Limits rebuild frequency to improve performance.
watchWhen (optional): Conditionally triggers rebuilds based on value changes.

Usage
final scrollCotroller = ScrollController();
final textCotroller = TextEditingController();
final counter = 0.watcher;
final listenables = <Listenable>[scrollCotroller, textCotroller, counter];

WatchAll(
watchers: listenables,
builder: (context) => ContentWidget(),
watchWhen: () => scrollController.offset > 100,
threshold: Duration(milliseconds: 300),
),
copied to clipboard
WARNING: The Watch andWatchAll widgets reacts to Listenable objects which might frequently change their states and thus triggers a rebuild. which might lead to unnecessary rebuilds if not used wisely, so consider using the watchWhen and threshold to limit and avoid unnecessary rebuilds.
Widgets Extensions #
For a simpler syntax, you can use the .watchValue, .watch, and .watchAll to create their corresponding Widget:
final counter = 0.watcher;
final scrollCotroller = ScrollController();
final listenables = <Listenable>[scrollCotroller, counter];

// .watchValue
counter.watchValue(
builder: (value) => Text('Counter: $value'),
),
// .watch
scrollCotroller.watch(
builder: (context) => Text('Current Scroll Position: ${scrollCotroller.position}'),
),
// .watchAll
listenables.watchAll(
builder: (context) => Text('Position: ${scrollCotroller.position}, Counter: $counter'),
),
copied to clipboard
Note that each one does not need the watcher/s since they are already used in the context.
CachedWatcher #
CachedWatcher is an abstract class extending the standard Watcher with added local caching capabilities. it is designed to be subclassed for specific data types.
Simple Usage #
In this package there is a good set of extensions and classes that help you create a CachedWatcher<T> in a simple way
with built-in types.
// Example: Using a subclass for integers
final counter = IntCachedWatcher(0, 'counter_key'); // with class.
final counter = 0.cachedWatcher('counter_key'); // with extension.

// BoolCachedWatcher, ListCachedWatcher, DateTimeCachedWatcher, and so on for premitive/built-in types are supported.

// use it just like any watcher.
counter.watchValue(
builder: (value) => Text('Counter: $value'),
),
copied to clipboard
Advanced Usage (Custom Types) #
Subclasses can be created for any custom type, providing tailored serialization and deserialization strategies for different kinds of data stored in local cache.
class AuthService extends CachedWatcher<AuthState> {
AuthService() : super(AuthState.initial, key: 'AuthServiceKey'); // Default key is the type name 'AuthState'

@override
AuthState? read(dynamic data) {
// Implement logic to deserialize data from cache
// for example return success and assign data to ur token.
}

@override
dynamic write(AuthState value) {
// Implement logic to serialize value to be cached
// use premitive/built-in data types to avoid errors.
// for example write the token when `AuthState is success`.
}
}
copied to clipboard
Initialization Properties #

initialValue: The starting value for the watcher.
key (Optional): A unique identifier for the CachedWatcher's stored data, used to save and retrieve the cached
value from local storage. Default is the type name

The CachedWatcher instance can be used with all Watcher Widgets.
Utilities #


updateIf: Update the value conditionally.
intWatcher.updateIf((val) => val < 10, 5);
copied to clipboard


onChange: Perform action on value change.
stringWatcher.onChange((val) => print(val));
// it returns call back that disposes the listener do not forget to call it in your close/dispose methods.
copied to clipboard


stream: Convert Watcher changes into a stream.
final streamWatcher = watcherInstance.stream;
streamWatcher.listen((value) {
// Handle the stream of changes
});
copied to clipboard


debounce: Debounce value changes.
numWatcher.debounce(Duration(seconds: 1), (val) => print(val));
copied to clipboard


map: Transform the value to another type.
final stringWatcher = intWatcher.map((val) => val.toString());
copied to clipboard


combineWith: Combine with another Watcher.
final combinedWatcher = intWatcher.combineWith(stringWatcher, (int a, String b) => '$a and $b');
copied to clipboard


refresh: Notifies listeners to force UI refresh.
myWatcher.refresh();
copied to clipboard


dispose: disposes the watcher and closes all listeners.
myWatcher.dispose();
copied to clipboard


Full Counter Example #
import 'package:flutter/material.dart';
import 'package:flutter_watcher/flutter_watcher.dart';

main() => runApp(MyCounter());

class MyCounter extends StatelessWidget {
MyCounter({super.key});

final counter = 0.watcher;

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Watch Counter',
home: Scaffold(
appBar: AppBar(title: const Text('Watch Counter')),
body: Center(
child: WatchValue<int>(
watcher: counter,
builder: (context, value) {
return Text('Counter: $value');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.increment(),
child: const Icon(Icons.add),
),
),
);
}
}
copied to clipboard
FAQ #
Why should I use Watcher:
While there is a great state management available I can recommend to use watcher in some cases:

If your app is simple and won't require complex state management solution watcher is a good choice.
If you already use strong state management, but you want to manage a specific (simple) feature or component in your
app Watcher can help with that. Sometimes when you use BLoC its just not convenient to emit new state for the sake
to toggle a switcher in the screen.
If you are using the built in ValueNotifer but need a simpler syntax, avoiding errors if state updates after disposal, and need your UI to automatically react to changes within lists, maps, or similar data structures watcher is the best alternative for you.
If you want to use singletons, watcher instances in a singleton class is a great choice, for example you can use
CachedWatcher with user settings, or Themes, etc.

How to move from ValueNotifier to Watcher:

For new state elements: Start using the Watcher class directly (final counter = 0.watcher).
Gradual Transition: Your existing ValueNotifier instances can be used seamlessly with Watcher's widgets (like .watchValue, .watch, and .watchAll) . This lets you transition gradually without needing to immediately convert all your ValueNotifiers.
Replace as Needed As your project evolves, consider replacing ValueNotifier instances with Watcher where you specifically want the advantages of simplified syntax, built-in asynchronous safety, or automatic tracking of complex data changes.

Does it offer any dependency injection?
No, the flutter watcher package designed to be simple, you can use it with Provider if you wish to make your watcher
instance available in your widget tree.
Are there any examples that demonstrate the use of the package?
I am planning to publish a documentation website soon with useful examples and detailed documentation.
Contributions #
Contributions to this package are welcome. If you have any suggestions, issues, or feature requests, please create a
pull request in the repository.
License #
flutter_watcher is available under the BSD 3-Clause License.
Support #

License:

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Files In This Product:

Customer Reviews

There are no reviews.