istornz / flutter_live_activities

A Flutter plugin to use iOS 16.1+ Live Activities ⛹️ & iPhone Dynamic Island 🏝️ features
https://dimitridessus.fr/
MIT License
177 stars 54 forks source link

Error: The 'live_activities' channel sent a message from native to Flutter on a non-platform thread #96

Open daniil-shumko opened 1 week ago

daniil-shumko commented 1 week ago

After upgrading to version 2.0.0, I encountered the following error in the debug console:

[ERROR:flutter/shell/common/shell.cc(1065)] The 'live_activities' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel.
See https://docs.flutter.dev/platform-integration/platform-channels#channels-and-platform-threading for more information.

This error appears to indicate a threading issue with platform channel communication. Has anyone else experienced this, or is it specific to my environment?

flutter doctor output:

➜ flutter doctor          
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.24.3, on macOS 15.0.1 24A348 darwin-arm64, locale en-GB)
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 16.0)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.2)
[✓] VS Code (version 1.94.2)
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!
DKMonster commented 1 week ago

I had the same issue. When I create the activity (createActivity) that will show this error.

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.24.3, on macOS 14.3 23D56 darwin-arm64, locale en-TW)
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.1)
[✓] Connected device (4 available)
[✓] Network resources
istornz commented 1 week ago

Hello, thanks for raising an issue, I can't reproduce it with the plugin's example. Can you provide reproductible example?

DKMonster commented 4 days ago

I'm encountering an issue with the live_activities plugin where method channel messages are being sent on a non-platform thread. This results in the following warning:

[ERROR:flutter/shell/common/shell.cc(1065)] The 'live_activities' channel sent a message from native to Flutter on a non-platform thread.

I've attempted to resolve this by:

  1. Adding WidgetsBinding.instance.addPostFrameCallback to ensure Flutter-side operations run on the main thread
  2. Implementing proper timer cleanup in the widget using onDisappear
  3. Adding logging to track method channel calls

However, the warning persists. I believe this might be an issue within the plugin's native implementation that needs to be addressed.

Current workaround: While the functionality works, the warning suggests this could lead to potential issues. Would it be possible to update the plugin to ensure all method channel communications occur on the platform thread?

Environment:

Code: First I make the new page to test the live activity, The warning only show once time when I call the _createActivity. And the data sent had work fine. That always change the data when I click the 01 or 02 button. :

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:live_activities/live_activities.dart';
import 'package:live_activities/models/activity_update.dart';
import 'package:live_activities/models/live_activity_image.dart';
import 'package:live_activities/models/url_scheme_data.dart';
import 'package:usp/config/main.dart';
import 'package:usp/models/parking_activity.dart';

class LiveActivityPage extends ConsumerStatefulWidget {
  const LiveActivityPage({super.key});

  @override
  ConsumerState<ConsumerStatefulWidget> createState() =>
      _LiveActivityPageState();
}

class _LiveActivityPageState extends ConsumerState<LiveActivityPage> {
  final _liveActivitiesPlugin = LiveActivities();
  String? _latestActivityId;
  StreamSubscription<UrlSchemeData>? urlSchemeSubscribe;

