A set of practical utilities for building Flutter apps faster: typed builders, wrappers for common patterns (ValueNotifier, StreamController), stream extensions, and chunked transformations.
- Typed Widget Builders (
CallbackWidgetBuilder) - Reactive Wrappers & Listeners for
ValueNotifierandStreamController - Stream Extensions (e.g., doOnFirst)
- Chunked List Stream Transformer
- Navigation & Route Guards (
BlocPopBuilder)
Add to your pubspec.yaml:
dependencies:
booked_utils: ^<latest_version>Import in your Dart files:
import 'package:booked_utils/booked_utils.dart';Note:
BlocPopBuilderrequiresflutter_blocin your app. Add it to yourpubspec.yamlif you don't already use it.
dependencies:
flutter_bloc: ^<latest_version>typedef CallbackWidgetBuilder<T> = Widget Function(T value);A generic typedef for widget builders that take a value of type T.
ValueNotifierWrapper<int>(
initialValue: 0,
onChange: (value) => print('Value changed: $value'),
child: (notifier) => ValueListenableBuilder<int>(
valueListenable: notifier,
builder: (context, value, _) => Text('Value: $value'),
),
)Wraps a ValueNotifier for local, reactive state. Automatically disposes the notifier and can notify about changes.
final counter = ValueNotifier<int>(0);
ValueNotifierListener<int>(
notifier: counter,
onChange: (value) => debugPrint('Counter: $value'),
child: const Text('Static child'),
)Listens to a ValueNotifier and calls a callback on changes without rebuilding the child. Ideal for side effects such as snackbars, animations, or logging.
StreamControllerWrapper<String>(
child: (controller) => StreamBuilder<String>(
stream: controller.stream,
builder: (context, snapshot) => Text(snapshot.data ?? ''),
),
)Manages the lifecycle of a StreamController and exposes it to a widget subtree.
final chunkedStream = Stream.value(List.generate(10000, (i) => i))
.transform(const ChunkTransformer<int>(chunkSize: 1024));
await for (final chunk in chunkedStream) {
print('Chunk size: ${chunk.length}');
}A StreamTransformer that splits large incoming lists into fixed-size chunks. Useful for chunked uploads, network transfers, or file operations.
Stream<int>.fromIterable([1, 2, 3])
.doOnFirst((first) async => print('First event: $first'))
.listen(print); // Prints: First event: 1, then 1, 2, 3Allows performing an action (sync or async) on the first event of a stream, for initialization or logging.
BlocPopBuilder<MyBloc, MyState>(
bloc: context.read<MyBloc>(),
// Return `true` to BLOCK pop, `false` to ALLOW pop
blockWhen: (state) => state is SavingState,
child: const MyFormScreen(),
)A widget that controls back navigation based on BLoC/Cubit state using a blockWhen predicate. Internally it listens to the provided bloc and feeds a computed canPop value into PopScope, so system back gestures and buttons respect your rule.
- Reduces boilerplate: No need to manually manage controllers or notifiers for simple scenarios.
- Composable patterns: Designed for local state and micro-architecture without global dependencies.
- Production-ready: Null-safe, thoroughly tested in real Flutter apps.
Each class and extension is documented with code samples in the source. See
ValueNotifierWrapperValueNotifierListenerStreamControllerWrapperChunkTransformerDoOnFirstEventCallbackWidgetBuilderBlocPopBuilder
MIT