zego_pip 0.0.3
zego_pip: ^0.0.3 copied to clipboard
A Flutter Picture-in-Picture (PIP) plugin based on `zego_express_engine`, supporting iOS and Android platforms .
zego_pip #
A Flutter Picture-in-Picture (PIP) plugin based on zego_express_engine
, supporting iOS and Android platforms.
Demo #
iOS Platform #
Android Platform #
Features #
- π₯ Cross-platform Support: Supports iOS 15.0+ and Android 8.0+
- π Ready to Use: Zero configuration, one-click PIP enablement
- π Auto Switching: Intelligent PIP mode switching
- π― Performance Optimized: Built-in video rendering optimization and memory management
Installation #
1. Add Dependencies #
Add to pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
zego_pip: ^0.0.1
zego_express_engine: ^3.21.0
2. Install Dependencies #
flutter pub get
3. iOS Configuration #
Set minimum version in ios/Podfile
:
platform :ios, '13.0'
Then run:
cd ios
pod install
cd ..
4. Android Configuration #
Ensure minSdkVersion
in android/app/build.gradle
is at least 21:
android {
defaultConfig {
minSdkVersion 21
}
}
Add android:supportsPictureInPicture="true"
to the <activity>
tag in android/app/src/main/AndroidManifest.xml
:
<manifest>
<application>
<activity
android:name=".MainActivity"
android:supportsPictureInPicture="true"
...
/>
</application>
</manifest>
Android Permissions
The plugin automatically adds the following necessary permissions to your app:
Permission Category | Permission Name | Purpose | Minimum API Level |
---|---|---|---|
Network Permissions | android.permission.INTERNET |
Network access for video streaming | API 1 |
android.permission.ACCESS_NETWORK_STATE |
Network state access | API 1 | |
android.permission.ACCESS_WIFI_STATE |
WiFi state access | API 1 | |
Audio Permissions | android.permission.RECORD_AUDIO |
Audio recording for audio capture | API 1 |
android.permission.MODIFY_AUDIO_SETTINGS |
Audio settings modification | API 1 | |
Camera Permissions | android.permission.CAMERA |
Camera access for video capture | API 1 |
Foreground Service Permissions | android.permission.FOREGROUND_SERVICE |
Foreground service for background PIP | API 26 |
android.permission.FOREGROUND_SERVICE_MICROPHONE |
Foreground service microphone | API 34 | |
android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION |
Foreground service media projection | API 34 | |
System Permissions | android.permission.SYSTEM_ALERT_WINDOW |
System overlay for PIP window display | API 1 |
Quick Start #
1. Initialize #
Method 1: zego_pip Internal Engine Creation
import 'package:zego_pip/zego_pip.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await ZegoPIP().init(
/// zego_pip internally creates the express engine, listens for events, and logs into the room
expressConfig: ZegoPIPExpressConfig(
create: ZegoPIPExpressCreateConfig(
// Please fill in your own app id
appID: 1234567890,
// Please fill in your own app sign
appSign: 'your_app_sign_here',
),
room: ZegoPIPExpressRoomConfig(
// Please fill in your own room id
roomID: 'test_room_id',
// Please fill in your own room login user info
userID: 'test_user_id',
userName: 'test_user_name',
),
),
);
runApp(
const MaterialApp(
home: Scaffold(
// Please fill in your own stream id
body: Center(child: ZegoPIPVideoView(streamID: 'test_stream_id')),
),
),
);
}
Method 2: External Engine Creation(Recommended)
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:zego_express_engine/zego_express_engine.dart';
import 'package:zego_pip/zego_pip.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await ZegoExpressManager().init();
/// Before ZegoPIP.init, you must first create the express engine
await ZegoPIP().init(
expressConfig: ZegoPIPExpressConfig(
event: ZegoExpressManager().pipExpressEvent,
),
);
/// ZegoPIPVideoView can only be rendered after logging into the room from other parts of the app
await ZegoExpressManager().loginRoom();
runApp(
const MaterialApp(
home: Scaffold(
/// Please fill in your own stream id
body: Center(child: ZegoPIPVideoView(streamID: 'test_stream_id')),
),
),
);
}
class ZegoExpressManager {
factory ZegoExpressManager() {
return shared;
}
static final ZegoExpressManager shared = ZegoExpressManager._internal();
ZegoExpressManager._internal();
var pipExpressEvent = ZegoPIPExpressEvent();
Future<void> init() async {
ZegoExpressEngine.onEngineStateUpdate = onEngineStateUpdate;
ZegoExpressEngine.onDebugError = onDebugError;
ZegoExpressEngine.onRoomStreamUpdate = onRoomStreamUpdate;
ZegoExpressEngine.onRoomStateChanged = onRoomStateChanged;
ZegoExpressEngine.onPlayerStateUpdate = onPlayerStateUpdate;
ZegoExpressEngine.onRemoteCameraStateUpdate = onRemoteCameraStateUpdate;
ZegoExpressEngine.onRemoteMicStateUpdate = onRemoteMicStateUpdate;
await ZegoExpressEngine.createEngineWithProfile(
ZegoEngineProfile(
1234567890,
ZegoScenario.Default,
appSign: 'your app sign',
/// Please ensure that `enablePlatformView` is set to true
enablePlatformView: true,
),
);
}
Future<bool> loginRoom() async {
final result = await ZegoExpressEngine.instance.loginRoom(
/// Please fill in your own room id
'test_room_id',
ZegoUser(
/// Please fill in your own room login user info
'test_user_id',
'test_user_name',
),
config: ZegoRoomConfig(0, true, ''),
);
return 0 == result.errorCode;
}
/// Forward express event to zego_pip
void onEngineStateUpdate(ZegoEngineState state) {
pipExpressEvent.onEngineStateUpdate?.call(state);
}
void onDebugError(int errorCode, String funcName, String info) {
pipExpressEvent.onDebugError?.call(errorCode, funcName, info);
}
void onRoomStreamUpdate(
String roomID,
ZegoUpdateType updateType,
List<ZegoStream> streamList,
Map<String, dynamic> extendedData,
) {
pipExpressEvent.onRoomStreamUpdate?.call(
roomID,
updateType,
streamList,
extendedData,
);
}
void onRoomStateChanged(
String roomID,
ZegoRoomStateChangedReason reason,
int errorCode,
Map<String, dynamic> extendedData,
) {
pipExpressEvent.onRoomStateChanged?.call(
roomID,
reason,
errorCode,
extendedData,
);
}
void onPlayerStateUpdate(
String streamID,
ZegoPlayerState state,
int errorCode,
Map<String, dynamic> extendedData,
) {
pipExpressEvent.onPlayerStateUpdate?.call(
streamID,
state,
errorCode,
extendedData,
);
}
void onRemoteCameraStateUpdate(String streamID, ZegoRemoteDeviceState state) {
pipExpressEvent.onRemoteCameraStateUpdate?.call(streamID, state);
}
void onRemoteMicStateUpdate(String streamID, ZegoRemoteDeviceState state) {
pipExpressEvent.onRemoteMicStateUpdate?.call(streamID, state);
}
}
2. Use Video Component #
class VideoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Video Room')),
body: ZegoPIPVideoView(
streamID: 'stream_id',
// PIP functionality automatically enabled
),
);
}
}
3. Manual PIP Control (Optional) #
class PIPController {
final zegoPIP = ZegoPIP();
// Enable PIP
Future<void> enablePIP() async {
await zegoPIP.enable();
}
// Update PIP source
Future<void> updatePIPSource(String streamID) async {
await zegoPIP.updateIOSPIPSource(streamID);
}
// Stop PIP
Future<bool> stopPIP() async {
return await zegoPIP.stopPIP();
}
// Check if in PIP mode
Future<bool> isInPIP() async {
return await zegoPIP.isInPIP();
}
}
π API Documentation
Platform Support #
iOS #
- Minimum Version: iOS 13.0
- PIP Support: iOS 15.0+
- Features: Native AVPictureInPictureController support
Android #
- Minimum Version: Android 8.0 (API 26)
- PIP Support: Android 8.0+
- Features: Native PictureInPicture mode support
FAQ #
Q: Why doesn't PIP functionality work? #
A: Please check:
- Whether the plugin is properly initialized
- Whether iOS version supports PIP (iOS 15.0+)
- Whether Android version supports PIP (Android 8.0+)
- Whether
zego_express_engine
dependency is added
Q: How to handle PIP mode switching? #
A: The plugin automatically handles mode switching, no manual intervention required.
Q: How to customize PIP interface? #
- Android: You can use ZegoPIPVideoView as a normal Widget in your widget tree.
- iOS: Currently the plugin uses system default PIP interface, customization is not supported yet.
Known Issues #
- When the remote stream camera is not enabled and ZegoPIPVideoView is rendered, the first desktop minimization after the remote stream enables camera will not show PIP window
Contributing #
Issues and Pull Requests are welcome!
License #
This project is licensed under the MIT License.