How do I make a method that returns a stream<T> after modifying another stream<U> - flutter

I would like to make a helpfer method which takes a stream from a library (draw in this case) and changes the stream from Stream<UserContent> to Stream<Submission> and also filters the Submissions for duds.
So far I have this code but how do I return the stream now?
Stream<Submission> getSavedPosts({int limit = 20}) async* {
yield await _me!.saved(limit: limit).map((UserContent userContent) async {
try {
Submission sub = await _populateUserContent(userContent);
if (_filterPost(sub)) {
return sub;
}
} catch (err) {
print(err);
}
}); // The type 'Stream<Stream<Future<Submission?>>>' implied by the 'yield' expression must be assignable to 'Stream<Submission>'.
}

you should use asyncMap method on the stream rather than map.
Stream<ClassA> getOldStream() {}
Stream<ClassA> getNewStream() {
return getOldStream().asyncMap((event) async {
return event;
});
}
Note: yield and async* should be used in a generator function; mapping doesn't need a generator function

Related

How to invoke a listener from another listener?

I'm using in_app_purchase package and I need to convert/map listener which is listening for List<PurchaseDetails> to another listener as shown below:
class Foo {
Foo() {
InAppPurchase.instance.purchaseStream.listen(_listener);
}
void _listener(List<PurchaseDetails> list) {
// How to pass these ids to `addListener()`
final List<String> ids = list.map((e) => e.productID).toList();
}
void addListener(void Function(List<String>) f) {}
}
This is how I want to use my listener
void main() {
Foo().addListener((List<String> ids) {});
}
Despite what your code comment says, I think what you're really asking for is for the internal _listener to invoke the callback that was previously passed as an argument to addListener (and not for _listener to call addListener directly, which it could just do directly).
Just have addListener save the callback to a member variable and let your internal listener invoke that:
class Foo {
void Function(List<String>)? _listener;
Foo() {
InAppPurchase.instance.purchaseStream.listen(_internalListener);
}
void _internalListener(List<PurchaseDetails> list) {
var listener = _listener;
if (listener == null) {
return;
}
final List<String> ids = list.map((e) => e.productID).toList();
listener(ids);
}
void addListener(void Function(List<String>) f) => _listener = f;
}
If you want callers to be able to call addListener multiple times to register multiple callbacks, you would need to store them in a collection (and provide a mechanism to unregister callbacks):
class Foo {
final _listenerMap = <Object, void Function(List<String>)>{};
Foo() {
InAppPurchase.instance.purchaseStream.listen(_internalListener);
}
void _internalListener(List<PurchaseDetails> list) {
if (_listenerMap.isEmpty) {
return;
}
final List<String> ids = list.map((e) => e.productID).toList();
for (var listener in _listenerMap.values) {
listener(ids);
}
}
Object addListener(void Function(List<String>) f) {
var token = Object();
_listenerMap[token] = f;
return token;
}
void removeListener(Object token) {
_listenerMap.remove(token);
}
}

Dart destruction workround?

I'm trying to add some file logging to code invoked by the Workmanager package. The callback has the general structure of the callback routine starting another async task, viz:
Future<void> callbackDispatcher() async {
.
.
.
Workmanager.executeTask((task, inputData) async {
.
.
.
return Future.value(true);
});
.
.
.
}
At the conclusion of whichever of these two routines finishes last, iiuc, I need to do:
iosink.close();
await iosink.done;
before I can exit/return.
As dart doesn't have destructors, I'm not sure how best to accomplish this. I've written the following which, while it seems to work, is rather fragile and somewhat hokey:
class LogWriter2 {
static final File _callbackLogFile = getLocalFile( "callbackLog.out" );
static IOSink _writer;
static int _instanceCount = 0;
LogWriter2._privateConstructor() {
//_writer = _callbackLogFile.openWrite(mode: FileMode.append);
}
static final LogWriter2 _instance = LogWriter2._privateConstructor();
factory LogWriter2() {
if (_instanceCount <= 0) {
debugPrint("${dt.now} constr: opening writer...");
_writer = _callbackLogFile.openWrite(mode: FileMode.append);
}
_instanceCount++;
debugPrint("${dt.now} constr: instanceCount++ now $_instanceCount");
return _instance;
}
writeln(String string) {
var dtn = dt.now;
_writer.writeln("${dtn} $string");
debugPrint("${dtn} lw2.writeln: $string");
}
close() async {
_instanceCount--;
debugPrint("${dt.now} close: instanceCount-- now $_instanceCount");
if (_instanceCount <= 0) {
debugPrint("${dt.now} close: closing writer...");
await _writer.flush(); // close() does not guarantee flush!
_writer.close();
await _writer.done;
debugPrint("${dt.now} close: close complete");
}
}
}
// nb. Don't use this code as-is as it has a race condition that is fixed
// with a mutex (omitted here). Message me if you need a working version.
Can someone suggest a better way of achieving this?

Dart - Pass Interface as parameter

I'm looking for a similar solution for passing interface as paremeter in a function like Kotlin has:
test(object: Handler {
override fun onComplete() {
}
})
The only thing I find for Dart is using implements on the class, which is fine, but not what I need.
My approach so far has been making abstract class:
abstract class AuthListener{
void onChange(AuthState authState);
}
Triggering of listener:
isSignedInListener(AuthListener authListener){
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
return authListener.onChange(AuthState.SIGNED_OUT);
} else {
return authListener.onChange(AuthState.SIGNED_IN);
}
});
}
And now I need to listen for the response, which is where I need some help:
You can create a function and use it as a callback (pass to methods etc.).
Function can be placed into variable:
Function(AuthState authState) callback;
or as argument:
void isSignedInListener(Function(AuthState authState) callback) {}
auth.isSignedInListener((authState) {
// do something here
});

RxDart. Listener of BehaviorSubject does not get value after subject.add(value)

I have two classes: CurrencyRepo and CurrencyFetcher. From CurrencyFetcher I try to listen to the behaviorSubject that is in the repo. But when I add values in the behaviorSubject inside the repo, CurrencyFetcher does not get these values. What am I doing wrong?
class CurrencyFetcher implements CurrencyFetcherService {
final CurrencyRepo _currencyRepo;
StreamSubscription currencySubscription;
CurrencyFetcher(this._currencyRepo, this._preferencesService) {
_subscribeToCurrencies();
}
void _subscribeToCurrencies() {
currencySubscription = _currencyRepo
.getCurrenciesStream()
.listen((currencies) => _handleApiCurrencies);
}
Future<void> _handleApiCurrencies(List<ApiCurrency> apiCurrencies) async {
// implemetation
}
}
class CurrencyRepo {
final CurrencyApi _currencyApi;
final BehaviorSubject<List<ApiCurrency>> _currencySubject = BehaviorSubject.seeded([]);
Stream<List<ApiCurrency>> getCurrenciesStream() {
_updateCurrencies();
return _currencySubject.stream;
}
CurrencyRepo(this._currencyApi);
void _updateCurrencies() {
_currencyApi.getCurrencies().then((currencies) {
_currencySubject.add(currencies);
});
}
}
I have checked that the values are added to the stream after the CurrencyFetcher starts to listen. And I have checked that in the moment, when I add new value to the stream, it has a listener. The first time using RxDart, may someone help? :)
Inside the function _subscribeToCurrencies() try to write
_currencyRepo.currenciesStream.listen((currencies) {
_handleApiCurrencies(currencies);
});
instead of
_currencyRepo.currenciesStream.listen(_handleApiCurrencies);

compose with vertx for sequential code

I have two operations step_1() and step_2() and want to execute step_2() AFTER step_1().
With normal java this would be:
step_1();
step_2();
With vertx I have to use vertx-compose(). Am I right?
According to https://groups.google.com/forum/#!topic/vertx/FuvlPLpoGOA, I dont need Futures for sequential code.
"If you want to do each request sequencially you dont need futures."
So how can I do that without using futures?
I dont know, if this matters: My Vertx from which this code is executed is a "Worker"-Verticle.
#Override
public void start(Future<Void> fut) throws IOException {
Future<Void> step_1 = Future.future();
step_1.compose(res -> {
// If the future succeeded
Future<Void> step_2 = step_1();
step_2.compose(res2 -> {
step_2();
}, Future.future().setHandler(handler -> {
// If the future failed
}));
//I dont need that
}, Future.future().setHandler(handler -> {
// If the future failed
}));
}
public void step_1(){
..
}
public void step_2(){
..
}
Is this the right and shortest (!) way?
Below is an example of chaining of Future, I have made the example very trivial nonetheless it showcases the concept.
#RunWith(VertxUnitRunner.class)
public class Chaining {
private Vertx vertx = Vertx.vertx();
#Test
public void futures_chaining(TestContext context) throws Exception {
Async async = context.async();
firstOperation()
.compose((outcome) -> {
System.out.println(outcome);
return secondOperation();
})
.compose(outcome -> {
System.out.println(outcome);
/*
For stopping unit test we are returning this future
for production use-case this would be Future.succeededFuture
or Future.failedFuture depending on your method outcomes
*/
return Future.future(handle -> async.complete());
});
}
private Future<String> firstOperation() {
Future<String> future = Future.future();
vertx.setTimer(1000, delay -> future.complete("First Operation Complete"));
return future;
}
private Future<String> secondOperation() {
Future<String> future = Future.future();
vertx.setTimer(1000, delay -> future.complete("Second Operation Complete"));
return future;
}
}
"If you want to do each request sequencially you dont need futures."
No, it's not. In asynchronous frameworks like Vert.x, input/output operations are non-blocking. It means, that if you call few asynchronous operations, they'll start working simultaneously. And if you want to do few requests sequentially, then you should use futures or callbacks to execute new request only after previous one finished successfully.
Check this code with futures, newer version with RxJava 2 and article about project.
#Override
public Future<Optional<Todo>> getCertain(String todoID) {
Future<Optional<Todo>> result = Future.future();
redis.hget(Constants.REDIS_TODO_KEY, todoID, res -> {
if (res.succeeded()) {
result.complete(Optional.ofNullable(
res.result() == null ? null : new Todo(res.result())));
} else
result.fail(res.cause());
});
return result;
}
#Override
public Future<Todo> update(String todoId, Todo newTodo) {
return this.getCertain(todoId).compose(old -> {
if (old.isPresent()) {
Todo fnTodo = old.get().merge(newTodo);
return this.insert(fnTodo)
.map(r -> r ? fnTodo : null);
} else {
return Future.succeededFuture();
}
});
}
RxJava exists specifically to compose async events: http://vertx.io/docs/vertx-rx/java/
Assuming both step_1() and step_1() aren't designed to return results (i.e. they effectively return void) then you could change them to return Observable or Single and chain them together similar to this:
step_1().doOnSuccess(this::step_2()).subscribe(/* control resumes here */);
RxJava (or rather, reactive programming in general) takes a little bit to wrap your head around it, but I would strongly recommend using it if you're planning to chain together async operations.
Pass step_2 as argument to step_1
#Override
public void start(Future<Void> fut) throws IOException {
step_1(step_2);
}
private void step_1(Runnable function){
someAsynccall("some-arg", response -> {
function.run();
}).end();
}
private void step_2(){
// do something
}