1

I am trying to transform a stream from a database with the database data model to the domain data model.

I am quite confused bringing the different pieces of information together. While in the StreamTransformer examples that I have found, the stream is always a single object an not a list, my result from the example streaming ObjectBox data, returns Stream<List<PropertyObjectBox>>

And then it appears as if a piece of the puzzle is missing, how to come from Stream to StreamController.

So how what do I have to change at the following code?

/// <PropertyObjectBox> data model of Property in ObjectBox database
/// <PropertyModel> data model in data layer (not really needed, I know)
/// <Property> data model in domain layer


@override
// -->> should this return Stream<List<Property>?> or Stream<Property>?
// -->> or something else to comply with handleError?
Stream<List<Property>?> streamOnDeviceProperties() {
  Stream<List<PropertyObjectBox>> propObStream = objectbox.propertyBox.query()
      .watch(triggerImmediately: true).map((query) => query
  // Watching the query produces a Stream<Query<Property>>
  // To get the actual data inside a List<Property>, we need to call find() on the query
      .find());

//-->> again, PropertyObjectBox or List<PropertyObjectBox>?
  var streamTransformer = StreamTransformer<PropertyObjectBox, dynamic>.fromHandlers(
    handleData: (PropertyObjectBox data, EventSink sink) {
      final propertyModel = PropertyModel.fromObjectbox(data);
      final Property property = propertyModel.toDomain();
      sink.add(property);
    },
    handleError: (Object error, StackTrace stacktrace, EventSink sink) {
      sink.addError('Something went wrong: $error');
    },
    handleDone: (EventSink sink) => sink.close(),
  );
//-->> next line causes error 'the getter 'stream' isn't defined for the type 'Stream<List<PropertyObjectBox>>'  
  var controllerStream = propObStream.stream.transform(streamTransformer);
w461
  • 2,168
  • 4
  • 14
  • 40
  • so you want to convert `Stream>` into what? why do you think you need `StreamController`? – pskink Dec 11 '22 at 17:54
  • Do you want to Stream a single `property` or a list of `properties` – Georgina Dec 11 '22 at 20:31
  • I want to stream all properties in the database including every added one. So I assume I want to stream single properties – w461 Dec 11 '22 at 20:34
  • @pskink I had to change my initial headline, so this question came up. Not sure if I need a StreamController, I am just trying to put the different answers around this topic together. And the pieces of the puzzle to create the new stream were using the StreamController – w461 Dec 11 '22 at 20:38

1 Answers1

2

This should do the trick: Updated

@override
Stream<List<Property>?> streamOnDeviceProperties() {

  Stream<List<PropertyObjectBox>?> propObStream = objectbox.propertyBox.query()
      .watch(triggerImmediately: true).map((query) => query.find());

// List<PropertyObjectBox>?
  StreamTransformer<List<PropertyObjectBox>?,List<Property>?> streamTransformer = StreamTransformer<List<PropertyObjectBox>?,List<Property>?>.fromHandlers(
    handleData: (List<PropertyObjectBox>? data, EventSink sink) {
      var newList = data!.map((value) {
        final propertyModel = PropertyModel.fromObjectbox(value);
        final Property property = propertyModel.toDomain();
        return property;
      }).toList();
      sink.add(newList);
    },
    handleError: (Object error, StackTrace stacktrace, EventSink sink) {
      sink.addError('Something went wrong: $error');
    },
    handleDone: (EventSink sink) => sink.close(),
  );
  
 // if you need a Controller although I don't know why
  Stream<List<Property>?> newStream = propObStream.transform(streamTransformer);
  
  final StreamController<List<Property>?> streamController = StreamController<List<Property>?>(
    onPause: () => print('Paused'),
    onResume: () => print('Resumed'),
    onCancel: () => print('Cancelled'),
    onListen: () => print('Listens'),
  );
  streamController.addStream(newStream);
//********************************
  
  return streamTransformer.bind(propObStream);
}

Access the stream like this:

Stream<List<Property>?> _mystream = streamOnDeviceProperties();
Georgina
  • 764
  • 2
  • 13
  • So if I have a database with several properties (such as a phone book has several names), do I return a stream of a list of properties or a stream of properties. My feeling says a list of properties, however, the return type of the watch on the objectbox query is a stream of a list – w461 Dec 11 '22 at 20:47
  • stil causes an error: initially `A value of type 'Stream' can't be returned from the method 'streamOnDeviceProperties' because it has a return type of 'Stream?>'.`, after making the return type nullable `A value of type 'Stream' can't be returned from the method 'streamOnDeviceProperties' because it has a return type of 'Stream?>?'` – w461 Dec 11 '22 at 20:54
  • Let me check the code – Georgina Dec 11 '22 at 20:55
  • I guess this relates to my question whether the return type has to be dynamic to cope for `handleError` – w461 Dec 11 '22 at 20:57
  • @w461 Could you try making the return type dynamic – Georgina Dec 11 '22 at 21:06
  • already on to this. However, sounds like small change, with dependency injection, layers and blocs I am currently doing quite a few changes – w461 Dec 11 '22 at 21:08
  • @w461 Check new answer – Georgina Dec 11 '22 at 21:19
  • stupid question (heaven't done so much with streams yet), with a StreamBuilder and `itemCount: snapshot.data.documents.length,` I receive `NoSuchMethodError: Class 'List' has no instance getter 'documents'.`. What should I put behind `data`, I can't just put the type there? – w461 Dec 11 '22 at 21:27
  • @w461 Just do this `snapshot.data.length`. Meanwhile is my answer working? if so please tick it as the correct answer – Georgina Dec 11 '22 at 21:28
  • Yes, seems to work. Haven't yet gotten the real result but `print ('>>>> ${snapshot.data}');` shows 4 properties. Thank you so much! – w461 Dec 11 '22 at 21:35