Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

- added all `FocusNode` parameters to `useFocusNode`
- Fixed a bug where on hot-reload, a `HookWidget` could potentailly not rebuild
- Allow hooks to integrate with the devtool using the `Diagnosticable` API, and
implement it for all built-in hooks.

## 0.13.1

Expand Down
4 changes: 3 additions & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// ignore_for_file: omit_local_variable_types
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

import 'star_wars/planet_screen.dart';
import 'use_effect.dart';
Expand All @@ -11,9 +12,10 @@ void main() => runApp(HooksGalleryApp());
/// An App that demonstrates how to use hooks. It includes examples that cover
/// the hooks provided by this library as well as examples that demonstrate
/// how to write custom hooks.
class HooksGalleryApp extends StatelessWidget {
class HooksGalleryApp extends HookWidget {
@override
Widget build(BuildContext context) {
useAnimationController(duration: const Duration(seconds: 2));
return MaterialApp(
title: 'Flutter Hooks Gallery',
home: Scaffold(
Expand Down
37 changes: 36 additions & 1 deletion lib/src/animation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,27 @@ part of 'hooks.dart';
/// * [Animation]
/// * [useValueListenable], [useListenable], [useStream]
T useAnimation<T>(Animation<T> animation) {
useListenable(animation);
use(_UseAnimationHook(animation));
return animation.value;
}

class _UseAnimationHook extends _ListenableHook {
const _UseAnimationHook(Animation animation) : super(animation);

@override
_UseAnimationStateHook createState() {
return _UseAnimationStateHook();
}
}

class _UseAnimationStateHook extends _ListenableStateHook {
@override
String get debugLabel => 'useAnimation';

@override
Object get debugValue => (hook.listenable as Animation).value;
}

/// Creates an [AnimationController] automatically disposed.
///
/// If no [vsync] is provided, the [TickerProvider] is implicitly obtained using [useSingleTickerProvider].
Expand Down Expand Up @@ -72,6 +89,12 @@ class _AnimationControllerHook extends Hook<AnimationController> {
@override
_AnimationControllerHookState createState() =>
_AnimationControllerHookState();

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty('duration', duration));
}
}

class _AnimationControllerHookState
Expand Down Expand Up @@ -113,6 +136,12 @@ class _AnimationControllerHookState
void dispose() {
_animationController.dispose();
}

@override
bool get debugHasShortDescription => false;

@override
String get debugLabel => 'useAnimationController';
}

/// Creates a single usage [TickerProvider].
Expand Down Expand Up @@ -176,4 +205,10 @@ class _TickerProviderHookState
}
return this;
}

@override
String get debugLabel => 'useSingleTickerProvider';

@override
bool get debugSkipValue => true;
}
12 changes: 12 additions & 0 deletions lib/src/async.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ class _FutureStateHook<T> extends HookState<AsyncSnapshot<T>, _FutureHook<T>> {
AsyncSnapshot<T> build(BuildContext context) {
return _snapshot;
}

@override
String get debugLabel => 'useFuture';

@override
Object get debugValue => _snapshot;
}

/// Subscribes to a [Stream] and return its current state in an [AsyncSnapshot].
Expand Down Expand Up @@ -201,6 +207,9 @@ class _StreamHookState<T> extends HookState<AsyncSnapshot<T>, _StreamHook<T>> {

AsyncSnapshot<T> afterDisconnected(AsyncSnapshot<T> current) =>
current.inState(ConnectionState.none);

@override
String get debugLabel => 'useStream';
}

/// Creates a [StreamController] automatically disposed.
Expand Down Expand Up @@ -269,4 +278,7 @@ class _StreamControllerHookState<T>
void dispose() {
_controller.close();
}

