Flutter: Cannot use this MethodChannel before the binary messenger has been initialized when running a function using isolates

Issue

I’m trying to use isolates in my Flutter app with Bloc as a state management solution. Right now, I’m running into an error when running a particular function in wallet_list.dart.

// wallet_list.dart
final FlutterSecureStorage _storage = const FlutterSecureStorage();

FutureOr<List<alan.Wallet>> getWalletList(int value) async {
  Map<String, String> allValues = await _storage.readAll();
  final List<alan.Wallet> _walletList = [];

  allValues.forEach((key, value) {
    if (key.startsWith(WalletOverviewHomeScreen.walletKey)) {
      final arrMnemonic = value.split(' ');
      final _wallet = alan.Wallet.derive(arrMnemonic, certikNetworkInfo);
      // debugPrint('Adding wallet: $_wallet');
      _walletList.add(_wallet);
    }
  });

The following function is used in one of the methods in the cubit class WalletInitializationCubit.

// inside WalletInitializationCubit
Future<void> getWalletListAndDefaultWallet() async {
    try {
      debugPrint('WalletListInitialization');
      emit(WalletInitializationLoading());
      final List<alan.Wallet> walletList = await compute(
        getWalletList,
        1
      );

      // other cubit logic
  }

When trying to build the app, it returns this error:

Exception has occurred.
_AssertionError ('package:flutter/src/services/platform_channel.dart': Failed assertion: 
line 134 pos 7: '_binaryMessenger != null || ServicesBinding.instance != null': Cannot use 
this MethodChannel before the binary messenger has been initialized. This happens when you 
invoke platform methods before the WidgetsFlutterBinding has been initialized. You can fix 
this by either calling WidgetsFlutterBinding.ensureInitialized() before this or by passing
a custom BinaryMessenger instance to MethodChannel().)

For context, the main.dart file looks similar to this.

void main() async {
  runApp(
    const MyApp(),
  );
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  const MyApp();

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = ThemeData();
    return MultiBlocProvider(
      providers: [
        BlocProvider<WalletInitializationCubit>(
          create: (context) => WalletInitializationCubit(),
        ), 
      ],
      child: HomeScreen(),
      .....
  }
}

The relevant line of code that causes the error is

// in getWalletList function
Map<String, String> allValues = await _storage.readAll();

I’ve tried adding WidgetsFlutterBinding.ensureInitialized() before that line as so:

// in getWalletList function
WidgetsFlutterBinding.ensureInitialized()
Map<String, String> allValues = await _storage.readAll();

But that would then cause a UI actions are only available on root isolate error.

How can I fix this error? I can’t use the usual way of using async and await since the process being ran is rather heavy and causes the app to freeze.

Solution

Based on the error UI actions are only available on root isolate it is clear that you cannot execute WidgetsFlutterBinding.ensureInitialized() inside the isolate. Also, as you mentioned in the comments, _storage.readAll() method causes this problem.

Having this in mind, what you could do is run the _storage.readAll() in your main isolate (what’s run by the main function) and pass the result to the isolate as a parameter.

Answered By – mkobuolys

Answer Checked By – Terry (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *