-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
watch.dart
180 lines (166 loc) · 6.57 KB
/
watch.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
part of katana_scoped.value;
/// Provides an extended method for [Ref] to monitor [ChangeNotifier].
///
/// [ChangeNotifier]の監視を行うための[Ref]用の拡張メソッドを提供します。
extension RefWatchExtensions on Ref {
/// You can monitor it by passing a [callback] that returns a [ChangeNotifier].
///
/// When [ChangeNotifier.notifyListeners] is executed, the update is notified and the associated widget is redrawn.
///
/// If [keys] is different from the previous value, [callback] is executed again and the new [ChangeNotifier] is saved.
///
/// If [name] is specified, it is saved as a separate type. If [keys] is changed, the previous state is discarded, but if [name] is changed, it is kept as a separate state.
///
/// If [disposal] is set to `false`, when [ScopedValue] is destroyed, the content [T] is not destroyed.
///
/// If you want [ScopedValue] to be automatically disposed of when it is no longer referenced by any widget, set [autoDisposeWhenUnreferenced] to `true`.
///
/// [ChangeNotifier]を返す[callback]を渡すとそれを監視することができます。
///
/// [ChangeNotifier.notifyListeners]が実行されると更新が通知され、関連するウィジェットが再描画されます。
///
/// [keys]が前の値と違う場合再度[callback]が実行され、新しい[ChangeNotifier]が保存されます。
///
/// [name]を指定すると別のタイプとして保存されます。[keys]を変えた場合は以前の状態は破棄されますが、[name]を変えた場合は別々の状態として保持されます。
///
/// [disposal]を`false`にすると[ScopedValue]破棄時、中身の[T]は破棄されません。
///
/// [ScopedValue]がどのウィジェットにも参照されなくなったときに自動的に破棄させたい場合は[autoDisposeWhenUnreferenced]を`true`にしてください。
T watch<T extends Listenable?>(
T Function(Ref ref) callback, {
List<Object> keys = const [],
String? name,
bool disposal = true,
bool autoDisposeWhenUnreferenced = false,
}) {
return getScopedValue<T, _WatchValue<T>>(
(ref) => _WatchValue<T>(
callback: callback,
keys: keys,
ref: ref,
disposal: disposal,
autoDisposeWhenUnreferenced: autoDisposeWhenUnreferenced,
),
listen: true,
name: name,
);
}
}
/// Provides an extended method for [RefHasPage] to monitor [ChangeNotifier].
///
/// [ChangeNotifier]の監視を行うための[RefHasPage]用の拡張メソッドを提供します。
extension RefHasPageWatchExtensions on RefHasPage {
/// You can monitor it by passing a [callback] that returns a [ChangeNotifier].
///
/// When [ChangeNotifier.notifyListeners] is executed, the update is notified and the associated widget is redrawn.
///
/// If [keys] is different from the previous value, [callback] is executed again and the new [ChangeNotifier] is saved.
///
/// If [name] is specified, it is saved as a separate type. If [keys] is changed, the previous state is discarded, but if [name] is changed, it is kept as a separate state.
///
/// If [disposal] is set to `false`, when [ScopedValue] is destroyed, the content [T] is not destroyed.
///
/// If you want [ScopedValue] to be automatically disposed of when it is no longer referenced by any widget, set [autoDisposeWhenUnreferenced] to `true`.
///
/// [ChangeNotifier]を返す[callback]を渡すとそれを監視することができます。
///
/// [ChangeNotifier.notifyListeners]が実行されると更新が通知され、関連するウィジェットが再描画されます。
///
/// [keys]が前の値と違う場合再度[callback]が実行され、新しい[ChangeNotifier]が保存されます。
///
/// [name]を指定すると別のタイプとして保存されます。[keys]を変えた場合は以前の状態は破棄されますが、[name]を変えた場合は別々の状態として保持されます。
///
/// [disposal]を`false`にすると[ScopedValue]破棄時、中身の[T]は破棄されません。
///
/// [ScopedValue]がどのウィジェットにも参照されなくなったときに自動的に破棄させたい場合は[autoDisposeWhenUnreferenced]を`true`にしてください。
T watch<T extends Listenable?>(
T Function(Ref ref) callback, {
List<Object> keys = const [],
String? name,
bool disposal = true,
bool autoDisposeWhenUnreferenced = false,
}) {
return page.watch<T>(
callback,
keys: keys,
name: name,
disposal: disposal,
autoDisposeWhenUnreferenced: autoDisposeWhenUnreferenced,
);
}
}
@immutable
class _WatchValue<T> extends ScopedValue<T> {
const _WatchValue({
required this.callback,
required this.keys,
required Ref ref,
this.disposal = true,
this.autoDisposeWhenUnreferenced = false,
}) : super(ref: ref);
final T Function(Ref ref) callback;
final List<Object> keys;
final bool disposal;
final bool autoDisposeWhenUnreferenced;
@override
ScopedValueState<T, ScopedValue<T>> createState() => _WatchValueState<T>();
}
class _WatchValueState<T> extends ScopedValueState<T, _WatchValue<T>> {
_WatchValueState();
late T _value;
@override
bool get autoDisposeWhenUnreferenced => value.autoDisposeWhenUnreferenced;
@override
void initValue() {
super.initValue();
_value = value.callback(ref);
final val = _value;
if (val is Listenable) {
val.addListener(_handledOnUpdate);
}
}
void _handledOnUpdate() {
setState(() {});
}
@override
void didUpdateValue(_WatchValue<T> oldValue) {
super.didUpdateValue(oldValue);
if (!equalsKeys(value.keys, oldValue.keys)) {
final oldVal = _value;
if (oldVal is Listenable) {
oldVal.removeListener(_handledOnUpdate);
}
_value = value.callback(ref);
final newVal = _value;
if (newVal is Listenable) {
newVal.addListener(_handledOnUpdate);
}
}
}
@override
void didUpdateDescendant() {
super.didUpdateDescendant();
final oldVal = _value;
if (oldVal is Listenable) {
oldVal.removeListener(_handledOnUpdate);
}
_value = value.callback(ref);
final newVal = _value;
if (newVal is Listenable) {
newVal.addListener(_handledOnUpdate);
}
}
@override
void dispose() {
super.dispose();
final val = _value;
if (val is Listenable) {
val.removeListener(_handledOnUpdate);
if (value.disposal && val is ChangeNotifier) {
val.dispose();
}
}
}
@override
T build() => _value;
}