@override
String get debugLabel => 'useStreamController';
}
3 changes: 3 additions & 0 deletions lib/src/focus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,7 @@ class _FocusNodeHookState extends HookState<FocusNode, _FocusNodeHook> {

@override
void dispose() => _focusNode?.dispose();

@override
String get debugLabel => 'useFocusNode';
}
59 changes: 57 additions & 2 deletions lib/src/framework.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ R use<R>(Hook<R> hook) => Hook.use(hook);
/// In fact this has secondary bonus: `duration` is kept updated with the latest value.
/// If we were to pass a variable as `duration` instead of a constant, then on value change the [AnimationController] will be updated.
@immutable
abstract class Hook<R> {
abstract class Hook<R> with Diagnosticable {
/// Allows subclasses to have a `const` constructor
const Hook({this.keys});

Expand Down Expand Up @@ -203,12 +203,28 @@ Calling them outside of build method leads to an unstable state and is therefore
}

/// The logic and internal state for a [HookWidget]
abstract class HookState<R, T extends Hook<R>> {
abstract class HookState<R, T extends Hook<R>> with Diagnosticable {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hook probably should be Diagnosticable too.

Copy link
Contributor Author

@kranfix kranfix Aug 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the need to make Hook diagnosticable?

StatelessWidget is Diagnosticable, but StatefulWidget isn't. Instead, State is Diagnosticable. In this case, Hook is like StatefulWidget and HookState is like State.

/// Equivalent of [State.context] for [HookState]
@protected
BuildContext get context => _element;
HookElement _element;

R _debugLastBuiltValue;

/// The value shown in the devtool.
///
/// Defaults to the last value returned by [build].
Object get debugValue => _debugLastBuiltValue;

/// A flag to not show [debugValue] in the devtool, for hooks that returns nothing.
bool get debugSkipValue => false;

/// A label used by the devtool to show the state of a hook
String get debugLabel => null;

/// Whether the devtool description should skip [debugFillProperties] or not.
bool get debugHasShortDescription => true;

/// Equivalent of [State.widget] for [HookState]
T get hook => _hook;
T _hook;
Expand Down Expand Up @@ -282,6 +298,16 @@ abstract class HookState<R, T extends Hook<R>> {
.._isOptionalRebuild = false
..markNeedsBuild();
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
final value = debugValue;
if (value != this) {
properties.add(DiagnosticsProperty(null, value));
}
hook.debugFillProperties(properties);
}
}

class _Entry<T> extends LinkedListEntry<_Entry<T>> {
Expand Down Expand Up @@ -447,6 +473,10 @@ Type mismatch between hooks:
}

final result = _currentHookState.value.build(this) as R;
assert(() {
_currentHookState.value._debugLastBuiltValue = result;
return true;
}(), '');
_currentHookState = _currentHookState.next;
return result;
}
Expand Down Expand Up @@ -508,6 +538,31 @@ Type mismatch between hooks:
}
super.deactivate();
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
for (final hookState in debugHooks) {
if (hookState.debugHasShortDescription) {
if (hookState.debugSkipValue) {
properties.add(
StringProperty(hookState.debugLabel, '', ifEmpty: ''),
);
} else {
properties.add(
DiagnosticsProperty<dynamic>(
hookState.debugLabel,
hookState.debugValue,
),
);
}
} else {
properties.add(
DiagnosticsProperty(hookState.debugLabel, hookState),
);
}
}
}
}

/// A [Widget] that can use [Hook]
Expand Down
29 changes: 28 additions & 1 deletion lib/src/listenable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,25 @@ part of 'hooks.dart';
/// * [ValueListenable], the created object
/// * [useListenable]
T useValueListenable<T>(ValueListenable<T> valueListenable) {
return useListenable(valueListenable).value;
use(_UseValueListenableHook(valueListenable));
return valueListenable.value;
}

class _UseValueListenableHook extends _ListenableHook {
const _UseValueListenableHook(ValueListenable animation) : super(animation);

@override
_UseValueListenableStateHook createState() {
return _UseValueListenableStateHook();
}
}

class _UseValueListenableStateHook extends _ListenableStateHook {
@override
String get debugLabel => 'useValueListenable';

@override
Object get debugValue => (hook.listenable as ValueListenable).value;
}

/// Subscribes to a [Listenable] and mark the widget as needing build
Expand Down Expand Up @@ -57,6 +75,12 @@ class _ListenableStateHook extends HookState<void, _ListenableHook> {
void dispose() {
hook.listenable.removeListener(_listener);
}

@override
String get debugLabel => 'useListenable';

@override
Object get debugValue => hook.listenable;
}

/// Creates a [ValueNotifier] automatically disposed.
Expand Down Expand Up @@ -103,4 +127,7 @@ class _UseValueNotiferHookState<T>
void dispose() {
notifier.dispose();
}

@override
String get debugLabel => 'useValueNotifier';
}
33 changes: 31 additions & 2 deletions lib/src/misc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ Store<State, Action> useReducer<State extends Object, Action>(
State initialState,
Action initialAction,
}) {
return use(_ReducerdHook(reducer,
initialAction: initialAction, initialState: initialState));
return use(
_ReducerdHook(
reducer,
initialAction: initialAction,
initialState: initialState,
),
);
}

class _ReducerdHook<State, Action> extends Hook<Store<State, Action>> {
Expand Down Expand Up @@ -82,6 +87,12 @@ class _ReducerdHookState<State, Action>
Store<State, Action> build(BuildContext context) {
return this;
}

@override
String get debugLabel => 'useReducer';

@override
Object get debugValue => state;
}

/// Returns the previous argument called to [usePrevious].
Expand All @@ -108,6 +119,12 @@ class _PreviousHookState<T> extends HookState<T, _PreviousHook<T>> {

@override
T build(BuildContext context) => previous;

@override
String get debugLabel => 'usePrevious';

@override
Object get debugValue => previous;
}

/// Runs the callback on every hot reload
Expand Down Expand Up @@ -142,6 +159,12 @@ class _ReassembleHookState extends HookState<void, _ReassembleHook> {

@override
void build(BuildContext context) {}

@override
String get debugLabel => 'useReassemble';

@override
bool get debugSkipValue => true;
}

/// Returns an [IsMounted] object that you can use
Expand Down Expand Up @@ -185,6 +208,12 @@ class _IsMountedHookState extends HookState<IsMounted, _IsMountedHook> {
_mounted = false;
super.dispose();
}

@override
String get debugLabel => 'useIsMounted';

@override
Object get debugValue => _mounted;
}

typedef IsMounted = bool Function();
Loading