rrousselGit / flutter_hooks

React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget.
MIT License
3.07k stars 175 forks source link

Trigger a callback when value listenable changes #326

Open AAverin opened 1 year ago

AAverin commented 1 year ago

Is your feature request related to a problem? Please describe. Having a big and complicated screen, that is optimised using useValueNotifier instead of useState, often there is a need to do something when value notifier changes. Using useValueListenable would trigger rebuild, which is unwanted due to rebuilds optimisation

Describe the solution you'd like There is a way to add and remove listener to value notifier. This could be utilised to build a useValueListenableChanged hook, similar to useValueChanged hook that is present already.

Describe alternatives you've considered useValueChanged hook works on a regular variable, not on valueListenable

Additional context Here is what seemed to have worked. If there is no negative feedback I can make a PR:

import 'package:flutter/foundation.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

useValueListenableChanged<T>(
    ValueListenable<T> listenable, Function(T value) valueListenableChanged) {
  return use(
      _UseValueListenableChangedHook(listenable, valueListenableChanged));
}

class _UseValueListenableChangedHook<T> extends Hook {
  const _UseValueListenableChangedHook(
      this.valueListenable, this.valueListenableChanged);

  final Function(T value) valueListenableChanged;
  final ValueListenable<T> valueListenable;

  @override
  _ValueListenableChangedState createState() =>
      _ValueListenableChangedState<T>();
}

class _ValueListenableChangedState<T>
    extends HookState<dynamic, _UseValueListenableChangedHook<T>> {
  _ValueListenableChangedState();

  late VoidCallback _valueListenableChanged;

  @override
  void initHook() {
    _valueListenableChanged = () {
      hook.valueListenableChanged(hook.valueListenable.value);
    };
    hook.valueListenable.addListener(_valueListenableChanged);
  }

  @override
  void dispose() {
    hook.valueListenable.removeListener(_valueListenableChanged);
  }

  @override
  build(BuildContext context) {}

  @override
  String get debugLabel => 'useValueListenableChanged';

  @override
  Object? get debugValue => hook.valueListenable;
}