Skip to content

Commit 9fb1610

Browse files
authored
Merge pull request #9 from rodydavis/value-signals
Creating signals for Map, Set, Iterable, List
2 parents 37f4ce8 + b7cc574 commit 9fb1610

38 files changed

+1751
-393
lines changed

README.md

Lines changed: 5 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55

66
Complete dart port of [Preact signals](https://preactjs.com/blog/introducing-signals/) and takes full advantage of [signal boosting](https://preactjs.com/blog/signal-boosting/).
77

8-
| Package | Pub |
9-
|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
10-
| `preact_signals` | [![preact_signals](https://img.shields.io/pub/v/preact_signals.svg)](https://pub.dev/packages/preact_signals) |
11-
| `flutter_preact_signals` | [![flutter_preact_signals](https://img.shields.io/pub/v/flutter_preact_signals.svg)](https://pub.dev/packages/flutter_preact_signals) |
8+
| Package | Pub |
9+
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
10+
| [`preact_signals`](packages/preact_signals) | [![preact_signals](https://img.shields.io/pub/v/preact_signals.svg)](https://pub.dev/packages/preact_signals) |
11+
| [`flutter_preact_signals`](packages/flutter_preact_signals) | [![flutter_preact_signals](https://img.shields.io/pub/v/flutter_preact_signals.svg)](https://pub.dev/packages/flutter_preact_signals) |
12+
| [`preact_signals_devtools_extension`](packages/preact_signals_devtools_extension) | |
1213

1314
## Guide / API
1415

@@ -193,223 +194,3 @@ batch(() {
193194
});
194195
// Now the callback completed and we'll trigger the effect.
195196
```
196-
197-
## Extensions
198-
199-
### `Future`
200-
201-
Futures can be converted to signals by either a method `signalFromFuture` or as an extension method on a `Future`:
202-
203-
```dart
204-
import 'package:preact_signals/preact_signals.dart';
205-
206-
final future = Future(() => 1);
207-
final signal = future.toSignal(); // or signalFromFuture(future)
208-
```
209-
210-
> This will return a sealed union based on `SignalState` that will return `SignalValue` for success, `SignalError` for errors (and `SignalTimeout` on optional timeout), and `SignalLoading`.
211-
212-
### `Stream`
213-
214-
Futures can be converted to signals by either a method `signalFromFuture` or as an extension method on a `Future`:
215-
216-
```dart
217-
import 'package:preact_signals/preact_signals.dart';
218-
219-
Stream<int> createStream() async* {
220-
yield 1;
221-
yield 2;
222-
yield 3;
223-
}
224-
final stream = createStream();
225-
final signal = stream.toSignal(); // or signalFromStream(stream)
226-
```
227-
228-
> This will return a sealed union based on `SignalState` that will return `SignalValue` for success, `SignalError` for errors (and `SignalTimeout` on optional timeout), and `SignalLoading`.
229-
230-
### `ValueListenable`
231-
232-
To create a `ReadonlySignal` from `ValueListenable`:
233-
234-
```dart
235-
import 'package:flutter_preact_signals/flutter_preact_signals.dart';
236-
import 'package:flutter/material.dart';
237-
238-
final ValueListenable listenable = ValueNotifier(10);
239-
final signal = listenable.toSignal(); // or signalFromValueListenable(listenable)
240-
```
241-
242-
### `ValueNotifier`
243-
244-
To create a `MutableSignal` from `ValueNotifier`:
245-
246-
```dart
247-
import 'package:flutter_preact_signals/flutter_preact_signals.dart';
248-
import 'package:flutter/material.dart';
249-
250-
final notifier = ValueNotifier(10);
251-
final signal = notifier.toSignal(); // or signalFromValueNotifier(notifier)
252-
```
253-
254-
255-
### `BuildContext` and Widgets
256-
257-
`StatefulWidget` and `StatelessWidget` widgets can both react to changes on a signal by adding a `watch`` command:
258-
259-
```dart
260-
import 'package:flutter_preact_signals/flutter_preact_signals.dart';
261-
262-
Text(
263-
'${counter.watch(context)}',
264-
style: Theme.of(context).textTheme.headlineMedium!,
265-
)
266-
```
267-
268-
or with `watchSignal`:
269-
270-
```dart
271-
import 'package:flutter_preact_signals/flutter_preact_signals.dart';
272-
273-
Text(
274-
'${watchSignal(context, counter)}',
275-
style: Theme.of(context).textTheme.headlineMedium!,
276-
)
277-
```
278-
279-
This will mark the widget as dirty and rebuild on next frame. This will all be optimized for batched effects and multiple signals being updated at the same time.
280-
281-
## Examples
282-
283-
### Dart
284-
285-
```dart
286-
import 'package:preact_signals/preact_signals.dart';
287-
288-
// Create signals
289-
final count = signal(0);
290-
final multiplier = signal(2);
291-
292-
// Creating a computed value
293-
final multipliedCount = computed(() {
294-
return count.value * multiplier.value;
295-
});
296-
297-
effect(() {
298-
print('Effect called: Count is ${count.value} and multiplier is ${multiplier.value}');
299-
});
300-
301-
expect(multipliedCount.value, 0);
302-
303-
count.value = 1;
304-
expect(multipliedCount.value, 2);
305-
306-
multiplier.value = 3;
307-
expect(multipliedCount.value, 3);
308-
```
309-
310-
This should print:
311-
312-
```
313-
Effect called: Count is 0 and multiplier is 2
314-
Effect called: Count is 1 and multiplier is 2
315-
Effect called: Count is 1 and multiplier is 3
316-
```
317-
318-
See [preact_signals/example](packages/preact_signals/example/web/main.dart) for a full example.
319-
320-
### Flutter
321-
322-
Reacting to signal changes can be done with one extension method: `watch(context)`:
323-
324-
```dart
325-
import 'package:flutter/material.dart';
326-
import 'package:flutter_preact_signals/flutter_preact_signals.dart';
327-
328-
void main() {
329-
runApp(const MyApp());
330-
}
331-
332-
final brightness = signal(Brightness.light);
333-
final counter = signal(0);
334-
335-
class MyApp extends StatelessWidget {
336-
const MyApp({super.key});
337-
338-
@override
339-
Widget build(BuildContext context) {
340-
return MaterialApp(
341-
title: 'Flutter Demo',
342-
debugShowCheckedModeBanner: false,
343-
theme: ThemeData(
344-
colorScheme: ColorScheme.fromSeed(
345-
seedColor: Colors.deepPurple,
346-
brightness: Brightness.light,
347-
),
348-
brightness: Brightness.light,
349-
useMaterial3: true,
350-
),
351-
darkTheme: ThemeData(
352-
colorScheme: ColorScheme.fromSeed(
353-
seedColor: Colors.deepPurple,
354-
brightness: Brightness.dark,
355-
),
356-
brightness: Brightness.dark,
357-
useMaterial3: true,
358-
),
359-
themeMode: brightness.watch(context) == Brightness.dark
360-
? ThemeMode.dark
361-
: ThemeMode.light,
362-
home: const MyHomePage(title: 'Flutter Demo Home Page'),
363-
);
364-
}
365-
}
366-
367-
class MyHomePage extends StatelessWidget {
368-
const MyHomePage({super.key, required this.title});
369-
370-
final String title;
371-
372-
void _incrementCounter() {
373-
counter.value++;
374-
}
375-
376-
@override
377-
Widget build(BuildContext context) {
378-
return Scaffold(
379-
appBar: AppBar(
380-
title: Text(title),
381-
actions: [
382-
Builder(builder: (context) {
383-
final isDark = brightness.watch(context) == Brightness.dark;
384-
return IconButton(
385-
onPressed: () {
386-
brightness.value = isDark ? Brightness.light : Brightness.dark;
387-
},
388-
icon: Icon(isDark ? Icons.light_mode : Icons.dark_mode),
389-
);
390-
}),
391-
],
392-
),
393-
body: Center(
394-
child: Column(
395-
mainAxisAlignment: MainAxisAlignment.center,
396-
children: <Widget>[
397-
const Text(
398-
'You have pushed the button this many times:',
399-
),
400-
Text(
401-
'${counter.watch(context)}',
402-
style: Theme.of(context).textTheme.headlineMedium!,
403-
),
404-
],
405-
),
406-
),
407-
floatingActionButton: FloatingActionButton(
408-
onPressed: _incrementCounter,
409-
tooltip: 'Increment',
410-
child: const Icon(Icons.add),
411-
),
412-
);
413-
}
414-
}
415-
```

packages/flutter_preact_signals/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 0.2.1
2+
3+
- Fixing `watchSignal` so it works in StatefulWidgets and inside rebuilds and subscriber list will remain stable
4+
- Updating example to show non global signal
5+
- Adding extension methods on `ReadonlySignal`: `stream`, `listenable`
6+
- Adding extension methods on `MutableSignal`: `notifier`, `readonly`
7+
- Updating `preact_signals` to 0.3.0
8+
19
## 0.2.0
210

311
- Updating `preact_signals` to 0.2.1
@@ -14,7 +22,7 @@
1422
## 0.0.2
1523

1624
- Fixing recursive call after refactor
17-
-
25+
1826
## 0.0.1
1927

2028
- Adding extensions for `ValueNotifier`, `ValueListenable` and `BuildContext`

packages/flutter_preact_signals/README.md

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
[![flutter_preact_signals](https://img.shields.io/pub/v/flutter_preact_signals.svg)](https://pub.dev/packages/flutter_preact_signals)
2+
[![Tests](https://github.com/rodydavis/preact_signals.dart/actions/workflows/tests.yml/badge.svg)](https://github.com/rodydavis/preact_signals.dart/actions/workflows/tests.yml)
3+
[![Publish Example](https://github.com/rodydavis/preact_signals.dart/actions/workflows/main.yml/badge.svg)](https://github.com/rodydavis/preact_signals.dart/actions/workflows/main.yml)
24

35
# Preact Signals (Flutter)
46

57
Complete dart port of [Preact signals](https://preactjs.com/blog/introducing-signals/) and takes full advantage of [signal boosting](https://preactjs.com/blog/signal-boosting/).
68

9+
This package is a set of Flutter extensions for the [`preact_signals`](https://pub.dev/packages/preact_signals) package.
10+
711
## Guide / API
812

913
The signals library exposes four functions which are the building blocks to model any business logic you can think of.
@@ -188,6 +192,87 @@ batch(() {
188192
// Now the callback completed and we'll trigger the effect.
189193
```
190194

195+
## Value Signals
196+
197+
To provide a more connivent API for common value types, various wrappers are created that implement all the public methods and notify on mutations will keeping the same object reference.
198+
199+
### List
200+
201+
List signals can be created by extension, method or class:
202+
203+
```dart
204+
import 'package:preact_signals/preact_signals.dart';
205+
206+
final list = ['a', 'b', 'c'];
207+
208+
final s1 = list.toSignal();
209+
final s2 = listSignal(list);
210+
final s3 = ListSignal(list);
211+
```
212+
213+
Mutations can also be done directly:
214+
215+
```dart
216+
final s = listSignal([1, 2, 3]);
217+
s[0] = -1;
218+
print(s.length); // 3
219+
s.addAll([4, 5, 6]);
220+
s.first = 1;
221+
```
222+
223+
### Set
224+
225+
Set signals can be created by extension, method or class:
226+
227+
```dart
228+
import 'package:preact_signals/preact_signals.dart';
229+
230+
final set = {'a', 'b', 'c'};
231+
232+
final s1 = set.toSignal();
233+
234+
final s2 = setSignal(set);
235+
236+
final s3 = SetSignal(set);
237+
```
238+
239+
### Map
240+
241+
Map signals can be created by extension, method or class:
242+
243+
```dart
244+
import 'package:preact_signals/preact_signals.dart';
245+
246+
final map = {'a': 1, 'b': 2, 'c': 3};
247+
248+
final s1 = map.toSignal();
249+
250+
final s2 = mapSignal(map);
251+
252+
final s3 = MapSignal(map);
253+
```
254+
255+
### Iterable
256+
257+
Iterable signals can be created by extension, method or class:
258+
259+
```dart
260+
import 'package:preact_signals/preact_signals.dart';
261+
262+
final iterable = () sync* {
263+
yield '1';
264+
yield '2';
265+
yield '3';
266+
return '4';
267+
};
268+
269+
final s1 = iterable.toSignal();
270+
271+
final s2 = iterableSignal(iterable);
272+
273+
final s3 = IterableSignal(iterable);
274+
```
275+
191276
## Extensions
192277

193278
### `Future`
@@ -245,7 +330,6 @@ final notifier = ValueNotifier(10);
245330
final signal = notifier.toSignal(); // or signalFromValueNotifier(notifier)
246331
```
247332

248-
249333
### `BuildContext` and Widgets
250334

251335
`StatefulWidget` and `StatelessWidget` widgets can both react to changes on a signal by adding a `watch`` command:
@@ -367,4 +451,8 @@ class MyHomePage extends StatelessWidget {
367451
);
368452
}
369453
}
370-
```
454+
```
455+
456+
## DevTools
457+
458+
There is an early version of a devtools extension included with the [preact_signals] package.

0 commit comments

Comments
 (0)