Last active
June 12, 2022 02:20
-
-
Save terryl1900/85d541dbdd7618e34910e13ac91a20b7 to your computer and use it in GitHub Desktop.
Build Creator - Step 2
This file contains 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/material.dart'; | |
// A counter app shows basic Creator/Watcher usage. | |
// Creator creates a stream of data. | |
final counter = Creator((ref, self) => 0); | |
void main() { | |
// Wrap the app with a creator graph. | |
runApp(CreatorGraph(child: const MyApp())); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
home: Scaffold( | |
appBar: AppBar(title: const Text('Counter example')), | |
body: Center( | |
// Watcher will rebuild whenever counter changes. | |
child: Watcher((context, ref, self) { | |
return Text('${ref.watch(counter, self)}'); | |
}), | |
), | |
floatingActionButton: FloatingActionButton( | |
// Update state is easy. | |
onPressed: () => | |
context.ref.update<int>(counter, (count) => count + 1), | |
child: const Icon(Icons.add), | |
), | |
), | |
); | |
} | |
} | |
// -------------------------- Creator Library ---------------------------------- | |
/// Creator creates a stream of T. | |
class Creator<T> { | |
const Creator(this.create); | |
final T Function(Ref ref, Creator<T> self) create; | |
Element<T> _createElement(Ref ref) => Element<T>(ref, this); | |
} | |
// Element holds the state for creator. | |
class Element<T> { | |
Element(this.ref, this.creator) : state = creator.create(ref, creator); | |
final Ref ref; | |
final Creator<T> creator; | |
T state; | |
void recreate() { | |
final newState = creator.create(ref, creator); | |
if (newState != state) { | |
state = newState; | |
ref._onStateChange(creator); | |
} | |
} | |
} | |
/// Ref holds the creator states and dependencies. | |
class Ref { | |
Ref(); | |
/// Elements which hold state. | |
final Map<Creator, Element> _elements = {}; | |
/// Dependency graph. Think this as a directional graph. | |
/// A -> [B, C] means if A changes, B and C need change too. | |
final Map<Creator, Set<Creator>> _graph = {}; | |
/// Get or create an element for creator. | |
Element _element<T>(Creator creator) => | |
_elements.putIfAbsent(creator, () => creator._createElement(this)); | |
/// Add an edge creator -> watcher to the graph, then return creator's state. | |
T watch<T>(Creator<T> creator, Creator? watcher) { | |
if (watcher != null) { | |
(_graph[creator] ??= {}).add(watcher); | |
} | |
return _element<T>(creator).state; | |
} | |
/// Set state of the creator. | |
void set<T>(Creator<T> creator, T state) { | |
final element = _element<T>(creator); | |
if (state != element.state) { | |
element.state = state; | |
_onStateChange(creator); | |
} | |
} | |
/// Set state of creator using an update function. See [set]. | |
void update<T>(Creator<T> creator, T Function(T) update) => | |
set<T>(creator, update(_element(creator).state)); | |
/// Force creator to recreate its state. | |
void recreate(Creator creator) { | |
_element(creator).recreate(); | |
} | |
/// Delete the creator if it has no watcher. Also delete other creators who | |
/// loses all their watchers. | |
void dispose(Creator creator) { | |
if ((_graph[creator] ?? {}).isNotEmpty) { | |
return; // The creator is being watched by someone, cannot dispose it. | |
} | |
_elements.remove(creator); | |
_graph.remove(creator); | |
for (final c in _elements.keys.toSet()) { | |
if ((_graph[c] ?? {}).contains(creator)) { | |
_graph[c]!.remove(creator); | |
dispose(c); // Dispose c if creator is the only watcher of c. | |
} | |
} | |
} | |
/// Propagate state changes. | |
void _onStateChange(Creator creator) { | |
for (final c in _graph[creator] ?? {}) { | |
_element(c).recreate(); | |
} | |
} | |
} | |
/// CreatorGraph simply expose Ref through context. | |
class CreatorGraph extends InheritedWidget { | |
CreatorGraph({Key? key, required Widget child}) | |
: super(key: key, child: child); | |
final Ref ref = Ref(); | |
static CreatorGraph of(BuildContext context) => | |
context.dependOnInheritedWidgetOfExactType<CreatorGraph>()!; | |
@override | |
bool updateShouldNotify(CreatorGraph oldWidget) => ref != oldWidget.ref; | |
} | |
extension ContextRef on BuildContext { | |
Ref get ref => CreatorGraph.of(this).ref; | |
} | |
/// Watch creators to build a widget or to perform other action. | |
class Watcher extends StatefulWidget { | |
const Watcher(this.builder, {Key? key}) : super(key: key); | |
/// Allows watching creators to populate a widget. | |
final Widget Function(BuildContext context, Ref ref, Creator self)? builder; | |
@override | |
State<Watcher> createState() => _WatcherState(); | |
} | |
class _WatcherState extends State<Watcher> { | |
late Creator<Widget> builder; | |
late Ref ref; | |
@override | |
void didChangeDependencies() { | |
super.didChangeDependencies(); | |
ref = CreatorGraph.of(context).ref; // Save ref to use in dispose. | |
builder = Creator((ref, self) { | |
setState(() {}); | |
return widget.builder!(context, ref, self); | |
}); | |
} | |
@override | |
void dispose() { | |
ref.dispose(builder); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
ref.recreate(builder); | |
return ref.watch(builder, null); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment