Search code examples
firebasefluttersharedpreferencesdart-isolates

Shared Preference in flutter Isolate


I'm new to flutter and I just learned how to use isolates in Dart. When I try to access shared preference via an isolate it throws the given below error. This error also appears when I try to access Firebase analytics and remote config. How can I resolve this issue and access SharedPreference, FirebaseRemote config, FirebaseFirestore inside an isolate?

[ERROR:flutter/runtime/dart_isolate.cc(882)] Unhandled exception:
E/flutter (23694): ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
E/flutter (23694): If you're running an application and need to access the binary messenger before `runApp()` has been called (for example, during plugin initialization), then you need to explicitly call the `WidgetsFlutterBinding.ensureInitialized()` first.
E/flutter (23694): If you're running a test, you can call the `TestWidgetsFlutterBinding.ensureInitialized()` as the first line in your test's `main()` method to initialize the binding.
E/flutter (23694): #0      defaultBinaryMessenger.<anonymous closure> (package:flutter/src/services/binary_messenger.dart:92:7)
E/flutter (23694): #1      defaultBinaryMessenger (package:flutter/src/services/binary_messenger.dart:105:4)
E/flutter (23694): #2      MethodChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:143:62)
E/flutter (23694): #3      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:149:36)
E/flutter (23694): #4      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:332:12)
E/flutter (23694): #5      MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:359:49)
E/flutter (23694): #6      MethodChannelSharedPreferencesStore.getAll (package:shared_preferences_platform_interface/method_channel_shared_preferences.dart:54:22)
E/flutter (23694): #7      SharedPreferences._getSharedPreferencesMap (package:shared_preferences/shared_preferences.dart:191:57)
E/flutter (23694): #8      SharedPreferences.getInstance (package:shared_preferences/shared_preferences.dart:58:19)
E/flutter (23694): #9      _wraperState.islt (package:spynett/main.dart:122:55)
E/flutter (23694): <asynchronous suspension>
E/flutter (23694): #10     _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:304:17)
E/flutter (23694): #11     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)


void main() async{
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  await setupLocator();
  runApp(MyApp());
}

@override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_){
      startinitsetup();
    });
  }

Future startinitsetup() async{
    debugPrint('Calling setup');
    await _dynamiclink.handledynamiclink();
    await _pushNotificationService.initilalise();
    await _remoteConfigService.initialise();

    ReceivePort reciveport = ReceivePort();
    Isolate.spawn(islt, reciveport.sendPort);

    SendPort childSendPort = await reciveport.first;
    ReceivePort responceport = ReceivePort();
    childSendPort.send(['message',responceport.sendPort]);
    await responceport.first;
  }

static Future<int> islt(SendPort mainSendPort) async{
  ReceivePort childRecivePort = ReceivePort();
  mainSendPort.send(childRecivePort.sendPort);
  await for (var message in childRecivePort){
    SendPort replyport = message[1];
    SharedPreferences _pref = await SharedPreferences.getInstance();
    replyport.send('done');
  }
}


Solution

  • You can copy paste run full code below
    To use SharedPreferences in Isolate, you can use package https://pub.dev/packages/flutter_isolate
    You can change from

    Isolate.spawn(islt, reciveport.sendPort);
    

    to

    FlutterIsolate.spawn(islt, reciveport.sendPort);
    

    output of full test code

    I/flutter (12689): Calling setup
    ...
    I/flutter (12689): test test
    I/flutter (12689): isolate when msg recived
    I/flutter (12689): reply done
    

    full test code

    import 'dart:isolate';
    import 'package:flutter/material.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    import 'package:flutter_isolate/flutter_isolate.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      //await Firebase.initializeApp();
      //await setupLocator();
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addPostFrameCallback((_) {
          startinitsetup();
        });
      }
    
      Future startinitsetup() async {
        debugPrint('Calling setup');
    
        ReceivePort reciveport = ReceivePort();
        FlutterIsolate.spawn(islt, reciveport.sendPort);
    
        SendPort childSendPort = await reciveport.first;
        ReceivePort responceport = ReceivePort();
        childSendPort.send(['message', responceport.sendPort]);
        String reply = await responceport.first;
        print("reply $reply");
      }
    
      static Future<int> islt(SendPort mainSendPort) async {
        ReceivePort childRecivePort = ReceivePort();
        mainSendPort.send(childRecivePort.sendPort);
        await for (var message in childRecivePort) {
          SendPort replyport = message[1];
          SharedPreferences _pref = await SharedPreferences.getInstance();
          await _pref.setString("yourKey", "test");
          String testPref = _pref.get("yourKey");
          print("test $testPref");
          debugPrint('isolate when msg recived');
          replyport.send('done');
        }
      }
    
      int _counter = 0;
    
      void _incrementCounter() {
        setState(() {
          _counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '$_counter',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }