├── format.sh
├── android
├── settings.gradle
├── .gitignore
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── res
│ │ └── values
│ │ │ └── styles.xml
│ │ └── kotlin
│ │ └── fl
│ │ └── pip
│ │ ├── FlPiPActivity.kt
│ │ ├── FlPiPPlugin.kt
│ │ └── PiPHelper.kt
└── build.gradle
├── analysis_options.yaml
├── assets
├── audio.mp3
└── landscape.mp4
├── example
├── ios
│ ├── Runner
│ │ ├── Runner-Bridging-Header.h
│ │ ├── Assets.xcassets
│ │ │ ├── LaunchImage.imageset
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ ├── README.md
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ ├── SceneDelegate.swift
│ │ ├── Base.lproj
│ │ │ ├── Main.storyboard
│ │ │ └── LaunchScreen.storyboard
│ │ └── Info.plist
│ ├── Flutter
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── AppFrameworkInfo.plist
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── Podfile.lock
│ ├── .gitignore
│ ├── Podfile
│ └── Runner.xcodeproj
│ │ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ │ └── project.pbxproj
├── assets
│ ├── close.png
│ ├── logo.png
│ ├── android.mp4
│ └── landscape.mp4
├── android
│ ├── app
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── drawable
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── drawable-v21
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── values
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ └── values-night
│ │ │ │ │ │ └── styles.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── fl
│ │ │ │ │ │ └── pip
│ │ │ │ │ │ └── example
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle.kts
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── settings.gradle.kts
├── pubspec.yaml
├── README.md
├── .gitignore
├── analysis_options.yaml
└── lib
│ ├── main.dart
│ └── src
│ ├── pip_home_page.dart
│ └── home_page.dart
├── ios
├── Classes
│ ├── FlPiPPlugin.swift
│ ├── FlFlutterAppDelegate.swift
│ ├── BackgroundAudioPlayer.swift
│ └── PiPHelper.swift
├── Resources
│ └── PrivacyInfo.xcprivacy
├── .gitignore
└── fl_pip.podspec
├── pubspec.yaml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
└── lib
└── fl_pip.dart
/format.sh:
--------------------------------------------------------------------------------
1 | dart format lib
2 | dart format example/lib
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'fl_pip'
2 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
--------------------------------------------------------------------------------
/assets/audio.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/assets/audio.mp3
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/assets/landscape.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/assets/landscape.mp4
--------------------------------------------------------------------------------
/example/assets/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/assets/close.png
--------------------------------------------------------------------------------
/example/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/assets/logo.png
--------------------------------------------------------------------------------
/example/assets/android.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/assets/android.mp4
--------------------------------------------------------------------------------
/example/assets/landscape.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/assets/landscape.mp4
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .cxx
10 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/fl/pip/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package fl.pip.example
2 |
3 | import fl.pip.FlPiPActivity
4 |
5 | class MainActivity : FlPiPActivity()
6 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wayaer/fl_pip/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Classes/FlPiPPlugin.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 |
3 | public class FlPiPPlugin: NSObject, FlutterPlugin {
4 | public static func register(with registrar: FlutterPluginRegistrar) {
5 | PiPHelper.shared.setRegistrar(registrar)
6 | PiPHelper.shared.newFlutterMethodChannel(registrar.messenger())
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /.kotlin
7 | /local.properties
8 | GeneratedPluginRegistrant.java
9 |
10 | # Remember to never publicly share your keystore.
11 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
12 | key.properties
13 | **/*.keystore
14 | **/*.jks
15 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Resources/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyTrackingDomains
6 |
7 | NSPrivacyAccessedAPITypes
8 |
9 | NSPrivacyCollectedDataTypes
10 |
11 | NSPrivacyTracking
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: example
2 | description: Demonstrates how to use the fl_pip plugin.
3 | publish_to: 'none'
4 | version: 1.0.0+1
5 |
6 | environment:
7 | sdk: '>=3.6.0 <4.0.0'
8 | flutter: '>=3.27.0'
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 | fl_pip:
14 | path: ../
15 | flutter_waya: ^10.0.1
16 |
17 | dev_dependencies:
18 | flutter_lints: ^5.0.0
19 |
20 | flutter:
21 | uses-material-design: true
22 | assets:
23 | - assets/
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/ios/Classes/FlFlutterAppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 |
3 | open class FlFlutterAppDelegate: FlutterAppDelegate {
4 | open func registerPlugin(_ registry: FlutterPluginRegistry) {}
5 |
6 | override open func applicationWillEnterForeground(_ application: UIApplication) {
7 | PiPHelper.shared.applicationWillEnterForeground(application)
8 | }
9 |
10 | override open func applicationDidEnterBackground(_ application: UIApplication) {
11 | PiPHelper.shared.applicationDidEnterBackground(application)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - fl_pip (0.0.1):
3 | - Flutter
4 | - Flutter (1.0.0)
5 |
6 | DEPENDENCIES:
7 | - fl_pip (from `.symlinks/plugins/fl_pip/ios`)
8 | - Flutter (from `Flutter`)
9 |
10 | EXTERNAL SOURCES:
11 | fl_pip:
12 | :path: ".symlinks/plugins/fl_pip/ios"
13 | Flutter:
14 | :path: Flutter
15 |
16 | SPEC CHECKSUMS:
17 | fl_pip: 8161c3d6fb04a73d698d7a80c1bf83cd046d738a
18 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
19 |
20 | PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb
21 |
22 | COCOAPODS: 1.16.2
23 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 | /Flutter/ephemeral/
38 | /Flutter/flutter_export_environment.sh
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: fl_pip
2 | description: A picture-in-picture plugin for android and ios that can display any flutter view
3 | version: 3.2.1
4 | repository: https://github.com/Wayaer/fl_pip.git
5 |
6 | environment:
7 | sdk: '>=3.5.0 <4.0.0'
8 | flutter: ">=3.24.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 | dev_dependencies:
15 | flutter_lints: ^5.0.0
16 |
17 | flutter:
18 | plugin:
19 | platforms:
20 | android:
21 | package: fl.pip
22 | pluginClass: FlPiPPlugin
23 | ios:
24 | pluginClass: FlPiPPlugin
25 | assets:
26 | - assets/
27 |
--------------------------------------------------------------------------------
/example/android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
9 | rootProject.layout.buildDirectory.value(newBuildDir)
10 |
11 | subprojects {
12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
13 | project.layout.buildDirectory.value(newSubprojectBuildDir)
14 | }
15 | subprojects {
16 | project.evaluationDependsOn(":app")
17 | }
18 |
19 | tasks.register("clean") {
20 | delete(rootProject.layout.buildDirectory)
21 | }
22 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # fl_pip_example
2 |
3 | Demonstrates how to use the fl_pip plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
13 |
14 | For help getting started with Flutter development, view the
15 | [online documentation](https://docs.flutter.dev/), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import fl_pip
2 | import Flutter
3 | import UIKit
4 |
5 | @main
6 | @objc class AppDelegate: FlFlutterAppDelegate {
7 | override func application(
8 | _ application: UIApplication,
9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
10 | ) -> Bool {
11 | GeneratedPluginRegistrant.register(with: self)
12 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
13 | }
14 |
15 | override func registerPlugin(_ registry: FlutterPluginRegistry) {
16 | GeneratedPluginRegistrant.register(with: registry)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26 | /pubspec.lock
27 | **/doc/api/
28 | .dart_tool/
29 | .packages
30 | build/
31 |
--------------------------------------------------------------------------------
/android/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/example/ios/Runner/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //import UIKit
2 | //
3 | //class SceneDelegate: UIResponder, UIWindowSceneDelegate {
4 | //
5 | // var window: UIWindow?
6 | //
7 | //
8 | // func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
9 | // guard let _ = (scene as? UIWindowScene) else { return }
10 | // }
11 | //
12 | // func sceneDidDisconnect(_ scene: UIScene) {
13 | //
14 | // }
15 | //
16 | // func sceneDidBecomeActive(_ scene: UIScene) {
17 | // print("sceneDidBecomeActive")
18 | // BackgroundTaskManager.shared.stopPlay()
19 | // }
20 | //
21 | // func sceneDidEnterBackground(_ scene: UIScene) {
22 | // print("sceneDidEnterBackground")
23 | // BackgroundTaskManager.shared.startPlay()
24 | // }
25 | //
26 | //}
27 | //
28 |
--------------------------------------------------------------------------------
/example/android/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | val flutterSdkPath = run {
3 | val properties = java.util.Properties()
4 | file("local.properties").inputStream().use { properties.load(it) }
5 | val flutterSdkPath = properties.getProperty("flutter.sdk")
6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
7 | flutterSdkPath
8 | }
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0"
21 | id("com.android.application") version "8.7.0" apply false
22 | id("org.jetbrains.kotlin.android") version "2.0.20" apply false
23 | }
24 |
25 | include(":app")
26 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/fl_pip.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint fl_pip.podspec` to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'fl_pip'
7 | s.version = '0.0.1'
8 | s.summary = 'A new Flutter project.'
9 | s.description = <<-DESC
10 | A new Flutter project.
11 | DESC
12 | s.homepage = 'http://example.com'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'Your Company' => 'email@example.com' }
15 | s.source = { :path => '.' }
16 | s.source_files = 'Classes/**/*'
17 | s.dependency 'Flutter'
18 | s.platform = :ios, '11.0'
19 | # Flutter.framework does not contain a i386 slice.
20 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
21 | s.swift_version = '5.0'
22 | end
23 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("kotlin-android")
4 | id("dev.flutter.flutter-gradle-plugin")
5 | }
6 |
7 | android {
8 | namespace = "fl.pip.example"
9 | compileSdk = flutter.compileSdkVersion
10 |
11 | compileOptions {
12 | sourceCompatibility = JavaVersion.VERSION_21
13 | targetCompatibility = JavaVersion.VERSION_21
14 | }
15 |
16 | kotlinOptions {
17 | jvmTarget = JavaVersion.VERSION_21.toString()
18 | }
19 |
20 | defaultConfig {
21 | applicationId = "fl.pip.example"
22 | minSdk = flutter.minSdkVersion
23 | targetSdk = flutter.targetSdkVersion
24 | versionCode = flutter.versionCode
25 | versionName = flutter.versionName
26 | }
27 |
28 | buildTypes {
29 | release {
30 | signingConfig = signingConfigs.getByName("debug")
31 | }
32 | }
33 | }
34 |
35 | flutter {
36 | source = "../.."
37 | }
38 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'fl.pip'
2 | version '1.0'
3 |
4 | buildscript {
5 | repositories {
6 | google()
7 | mavenCentral()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:8.5.2'
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | mavenCentral()
19 | }
20 | }
21 |
22 | apply plugin: 'com.android.library'
23 | apply plugin: 'kotlin-android'
24 |
25 | android {
26 | compileSdk 34
27 |
28 | if (project.android.hasProperty("namespace")) {
29 | namespace 'fl.pip'
30 | }
31 |
32 | compileOptions {
33 | sourceCompatibility = JavaVersion.VERSION_17
34 | targetCompatibility = JavaVersion.VERSION_17
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = JavaVersion.VERSION_17.toString()
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | minSdk = 16
47 | }
48 |
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/fl/pip/FlPiPActivity.kt:
--------------------------------------------------------------------------------
1 | package fl.pip
2 |
3 | import android.content.res.Configuration
4 | import android.os.Bundle
5 | import io.flutter.embedding.android.FlutterActivity
6 |
7 | open class FlPiPActivity : FlutterActivity() {
8 | private val pipHelper: PiPHelper = PiPHelper.getInstance()
9 |
10 |
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 | pipHelper.setActivity(this, this.applicationContext)
14 | }
15 |
16 |
17 | override fun onPictureInPictureModeChanged(
18 | isInPictureInPictureMode: Boolean, newConfig: Configuration?
19 | ) {
20 | super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
21 | pipHelper.onPictureInPictureModeChanged(isInPictureInPictureMode)
22 | }
23 |
24 | override fun onPause() {
25 | super.onPause()
26 | pipHelper.onActivityPaused()
27 | }
28 |
29 | override fun onResume() {
30 | super.onResume()
31 | pipHelper.onActivityResume()
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 3.2.1
2 |
3 | * Update gradle version
4 | * Optimize background music playback on iOS backend
5 |
6 | ## 3.1.0
7 |
8 | * Breaking changes , Please refer to the example
9 | * Fix the error when calling plug-in methods in Pip mode
10 | * Modify some configuration parameters of Android and iOS
11 | * Modified examples and some documents
12 | * When the app enters the background, the picture in picture still cannot work properly on iOS
13 |
14 | ## 2.0.0
15 |
16 | * Add the `PiPStatusInfo` class and add the `isCreateNewEngine` and `isEnabledWhenBackground` for
17 | the current pip
18 | * Fixed `disable()` not working in android when `createNewEngine=true`
19 | * Change the `isActive()` return parameter to `PiPStatusInfo`
20 |
21 | ## 1.0.0
22 |
23 | * Removed `FlPiP().enableWithEngine`
24 | * Add `createNewEngine`、`enabledWhenBackground` to `FlPiPConfig()`
25 |
26 | ## 0.1.1
27 |
28 | * Fixed ios gesture conflicts
29 | * Added the method for creating an engine
30 | * Added a system-level window for android
31 |
32 | ## 0.0.1
33 |
34 | * TODO: Describe initial release.
35 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Wayaer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/fl/pip/FlPiPPlugin.kt:
--------------------------------------------------------------------------------
1 | package fl.pip
2 |
3 | import io.flutter.embedding.engine.plugins.FlutterPlugin
4 | import io.flutter.plugin.common.MethodCall
5 | import io.flutter.plugin.common.MethodChannel
6 |
7 |
8 | /** FlPiPPlugin */
9 | class FlPiPPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
10 | private lateinit var channel: MethodChannel
11 | private val pipHelper: PiPHelper = PiPHelper.getInstance()
12 | private lateinit var pluginBinding: FlutterPlugin.FlutterPluginBinding
13 | override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
14 | pluginBinding = binding
15 | channel = MethodChannel(binding.binaryMessenger, "fl_pip")
16 | channel.setMethodCallHandler(this)
17 | pipHelper.channels.add(channel)
18 | }
19 |
20 | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
21 | pipHelper.onMethodCall(call, result, pluginBinding)
22 | }
23 |
24 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
25 | channel.setMethodCallHandler(null)
26 | pipHelper.channels.remove(channel)
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '15.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:example/src/home_page.dart';
2 | import 'package:example/src/pip_home_page.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_waya/flutter_waya.dart';
5 |
6 | void main() {
7 | runApp(App(home: HomePage()));
8 | }
9 |
10 | /// mainName must be the same as the method name
11 | @pragma('vm:entry-point')
12 | void pipMain() {
13 | runApp(ClipRRect(
14 | borderRadius: BorderRadius.circular(12),
15 | child: App(home: PiPHomePage())));
16 | }
17 |
18 | class App extends StatelessWidget {
19 | const App({super.key, required this.home});
20 |
21 | final Widget home;
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | return MaterialApp(
26 | debugShowCheckedModeBanner: false,
27 | theme: ThemeData.light(),
28 | darkTheme: ThemeData.dark(),
29 | home: home);
30 | }
31 | }
32 |
33 | class Timer extends StatelessWidget {
34 | const Timer({super.key});
35 |
36 | @override
37 | Widget build(BuildContext context) => Counter.down(
38 | value: const Duration(seconds: 500),
39 | builder: (Duration duration, bool isRunning, VoidCallback startTiming,
40 | VoidCallback stopTiming) {
41 | return Padding(
42 | padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 12),
43 | child: Text('timer:${duration.inSeconds.toString()}'));
44 | });
45 | }
46 |
47 | class Filled extends StatelessWidget {
48 | const Filled({super.key, required this.text, this.onPressed});
49 |
50 | final String text;
51 | final VoidCallback? onPressed;
52 |
53 | @override
54 | Widget build(BuildContext context) {
55 | return FilledButton(
56 | onPressed: onPressed, child: Text(text, textAlign: TextAlign.center));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/example/lib/src/pip_home_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:example/main.dart';
2 | import 'package:fl_pip/fl_pip.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_waya/flutter_waya.dart';
5 |
6 | class PiPHomePage extends StatelessWidget {
7 | const PiPHomePage({super.key});
8 |
9 | @override
10 | Widget build(BuildContext context) => Scaffold(
11 | backgroundColor: Colors.white70,
12 | body: Center(
13 | child: Column(mainAxisSize: MainAxisSize.min, children: [
14 | Timer(),
15 | const Text('The current pip is created using a new engine'),
16 | Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
17 | Filled(text: 'disable', onPressed: FlPiP().disable),
18 | Filled(
19 | text: 'PiPStatus isAvailable',
20 | onPressed: () async {
21 | final state = await FlPiP().isAvailable;
22 | if (context.mounted) {
23 | ScaffoldMessenger.of(context).showSnackBar(SnackBar(
24 | content: state
25 | ? const Text('PiP available')
26 | : const Text('PiP unavailable')));
27 | }
28 | }),
29 | ]),
30 | Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
31 | Filled(
32 | text: 'foreground',
33 | onPressed: () {
34 | FlPiP().toggle(AppState.foreground);
35 | }),
36 | Filled(
37 | text: 'background',
38 | onPressed: () {
39 | FlPiP().toggle(AppState.background);
40 | }),
41 | ]),
42 | const SizedBox(
43 | height: 20,
44 | width: double.infinity,
45 | child: FlAnimationWave(
46 | value: 0.5, color: Colors.red, direction: Axis.vertical)),
47 | ])));
48 | }
49 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/ios/Classes/BackgroundAudioPlayer.swift:
--------------------------------------------------------------------------------
1 | import AVFAudio
2 | import AVKit
3 | import Foundation
4 |
5 | class BackgroundAudioPlayer: NSObject {
6 | static let shared = BackgroundAudioPlayer()
7 |
8 | var audioPlayer: AVAudioPlayer?
9 | var audioSession = AVAudioSession.sharedInstance()
10 | var backgroundTaskIdentifier: UIBackgroundTaskIdentifier?
11 |
12 | func startPlay(_ path: String) -> Bool {
13 | do {
14 | stopPlay()
15 | if !FileManager.default.fileExists(atPath: path) {
16 | return false
17 | }
18 | let audio = Bundle.main.path(forResource: path, ofType: nil)
19 | if audio != nil {
20 | // 设置后台模式和锁屏模式下依旧能够播放
21 | try audioSession.setCategory(.playback, options: .mixWithOthers)
22 | try audioSession.setActive(true)
23 | backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(withName: "FlPiPBackgroundAudio") {
24 | // 后台任务结束时的清理工作
25 | print("Background task ended.")
26 | }
27 | if audioPlayer == nil {
28 | audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: audio!))
29 | }
30 | audioPlayer!.volume = 0
31 | audioPlayer!.numberOfLoops = -1
32 | return true
33 | }
34 |
35 | } catch {
36 | print("FlPiP BackgroundAudioPlayer error")
37 | }
38 | return false
39 | }
40 |
41 | func stopPlay() {
42 | audioPlayer?.stop()
43 | audioPlayer = nil
44 | do {
45 | try audioSession.setActive(false)
46 | } catch {
47 | print("FlPiP AVAudioSession setActive error")
48 | }
49 | if backgroundTaskIdentifier != nil {
50 | UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier!)
51 | backgroundTaskIdentifier = nil
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | FlPiP
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | FlPiP
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(FLUTTER_BUILD_NAME)
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | $(FLUTTER_BUILD_NUMBER)
27 | LSRequiresIPhoneOS
28 |
29 | UIApplicationSupportsIndirectInputEvents
30 |
31 | UIBackgroundModes
32 |
33 | fetch
34 | audio
35 | processing
36 |
37 | UILaunchStoryboardName
38 | LaunchScreen
39 | UIMainStoryboardFile
40 | Main
41 | UISupportedInterfaceOrientations
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 | UISupportedInterfaceOrientations~ipad
48 |
49 | UIInterfaceOrientationPortrait
50 | UIInterfaceOrientationPortraitUpsideDown
51 | UIInterfaceOrientationLandscapeLeft
52 | UIInterfaceOrientationLandscapeRight
53 |
54 | UIViewControllerBasedStatusBarAppearance
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # fl_pip
2 |
3 | ## 基于原生ios和android的画中画模式,实现显示flutter的view,可以通过修改flutter 栈顶的view来显示任意UI
4 |
5 | ## The picture-in-picture mode is implemented in native ios and android to display flutter's view
6 |
7 | ### 目前在ios上遇到了一个问题,当app在后台的时候,FlutterUi停止运行或者画中画直接黑屏,猜测可能是由于ios冻结app导致,本人目前没有好的解决办法,如果你有想法,请提交pr
8 |
9 | ### At present, there is a problem in ios, when the app is in the background, FlutterUi will stop running or black screen directly, which may be caused by ios freezing the app, I have no good solution at present, if you have ideas, please submit PR
10 |
11 | ## Use configuration
12 |
13 | - ios 配置 : `Signing & Capabilities` -> `Capability` 添加 `BackgroundModes`
14 | 勾选 `Audio,AirPlay,And Picture in Picture`
15 | - ios configuration : `Signing & Capabilities` -> `Capability` Add `BackgroundModes`
16 | check `Audio,AirPlay,And Picture in Picture`
17 | - 修改`/ios/Runner/AppDelegate.swift` 的内容
18 | - Modify the content of `/ios/Runner/AppDelegate.swift`
19 |
20 | ### swift
21 |
22 | ```swift
23 | import fl_pip
24 | import Flutter
25 | import UIKit
26 |
27 | @main
28 | @objc class AppDelegate: FlFlutterAppDelegate {
29 | override func application(
30 | _ application: UIApplication,
31 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
32 | ) -> Bool {
33 | GeneratedPluginRegistrant.register(with: self)
34 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
35 | }
36 |
37 | override func registerPlugin(_ registry: FlutterPluginRegistry) {
38 | GeneratedPluginRegistrant.register(with: registry)
39 | }
40 | }
41 |
42 |
43 | ```
44 |
45 | - android 配置 : `android/app/src/main/${your package name}/MainActivity` 修改 MainActivity 继承,
46 | - android configuration : `android/app/src/main/${your package name}/MainActivity`,
47 |
48 | ### kotlin
49 |
50 | ```kotlin
51 |
52 | class MainActivity : FlPiPActivity()
53 |
54 | ```
55 |
56 | ### java
57 |
58 | ```java
59 |
60 | class MainActivity extends FlPiPActivity {
61 |
62 | }
63 |
64 | ```
65 |
66 | android AndroidManifest file `android/app/src/main/AndroidManifest.xml`,
67 | add ` android:supportsPictureInPicture="true"`
68 |
69 | ```xml
70 |
71 |
72 |
73 |
74 | ```
75 |
76 | ## Methods available
77 |
78 | ```dart
79 | /// 开启画中画
80 | /// Open picture-in-picture
81 | void enable() {
82 | FlPiP().enable(
83 | iosConfig: FlPiPiOSConfig(),
84 | androidConfig: FlPiPAndroidConfig(
85 | aspectRatio: const Rational.maxLandscape()));
86 | }
87 |
88 | /// 是否支持画中画
89 | /// Whether to support picture in picture
90 | void isAvailable() {
91 | FlPiP().isAvailable;
92 | }
93 |
94 | /// 画中画状态
95 | /// Picture-in-picture window state
96 | void isActive() {
97 | FlPiP().isActive;
98 | }
99 |
100 | /// 切换前后台
101 | /// Toggle front and back
102 | /// ios仅支持切换后台
103 | /// ios supports background switching only
104 | void toggle() {
105 | FlPiP().toggle();
106 | }
107 |
108 | /// 退出画中画
109 | /// Quit painting in picture
110 | void disable() {
111 | FlPiP().disable();
112 | }
113 | ```
114 |
115 | - 如果使用enableWithEngine方法必须在main文件中添加这个main方法
116 | - The main method must be added to the main file if the enableWithEngine method is used
117 |
118 | ```dart
119 | /// mainName must be the same as the method name
120 | @pragma('vm:entry-point')
121 | void pipMain() {
122 | runApp(YourApp());
123 | }
124 |
125 | ```
126 |
127 | - Android
128 |
129 | https://github.com/user-attachments/assets/1ba2238e-e556-4f87-8ccb-1b25440a6649
130 |
131 | - IOS
132 |
133 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/example/lib/src/home_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:example/main.dart';
2 | import 'package:fl_pip/fl_pip.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | const videoPath = 'assets/landscape.mp4';
6 | const closeIconPath = 'assets/close.png';
7 |
8 | class HomePage extends StatelessWidget {
9 | const HomePage({super.key});
10 |
11 | @override
12 | Widget build(BuildContext context) => Scaffold(
13 | body: SafeArea(
14 | child: SingleChildScrollView(
15 | child: Column(mainAxisSize: MainAxisSize.min, children: [
16 | SizedBox(width: double.infinity, height: 20),
17 | Timer(),
18 | Container(
19 | margin: EdgeInsets.all(20),
20 | padding: EdgeInsets.all(12),
21 | decoration: BoxDecoration(
22 | borderRadius: BorderRadius.circular(10),
23 | border: Border.all(color: Theme.of(context).dividerColor)),
24 | child: PiPBuilder(builder: (PiPStatusInfo? statusInfo) {
25 | switch (statusInfo?.status) {
26 | case PiPStatus.enabled:
27 | return builderEnabled(statusInfo);
28 | case PiPStatus.disabled:
29 | return builderDisabled;
30 | case PiPStatus.unavailable:
31 | return buildUnavailable(context);
32 | case null:
33 | return builderDisabled;
34 | }
35 | })),
36 | Filled(
37 | text: 'PiPStatus isAvailable',
38 | onPressed: () async {
39 | final state = await FlPiP().isAvailable;
40 | if (context.mounted) {
41 | ScaffoldMessenger.of(context).showSnackBar(SnackBar(
42 | content: state
43 | ? const Text('PiP available')
44 | : const Text('PiP unavailable')));
45 | }
46 | }),
47 | Filled(
48 | text: 'toggle',
49 | onPressed: () {
50 | FlPiP().toggle(AppState.background);
51 | }),
52 | ])),
53 | ));
54 |
55 | Widget builderEnabled(PiPStatusInfo? statusInfo) => Column(children: [
56 | const Text('PiPStatus enabled'),
57 | Text('isCreateNewEngine: ${statusInfo!.isCreateNewEngine}',
58 | style: const TextStyle(fontSize: 10)),
59 | Text('isEnabledWhenBackground: ${statusInfo.isEnabledWhenBackground}',
60 | style: const TextStyle(fontSize: 10)),
61 | Filled(text: 'disable', onPressed: FlPiP().disable),
62 | ]);
63 |
64 | Widget get builderDisabled =>
65 | Column(mainAxisSize: MainAxisSize.min, children: [
66 | Text('Currently using picture in picture mode'),
67 | Filled(
68 | onPressed: () async {
69 | await FlPiP().enable(
70 | ios: const FlPiPiOSConfig(
71 | videoPath: videoPath, packageName: null),
72 | android: const FlPiPAndroidConfig(
73 | aspectRatio: Rational.maxLandscape()));
74 | Future.delayed(const Duration(seconds: 10), () {
75 | FlPiP().disable();
76 | });
77 | },
78 | text: 'Enable PiP'),
79 | Text(
80 | 'The picture in picture mode will only be activated when the app enters the background'),
81 | Filled(
82 | onPressed: () {
83 | FlPiP().enable(
84 | ios: const FlPiPiOSConfig(
85 | enabledWhenBackground: true,
86 | videoPath: videoPath,
87 | packageName: null),
88 | android: const FlPiPAndroidConfig(
89 | enabledWhenBackground: true,
90 | aspectRatio: Rational.maxLandscape()));
91 | },
92 | text: 'Enabled when background'),
93 | Divider(),
94 | Text(
95 | 'This still uses picture in picture mode in iOS and has created a new FlutterEngine that cannot be shared with the current main,But in Android, the picture in picture mode is not used, and WindowManager is used, similar to a system pop-up window'),
96 | Filled(
97 | onPressed: () {
98 | FlPiP().enable(
99 | android: const FlPiPAndroidConfig(
100 | createNewEngine: true, closeIconPath: closeIconPath),
101 | ios: const FlPiPiOSConfig(
102 | createNewEngine: true,
103 | videoPath: videoPath,
104 | packageName: null));
105 | },
106 | text: 'Create new engine'),
107 | Text('Start when the app enters the background'),
108 | Filled(
109 | onPressed: () {
110 | FlPiP().enable(
111 | android: const FlPiPAndroidConfig(
112 | closeIconPath: closeIconPath,
113 | enabledWhenBackground: true,
114 | createNewEngine: true),
115 | ios: const FlPiPiOSConfig(
116 | enabledWhenBackground: true,
117 | createNewEngine: true,
118 | videoPath: videoPath,
119 | packageName: null));
120 | },
121 | text: 'Create new engine and enabled when background'),
122 | ]);
123 |
124 | Widget buildUnavailable(BuildContext context) => Filled(
125 | text: 'PiP unavailable',
126 | onPressed: () async {
127 | final state = await FlPiP().isAvailable;
128 | if (!context.mounted) return;
129 | if (!state) {
130 | ScaffoldMessenger.of(context)
131 | .showSnackBar(const SnackBar(content: Text('PiP unavailable')));
132 | }
133 | });
134 | }
135 |
--------------------------------------------------------------------------------
/lib/fl_pip.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter/services.dart';
6 |
7 | typedef PiPBuilderCallback = Widget Function(PiPStatusInfo? status);
8 |
9 | class PiPBuilder extends StatefulWidget {
10 | const PiPBuilder({
11 | super.key,
12 | required this.builder,
13 | });
14 |
15 | final PiPBuilderCallback builder;
16 |
17 | @override
18 | State createState() => _PiPBuilderState();
19 | }
20 |
21 | class _PiPBuilderState extends State {
22 | @override
23 | void initState() {
24 | super.initState();
25 | WidgetsBinding.instance.addPostFrameCallback((_) async {
26 | final value = await FlPiP().isAvailable;
27 | if (value) {
28 | FlPiP().status.addListener(listener);
29 | await FlPiP().isActive;
30 | } else {
31 | FlPiP().status.value = PiPStatusInfo(status: PiPStatus.unavailable);
32 | }
33 | });
34 | }
35 |
36 | void listener() {
37 | if (mounted) setState(() {});
38 | }
39 |
40 | @override
41 | Widget build(BuildContext context) => widget.builder(FlPiP().status.value);
42 |
43 | @override
44 | void dispose() {
45 | FlPiP().status.removeListener(listener);
46 | super.dispose();
47 | }
48 | }
49 |
50 | enum PiPStatus {
51 | /// Show picture in picture
52 | enabled,
53 |
54 | /// Does not display picture-in-picture
55 | disabled,
56 |
57 | /// Picture-in-picture is not supported
58 | unavailable
59 | }
60 |
61 | class PiPStatusInfo {
62 | PiPStatusInfo(
63 | {required this.status,
64 | this.isCreateNewEngine = false,
65 | this.isEnabledWhenBackground = false});
66 |
67 | PiPStatusInfo.fromMap(Map map)
68 | : status = PiPStatus.values[map['status'] as int],
69 | isCreateNewEngine = map['createNewEngine'] as bool,
70 | isEnabledWhenBackground = map['enabledWhenBackground'] as bool;
71 |
72 | /// pip status
73 | final PiPStatus status;
74 |
75 | /// is create new engine
76 | final bool isCreateNewEngine;
77 |
78 | /// is enabled when background
79 | final bool isEnabledWhenBackground;
80 | }
81 |
82 | const _channel = MethodChannel('fl_pip');
83 |
84 | class FlPiP {
85 | factory FlPiP() => _singleton ??= FlPiP._();
86 |
87 | static FlPiP? _singleton;
88 |
89 | FlPiP._() {
90 | _channel.setMethodCallHandler((call) async {
91 | switch (call.method) {
92 | case 'onPiPStatus':
93 | status.value = PiPStatusInfo.fromMap(call.arguments);
94 | break;
95 | }
96 | });
97 | }
98 |
99 | final ValueNotifier status = ValueNotifier(null);
100 |
101 | /// 开启画中画
102 | /// enable picture-in-picture
103 | Future enable({
104 | FlPiPAndroidConfig android = const FlPiPAndroidConfig(),
105 | FlPiPiOSConfig ios = const FlPiPiOSConfig(),
106 | }) async {
107 | if (!(_isAndroid || _isIOS)) {
108 | return false;
109 | }
110 |
111 | if (_isAndroid &&
112 | !(android.aspectRatio.fitsInAndroidRequirements) &&
113 | !android.createNewEngine) {
114 | throw RationalNotMatchingAndroidRequirementsException(
115 | android.aspectRatio);
116 | }
117 | final state = await _channel.invokeMethod(
118 | 'enable', _isAndroid ? android.toMap() : ios.toMap());
119 | return state ?? false;
120 | }
121 |
122 | /// 关闭画中画
123 | /// disable picture-in-picture
124 | Future disable() async {
125 | final state = await _channel.invokeMethod('disable');
126 | return state ?? false;
127 | }
128 |
129 | /// 画中画状态
130 | /// Picture-in-picture window state
131 | Future get isActive async {
132 | final map = await _channel.invokeMethod