Home > Back-end >  Is there any way to use Streamcontroller in generic map?
Is there any way to use Streamcontroller in generic map?

Time:03-02

I am trying to make a state broadcaster that broadcasts some value to listeners via single path. And wanna use generic to reduce duplicated logic.

The following is what I am trying.

import 'dart:async';

class StateBroadcaster {
  final _sinkMap = <Type, StreamController<dynamic>>{
    StudyTimerStatus: StreamController<StudyTimerStatus?>.broadcast(),
  };

  void notify<T>(T value) => _sinkMap[T]?.add(value);

  StreamSubscription<T> listen<T>(void Function(T) onData) {
    return (_sinkMap[T] as StreamController<T>).stream.listen(onData);
  }
}

But when I run this code, the runtime error comes in (_sinkMap[T] as StreamController<T>).

The following _CastError was thrown building Builder: type '_AsyncBroadcastStreamController<StudyTimerStatus?>' is not a subtype of type 'StreamController' in type cast

How can I fix this?

It is fine to change the code if it can reach the goal I want(broadcasting the data).

I also tried omit upcasting as follows but the compile error comes.

return _sinkMap[T].stream.listen(onData);

A value of type 'StreamSubscription' can't be returned from the method 'listen' because it has a return type of 'StreamSubscription'

The argument type 'void Function(T)' can't be assigned to the parameter type 'void Function(dynamic)?

CodePudding user response:

I think this should accomplish what you are asking.

If you subscribe with listen<T>(), then you should get the corresponding values of type T passed to notify.

import 'dart:async';

class StateBroadcaster {
  final _sinkMap = <Type, StreamController<dynamic>>{};

  void notify<T>(T value) {
    _sinkMap[T] ??= StreamController<T>.broadcast();
    _sinkMap[T]?.add(value);
  }

  StreamSubscription<T> listen<T>(void Function(T) onData) {
    _sinkMap[T] ??= StreamController<T>.broadcast();
    return (_sinkMap[T] as StreamController<T>).stream.listen(onData);
  }
}

void main() {
  final broadcaster = StateBroadcaster();
  broadcaster.listen<int>((v) => print('int: $v'));
  broadcaster.listen<String>((v) => print('String: $v'));
  broadcaster.notify(1);
  broadcaster.notify('a');
  broadcaster.notify(2);
  broadcaster.notify('b');
  broadcaster.notify(3);
  broadcaster.notify('c');
}

If you want to define the key value pairs within _sinkMap rather than creating the key/value pairs as needed, it should be possible, but you will get a runtime exception if you try to use a key that isn't predefined in the map.

Also, you need to decide if the stream values should be nullable or not. If you want them to be nullable, then you need to add a ? to the type in listen.

import 'dart:async';

class StateBroadcaster {
  final _sinkMap = <Type, StreamController<dynamic>>{
    StudyTimerStatus: StreamController<StudyTimerStatus?>.broadcast(),
  };

  void notify<T>(T value) => _sinkMap[T]?.add(value);

  StreamSubscription<T?> listen<T>(void Function(T) onData) {
    return (_sinkMap[T] as StreamController<T?>).stream.listen(onData);
  }
}

If you don't want them to be nullable, then you need to remove the ? from the type in _sinkMap

import 'dart:async';

class StateBroadcaster {
  final _sinkMap = <Type, StreamController<dynamic>>{
    StudyTimerStatus: StreamController<StudyTimerStatus>.broadcast(),
  };

  void notify<T>(T value) => _sinkMap[T]?.add(value);

  StreamSubscription<T> listen<T>(void Function(T) onData) {
    return (_sinkMap[T] as StreamController<T>).stream.listen(onData);
  }
}
  • Related