signals_hooks 0.2.0 copy "signals_hooks: ^0.2.0" to clipboard
signals_hooks: ^0.2.0 copied to clipboard

flutter_hooks bindings for signals

signals_hooks #

Helper library to make working with signals in flutter_hooks easier.

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:signals_hooks/signals_hooks.dart';

class Example extends HookWidget {
  const Example({super.key});

  @override
  Widget build(BuildContext context) {
    final count = useSignal(0);
    final doubleCount = useComputed(() => count.value * 2);
    useSignalEffect(() {
      debugPrint('count: $count, double: $doubleCount');
    });
    return Scaffold(
      body: Center(
        child: Text('Count: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count.value++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

All of the signals and effects created will get cleaned up when the widget gets unmounted.

Core #

useSignal #

How to create a new signal inside of a hook widget:

class Example extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final count = useSignal(0);
    return Text('Count: $count');
  }
}

The value will auto rebuild the widget when it changes.

useComputed #

How to create a new computed signal inside of a hook widget:

class Example extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final count = useSignal(0);
    final countStr = useComputed(() => count.toString());
    return Text('Count: $countStr');
  }
}

The value will auto rebuild the widget when it changes.

useSignalEffect #

How to create a new effect inside of a hook widget:

class Example extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final count = useSignal(0);
    useSignalEffect(() {
        print('count: $count');
    });
    return Text('Count: $count');
  }
}

useExistingSignal #

How to bind an existing signal inside of a hook widget:

class Example extends HookWidget {
  final Signal<int> count;

  const Example(this.count, {super.key});

  @override
  Widget build(BuildContext context) {
    final counter = useExistingSignal(count);
    return Text('Count: $counter');
  }
}

The value will auto rebuild the widget when it changes.

useSignalValue #

How to get the value of a signal directly:

class Example extends HookWidget {
  final Signal<int> count;

  const Example(this.count, {super.key});

  @override
  Widget build(BuildContext context) {
    final counter = useSignalValue(count);
    return Text('Count: $counter');
  }
}

The value will auto rebuild the widget when it changes.

Async #

useFutureSignal #

Creates a new FutureSignal and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final future = useFutureSignal(() => Future.delayed(const Duration(seconds: 1), () => 1));
    return future.value.map(
      data: (value) => Text('$value'),
      error: (error, stack) => Text('$error'),
      loading: () => const CircularProgressIndicator(),
    );
  }
}

useStreamSignal #

Creates a new StreamSignal and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final stream = useStreamSignal(() => Stream.periodic(const Duration(seconds: 1), (i) => i));
    return stream.value.map(
      data: (value) => Text('$value'),
      error: (error, stack) => Text('$error'),
      loading: () => const CircularProgressIndicator(),
    );
  }
}

useAsyncSignal #

Creates a new AsyncSignal and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final signal = useAsyncSignal<int>(AsyncState.loading());
    return signal.value.map(
      data: (value) => Text('$value'),
      error: (error, stack) => Text('$error'),
      loading: () => const CircularProgressIndicator(),
    );
  }
}

useAsyncComputed #

Creates a new FutureSignal from a computed async value and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final count = useSignal(0);
    final future = useAsyncComputed(() async {
      await Future.delayed(const Duration(seconds: 1));
      return count.value * 2;
    }, dependencies: [count]);
    return future.value.map(
      data: (value) => Text('$value'),
      error: (error, stack) => Text('$error'),
      loading: () => const CircularProgressIndicator(),
    );
  }
}

Collections #

useListSignal #

Creates a new ListSignal and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final list = useListSignal([1, 2, 3]);
    return Text('${list.value}');
  }
}

useSetSignal #

Creates a new SetSignal and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final set = useSetSignal({1, 2, 3});
    return Text('${set.value}');
  }
}

useMapSignal #

Creates a new MapSignal and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final map = useMapSignal({'a': 1, 'b': 2});
    return Text('${map.value}');
  }
}

Flutter #

useValueNotifierToSignal #

Creates a new Signal from a ValueNotifier and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final notifier = useValueNotifier(0);
    final signal = useValueNotifierToSignal(notifier);
    return Text('${signal.value}');
  }
}

useValueListenableToSignal #

Creates a new ReadonlySignal from a ValueListenable and subscribes to it.

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final notifier = useValueNotifier(0);
    final signal = useValueListenableToSignal(notifier);
    return Text('${signal.value}');
  }
}

Testing #

To test hooks that use signals you can use flutter_test and HookBuilder.

Here is an example of how to test useSignal:

import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:signals_hooks/signals_hooks.dart';

void main() {
  testWidgets('useSignal', (tester) async {
    late Signal<int> state;
    await tester.pumpWidget(
      HookBuilder(builder: (context) {
        state = useSignal(42);
        return GestureDetector(
          onTap: () => state.value++,
          child: Text('$state', textDirection: TextDirection.ltr),
        );
      }),
    );

    expect(state.value, 42);
    expect(find.text('42'), findsOneWidget);

    // Click text and wait
    await tester.tap(find.text('42'));
    await tester.pumpAndSettle();

    expect(state.value, 43);
    expect(find.text('43'), findsOneWidget);
  });
}

You can find more examples in the test folder.

8
likes
0
points
455
downloads

Publisher

verified publisherrodydavis.com

Weekly Downloads

flutter_hooks bindings for signals

Homepage
Repository (GitHub)
View/report issues

Documentation

Documentation

License

unknown (license)

Dependencies

flutter, flutter_hooks, signals_flutter

More

Packages that depend on signals_hooks