Skip to content

Commit 8507708

Browse files
author
Nguyễn Thái Học
committed
init
0 parents  commit 8507708

12 files changed

+542
-0
lines changed

.gitignore

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
12+
# IntelliJ related
13+
*.iml
14+
*.ipr
15+
*.iws
16+
.idea/
17+
18+
# The .vscode folder contains launch configuration and tasks you configure in
19+
# VS Code which you may wish to be included in version control, so this line
20+
# is commented out by default.
21+
#.vscode/
22+
23+
# Flutter/Dart/Pub related
24+
**/doc/api/
25+
.dart_tool/
26+
.flutter-plugins
27+
.flutter-plugins-dependencies
28+
.packages
29+
.pub-cache/
30+
.pub/
31+
build/
32+
33+
# Android related
34+
**/android/**/gradle-wrapper.jar
35+
**/android/.gradle
36+
**/android/captures/
37+
**/android/gradlew
38+
**/android/gradlew.bat
39+
**/android/local.properties
40+
**/android/**/GeneratedPluginRegistrant.java
41+
42+
# iOS/XCode related
43+
**/ios/**/*.mode1v3
44+
**/ios/**/*.mode2v3
45+
**/ios/**/*.moved-aside
46+
**/ios/**/*.pbxuser
47+
**/ios/**/*.perspectivev3
48+
**/ios/**/*sync/
49+
**/ios/**/.sconsign.dblite
50+
**/ios/**/.tags*
51+
**/ios/**/.vagrant/
52+
**/ios/**/DerivedData/
53+
**/ios/**/Icon?
54+
**/ios/**/Pods/
55+
**/ios/**/.symlinks/
56+
**/ios/**/profile
57+
**/ios/**/xcuserdata
58+
**/ios/.generated/
59+
**/ios/Flutter/App.framework
60+
**/ios/Flutter/Flutter.framework
61+
**/ios/Flutter/Flutter.podspec
62+
**/ios/Flutter/Generated.xcconfig
63+
**/ios/Flutter/app.flx
64+
**/ios/Flutter/app.zip
65+
**/ios/Flutter/flutter_assets/
66+
**/ios/Flutter/flutter_export_environment.sh
67+
**/ios/ServiceDefinitions.json
68+
**/ios/Runner/GeneratedPluginRegistrant.*
69+
70+
# Exceptions to above rules.
71+
!**/ios/**/default.mode1v3
72+
!**/ios/**/default.mode2v3
73+
!**/ios/**/default.pbxuser
74+
!**/ios/**/default.perspectivev3

