Last active
December 3, 2022 18:42
-
-
Save purplenoodlesoop/bd0695d3b308bcf77547ec89e924071b to your computer and use it in GitHub Desktop.
Inherited widget with granular updates that can be used in `didChangeDependencies`
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:collection/collection.dart'; | |
import 'package:flutter/material.dart'; | |
A id<A>(A a) => a; | |
bool _unaryTrue(dynamic _) => true; | |
bool _notEquals(dynamic first, dynamic second) => | |
!const DeepCollectionEquality().equals(first, second); | |
class InheritedSelector<T> extends InheritedWidget { | |
final T data; | |
const InheritedSelector({ | |
required this.data, | |
required super.child, | |
super.key, | |
}); | |
static R select<T, R>( | |
BuildContext context, | |
R Function(T data) selector, { | |
bool listen = false, | |
}) { | |
if (listen) { | |
context.dependOnInheritedWidgetOfExactType<InheritedSelector<T>>( | |
aspect: selector, | |
); | |
} | |
final widget = context | |
.getElementForInheritedWidgetOfExactType<InheritedSelector<T>>() | |
?.widget as InheritedSelector<T>?; | |
return selector(widget!.data); | |
} | |
static T of<T>(BuildContext context, {bool listen = false}) => select<T, T>( | |
context, | |
id<T>, | |
listen: listen, | |
); | |
@override | |
bool updateShouldNotify(InheritedSelector<T> oldWidget) => _notEquals( | |
data, | |
oldWidget.data, | |
); | |
@override | |
InheritedSelectorElement<T> createElement() => InheritedSelectorElement(this); | |
} | |
typedef _Selector<T> = dynamic Function(T value); | |
typedef _Dependencies<T> = Set<_Selector<T>>; | |
class InheritedSelectorElement<T> extends InheritedElement { | |
InheritedSelectorElement(InheritedSelector<T> widget) : super(widget); | |
bool _shouldClearDependencies = false; | |
bool _shouldScheduleResetFlags = true; | |
@override | |
InheritedSelector<T> get widget => super.widget as InheritedSelector<T>; | |
_Dependencies<T>? _currentDependencies(Element dependent) => | |
(getDependencies(dependent) as _Dependencies<T>?); | |
void _clearDependencies(_Dependencies<T>? dependencies) { | |
if (dependencies != null && dependencies.isNotEmpty) { | |
dependencies.clear(); | |
} | |
} | |
Future<void> _scheduleResetFlags() => Future.microtask(() { | |
_shouldClearDependencies = true; | |
_shouldScheduleResetFlags = true; | |
}); | |
bool _shouldNotify(InheritedSelector<T> oldWidget, Element dependent) => | |
_currentDependencies(dependent) | |
?.map( | |
(selector) => _notEquals( | |
selector(widget.data), | |
selector(oldWidget.data), | |
), | |
) | |
.any(id) ?? | |
false; | |
@override | |
void updateDependencies(Element dependent, Object? aspect) { | |
final dependencies = _currentDependencies(dependent); | |
if (!(dependencies?.contains(_unaryTrue) ?? false)) { | |
if (_shouldClearDependencies) { | |
_shouldClearDependencies = false; | |
_clearDependencies(dependencies); | |
} | |
if (_shouldScheduleResetFlags) { | |
_shouldScheduleResetFlags = false; | |
_scheduleResetFlags(); | |
} | |
setDependencies( | |
dependent, | |
(dependencies ?? <_Selector<T>>{}) | |
..add((aspect as _Selector<T>?) ?? _unaryTrue), | |
); | |
} | |
} | |
@override | |
void notifyDependent(InheritedSelector<T> oldWidget, Element dependent) { | |
if (_shouldNotify(oldWidget, dependent)) dependent.didChangeDependencies(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment