Skip to content

Instantly share code, notes, and snippets.

@TesteurManiak
Last active March 6, 2025 15:50
Show Gist options
  • Select an option

  • Save TesteurManiak/594f83a0b3ec33999067b31e30b04855 to your computer and use it in GitHub Desktop.

Select an option

Save TesteurManiak/594f83a0b3ec33999067b31e30b04855 to your computer and use it in GitHub Desktop.
Simple state management experiment only with Flutter tools.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final stateNotifier = SelectableValueNotifier<HomePageState>(
const HomePageState(),
);
late final counterANotifier = stateNotifier.select((s) => s.counterA);
late final counterBNotifier = stateNotifier.select((s) => s.counterB);
@override
void dispose() {
stateNotifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Text('A'),
onPressed: () {
stateNotifier.value = stateNotifier.value.copyWith(
counterA: counterANotifier.value + 1,
);
},
),
body: Center(
child: Row(
spacing: 8,
children: [
ValueListenableBuilder<int>(
valueListenable: counterANotifier,
builder: (context, counter, _) {
print('Rebuild Counter A');
return Text('Counter A: $counter');
},
),
ValueListenableBuilder<int>(
valueListenable: counterBNotifier,
builder: (context, counter, _) {
print('Rebuild Counter B');
return Text('Counter B: $counter');
},
),
],
),
),
);
}
}
class HomePageState {
const HomePageState({this.counterA = 0, this.counterB = 0});
final int counterA;
final int counterB;
HomePageState copyWith({int? counterA, int? counterB}) => HomePageState(
counterA: counterA ?? this.counterA,
counterB: counterB ?? this.counterB,
);
}
class SelectableValueNotifier<T> extends ValueNotifier<T> {
SelectableValueNotifier(super.value);
final _listenables = <ChangeNotifier>[];
SelectNotifier<E> select<E>(E Function(T value) s) {
final notifier = SelectNotifier(s(value));
void onUpdateListener() => notifier._setValue(s(value));
_listenables.add(notifier);
addListener(onUpdateListener);
notifier._onDispose = () {
removeListener(onUpdateListener);
_listenables.remove(notifier);
};
return notifier;
}
@override
void dispose() {
for (final l in _listenables) {
l.dispose();
}
super.dispose();
}
}
class SelectNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
SelectNotifier(T value) : _value = value;
VoidCallback? _onDispose;
T _value;
@override
T get value => _value;
void _setValue(T newValue) {
if (_value == newValue) return;
_value = newValue;
notifyListeners();
}
bool _disposed = false;
bool get disposed => _disposed;
@override
void removeListener(VoidCallback listener) {
super.removeListener(listener);
if (!hasListeners) {
dispose();
}
}
@override
void dispose() {
if (_disposed) return;
_onDispose?.call();
super.dispose();
_disposed = true;
}
}
/// ## Example:
///
/// ```dart
/// final myHttpClient = Singleton<HttpClient>(create: () => HttpClient());
/// ```
class Singleton<T> {
Singleton({required this.create});
final T Function() create;
T? _value;
T get instance => _value ??= create();
@visibleForTesting
void overrideWithValue(T value) => _value = value;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment