communication_channels 1.0.1 copy "communication_channels: ^1.0.1" to clipboard
communication_channels: ^1.0.1 copied to clipboard

This package is used to make the communication process between isolates(services) more maintainable and systematic.

example/main.dart

import 'dart:async';
import 'dart:io';
import 'dart:math';

import 'package:communication_channels/communication_channels.dart';
import 'dart:isolate';

import 'package:easy_serialization/easy_serialization.dart';

// Define the channels you will work with, and use the type of provider
// you want to send the data with.
// Here we use Sockets.

// * The channel code MUST be unique.
// * All channels MUST use the SAME TYPE OF PROVIDER.
//    BUT they must provide unique parameters to it.

final mainChannel = ChannelType(
  code: 0,
  debugName: "MAIN",
  provider: SocketComProvider(
    host: InternetAddress.tryParse("127.0.0.1"),
    port: 5000,
  ),
);

final isolateChannel = ChannelType(
  code: 1,
  debugName: "ISOLATE",
  provider: SocketComProvider(
    host: InternetAddress.tryParse("127.0.0.1"),
    port: 5001,
  ),
);

/// Pass all channels you have defined here.
final channelsDefinitions = ChannelsDefinitions([mainChannel, isolateChannel]);

final allAvailableChannelFunctions = <ChannelFunctionConfig>[
  PrintIsolateHashCode.config,
  CircleTheShapes.config,
];

/// This is commented in details in the example of `easy_serialization` Package.
final customSerializableObjects = <SerializationConfig>[
  SerializationConfig<Offset>(
    toMarkupObj: (obj) => {
      "dx": obj.dx,
      "dy": obj.dy,
    },
    fromMarkupObj: (markup) => Offset(
      markup["dx"],
      markup["dy"],
    ),
  ),
  ShapeFillType.values.config,
  SerializationConfig.abstract<Shape>(),
  SerializationConfig.serializable<Circle>(Circle.fromMarkup),
  SerializationConfig.serializable<Rectangle>(Rectangle.fromMarkup),
  SerializationConfig.serializable<Square>(Square.fromMarkup),
];

/// Try to call the function that in it you register [Prop], [ChannelFunction]
/// and initializing the channel at FIRST in any new isolate(channel)
/// you want to communicate with.
Future<void> initChannelCommunications(ChannelType channel) async {
  /// Configure registered-types that can be sent throw a channel.
  /// This is commented in details in the example of `easy_serialization` Package.
  Prop.registerSerializationConfigs(customSerializableObjects);

  // Configure channel functions in use.
  ChannelFunction.register(allAvailableChannelFunctions);

  // Register this channel and initialize it.
  await ThisChannel().init(
    channelType: channel,
    definitions: channelsDefinitions,
  );
}

void main() async {
  print("Main hashcode ${Isolate.current.hashCode}");

  // call at FIRST.
  await initChannelCommunications(mainChannel);

  await Isolate.spawn(isolateMain, "");

  // This waits until the isolate channel is created.
  await isolateChannel.waitUntilStart();

  /// Any [ChannelFunction] return and [ChannelResult]
  /// that contains the data of the type of error(if some error occurs).
  final isolateHashCodeRes = await PrintIsolateHashCode().raise();
  print("The isolate hashcode is ${isolateHashCodeRes.correctData}");

  await Future.delayed(const Duration(seconds: 2));

  // Here we deAttach the channel when we finish.
  await ThisChannel().deAttach();
}

void isolateMain(String str) async {
  print("isolate hashcode ${Isolate.current.hashCode}");

  // call at FIRST.
  await initChannelCommunications(isolateChannel);

  final rect = Rectangle(10, 10)
    ..fill = ShapeFillType.outlined
    ..offset = Offset(10, 10);

  final square = Square(75);

  final shapes = <Shape>[rect, square];

  /// Any [ChannelFunction] return and [ChannelResult]
  /// that contains the data of the type of error(if some error occurs).
  final circlesRes = await CircleTheShapes(shapes: shapes).raise();
  print("First shape area ${shapes.first.area()}"); // 100
  print("First circle area ${circlesRes.correctData.first.area()}"); // 100

  await Future.delayed(const Duration(seconds: 2));

  // Here we deAttach the channel when we finish.
  await ThisChannel().deAttach();
}

//*/////////////////////// The channel functions ///////////////////////*//

// You can create them easily with the snippets I provide for you.

/// This function shows you that [callFunWithArgs] is executed in [isolateChannel].
///
/// It also returns the [hashCode] of [isolateChannel].
class PrintIsolateHashCode extends ChannelFunction<int> {
  @override
  late final List<Prop> args = [];

  PrintIsolateHashCode()
      : super.to(
          toChannel: isolateChannel,
        );

  @override
  TypeAsync<int> callFunWithArgs() async => Isolate.current.hashCode;

  //*////////////////////////// DO NOT MODIFY THIS SECTION BY HAND //////////////////////////*//

  @override
  ChannelFunctionConfig getConfig() => config;
  static final config = ChannelFunctionConfig<PrintIsolateHashCode>(
    templateFunction: PrintIsolateHashCode._temp,
    fromMarkup: PrintIsolateHashCode.fromJson,
  );
  PrintIsolateHashCode._temp() : super.temp();
  PrintIsolateHashCode.fromJson(Map<String, dynamic> json)
      : super.fromJson(json);
}

/// Here we pass the shapes and send them to [mainChannel],
/// then we will return back the [List<Circle>] that have the same area as that shape.
///
/// Obviously we can do that in the same isolate, but we do that to show the IDEA
/// of executing code in different channel, sending and recieving to or from any channel.
class CircleTheShapes extends ChannelFunction<List<Circle>> {
  ///> here we define the arguments of the function.
  final _shapes = Prop<List<Shape>>.list([]);

  ///> add all the arguments here.
  @override
  late final List<Prop> args = [
    _shapes,
  ];

  ///> pass your argument to the function constructor.
  CircleTheShapes({
    required List<Shape> shapes,
  }) : super.to(
          toChannel: mainChannel,
        ) {
    ///> initialize the data.
    _shapes.data = shapes;
  }

  @override
  TypeAsync<List<Circle>> callFunWithArgs() async {
    print("CircleTheShapes in ${Isolate.current.hashCode}");

    /// Here we do the functionality.

    /// Here we use our [_shapes] prop.
    return _shapes.data.map((shape) {
      final r = sqrt(shape.area() / 3.14);

      return Circle(r);
    }).toList();
  }

  ///> in case of returning a [List] we must provide an empty instance.
  @override
  List<Circle>? get emptyListFallback => [];

  //*////////////////////////// DO NOT MODIFY THIS SECTION BY HAND //////////////////////////*//

  @override
  ChannelFunctionConfig getConfig() => config;
  static final config = ChannelFunctionConfig<CircleTheShapes>(
    templateFunction: CircleTheShapes._temp,
    fromMarkup: CircleTheShapes.fromJson,
  );
  CircleTheShapes._temp() : super.temp();
  CircleTheShapes.fromJson(Map<String, dynamic> json) : super.fromJson(json);
}

//*/////////////////////// Types that can be sent through channels ///////////////////////*//

// This is commented in details in the example of `easy_serialization` Package.

/// Representative of [Offset] type that comes with Flutter SDK.
class Offset {
  final double dx, dy;
  const Offset(this.dx, this.dy);
}

enum ShapeFillType {
  solid,
  outlined,
}

abstract class Shape with SerializableMixin {
  Offset offset = const Offset(0, 0);
  ShapeFillType fill = ShapeFillType.solid;
  Shape();

  double area();

  @override
  MarkupObj toMarkupObj() => {
        "offset": Prop.valueToMarkup(offset),
        "fill": Prop.valueToMarkup(fill),
      };

  Shape.fromMarkup(MarkupObj markup)
      : offset = Prop.valueFromMarkup(markup["offset"]),
        fill = Prop.valueFromMarkup(markup["fill"]);
}

class Circle extends Shape {
  double radius;
  Circle(this.radius);

  @override
  double area() => 3.14 * radius * radius;

  @override
  MarkupObj toMarkupObj() => {
        ...super.toMarkupObj(),
        "radius": radius,
      };

  Circle.fromMarkup(MarkupObj markup)
      : radius = markup["radius"],
        super.fromMarkup(markup);
}

class Rectangle extends Shape {
  double height, width;
  Rectangle(this.height, this.width);

  @override
  double area() => height * width;

  @override
  MarkupObj toMarkupObj() => {
        ...super.toMarkupObj(),
        "height": height,
        "width": width,
      };

  Rectangle.fromMarkup(MarkupObj markup)
      : height = markup["height"],
        width = markup["width"],
        super.fromMarkup(markup);
}

class Square extends Rectangle {
  Square(double sideLen) : super(sideLen, sideLen);

  Square.fromMarkup(MarkupObj markup) : super.fromMarkup(markup);
}
1
likes
135
points
44
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

This package is used to make the communication process between isolates(services) more maintainable and systematic.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

easy_serialization, equatable

More

Packages that depend on communication_channels