Last active
March 6, 2025 15:50
-
-
Save TesteurManiak/594f83a0b3ec33999067b31e30b04855 to your computer and use it in GitHub Desktop.
Simple state management experiment only with Flutter tools.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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