Skip to content

Instantly share code, notes, and snippets.

@leecommamichael
Created December 1, 2019 22:55
Show Gist options
  • Save leecommamichael/28f290c3bc6f348de63b3fb5c300538d to your computer and use it in GitHub Desktop.
Save leecommamichael/28f290c3bc6f348de63b3fb5c300538d to your computer and use it in GitHub Desktop.
Hiding a package in DartPad
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'dart:async';
T useAnimation<T>(Animation<T> animation) {
useListenable(animation);
return animation.value;
}
AnimationController useAnimationController({
Duration duration,
String debugLabel,
double initialValue = 0,
double lowerBound = 0,
double upperBound = 1,
TickerProvider vsync,
AnimationBehavior animationBehavior = AnimationBehavior.normal,
List<Object> keys,
}) {
return Hook.use(_AnimationControllerHook(
duration: duration,
debugLabel: debugLabel,
initialValue: initialValue,
lowerBound: lowerBound,
upperBound: upperBound,
vsync: vsync,
animationBehavior: animationBehavior,
keys: keys,
));
}
class _AnimationControllerHook extends Hook<AnimationController> {
final Duration duration;
final String debugLabel;
final double initialValue;
final double lowerBound;
final double upperBound;
final TickerProvider vsync;
final AnimationBehavior animationBehavior;
const _AnimationControllerHook({
this.duration,
this.debugLabel,
this.initialValue,
this.lowerBound,
this.upperBound,
this.vsync,
this.animationBehavior,
List<Object> keys,
}) : super(keys: keys);
@override
_AnimationControllerHookState createState() =>
_AnimationControllerHookState();
}
class _AnimationControllerHookState
extends HookState<AnimationController, _AnimationControllerHook> {
AnimationController _animationController;
@override
void didUpdateHook(_AnimationControllerHook oldHook) {
super.didUpdateHook(oldHook);
if (hook.vsync != oldHook.vsync) {
assert(hook.vsync != null && oldHook.vsync != null, '''
Switching between controller and uncontrolled vsync is not allowed.
''');
_animationController.resync(hook.vsync);
}
if (hook.duration != oldHook.duration) {
_animationController.duration = hook.duration;
}
}
@override
AnimationController build(BuildContext context) {
final vsync = hook.vsync ?? useSingleTickerProvider(keys: hook.keys);
_animationController ??= AnimationController(
vsync: vsync,
duration: hook.duration,
debugLabel: hook.debugLabel,
lowerBound: hook.lowerBound,
upperBound: hook.upperBound,
animationBehavior: hook.animationBehavior,
value: hook.initialValue,
);
return _animationController;
}
@override
void dispose() {
_animationController.dispose();
}
}
TickerProvider useSingleTickerProvider({List<Object> keys}) {
return Hook.use(
keys != null
? _SingleTickerProviderHook(keys)
: const _SingleTickerProviderHook(),
);
}
class _SingleTickerProviderHook extends Hook<TickerProvider> {
const _SingleTickerProviderHook([List<Object> keys]) : super(keys: keys);
@override
_TickerProviderHookState createState() => _TickerProviderHookState();
}
class _TickerProviderHookState
extends HookState<TickerProvider, _SingleTickerProviderHook>
implements TickerProvider {
Ticker _ticker;
@override
Ticker createTicker(TickerCallback onTick) {
assert(() {
if (_ticker == null) return true;
throw FlutterError(
'${context.widget.runtimeType} attempted to use a useSingleTickerProvider multiple times.\n'
'A SingleTickerProviderStateMixin can only be used as a TickerProvider once. If a '
'TickerProvider is used for multiple AnimationController objects, or if it is passed to other '
'objects and those objects might use it more than one time in total, then instead of '
'using useSingleTickerProvider, use a regular useTickerProvider.');
}());
_ticker = Ticker(onTick, debugLabel: 'created by $context');
return _ticker;
}
@override
void dispose() {
assert(() {
if (_ticker == null || !_ticker.isActive) return true;
throw FlutterError(
'useSingleTickerProvider created a Ticker, but at the time '
'dispose() was called on the Hook, that Ticker was still active. Tickers used '
' by AnimationControllers should be disposed by calling dispose() on '
' the AnimationController itself. Otherwise, the ticker will leak.\n');
}());
}
@override
TickerProvider build(BuildContext context) {
if (_ticker != null) _ticker.muted = !TickerMode.of(context);
return this;
}
}
AsyncSnapshot<T> useFuture<T>(Future<T> future,
{T initialData, bool preserveState = true}) {
return Hook.use(_FutureHook(future,
initialData: initialData, preserveState: preserveState));
}
class _FutureHook<T> extends Hook<AsyncSnapshot<T>> {
final Future<T> future;
final bool preserveState;
final T initialData;
const _FutureHook(this.future, {this.initialData, this.preserveState = true});
@override
_FutureStateHook<T> createState() => _FutureStateHook<T>();
}
class _FutureStateHook<T> extends HookState<AsyncSnapshot<T>, _FutureHook<T>> {
Object _activeCallbackIdentity;
AsyncSnapshot<T> _snapshot;
@override
void initHook() {
super.initHook();
_snapshot =
AsyncSnapshot<T>.withData(ConnectionState.none, hook.initialData);
_subscribe();
}
@override
void didUpdateHook(_FutureHook<T> oldHook) {
super.didUpdateHook(oldHook);
if (oldHook.future != hook.future) {
if (_activeCallbackIdentity != null) {
_unsubscribe();
if (hook.preserveState) {
_snapshot = _snapshot.inState(ConnectionState.none);
} else {
_snapshot =
AsyncSnapshot<T>.withData(ConnectionState.none, hook.initialData);
}
}
_subscribe();
}
}
@override
void dispose() {
_unsubscribe();
}
void _subscribe() {
if (hook.future != null) {
final callbackIdentity = Object();
_activeCallbackIdentity = callbackIdentity;
hook.future.then<void>((T data) {
if (_activeCallbackIdentity == callbackIdentity) {
setState(() {
_snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
});
}
}, onError: (Object error) {
if (_activeCallbackIdentity == callbackIdentity) {
setState(() {
_snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error);
});
}
});
_snapshot = _snapshot.inState(ConnectionState.waiting);
}
}
void _unsubscribe() {
_activeCallbackIdentity = null;
}
@override
AsyncSnapshot<T> build(BuildContext context) {
return _snapshot;
}
}
AsyncSnapshot<T> useStream<T>(Stream<T> stream,
{T initialData, bool preserveState = true}) {
return Hook.use(_StreamHook(
stream,
initialData: initialData,
preserveState: preserveState,
));
}
class _StreamHook<T> extends Hook<AsyncSnapshot<T>> {
final Stream<T> stream;
final T initialData;
final bool preserveState;
_StreamHook(this.stream, {this.initialData, this.preserveState = true});
@override
_StreamHookState<T> createState() => _StreamHookState<T>();
}
class _StreamHookState<T> extends HookState<AsyncSnapshot<T>, _StreamHook<T>> {
StreamSubscription<T> _subscription;
AsyncSnapshot<T> _summary;
@override
void initHook() {
super.initHook();
_summary = initial();
_subscribe();
}
@override
void didUpdateHook(_StreamHook<T> oldWidget) {
super.didUpdateHook(oldWidget);
if (oldWidget.stream != hook.stream) {
if (_subscription != null) {
_unsubscribe();
if (hook.preserveState) {
_summary = afterDisconnected(_summary);
} else {
_summary = initial();
}
}
_subscribe();
}
}
@override
void dispose() {
_unsubscribe();
}
void _subscribe() {
if (hook.stream != null) {
_subscription = hook.stream.listen((T data) {
setState(() {
_summary = afterData(_summary, data);
});
}, onError: (Object error) {
setState(() {
_summary = afterError(_summary, error);
});
}, onDone: () {
setState(() {
_summary = afterDone(_summary);
});
});
_summary = afterConnected(_summary);
}
}
void _unsubscribe() {
if (_subscription != null) {
_subscription.cancel();
_subscription = null;
}
}
@override
AsyncSnapshot<T> build(BuildContext context) {
return _summary;
}
AsyncSnapshot<T> initial() =>
AsyncSnapshot<T>.withData(ConnectionState.none, hook.initialData);
AsyncSnapshot<T> afterConnected(AsyncSnapshot<T> current) =>
current.inState(ConnectionState.waiting);
AsyncSnapshot<T> afterData(AsyncSnapshot<T> current, T data) {
return AsyncSnapshot<T>.withData(ConnectionState.active, data);
}
AsyncSnapshot<T> afterError(AsyncSnapshot<T> current, Object error) {
return AsyncSnapshot<T>.withError(ConnectionState.active, error);
}
AsyncSnapshot<T> afterDone(AsyncSnapshot<T> current) =>
current.inState(ConnectionState.done);
AsyncSnapshot<T> afterDisconnected(AsyncSnapshot<T> current) =>
current.inState(ConnectionState.none);
}
StreamController<T> useStreamController<T>(
{bool sync = false,
VoidCallback onListen,
VoidCallback onCancel,
List<Object> keys}) {
return Hook.use(_StreamControllerHook(
onCancel: onCancel,
onListen: onListen,
sync: sync,
keys: keys,
));
}
class _StreamControllerHook<T> extends Hook<StreamController<T>> {
final bool sync;
final VoidCallback onListen;
final VoidCallback onCancel;
const _StreamControllerHook(
{this.sync = false, this.onListen, this.onCancel, List<Object> keys})
: super(keys: keys);
@override
_StreamControllerHookState<T> createState() =>
_StreamControllerHookState<T>();
}
class _StreamControllerHookState<T>
extends HookState<StreamController<T>, _StreamControllerHook<T>> {
StreamController<T> _controller;
@override
void initHook() {
super.initHook();
_controller = StreamController.broadcast(
sync: hook.sync,
onCancel: hook.onCancel,
onListen: hook.onListen,
);
}
@override
void didUpdateHook(_StreamControllerHook<T> oldHook) {
super.didUpdateHook(oldHook);
if (oldHook.onListen != hook.onListen) {
_controller.onListen = hook.onListen;
}
if (oldHook.onCancel != hook.onCancel) {
_controller.onCancel = hook.onCancel;
}
}
@override
StreamController<T> build(BuildContext context) {
return _controller;
}
@override
void dispose() {
_controller.close();
}
}
bool debugHotReloadHooksEnabled = true;
@immutable
abstract class Hook<R> {
const Hook({this.keys});
static R use<R>(Hook<R> hook) {
assert(HookElement._currentContext != null, '''
`Hook.use` can only be called from the build method of HookWidget.
Hooks should only be called within the build method of a widget.
Calling them outside of build method leads to an unstable state and is therefore prohibited.
''');
return HookElement._currentContext._use(hook);
}
final List<Object> keys;
static bool shouldPreserveState(Hook hook1, Hook hook2) {
final p1 = hook1.keys;
final p2 = hook2.keys;
if (p1 == p2) {
return true;
}
if ((p1 != p2 && (p1 == null || p2 == null)) || p1.length != p2.length) {
return false;
}
var i1 = p1.iterator;
var i2 = p2.iterator;
while (true) {
if (!i1.moveNext() || !i2.moveNext()) {
return true;
}
if (i1.current != i2.current) {
return false;
}
}
}
@protected
HookState<R, Hook<R>> createState();
}
abstract class HookState<R, T extends Hook<R>> {
@protected
BuildContext get context => _element.context;
State _element;
T get hook => _hook;
T _hook;
@protected
void initHook() {}
@protected
void dispose() {}
@protected
void didBuild() {}
@protected
R build(BuildContext context);
@protected
void didUpdateHook(T oldHook) {}
void reassemble() {}
@protected
void setState(VoidCallback fn) {
_element.setState(fn);
}
}
class HookElement extends StatefulElement {
static HookElement _currentContext;
HookElement(HookWidget widget) : super(widget);
Iterator<HookState> _currentHook;
int _hookIndex;
List<HookState> _hooks;
bool _didFinishBuildOnce = false;
bool _debugDidReassemble;
bool _debugShouldDispose;
bool _debugIsInitHook;
@override
HookWidget get widget => super.widget as HookWidget;
@override
Widget build() {
_currentHook = _hooks?.iterator;
_currentHook?.moveNext();
_hookIndex = 0;
assert(() {
_debugShouldDispose = false;
_debugIsInitHook = false;
_debugDidReassemble ??= false;
return true;
}());
HookElement._currentContext = this;
final result = super.build();
HookElement._currentContext = null;
assert(() {
if (!debugHotReloadHooksEnabled) return true;
if (_debugDidReassemble && _hooks != null) {
for (var i = _hookIndex; i < _hooks.length;) {
_hooks.removeAt(i).dispose();
}
}
return true;
}());
assert(_hookIndex == (_hooks?.length ?? 0), '''
Build for $widget finished with less hooks used than a previous build.
Used $_hookIndex hooks while a previous build had ${_hooks.length}.
This may happen if the call to `Hook.use` is made under some condition.
''');
assert(() {
if (!debugHotReloadHooksEnabled) return true;
_debugDidReassemble = false;
return true;
}());
_didFinishBuildOnce = true;
return result;
}
@visibleForTesting
List<HookState> get debugHooks => List<HookState>.unmodifiable(_hooks);
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType,
{Object aspect}) {
assert(!_debugIsInitHook);
return super.inheritFromWidgetOfExactType(targetType, aspect: aspect);
}
@override
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (_hooks != null) {
for (final hook in _hooks.reversed) {
try {
hook.didBuild();
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'hooks library',
context: DiagnosticsNode.message(
'while calling `didBuild` on ${hook.runtimeType}'),
));
}
}
}
return super.updateChild(child, newWidget, newSlot);
}
@override
void unmount() {
super.unmount();
if (_hooks != null) {
for (final hook in _hooks) {
try {
hook.dispose();
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'hooks library',
context:
DiagnosticsNode.message('while disposing ${hook.runtimeType}'),
));
}
}
}
}
@override
void reassemble() {
super.reassemble();
assert(() {
_debugDidReassemble = true;
if (_hooks != null) {
for (final hook in _hooks) {
hook.reassemble();
}
}
return true;
}());
}
R _use<R>(Hook<R> hook) {
HookState<R, Hook<R>> hookState;
if (_currentHook == null) {
assert(_debugDidReassemble || !_didFinishBuildOnce);
hookState = _createHookState(hook);
_hooks ??= [];
_hooks.add(hookState);
} else {
assert(() {
if (!debugHotReloadHooksEnabled) return true;
if (!_debugDidReassemble) {
return true;
}
if (!_debugShouldDispose &&
_currentHook.current?.hook?.runtimeType == hook.runtimeType) {
return true;
}
_debugShouldDispose = true;
if (_currentHook.current != null) {
_hooks.remove(_currentHook.current..dispose());
hookState = _insertHookAt(_hookIndex, hook);
} else {
hookState = _pushHook(hook);
}
return true;
}());
if (!_didFinishBuildOnce && _currentHook.current == null) {
hookState = _pushHook(hook);
_currentHook.moveNext();
} else {
assert(_currentHook.current != null);
assert(_debugTypesAreRight(hook));
if (_currentHook.current.hook == hook) {
hookState = _currentHook.current as HookState<R, Hook<R>>;
_currentHook.moveNext();
} else if (Hook.shouldPreserveState(_currentHook.current.hook, hook)) {
hookState = _currentHook.current as HookState<R, Hook<R>>;
_currentHook.moveNext();
final previousHook = hookState._hook;
hookState
.._hook = hook
..didUpdateHook(previousHook);
} else {
hookState = _replaceHookAt(_hookIndex, hook);
_resetsIterator(hookState);
_currentHook.moveNext();
}
}
}
_hookIndex++;
return hookState.build(this);
}
HookState<R, Hook<R>> _replaceHookAt<R>(int index, Hook<R> hook) {
_hooks.removeAt(_hookIndex).dispose();
var hookState = _createHookState(hook);
_hooks.insert(_hookIndex, hookState);
return hookState;
}
HookState<R, Hook<R>> _insertHookAt<R>(int index, Hook<R> hook) {
var hookState = _createHookState(hook);
_hooks.insert(index, hookState);
_resetsIterator(hookState);
return hookState;
}
HookState<R, Hook<R>> _pushHook<R>(Hook<R> hook) {
var hookState = _createHookState(hook);
_hooks.add(hookState);
_resetsIterator(hookState);
return hookState;
}
bool _debugTypesAreRight(Hook hook) {
assert(_currentHook.current.hook.runtimeType == hook.runtimeType);
return true;
}
void _resetsIterator(HookState hookState) {
_currentHook = _hooks.iterator;
while (_currentHook.current != hookState) {
_currentHook.moveNext();
}
}
HookState<R, Hook<R>> _createHookState<R>(Hook<R> hook) {
assert(() {
_debugIsInitHook = true;
return true;
}());
final state = hook.createState()
.._element = this.state
.._hook = hook
..initHook();
assert(() {
_debugIsInitHook = false;
return true;
}());
return state;
}
}
abstract class HookWidget extends StatefulWidget {
const HookWidget({Key key}) : super(key: key);
@override
HookElement createElement() => HookElement(this);
@override
_HookWidgetState createState() => _HookWidgetState();
@protected
Widget build(BuildContext context);
}
class _HookWidgetState extends State<HookWidget> {
@override
Widget build(BuildContext context) {
return widget.build(context);
}
}
BuildContext useContext() {
assert(HookElement._currentContext != null,
'`useContext` can only be called from the build method of HookWidget');
return HookElement._currentContext;
}
class HookBuilder extends HookWidget {
final Widget Function(BuildContext context) builder;
const HookBuilder({
@required this.builder,
Key key,
}) : assert(builder != null),
super(key: key);
@override
Widget build(BuildContext context) => builder(context);
}
T useValueListenable<T>(ValueListenable<T> valueListenable) {
return useListenable(valueListenable).value;
}
T useListenable<T extends Listenable>(T listenable) {
Hook.use(_ListenableHook(listenable));
return listenable;
}
class _ListenableHook extends Hook<void> {
final Listenable listenable;
const _ListenableHook(this.listenable) : assert(listenable != null);
@override
_ListenableStateHook createState() => _ListenableStateHook();
}
class _ListenableStateHook extends HookState<void, _ListenableHook> {
@override
void initHook() {
super.initHook();
hook.listenable.addListener(_listener);
}
@override
void didUpdateHook(_ListenableHook oldHook) {
super.didUpdateHook(oldHook);
if (hook.listenable != oldHook.listenable) {
oldHook.listenable.removeListener(_listener);
hook.listenable.addListener(_listener);
}
}
@override
void build(BuildContext context) {}
void _listener() {
setState(() {});
}
@override
void dispose() {
hook.listenable.removeListener(_listener);
}
}
ValueNotifier<T> useValueNotifier<T>([T intialData, List<Object> keys]) {
return Hook.use(_ValueNotifierHook(
initialData: intialData,
keys: keys,
));
}
class _ValueNotifierHook<T> extends Hook<ValueNotifier<T>> {
final T initialData;
const _ValueNotifierHook({List<Object> keys, this.initialData})
: super(keys: keys);
@override
_UseValueNotiferHookState<T> createState() => _UseValueNotiferHookState<T>();
}
class _UseValueNotiferHookState<T>
extends HookState<ValueNotifier<T>, _ValueNotifierHook<T>> {
ValueNotifier<T> notifier;
@override
void initHook() {
super.initHook();
notifier = ValueNotifier(hook.initialData);
}
@override
ValueNotifier<T> build(BuildContext context) {
return notifier;
}
@override
void dispose() {
notifier.dispose();
}
}
abstract class Store<State, Action> {
State get state;
void dispatch(Action action);
}
typedef Reducer<State, Action> = State Function(State state, Action action);
Store<State, Action> useReducer<State extends Object, Action>(
Reducer<State, Action> reducer, {
State initialState,
Action initialAction,
}) {
return Hook.use(_ReducerdHook(reducer,
initialAction: initialAction, initialState: initialState));
}
class _ReducerdHook<State, Action> extends Hook<Store<State, Action>> {
final Reducer<State, Action> reducer;
final State initialState;
final Action initialAction;
const _ReducerdHook(this.reducer, {this.initialState, this.initialAction})
: assert(reducer != null);
@override
_ReducerdHookState<State, Action> createState() =>
_ReducerdHookState<State, Action>();
}
class _ReducerdHookState<State, Action>
extends HookState<Store<State, Action>, _ReducerdHook<State, Action>>
implements Store<State, Action> {
@override
State state;
@override
void initHook() {
super.initHook();
state = hook.reducer(hook.initialState, hook.initialAction);
assert(state != null);
}
@override
void dispatch(Action action) {
final res = hook.reducer(state, action);
assert(res != null);
if (state != res) {
setState(() {
state = res;
});
}
}
@override
Store<State, Action> build(BuildContext context) {
return this;
}
}
T usePrevious<T>(T val) {
return Hook.use(_PreviousHook(val));
}
class _PreviousHook<T> extends Hook<T> {
_PreviousHook(this.value);
final T value;
@override
_PreviousHookState<T> createState() => _PreviousHookState();
}
class _PreviousHookState<T> extends HookState<T, _PreviousHook<T>> {
T previous;
@override
void didUpdateHook(_PreviousHook<T> old) {
previous = old.value;
}
@override
T build(BuildContext context) => previous;
}
void useReassemble(VoidCallback callback) {
assert(() {
Hook.use(_ReassembleHook(callback));
return true;
}());
}
class _ReassembleHook extends Hook<void> {
final VoidCallback callback;
_ReassembleHook(this.callback) : assert(callback != null);
@override
_ReassembleHookState createState() => _ReassembleHookState();
}
class _ReassembleHookState extends HookState<void, _ReassembleHook> {
@override
void reassemble() {
super.reassemble();
hook.callback();
}
@override
void build(BuildContext context) {}
}
T useMemoized<T>(T Function() valueBuilder,
[List<Object> keys = const <dynamic>[]]) {
return Hook.use(_MemoizedHook(
valueBuilder,
keys: keys,
));
}
class _MemoizedHook<T> extends Hook<T> {
final T Function() valueBuilder;
const _MemoizedHook(this.valueBuilder,
{List<Object> keys = const <dynamic>[]})
: assert(valueBuilder != null),
assert(keys != null),
super(keys: keys);
@override
_MemoizedHookState<T> createState() => _MemoizedHookState<T>();
}
class _MemoizedHookState<T> extends HookState<T, _MemoizedHook<T>> {
T value;
@override
void initHook() {
super.initHook();
value = hook.valueBuilder();
}
@override
T build(BuildContext context) {
return value;
}
}
R useValueChanged<T, R>(T value, R valueChange(T oldValue, R oldResult)) {
return Hook.use(_ValueChangedHook(value, valueChange));
}
class _ValueChangedHook<T, R> extends Hook<R> {
final R Function(T oldValue, R oldResult) valueChanged;
final T value;
const _ValueChangedHook(this.value, this.valueChanged)
: assert(valueChanged != null);
@override
_ValueChangedHookState<T, R> createState() => _ValueChangedHookState<T, R>();
}
class _ValueChangedHookState<T, R>
extends HookState<R, _ValueChangedHook<T, R>> {
R _result;
@override
void didUpdateHook(_ValueChangedHook<T, R> oldHook) {
super.didUpdateHook(oldHook);
if (hook.value != oldHook.value) {
_result = hook.valueChanged(oldHook.value, _result);
}
}
@override
R build(BuildContext context) {
return _result;
}
}
typedef Dispose = void Function();
void useEffect(Dispose Function() effect, [List<Object> keys]) {
Hook.use(_EffectHook(effect, keys));
}
class _EffectHook extends Hook<void> {
final Dispose Function() effect;
const _EffectHook(this.effect, [List<Object> keys])
: assert(effect != null),
super(keys: keys);
@override
_EffectHookState createState() => _EffectHookState();
}
class _EffectHookState extends HookState<void, _EffectHook> {
Dispose disposer;
@override
void initHook() {
super.initHook();
scheduleEffect();
}
@override
void didUpdateHook(_EffectHook oldHook) {
super.didUpdateHook(oldHook);
if (hook.keys == null) {
if (disposer != null) {
disposer();
}
scheduleEffect();
}
}
@override
void build(BuildContext context) {}
@override
void dispose() {
if (disposer != null) {
disposer();
}
}
void scheduleEffect() {
disposer = hook.effect();
}
}
ValueNotifier<T> useState<T>([T initialData]) {
return Hook.use(_StateHook(initialData: initialData));
}
class _StateHook<T> extends Hook<ValueNotifier<T>> {
final T initialData;
const _StateHook({this.initialData});
@override
_StateHookState<T> createState() => _StateHookState();
}
class _StateHookState<T> extends HookState<ValueNotifier<T>, _StateHook<T>> {
ValueNotifier<T> _state;
@override
void initHook() {
super.initHook();
_state = ValueNotifier(hook.initialData)..addListener(_listener);
}
@override
void dispose() {
_state.dispose();
}
@override
ValueNotifier<T> build(BuildContext context) {
return _state;
}
void _listener() {
setState(() {});
}
}
class _TextEditingControllerHookCreator {
const _TextEditingControllerHookCreator();
TextEditingController call({String text, List<Object> keys}) {
return Hook.use(_TextEditingControllerHook(text, null, keys));
}
TextEditingController fromValue(TextEditingValue value, [List<Object> keys]) {
return Hook.use(_TextEditingControllerHook(null, value, keys));
}
}
const useTextEditingController = _TextEditingControllerHookCreator();
class _TextEditingControllerHook extends Hook<TextEditingController> {
final String initialText;
final TextEditingValue initialValue;
_TextEditingControllerHook(this.initialText, this.initialValue,
[List<Object> keys])
: assert(
initialText == null || initialValue == null,
"initialText and intialValue can't both be set on a call to "
'useTextEditingController!',
),
super(keys: keys);
@override
_TextEditingControllerHookState createState() {
return _TextEditingControllerHookState();
}
}
class _TextEditingControllerHookState
extends HookState<TextEditingController, _TextEditingControllerHook> {
TextEditingController _controller;
@override
void initHook() {
if (hook.initialValue != null) {
_controller = TextEditingController.fromValue(hook.initialValue);
} else {
_controller = TextEditingController(text: hook.initialText);
}
}
@override
TextEditingController build(BuildContext context) => _controller;
@override
void dispose() => _controller?.dispose();
}
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
// import 'package:flutter_hooks/flutter_hooks.dart';
class UseAnimationControllerExample extends HookWidget {
@override
Widget build(BuildContext context) {
final myAnimation = useAnimationController(
animationBehavior: AnimationBehavior.normal,
lowerBound: 0.0,
upperBound: 200.0,
duration: const Duration(milliseconds: 1500),
initialValue: 0.0)
..forward();
return Scaffold(
appBar: AppBar(
title: const Text('useAnimationController'),
),
body: AnimatedBuilder(
animation: myAnimation,
builder: (BuildContext context, Widget w) {
return Center(
child: Icon(Icons.mood, size: myAnimation.value),
);
}));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment