Skip to content

Instantly share code, notes, and snippets.

@lesnitsky
Created October 19, 2021 23:57
Show Gist options
  • Save lesnitsky/2300dfa3dae422c954375c0c0a432b06 to your computer and use it in GitHub Desktop.
Save lesnitsky/2300dfa3dae422c954375c0c0a432b06 to your computer and use it in GitHub Desktop.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class AppModel extends InheritedWidget {
const AppModel({
Key? key,
required Widget child,
}) : super(key: key, child: child);
static V get<K, V>(BuildContext context, K key) {
final el = context.getElementForInheritedWidgetOfExactType<AppModel>()
as AppModelElement;
el.subscribe(context as Element, key);
return el.get<K, V>(key);
}
static void set<K, V>(BuildContext context, K key, V value) {
final el = context.getElementForInheritedWidgetOfExactType<AppModel>()
as AppModelElement;
el.set<K, V>(key, value);
}
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
return false;
}
@override
InheritedElement createElement() {
return AppModelElement(this);
}
}
class AppModelElement extends InheritedElement {
AppModelElement(InheritedWidget widget) : super(widget);
final Map<Object?, Set<Element>> _subscribers = {};
final Map _storage = {};
V get<K, V>(K key) {
final value = _storage[key];
if (null is V && value == null) return value;
return value as V;
}
void set<K, V>(K key, V value) {
_storage[key] = value;
_notify(key);
}
void subscribe(Element dependent, Object? aspect) {
_subscribers[aspect] ??= {};
_subscribers[aspect]!.add(dependent);
setDependencies(dependent, this);
}
void unsubscribe(Element element, Object? aspect) {
_subscribers[aspect]?.remove(element);
}
@override
void updateDependencies(Element dependent, Object? aspect) {
subscribe(dependent, aspect);
}
_notify(Object? key) {
final subscribers = _subscribers[key];
final _inactive = [];
subscribers?.forEach((element) {
// subscriber element was deactivated and removed itself from dependants
if (getDependencies(element) != this) {
_inactive.add(element);
} else {
element.markNeedsBuild();
}
});
for (var element in _inactive) {
subscribers?.remove(element);
}
}
@override
void unmount() {
super.unmount();
_subscribers.clear();
}
}
// Displays the value of AppModel.get(context, appModelKey) and the number of times
// that this widget has been built.
class ShowAppModelValue<K, V> extends StatefulWidget {
const ShowAppModelValue({
Key? key,
required this.modelKey,
}) : super(key: key);
final K modelKey;
@override
_ShowAppModelValueState createState() => _ShowAppModelValueState<K, V>();
}
class _ShowAppModelValueState<K, V> extends State<ShowAppModelValue> {
int _buildCount = 0;
@override
Widget build(BuildContext context) {
_buildCount += 1;
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'${widget.modelKey}: ${AppModel.get<K, V>(context, widget.modelKey)} [$_buildCount]',
),
);
}
}
// Demonstrates that changes to the AppModel _only_ cause the dependent widgets
// to be rebuilt.
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _fooCount = 0;
int _barCount = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ShowAppModelValue<String, String?>(modelKey: 'Home'),
const ShowAppModelValue<String, String?>(modelKey: 'foo'),
const ShowAppModelValue<String, String?>(modelKey: 'bar'),
ElevatedButton(
child: const Text('foo = FOO'),
onPressed: () {
_fooCount += 1;
AppModel.set<String, String>(
context,
'foo',
'FOO $_fooCount',
); // note: no setState()
},
),
const SizedBox(height: 16),
ElevatedButton(
child: const Text('bar = BAR'),
onPressed: () {
_barCount += 1;
AppModel.set<String, String>(
context,
'bar',
'BAR $_barCount',
); // note: no setState()
},
),
],
),
),
);
}
}
void main() {
runApp(
AppModel(child: const MaterialApp(home: Home())),
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment