An attempt at a Flutter file logger using Dart isolates

Filed in the “FWIW” category ... I wrote the following Dart/Flutter code as an attempt to create a Flutter file logger using Dart isolates, but as it turns out, at the time of this writing (November, 2019) the Flutter platform won’t let you do that. So I thought I’d share/save my code here in case I can use it in the future.

First, here’s my Flutter file logging code that uses Dart isolates:

import 'dart:async';
import 'dart:isolate';
import 'dart:io';

import 'package:flutter/material.dart';

class Lager {

    // The port of the isolate;
    // this port will be used to further
    // send messages to that isolate
    static SendPort _isolateSendPort;
    static Isolate _isolate;

    /// create the isolate and proceed with
    /// the initial hand-shaking
    static void callerCreateIsolate() async {

        // Local and temporary ReceivePort to retrieve
        // the new isolate's SendPort
        ReceivePort receivePort = ReceivePort();

        // instantiate the new isolate
        _isolate = await Isolate.spawn(
            callbackFunction,
            receivePort.sendPort,
        );

        // retrieve the port to be used for further communication
        _isolateSendPort = await receivePort.first;
    }

    // Method that sends a message to the new isolate
    // and receives an answer
    //
    // In this example, I consider that the communication
    // operates with Strings (sent and received data)
    static Future<String> sendReceive(String messageToBeSent) async {
        // We create a temporary port to receive the answer
        ReceivePort port = ReceivePort();

        // We send the message to the Isolate, and also
        // tell the isolate which port to use to provide
        // any answer
        _isolateSendPort.send(
            CrossIsolatesMessage<String>(
                sender: port.sendPort,
                message: messageToBeSent,
            )
        );

        // Wait for the answer and return it
        return await port.first;
    }

    // Extension of the callback function to process incoming messages
    static void callbackFunction(SendPort callerSendPort){

        // Instantiate a SendPort to receive message
        // from the caller
        ReceivePort newIsolateReceivePort = ReceivePort();

        // Provide the caller with the reference of THIS isolate's SendPort
        callerSendPort.send(newIsolateReceivePort.sendPort);

        // Isolate main routine that listens to incoming messages,
        // processes it and provides an answer
        newIsolateReceivePort.listen((dynamic message){
            CrossIsolatesMessage incomingMessage = message as CrossIsolatesMessage;

            // Process the message
            String newMessage = incomingMessage.message;
            debugPrint('LOG: $newMessage');

            // Sends the outcome of the processing
            incomingMessage.sender.send(newMessage);
        });
    }

}

//
// Helper class
//
class CrossIsolatesMessage<T> {
    final SendPort sender;
    final T message;

    CrossIsolatesMessage({
        this.sender,  //required
        this.message,
    });
}

I should say that it attempts to use Dart isolates, because as I mentioned, the Flutter platform doesn’t allow this code to work. Attempting to use it results in this Flutter error message:

[VERBOSE-2:dart_isolate.cc(808)] Unhandled exception:                  
error: native function 'Window_sendPlatformMessage' (4 arguments) cannot be found
#0      Window.sendPlatformMessage (dart:ui/window.dart:1133:9)        
#1      _DefaultBinaryMessenger._sendPlatformMessage (package:flutter/src/services/binary_messenger.dart:85:15)
#2      _DefaultBinaryMessenger.send (package:flutter/src/services/binary_messenger.dart:129:12)
#3      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:309:51)
<asynchronous suspension>                                              
#4      getApplicationDocumentsDirectory (package:path_provider/path_provider.dart:73:22)
<asynchronous suspension>                                              
#5      _getDocsDir (package:just_be/file_logger.dart:46:29)            
<asynchronous suspension>                                              
#6      _getLogFile (package:just_be/file_logger.dart:51:24)            
<asynchronous suspension>                                              
#7      _log (package:just_be/file_logger.dart:57:24)                  
<asynchronous suspension>                                              
#8      _logger (package:just_be/file_logger.dart:76:15)                
<asynchronous suspension>                                              
#9      _startIsolate.<anonymous<…>

If you google the first line of that error message, you’ll find that this code won’t work on Flutter at this time. Note that it may be possible to get code like this working using the https://pub.dev/packages/flutter_isolate package, but that syntax is a little different than the standard Dart isolates syntax. I did briefly try to get my code working with that project, but during that process I found another solution to my problem, so I stopped trying to make it work. (It seems like there was a significant problem in trying to use that project, but as I write this, I can’t recall what that problem was.)

My current Flutter file logger

Note: This is how I ended up implementing my Flutter file logger code.

Summary

So, in summary, if you ever need some Flutter file-logging code that uses Dart isolates, I hope this source code can help you in that effort.