.metadata

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: 022b333a089afb81c471ec43d1f1f4f26305d876
8+
channel: dev
9+
10+
project_type: package

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## [0.0.1] - TODO: Add release date.
2+
3+
* TODO: Describe initial release.

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO: Add your license here.

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# listenable_stream
2+
3+
listenable_stream
4+
5+
## Getting Started
6+
7+
This project is a starting point for a Dart
8+
[package](https://flutter.dev/developing-packages/),
9+
a library module containing code that can be shared easily across
10+
multiple Flutter or Dart projects.
11+
12+
For help getting started with Flutter, view our
13+
[online documentation](https://flutter.dev/docs), which offers tutorials,
14+
samples, guidance on mobile development, and a full API reference.

analysis_options.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include: package:pedantic/analysis_options.1.9.0.yaml
2+
linter:
3+
rules:
4+
- prefer_final_locals
5+
- prefer_relative_imports
6+
- public_member_api_docs

lib/listenable_stream.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
library listenable_stream;
2+
3+
export './src/listenable_to_stream.dart';
4+
export './src/value_listenable_to_value_stream.dart';

lib/src/listenable_to_stream.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import 'dart:async';
2+
3+
import 'package:flutter/foundation.dart';
4+
5+
/// Convert this [Listenable] to a [Stream].
6+
extension ListenableToStream<T extends Listenable> on T {
7+
/// Convert this [Listenable] to a [Stream].
8+
Stream<T> toStream() {
9+
StreamController<T> controller;
10+
VoidCallback listener;
11+
12+
controller = StreamController<T>(
13+
sync: true,
14+
onListen: () => addListener(listener = () => controller.add(this)),
15+
onCancel: () {
16+
try {
17+
removeListener(listener);
18+
listener = null;
19+
} catch (_) {}
20+
},
21+
);
22+
23+
return controller.stream;
24+
}
25+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import 'dart:async';
2+
3+
import 'package:flutter/foundation.dart';
4+
import 'package:rxdart/rxdart.dart';
5+
6+
import 'listenable_to_stream.dart';
7+
8+
/// Convert this [ValueListenable] to a [ValueStream].
9+
/// The returned [ValueStream] is a Single-Subscription [Stream].
10+
///
11+
/// If [replayValue] is true, the returned [ValueStream] will replay latest value when listening to it.
12+
/// Otherwise, it does not.
13+
extension ValueListenableToValueStream<T> on ValueListenable<T> {
14+
/// Convert this [ValueListenable] to a [ValueStream].
15+
/// The returned [ValueStream] is a Single-Subscription [Stream].
16+
///
17+
/// If [replayValue] is true, the returned [ValueStream] will replay latest value when listening to it.
18+
/// Otherwise, it does not.
19+
ValueStream<T> toValueStream({bool replayValue = false}) =>
20+
ValueListenableStream<T>(this, replayValue);
21+
}
22+
23+
/// A Single-Subscription Stream will emits data when [ValueListenable.value] changed.
24+
class ValueListenableStream<T> extends Stream<T> implements ValueStream<T> {
25+
final ValueListenable<T> _valueListenable;
26+
final bool _replayValue;
27+
Stream<T> _stream;
28+
29+
/// Construct a [ValueListenableStream] from [ValueListenable].
30+
ValueListenableStream(this._valueListenable, this._replayValue);
31+
32+
@override
33+
bool get isBroadcast => false;
34+
35+
@override
36+
ErrorAndStackTrace get errorAndStackTrace => null;
37+
38+
@override
39+
bool get hasError => false;
40+
41+
@override
42+
bool get hasValue => true;
43+
44+
@override
45+
T get value => _valueListenable.value;
46+
47+
@override
48+
StreamSubscription<T> listen(
49+
void Function(T) onData, {
50+
Function onError,
51+
void Function() onDone,
52+
bool cancelOnError,
53+
}) {
54+
if (_replayValue) {
55+
_stream ??= _valueListenable
56+
.toStream()
57+
.map((_) => _valueListenable.value)
58+
.shareBehavior(value);
59+
} else {
60+
_stream ??=
61+
_valueListenable.toStream().map((_) => _valueListenable.value);
62+
}
63+
64+
return _stream.listen(
65+
onData,
66+
onError: onError,
67+
onDone: onDone,
68+
cancelOnError: cancelOnError,
69+
);
70+
}
71+
}
72+
73+
extension _ShareValueExtension<T> on Stream<T> {
74+
Stream<T> shareBehavior(T seeded) {
75+
final controllers = <MultiStreamController<T>>[];
76+
StreamSubscription<T> subscription;
77+
78+
var latestValue = seeded;
79+
var cancel = false;
80+
var done = false;
81+
82+
final listenUpStream = () => listen(
83+
(event) {
84+
latestValue = event;
85+
controllers.forEach((c) => c.addSync(event));
86+
},
87+
onError: (e, StackTrace st) =>
88+
controllers.forEach((c) => c.addErrorSync(e, st)),
89+
onDone: () {
90+
done = true;
91+
subscription = null;
92+
93+
controllers.forEach((c) {
94+
c.onCancel = null;
95+
c.closeSync();
96+
});
97+
controllers.clear();
98+
},
99+
);
100+
101+
final onListen = (MultiStreamController<T> controller) {
102+
if (cancel) {
103+
return controller.closeSync();
104+
}
105+
controller.addSync(latestValue);
106+
if (done) {
107+
return controller.closeSync();
108+
}
109+
110+
final wasEmpty = controllers.isEmpty;
111+
controllers.add(controller);
112+
if (wasEmpty) {
113+
subscription = listenUpStream();
114+
}
115+
116+
controller.onCancel = () {
117+
controllers.remove(controller);
118+
if (controllers.isEmpty) {
119+
subscription?.cancel();
120+
subscription = null;
121+
cancel = true;
122+
}
123+
};
124+
};
125+
126+
return Stream.multi(onListen, isBroadcast: true);
127+
}
128+
}

0 commit comments

Comments
 (0)