Home > front end >  How to write and read data anywhere by shared_preferences on Flutter 3.7 background isolates?
How to write and read data anywhere by shared_preferences on Flutter 3.7 background isolates?

Time:01-28

On Flutter 3.7 platform channels can run on any isolate. So I tried this sample,

import ‘package:flutter/services.dart’;
import ‘package:shared_preferences/shared_preferences.dart’;

void main() {
 // Identify the root isolate to pass to the background isolate.
 // (API introduced in Flutter 3.7)
 RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
 Isolate.spawn(_isolateMain, rootIsolateToken);
}

void _isolateMain(RootIsolateToken rootIsolateToken) async {
 // Register the background isolate with the root isolate.
 BackgroundIsolateBinaryMessenger
   .ensureInitialized(rootIsolateToken);
 // You can now use the shared_preferences plugin.
 SharedPreferences sharedPreferences =
   await SharedPreferences.getInstance();
 print(sharedPreferences.getBool(‘isDebug’));
}

I can read from data on shared_preferences in this sample okey. But how can I use this feature anywhere in my app? How can I set or read data using this isolate on initState for example?

CodePudding user response:

Basically you need to implement communication between isolates. You can read more about it here

Here is an example, you can change flutter_secure_storage that i used with shared_preferences package

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

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class CreationEvent {
  final RootIsolateToken isolateToken;
  final SendPort sendPort;

  CreationEvent(this.isolateToken, this.sendPort);
}

class DeletetionEvent {}

class ReadEvent {
  final String key;
  const ReadEvent(this.key);
}

class ReadResult {
  final String key;
  final String? content;
  const ReadResult(this.key, this.content);
}

class IsolateIO {
  IsolateIO._();

  final _toBgPort = Completer();
  final Map<Object, Completer> _completerMap = {};

  Isolate? _isolate;
  StreamSubscription? _fromBgListener;

  void start() async {
    RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
    ReceivePort fromBG = ReceivePort();

    _fromBgListener = fromBG.listen((message) {
      // setup process
      if (message is SendPort) {
        _toBgPort.complete(message);
        return;
      }

      if (message is ReadResult) {
        _completerMap[message.key]?.complete(message.content);
        _completerMap.remove(message.key);
      }
    });

    _isolate = await Isolate.spawn(
      (CreationEvent data) {
        final worker = IsolateWorker(data.isolateToken, data.sendPort);
        worker.listen();
      },
      CreationEvent(rootIsolateToken, fromBG.sendPort),
    );
  }

  Future<String?> readFromStorage(String key) async {
    // make sure isolate created with ports
    final port = await _toBgPort.future;

    // store completer
    final completer = Completer<String?>();
    _completerMap['read:$key'] = completer;

    // send key to be read
    port.send(ReadEvent('read:$key'));

    // return result
    return completer.future;
  }

  void stop() async {
    if (_toBgPort.isCompleted) {
      final port = await _toBgPort.future;
      port.send(DeletetionEvent());
    }
    _fromBgListener?.cancel();
    _isolate?.kill(priority: Isolate.immediate);
  }

  static final i = IsolateIO._();
}

class IsolateWorker {
  final RootIsolateToken rootIsolateToken;
  final SendPort toMain;
  final FlutterSecureStorage storage;

  StreamSubscription? subs;

  IsolateWorker(
    this.rootIsolateToken,
    this.toMain, {
    this.storage = const FlutterSecureStorage(
      aOptions: AndroidOptions(
        encryptedSharedPreferences: true,
      ),
    ),
  }) {
    // Register the background isolate with the root isolate.
    BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
  }

  void listen() {
    ReceivePort fromMain = ReceivePort();
    toMain.send(fromMain.sendPort);
    subs = fromMain.listen((message) => onMessage(message));
  }

  void onMessage(dynamic message) async {
    if (message is DeletetionEvent) {
      subs?.cancel();
      return;
    }

    if (message is ReadEvent) {
      final rawJson = await storage.read(key: message.key);
      toMain.send(ReadResult(message.key, rawJson));
    }
  }
}

class View extends StatefulWidget {
  const View({super.key});

  @override
  State<View> createState() => _ViewState();
}

class _ViewState extends State<View> {
  String username = '';
  @override
  void initState() {
    super.initState();
    IsolateIO.i.start();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      final name = await IsolateIO.i.readFromStorage('username');
      setState(() {
        username = name ?? '';
      });
    });
  }

  @override
  void dispose() {
    IsolateIO.i.stop();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: Text(username),
    );
  }
}
  • Related