  Future<void> _createActivity(ParkingActivity activity) async {
    try {
      WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
        final activityId = await _liveActivitiesPlugin.createActivity(
          activity.toMap(),
        );
        setState(() => _latestActivityId = activityId);
      });
    } catch (e) {
      debugPrint('Error creating activity: $e');
    }
  }

  Future<void> _updateParkingInfo(ParkingActivity activity) async {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      debugPrint('Start update');
      _liveActivitiesPlugin.updateActivity(
        _latestActivityId!,
        activity.toMap(),
      );
      debugPrint('Completed update');
    });
  }

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      _liveActivitiesPlugin.init(
        appGroupId: appGroupId,
        urlScheme: 'usp',
      );

      debugPrint('Live Activities plugin initialized');

      _liveActivitiesPlugin.activityUpdateStream.listen((event) {
        event.map(
          active: (ActiveActivityUpdate activity) {
            // token
            debugPrint('--------------------------------');
            debugPrint('Live Activity updated: ${activity.activityId}');
            debugPrint('--------------------------------');
          },
          ended: (EndedActivityUpdate activity) {
            debugPrint('Live Activity ended: $activity');
          },
          stale: (StaleActivityUpdate activity) {
            debugPrint('Live Activity stale: $activity');
          },
          unknown: (UnknownActivityUpdate event) {
            debugPrint('Live Activity unknown: $event');
          },
        );
        debugPrint('Live Activity updated: $_latestActivityId');
      });

      urlSchemeSubscribe = _liveActivitiesPlugin.urlSchemeStream().listen((
        schemaData,
      ) {
        debugPrint('Live Activity url scheme: $schemaData');
        debugPrint(schemaData.path);
        debugPrint(schemaData.url);
        debugPrint(schemaData.toString());
      });
    });
  }

  @override
  void dispose() {
    urlSchemeSubscribe?.cancel();
    _liveActivitiesPlugin.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Live Activity'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              // make a button to start live activity
              (_latestActivityId == null)
                  ? ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.indigo,
                      ),
                      onPressed: () {
                        ParkingActivity activity = ParkingActivity();

                        activity = activity.copyWith(
                          // some data
                        );

                        _createActivity(activity);
                      },
                      child: const Text('Start the Live Activity'),
                    )
                  : const SizedBox.shrink(),

              const SizedBox(height: 16),

              // make a button to update live activity
              ElevatedButton(
                onPressed: () {
                  ParkingActivity activity = ParkingActivity();

                  activity = activity.copyWith(
                    // some data
                  );
                  debugPrint('Updating activity: $_latestActivityId');
                  _updateParkingInfo(activity);
                },
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.blue,
                ),
                child: const Text('Update the Live Activity 01'),
              ),

              const SizedBox(height: 16),

              ElevatedButton(
                onPressed: () {
                  ParkingActivity activity = ParkingActivity();

                  activity = activity.copyWith(
                    // some data
                  );
                  debugPrint('Updating activity: $_latestActivityId');
                  _updateParkingInfo(activity);
                },
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.green,
                ),
                child: const Text('Update the Live Activity 02'),
              ),

              const SizedBox(height: 16),

              _latestActivityId != null
                  ? ElevatedButton(
                      onPressed: () {
                        _liveActivitiesPlugin.endActivity(_latestActivityId!);
                        setState(() => _latestActivityId = null);
                      },
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.red,
                      ),
                      child: const Text('End the Live Activity'),
                    )
                  : const SizedBox.shrink(),
            ],
          ),
        ),
      ),
    );
  }
}
istornz commented 3 days ago

Thanks for the detailed answer @DKMonster, I uploaded a new build (2.0.0+2), can you try with this new version? I can't reproduce it on my side (iPhone 14 Pro - iOS 18.0.1) but this build can fix your issue.

DKMonster commented 2 days ago

@istornz I have tested the device on iOS 17.5 and the issue still persists. Do you think this issue might be resolved in iOS 18?

Nikotecnology commented 1 day ago

Good Morning, i've tested it on IOS 18 and IOS 18.1 and i get the same error in the same conditions as @DKMonster

istornz commented 1 day ago

That's a strange behavior, do you have the same issue when trying to run the example?

DKMonster commented 7 hours ago

@istornz Good morning,

I can reproduce this issue. I used your example and tried using flutter run -v. When I clicked the Start football match button, the error showed up.

When I just used flutter run without -v, the error did not appear.

I tested my code using VSCode debug mode, which behaves similarly to using the -v flag.

The feature is currently working fine with my code. However, I'm not sure if this issue could potentially cause any serious problems.

Screenshot 2024-11-01 at 9 59 40 AM
istornz commented 23 minutes ago

Hello @DKMonster thanks for your explanation, I finally reproduce the error. It only appears on iOS Simulator (I don't see this error on a real device). In addition it seems the error pop at the start of the example and not when the activity is created. I think it's not a real issue but I can try to investigate more.

Thanks for your help