teleconsultation_core 0.2.0
teleconsultation_core: ^0.2.0 copied to clipboard
Shared teleconsultation runtime for Manastik Flutter apps.
Teleconsultation Core #
A robust Flutter plugin for managing and orchestrating teleconsultation meetings. It wraps the core meeting logic (using realtimekit_ui) and provides deep native integration for Android and iOS, including background processing, Picture-in-Picture (PiP) mode, and ongoing meeting notifications.
Features #
- Native Meeting Process: On Android, the meeting can run in a separate Flutter isolate (engine) to ensure stability and independent resource management.
- Picture-in-Picture (PiP): Built-in support for native PiP mode, allowing users to navigate the app or the device while continuing their consultation.
- Ongoing Meeting Tracker: A built-in
ChangeNotifierto track the state of a meeting (ongoing/ended) and its duration globally across the app. - Persistent Notifications: Native ongoing notifications (Android) indicating an active consultation.
- Device Management: API to query and switch between audio and camera devices.
- Screen Sharing: Meeting-scoped sharer controls and consumer rendering APIs without exposing SDK internals.
Getting Started #
Configuration #
Before launching any meetings, configure the core package (typically in your main.dart):
import 'package:teleconsultation_core/teleconsultation_core.dart';
void main() {
TeleconsultationCore.configure(
const TeleconsultationCoreConfig(
androidNotificationChannelId: 'ongoing_consultation',
androidNotificationChannelName: 'Ongoing consultation',
androidNotificationSmallIcon: 'ic_notification',
),
);
runApp(const MyApp());
}
Android Setup #
Because the package uses a background isolate on Android to run the meeting independently, you must define a top-level @pragma('vm:entry-point') function to serve as the entry point for the meeting process:
@pragma('vm:entry-point')
void meetingMain() {
runTeleconsultationMeetingProcess(
config: TeleconsultationMeetingProcessConfig(
meetingAppBuilder: (context, meetingContext) {
return CustomMeetingApp(
meetingId: meetingContext.credentials.meetingId,
doctorName: meetingContext.credentials.displayName,
// ...
);
},
),
);
}
Implementation & Usage #
Launching a Meeting #
To start a teleconsultation, use the TeleconsultationLauncher. This will handle the platform-specific routing (launching the MeetingActivity on Android or pushing the route natively).
await TeleconsultationLauncher.launch(
credentials: TeleconsultationCredentials(
authToken: 'your-auth-token',
displayName: 'Dr. Smith',
meetingId: 'meeting-123',
),
options: const TeleconsultationLaunchOptions(
audioEnabled: true,
videoEnabled: true,
skipSetupScreen: true,
),
details: const TeleconsultationMeetingDetails(
meetingId: 'meeting-123',
patientName: 'John Doe',
),
);
Tracking Meeting State #
You can track if a meeting is actively running and its duration from anywhere in your main app isolate. This is incredibly useful for showing global banners, preventing user logout during a meeting, or handling custom in-app PiP logic.
import 'package:teleconsultation_core/teleconsultation_core.dart';
// Inside a Widget:
ListenableBuilder(
listenable: TeleconsultationMeetingTracker.instance,
builder: (context, _) {
final tracker = TeleconsultationMeetingTracker.instance;
if (tracker.isOngoing) {
return Container(
color: Colors.green,
child: Text(
'Meeting in progress: ${tracker.duration.inMinutes}:${(tracker.duration.inSeconds % 60).toString().padLeft(2, '0')}',
),
);
}
return const SizedBox.shrink();
},
)
The tracker automatically starts when TeleconsultationLauncher.launch() is called, and automatically stops when the native layer broadcasts a meetingClosed event.
Picture-in-Picture (PiP) #
You can manage PiP state programmatically using the launcher:
// Check if PiP is supported on the device
bool isSupported = await TeleconsultationLauncher.isPipSupported();
// Enter PiP mode manually
if (isSupported) {
await TeleconsultationLauncher.enterPip();
}
// Update native PiP actions (e.g., mute/unmute buttons on the PiP window)
await TeleconsultationLauncher.updatePipActions(
audioEnabled: false,
videoEnabled: true,
);
You can also listen to PiP state changes:
TeleconsultationLauncher.setPipStateHandler((bool isPipActive) {
if (isPipActive) {
// Hide certain UI elements
}
});
Ending the Process Manually #
To forcefully terminate the meeting process from the main app isolate:
await TeleconsultationLauncher.finishMeetingProcess();
Screen Sharing #
Every TeleconsultationMeetingPage builder receives a meeting-scoped controller. Pass it to your room UI instead of reaching into RealtimeKit globals:
TeleconsultationMeetingPage(
credentials: credentials,
builder: (context, meetingContext) {
return MyMeetingRoom(screenShare: meetingContext.screenShare);
},
);
Sharers can use the controller as a Listenable and guard the action with canStart:
ListenableBuilder(
listenable: screenShare,
builder: (context, _) {
return IconButton(
onPressed: !screenShare.isRequestPending &&
(screenShare.isSharing || screenShare.canStart)
? screenShare.toggle
: null,
icon: Icon(
screenShare.isSharing
? Icons.stop_screen_share_outlined
: Icons.screen_share_outlined,
),
);
},
);
start(), stop(), and toggle() return whether the request was accepted locally. RealtimeKit completes capture asynchronously, so observe isSharing and lastError for the result.
Consumers can render any active local or remote share:
ListenableBuilder(
listenable: screenShare,
builder: (context, _) {
final shares = screenShare.shares;
if (shares.isEmpty) {
return const Center(child: Text('No one is sharing'));
}
return TeleconsultationScreenShareView(share: shares.first);
},
);
The plugin contributes FOREGROUND_SERVICE_MEDIA_PROJECTION to the merged Android manifest, as required for Android 14+. On iOS, the package invokes RealtimeKit's capture API, but a host app that needs system-wide sharing must complete RealtimeKit's ReplayKit target and entitlement setup. See the RealtimeKit Flutter screen-share documentation.