Skip to content

Instantly share code, notes, and snippets.

@Andrious
Created November 19, 2024 01:40
Show Gist options
  • Save Andrious/fbe53696ff3c4f7d37c61b9a4472d35d to your computer and use it in GitHub Desktop.
Save Andrious/fbe53696ff3c4f7d37c61b9a4472d35d to your computer and use it in GitHub Desktop.
A StatefulWidget that takes in a Listenable
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// Widget builder allows for null
typedef MaybeBuildWidgetType = Widget? Function(BuildContext context);
/// Creates a widget that rebuilds when the given listenable calls
/// notifyListeners() function
class ListenableWidgetBuilder extends StatefulWidget {
/// The arguments is not required.
const ListenableWidgetBuilder({
super.key,
this.listenable,
this.builder,
});
/// Checked in State object if, in fact, a Listenable
final dynamic listenable;
/// Supply a Widget builder
final MaybeBuildWidgetType? builder;
/// Subclasses typically do not override this method.
@override
State createState() => _ListenableState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Listenable>('listenable', listenable));
}
}
//
class _ListenableState extends State<ListenableWidgetBuilder> {
//
@override
void initState() {
super.initState();
// May actually be a Listenable
if (widget.builder != null && widget.listenable is Listenable) {
builder = widget.builder!;
listenable = widget.listenable;
listenable?.addListener(_callBuild);
} else {
// A widget must be provided. Even if it's nothing
builder = (_) => const SizedBox.shrink();
}
}
// Possibly not provided
Listenable? listenable;
// A Widget will be returned no matter what is provided.
late MaybeBuildWidgetType builder;
// Store the past widget displayed, and use it if builder returns null.
Widget? _widget;
@override
void didUpdateWidget(ListenableWidgetBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
// Possibly a O(2) expense if oldWidget == widget
if (oldWidget.listenable is Listenable) {
oldWidget.listenable?.removeListener(_callBuild);
}
if (widget.listenable is Listenable) {
widget.listenable?.addListener(_callBuild);
}
}
@override
void dispose() {
_widget = null;
listenable?.removeListener(_callBuild);
listenable = null;
super.dispose();
}
void _callBuild() {
if (mounted) {
setState(() {
// Call the build() function again
});
}
}
@override
Widget build(BuildContext context) {
final widget = builder.call(context);
// The builder is allowed to return null.
if (widget == null) {
_widget ??= const SizedBox.shrink();
} else {
_widget = widget;
}
return _widget!;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment