├── .gitignore
├── LICENSE
├── docs
├── .nojekyll
├── CNAME
├── _sidebar.md
├── assets
│ ├── full.gif
│ ├── logo.svg
│ ├── pip.gif
│ └── q2.gif
├── file-picker.md
├── home.md
├── index.html
├── listening-player-events.md
├── picture-in-picture.md
└── variables.json
└── plugin
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── app
│ └── meedu
│ └── player
│ ├── MeeduPlayerFlutterActivity.java
│ └── MeeduPlayerPlugin.java
├── assets
└── icons
│ ├── fast-forward.png
│ ├── fit.png
│ ├── fullscreen.png
│ ├── minimize.png
│ ├── mute.png
│ ├── pause.png
│ ├── picture-in-picture.png
│ ├── play.png
│ ├── repeat.png
│ ├── rewind.png
│ └── sound.png
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ └── app
│ │ │ │ │ └── meedu
│ │ │ │ │ └── player_example
│ │ │ │ │ └── MainActivity.java
│ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── 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
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle
│ └── settings_aar.gradle
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── 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-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
├── lib
│ ├── main.dart
│ └── pages
│ │ ├── basic_example_page.dart
│ │ ├── change_quality_example_page.dart
│ │ ├── custom_icons_example.dart
│ │ ├── disabled_buttons_example_page.dart
│ │ ├── fullscreen_example_page.dart
│ │ ├── listview_example.dart
│ │ ├── network_with_subtitle_page.dart
│ │ ├── one_page_to_other_page_example.dart
│ │ ├── pick_file_page_example.dart
│ │ ├── playback_speed_example_page.dart
│ │ └── player_with_header_page.dart
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── MeeduPlayerPlugin.h
│ ├── MeeduPlayerPlugin.m
│ └── SwiftMeeduPlayerPlugin.swift
└── meedu_player.podspec
├── lib
├── meedu_player.dart
└── src
│ ├── controller.dart
│ ├── helpers
│ ├── custom_icons.dart
│ ├── data_source.dart
│ ├── enabled_buttons.dart
│ ├── meedu_player_status.dart
│ ├── player_data_status.dart
│ ├── responsive.dart
│ ├── screen_manager.dart
│ └── utils.dart
│ ├── native
│ └── pip_manager.dart
│ └── widgets
│ ├── closed_caption_view.dart
│ ├── fullscreen_button.dart
│ ├── fullscreen_page.dart
│ ├── meedu_video_player.dart
│ ├── mute_sound_button.dart
│ ├── pip_button.dart
│ ├── play_pause_button.dart
│ ├── player_button.dart
│ ├── player_slider.dart
│ ├── styles
│ ├── controls_container.dart
│ ├── primary
│ │ ├── bottom_controls.dart
│ │ └── primary_player_controls.dart
│ └── secondary
│ │ ├── secondary_bottom_controls.dart
│ │ └── secondary_player_controls.dart
│ └── video_fit_button.dart
├── player.iml
├── pubspec.lock
└── pubspec.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | **/.DS_Store
2 | .vscode
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 MEEDU.APP
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.
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | player.meedu.app
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | - [Quick Start](/)
2 | - [Picture In Picture](picture-in-picture.md)
3 | - [Listening the player events](listening-player-events.md)
4 | - [File Picker and Android 11](file-picker.md)
--------------------------------------------------------------------------------
/docs/assets/full.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/docs/assets/full.gif
--------------------------------------------------------------------------------
/docs/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
68 |
--------------------------------------------------------------------------------
/docs/assets/pip.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/docs/assets/pip.gif
--------------------------------------------------------------------------------
/docs/assets/q2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/docs/assets/q2.gif
--------------------------------------------------------------------------------
/docs/file-picker.md:
--------------------------------------------------------------------------------
1 |
2 | If you are using the plugin [file_picker](https://pub.dev/packages/file_picker) maybe you need to add suport for android 11.
3 |
4 | Go to your `build.gradle` in `/android/build.gradle` and update your **build tools** to 4.0.1 or higher
5 | ```
6 | classpath 'com.android.tools.build:gradle:4.0.1'
7 | ```
8 |
--------------------------------------------------------------------------------
/docs/home.md:
--------------------------------------------------------------------------------
1 | # meedu_player
2 |
3 |
4 |
5 |
6 |
7 | > Modern video player UI for [video_player](https://pub.dev/packages/video_player)
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | | Features | iOS | Android |
20 | | ------------- | ------------- | ------------- |
21 | | Videos from Network | ✅ | ✅ |
22 | | Videos from Assets | ✅ | ✅ |
23 | | Videos from local files | ✅ | ✅ |
24 | | Looping | ✅ | ✅ |
25 | | Autoplay | ✅ | ✅ |
26 | | Mute / Sound | ✅ | ✅ |
27 | | Fullscreen | ✅ | ✅ |
28 | | Launch Player as Fullscreen | ✅ | ✅ |
29 | | Playback Speed | ✅ | ✅ |
30 | | fastForward / Rewind | ✅ | ✅ |
31 | | srt subtitles | ✅ | ✅ |
32 | | Customize | partially | partially |
33 | | Picture in Picture | ❌ | ✅ |
34 |
35 |
36 |
37 | > For correctly works don't use a device simulator (iOS) or emulator (Android)
38 |
39 |
40 |
41 |
42 | # Add to project
43 |
44 | Add the following to your `pubspec.yaml`:
45 |
46 | ```
47 | dependencies:
48 | meedu_player: ^${$.var.version}
49 | ```
50 |
51 | ---
52 |
53 | # Configuration
54 |
55 | > If you want to use urls with `http://` you need a little configuration.
56 |
57 | ## Android
58 |
59 | On Android go to your `/android/app/src/main/AndroidManifest.xml`:
60 |
61 | Add the next permission
62 | ``
63 |
64 | And add `android:usesCleartextTraffic="true"` in your Application tag.
65 |
66 |
67 | ## iOS
68 |
69 | Warning: The video player is not functional on iOS simulators. An iOS device must be used during development/testing.
70 |
71 | Add the following entry to your Info.plist file, located in `/ios/Runner/Info.plist`:
72 |
73 | ```xml
74 | NSAppTransportSecurity
75 |
76 | NSAllowsArbitraryLoads
77 |
78 |
79 | ```
80 |
81 | In your `Podfile` you need set a minimum target version like 9.0 or higher
82 |
83 | ```
84 | platform :ios, '9.0'
85 | ```
86 |
87 | ---
88 |
89 | # How to use
90 |
91 | Fisrt you need to import the `meedu_player` plugin and create an instance of `MeeduPlayerController`
92 |
93 | ```dart
94 | import 'package:meedu_player/meedu_player.dart';
95 | .
96 | .
97 | .
98 | final _meeduPlayerController = MeeduPlayerController();
99 | ```
100 |
101 | > When you create an instance of `MeeduPlayerController` you can pass params like `controlsStyle, colorTheme, placeholder, etc`.
102 |
103 | Now you can use the `MeeduVideoPlayer` widget to show your video, the `MeeduVideoPlayer` widget takes the size of its parent container (we recomend to use an AspectRatio widget)
104 | ```dart
105 | AspectRatio(
106 | aspectRatio: 16 / 9,
107 | child: MeeduVideoPlayer(
108 | controller: _meeduPlayerController,
109 | ),
110 | ),
111 | ```
112 |
113 | In this moment you only can watch a **black** `Container` that is because you need to set a `DataSource` and pass it to your `MeeduPlayerController` instance.
114 |
115 |
116 |
117 | To play a video from **network**
118 | ```dart
119 | _meeduPlayerController.setDataSource(
120 | DataSource(
121 | type: DataSourceType.network,
122 | source: "https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4",
123 | ),
124 | autoplay: true,
125 | );
126 | ```
127 |
128 |
129 |
130 | To play a video from **assets** (Make sure that your asset is defined in your `pubspec.yaml`)
131 | ```dart
132 | _meeduPlayerController.setDataSource(
133 | DataSource(
134 | type: DataSourceType.asset,
135 | source: "assets/video/big-buck-bunny-360p.mp4",
136 | ),
137 | autoplay: true,
138 | );
139 | ```
140 |
141 |
142 | To play a video from a local **File**
143 | ```dart
144 | _meeduPlayerController.setDataSource(
145 | DataSource(
146 | type: DataSourceType.file,
147 | file: ,
148 | ),
149 | autoplay: true,
150 | );
151 | ```
152 |
153 |
154 |
155 | > **IMPORTANT:** When you don't need more the player you need to call to `dispose` to release the player
156 | ```dart
157 | _meeduPlayerController.dispose();
158 | ```
159 |
160 | ---
161 |
162 | # Basic example
163 |
164 | > We are using [wakelock](https://pub.dev/packages/wakelock) to keep the screen on when the video player is using
165 |
166 | ```dart
167 | import 'package:flutter/material.dart';
168 | import 'package:meedu_player/meedu_player.dart';
169 | import 'package:wakelock/wakelock.dart';
170 |
171 | class BasicExamplePage extends StatefulWidget {
172 | BasicExamplePage({Key? key}) : super(key: key);
173 |
174 | @override
175 | _BasicExamplePageState createState() => _BasicExamplePageState();
176 | }
177 |
178 | class _BasicExamplePageState extends State {
179 | final _meeduPlayerController = MeeduPlayerController(
180 | controlsStyle: ControlsStyle.primary,
181 | );
182 |
183 | @override
184 | void initState() {
185 | super.initState();
186 | // The following line will enable the Android and iOS wakelock.
187 | Wakelock.enable();
188 |
189 | // Wait until the fisrt render the avoid posible errors when use an context while the view is rendering
190 | WidgetsBinding.instance!.addPostFrameCallback((_) {
191 | _init();
192 | });
193 | }
194 |
195 | @override
196 | void dispose() {
197 | // The next line disables the wakelock again.
198 | Wakelock.disable();
199 | _meeduPlayerController.dispose();// release the video player
200 | super.dispose();
201 | }
202 |
203 | /// play a video from network
204 | _init() {
205 | _meeduPlayerController.setDataSource(
206 | DataSource(
207 | type: DataSourceType.network,
208 | source: "https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4",
209 | ),
210 | autoplay: true,
211 | );
212 | }
213 |
214 | @override
215 | Widget build(BuildContext context) {
216 | return Scaffold(
217 | appBar: AppBar(),
218 | body: SafeArea(
219 | child: AspectRatio(
220 | aspectRatio: 16 / 9,
221 | child: MeeduVideoPlayer(
222 | controller: _meeduPlayerController,
223 | ),
224 | ),
225 | ),
226 | );
227 | }
228 | }
229 | ```
230 |
231 |
232 |
233 |
234 |
235 |
236 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
11 |
12 |
17 |
18 |
19 |
20 |
24 | Please wait...
25 |
26 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/docs/listening-player-events.md:
--------------------------------------------------------------------------------
1 | # Listening the player events
2 |
3 | To listen some events like loading, error, playing, paused, finished, etc. You can listen the stream events.
4 |
5 | For example to keep the screen on when the video player is playing a video
6 |
7 | ```dart
8 | StreamSubscription _playerEventSubs;
9 | .
10 | .
11 | .
12 | // in your initState or any other method
13 | _playerEventSubs = _meeduPlayerController.onPlayerStatusChanged.listen(
14 | (PlayerStatus status) {
15 | if (status == PlayerStatus.playing) {
16 | Wakelock.enable();// keep the screen on
17 | } else { // if the video is finished or paused
18 | Wakelock.disable();
19 | }
20 | },
21 | );
22 | .
23 | .
24 | .
25 | // DON'T FORGET CANCEL YOUR SUBSCRIPTIONS
26 | @override
27 | void dispose() {
28 | // The next line disables the wakelock again.
29 | _playerEventSubs?.cancel();
30 | Wakelock.disable();// if you are using wakelock
31 | _meeduPlayerController.dispose();
32 | super.dispose();
33 | }
34 | ```
35 |
36 | ## Streams
37 |
38 | - `Stream onDataStatusChanged`
39 | Stream to listen changes in the `dataSource` as **none, loading, loaded**, or **error**.
40 |
41 | - `Stream onPlayerStatusChanged`
42 | Stream to listen changes in the player as **playing, paused**, or **stopped**.
43 |
44 | - `Stream onPositionChanged`
45 | Stream to listen the video position
46 |
47 | - `Stream onDurationChanged`
48 | Stream to listen the video duration
49 |
50 | - `Stream onFullscreenChanged`
51 | Stream to listen when the player enters or exits from fullscreen
52 |
53 | - `Stream onShowControlsChanged`
54 | Stream to listen when the controls are visible or hidden
55 |
--------------------------------------------------------------------------------
/docs/picture-in-picture.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Picture in Picture
4 |
5 |
6 |
7 | > Only **Android** is supported for now, pip mode on iOS is supported since iOS 14 but the flutter SDK actually does not have a stable support for iOS 14
8 |
9 | ## Android
10 | To enable the picture in picture mode on Android you need the next requirements.
11 |
12 | First to allow to **meedu_player** listen the picture in picture events your `MainAcvity` class must extends from `MeeduPlayerFlutterActivity`
13 | ```java
14 | import app.meedu.player.MeeduPlayerFlutterActivity;
15 |
16 | public class MainActivity extends MeeduPlayerFlutterActivity {
17 | ...
18 | }
19 | ```
20 | > The `MeeduPlayerFlutterActivity` class extends from `FlutterActivity` and overrides the `onPictureInPictureModeChanged` method to listen the changes in the pip mode.
21 |
22 | In your `AndroidManifest.xml` in your MainActivity tag you must enable `android:supportsPictureInPicture` and `android:resizeableActivity`
23 | ```xml
24 | **NOTE:** The picture in picture mode is only available since **Android 7**
32 |
33 |
34 |
35 | > When you create your instance of `MeeduPlayerController` you need pass the `pipEnabled` param as **true** and if you want to show the **pip button** in the controls you can pass the `showPipButton` param and then you don't need call to `enterPip` method.
36 | ```dart
37 | final _meeduPlayerController = MeeduPlayerController(
38 | controlsStyle: ControlsStyle.primary,
39 | pipEnabled: true, // use false to hide pip button in the player
40 | showPipButton: true,
41 | );
42 | ```
43 |
44 | To enter to the picture in picture mode you can call the `enterPip` method
45 | ```dart
46 | _meeduPlayerController.enterPip(context);
47 | ```
--------------------------------------------------------------------------------
/docs/variables.json:
--------------------------------------------------------------------------------
1 | {
2 | "var": {
3 | "version": "0.5.0-nullsafety.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/plugin/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | .dart_tool/
4 |
5 | .packages
6 | .pub/
7 |
8 | build/
9 |
--------------------------------------------------------------------------------
/plugin/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: bbfbf1770cca2da7c82e887e4e4af910034800b6
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/plugin/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.5.0-nullsafety.0]
2 | - Added support for flutter 2 and null safety.
3 | ## [0.4.3]
4 | - Added video fit feature by [Utkarsh Sharma](https://github.com/uttusharma).
5 |
6 | ## [0.4.2]
7 | - Fixed loading animation autoplay false.
8 |
9 | ## [0.4.1+1]
10 | - Removed logs.
11 | ## [0.4.1]
12 | - Added loading view while the video is playing.
13 | - Removed GetX.
14 | - Updated video_player to 1.0.1
15 |
16 | ## [0.4.0+3]
17 | - Updated documentation.
18 | ## [0.4.0+2]
19 | - Fixed broken urls in documentation.
20 | ## [0.4.0+1]
21 | - Fixed broken url in documentation.
22 | ## [0.4.0]
23 | - Added custom icons
24 | - Added hide buttons support
25 | - Updated examples
26 | - Updated to video_player:^1.0.0
27 | - Updated to get:^3.15.0
28 |
29 | ## [0.3.4]
30 | - Updated examples
31 |
32 | ## [0.3.3]
33 | - Removed GetxController
34 |
35 | ## [0.3.2]
36 | - Remove listeners before dispose
37 |
38 | ## [0.3.1]
39 |
40 | - Updated documentation
41 | - Added picture in picture support for Android
42 |
43 | ## [0.3.0]
44 |
45 | - Breaking changes
46 | - Use GetX to manage the video player state
47 | - Added Playback Speed
48 | - Added setLooping
49 | - `MeeduPlayer` has been changed for `MeeduVideoPlayer`
50 | - Removed `MeeduPlayerEventsMixin` now to listen the players events you can use the streams
51 | - Added mutiples controls styles
52 | - Updated the examples
53 | - Added Official documentation
54 |
55 | ## [0.2.3]
56 |
57 | - Fixed rotation after close fullscreen mode
58 |
59 | ## [0.2.2]
60 |
61 | - Added onPlayerPlaying event
62 | - Updated example
63 |
64 | ## [0.2.1]
65 |
66 | - updated doc
67 |
68 | ## [0.2.0]
69 |
70 | - added auto dismiss controls, launch fullscreen as landscape mode
71 |
72 | ## [0.1.2]
73 |
74 | - updated examples
75 |
76 | ## [0.1.1]
77 |
78 | - added fullscreen events listener
79 |
80 | ## [0.1.0+1]
81 |
82 | - added license
83 |
84 | ## [0.1.0]
85 |
86 | - added launch as fullscreen
87 |
88 | ## [0.0.4]
89 |
90 | - removed unnecesary imports
91 |
92 | ## [0.0.3+1]
93 |
94 | - added exmaples
95 |
96 | ## [0.0.3]
97 |
98 | - added suport to subtitles (STR) and switch video resolution
99 |
100 | ## [0.0.2]
101 |
102 | - added Fullscreen support
103 |
104 | ## [0.0.1] - initial version
105 |
106 | - minimal version
107 |
--------------------------------------------------------------------------------
/plugin/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 MEEDU.APP
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.
--------------------------------------------------------------------------------
/plugin/README.md:
--------------------------------------------------------------------------------
1 | # meedu_player
2 |
3 |
4 |
5 |
6 |
7 | > Modern video player UI for [video_player](https://pub.dev/packages/video_player)
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | | Features | iOS | Android |
16 | | ------------- | ------------- | ------------- |
17 | | Videos from Network | ✅ | ✅ |
18 | | Videos from Assets | ✅ | ✅ |
19 | | Videos from local files | ✅ | ✅ |
20 | | Looping | ✅ | ✅ |
21 | | Autoplay | ✅ | ✅ |
22 | | Mute / Sound | ✅ | ✅ |
23 | | Fullscreen | ✅ | ✅ |
24 | | Launch Player as Fullscreen | ✅ | ✅ |
25 | | Playback Speed | ✅ | ✅ |
26 | | fastForward / Rewind | ✅ | ✅ |
27 | | srt subtitles | ✅ | ✅ |
28 | | Customize | partially | partially |
29 | | Picture in Picture | ❌ | ✅ |
30 |
31 | ---
32 |
33 | 👋 👉 [Complete documentation here](https://player.meedu.app)
--------------------------------------------------------------------------------
/plugin/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/plugin/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'app.meedu.player'
2 | version '1.0'
3 |
4 | buildscript {
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.5.0'
12 | }
13 | }
14 |
15 | rootProject.allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | apply plugin: 'com.android.library'
23 |
24 | android {
25 | compileSdkVersion 28
26 |
27 | defaultConfig {
28 | minSdkVersion 16
29 | }
30 | lintOptions {
31 | disable 'InvalidPackage'
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/plugin/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/plugin/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
6 |
--------------------------------------------------------------------------------
/plugin/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/plugin/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/plugin/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'player'
2 |
--------------------------------------------------------------------------------
/plugin/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/plugin/android/src/main/java/app/meedu/player/MeeduPlayerFlutterActivity.java:
--------------------------------------------------------------------------------
1 | package app.meedu.player;
2 |
3 | import android.content.res.Configuration;
4 |
5 | import io.flutter.embedding.android.FlutterActivity;
6 |
7 | public class MeeduPlayerFlutterActivity extends FlutterActivity {
8 |
9 | OnPictureInPictureListener onPictureInPictureListener;
10 |
11 |
12 | @Override
13 | public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
14 | super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
15 | if (onPictureInPictureListener != null) {
16 | onPictureInPictureListener.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
17 | }
18 | }
19 | }
20 |
21 |
22 | interface OnPictureInPictureListener {
23 | void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig);
24 | }
--------------------------------------------------------------------------------
/plugin/android/src/main/java/app/meedu/player/MeeduPlayerPlugin.java:
--------------------------------------------------------------------------------
1 | package app.meedu.player;
2 |
3 | import android.app.Activity;
4 | import android.content.res.Configuration;
5 | import android.os.Build;
6 |
7 | import androidx.annotation.NonNull;
8 |
9 |
10 | import io.flutter.embedding.engine.plugins.FlutterPlugin;
11 | import io.flutter.embedding.engine.plugins.activity.ActivityAware;
12 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
13 | import io.flutter.plugin.common.MethodCall;
14 | import io.flutter.plugin.common.MethodChannel;
15 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
16 | import io.flutter.plugin.common.MethodChannel.Result;
17 | import io.flutter.plugin.common.PluginRegistry.Registrar;
18 |
19 | /**
20 | * PlayerPlugin
21 | */
22 | public class MeeduPlayerPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, OnPictureInPictureListener {
23 | /// The MethodChannel that will the communication between Flutter and native Android
24 | ///
25 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it
26 | /// when the Flutter Engine is detached from the Activity
27 | private MethodChannel channel;
28 | private Activity activity;
29 |
30 | @Override
31 | public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
32 | channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "app.meedu.player");
33 | channel.setMethodCallHandler(this);
34 | }
35 |
36 | // This static function is optional and equivalent to onAttachedToEngine. It supports the old
37 | // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
38 | // plugin registration via this function while apps migrate to use the new Android APIs
39 | // post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
40 | //
41 | // It is encouraged to share logic between onAttachedToEngine and registerWith to keep
42 | // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
43 | // depending on the user's project. onAttachedToEngine or registerWith must both be defined
44 | // in the same class.
45 | public static void registerWith(Registrar registrar) {
46 | final MethodChannel channel = new MethodChannel(registrar.messenger(), "player");
47 | channel.setMethodCallHandler(new MeeduPlayerPlugin());
48 | }
49 |
50 | @Override
51 | public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
52 |
53 | switch (call.method) {
54 | case "initPipConfiguration":
55 | this.initPipConfiguration();
56 | result.success(null);
57 | break;
58 | case "osVersion":
59 | result.success(Build.VERSION.RELEASE);
60 | break;
61 |
62 | case "enterPip":
63 | this.enterPipMode();
64 | result.success(null);
65 | break;
66 | default:
67 | result.notImplemented();
68 | }
69 | }
70 |
71 | @Override
72 | public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
73 | channel.setMethodCallHandler(null);
74 | }
75 |
76 |
77 | private void initPipConfiguration() {
78 | ((MeeduPlayerFlutterActivity) this.activity).onPictureInPictureListener = this;
79 | }
80 |
81 | /**
82 | * Start the picture in picture mode
83 | */
84 | private void enterPipMode() {
85 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
86 | if (activity != null) {
87 | this.activity.enterPictureInPictureMode();
88 | }
89 | }
90 | }
91 |
92 | @Override
93 | public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
94 | this.activity = binding.getActivity();
95 | }
96 |
97 | @Override
98 | public void onDetachedFromActivityForConfigChanges() {
99 |
100 | }
101 |
102 | @Override
103 | public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
104 |
105 | }
106 |
107 | @Override
108 | public void onDetachedFromActivity() {
109 |
110 | }
111 |
112 |
113 | @Override
114 | public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
115 | channel.invokeMethod("onPictureInPictureModeChanged", isInPictureInPictureMode);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/plugin/assets/icons/fast-forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/fast-forward.png
--------------------------------------------------------------------------------
/plugin/assets/icons/fit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/fit.png
--------------------------------------------------------------------------------
/plugin/assets/icons/fullscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/fullscreen.png
--------------------------------------------------------------------------------
/plugin/assets/icons/minimize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/minimize.png
--------------------------------------------------------------------------------
/plugin/assets/icons/mute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/mute.png
--------------------------------------------------------------------------------
/plugin/assets/icons/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/pause.png
--------------------------------------------------------------------------------
/plugin/assets/icons/picture-in-picture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/picture-in-picture.png
--------------------------------------------------------------------------------
/plugin/assets/icons/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/play.png
--------------------------------------------------------------------------------
/plugin/assets/icons/repeat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/repeat.png
--------------------------------------------------------------------------------
/plugin/assets/icons/rewind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/rewind.png
--------------------------------------------------------------------------------
/plugin/assets/icons/sound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/assets/icons/sound.png
--------------------------------------------------------------------------------
/plugin/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Exceptions to above rules.
44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
45 |
--------------------------------------------------------------------------------
/plugin/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: bbfbf1770cca2da7c82e887e4e4af910034800b6
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/plugin/example/README.md:
--------------------------------------------------------------------------------
1 | # player_example
2 |
3 | Demonstrates how to use the player 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://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/plugin/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/plugin/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 28
29 |
30 | lintOptions {
31 | disable 'InvalidPackage'
32 | }
33 |
34 | defaultConfig {
35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
36 | applicationId "app.meedu.player_example"
37 | minSdkVersion 21
38 | targetSdkVersion 28
39 | versionCode flutterVersionCode.toInteger()
40 | versionName flutterVersionName
41 | }
42 |
43 | buildTypes {
44 | release {
45 | // TODO: Add your own signing config for the release build.
46 | // Signing with the debug keys for now, so `flutter run --release` works.
47 | signingConfig signingConfigs.debug
48 | }
49 | }
50 | }
51 |
52 | flutter {
53 | source '../..'
54 | }
55 |
--------------------------------------------------------------------------------
/plugin/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
9 |
14 |
23 |
27 |
31 |
36 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/java/app/meedu/player_example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package app.meedu.player_example;
2 |
3 | import app.meedu.player.MeeduPlayerFlutterActivity;
4 |
5 | public class MainActivity extends MeeduPlayerFlutterActivity {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/plugin/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/plugin/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:4.0.2'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/plugin/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/plugin/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/plugin/example/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/plugin/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/plugin/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/plugin/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/plugin/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/plugin/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.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 |
--------------------------------------------------------------------------------
/plugin/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - DKImagePickerController/Core (4.3.2):
3 | - DKImagePickerController/ImageDataManager
4 | - DKImagePickerController/Resource
5 | - DKImagePickerController/ImageDataManager (4.3.2)
6 | - DKImagePickerController/PhotoGallery (4.3.2):
7 | - DKImagePickerController/Core
8 | - DKPhotoGallery
9 | - DKImagePickerController/Resource (4.3.2)
10 | - DKPhotoGallery (0.0.17):
11 | - DKPhotoGallery/Core (= 0.0.17)
12 | - DKPhotoGallery/Model (= 0.0.17)
13 | - DKPhotoGallery/Preview (= 0.0.17)
14 | - DKPhotoGallery/Resource (= 0.0.17)
15 | - SDWebImage
16 | - SwiftyGif
17 | - DKPhotoGallery/Core (0.0.17):
18 | - DKPhotoGallery/Model
19 | - DKPhotoGallery/Preview
20 | - SDWebImage
21 | - SwiftyGif
22 | - DKPhotoGallery/Model (0.0.17):
23 | - SDWebImage
24 | - SwiftyGif
25 | - DKPhotoGallery/Preview (0.0.17):
26 | - DKPhotoGallery/Model
27 | - DKPhotoGallery/Resource
28 | - SDWebImage
29 | - SwiftyGif
30 | - DKPhotoGallery/Resource (0.0.17):
31 | - SDWebImage
32 | - SwiftyGif
33 | - file_picker (0.0.1):
34 | - DKImagePickerController/PhotoGallery
35 | - Flutter
36 | - Flutter (1.0.0)
37 | - meedu_player (0.3.4):
38 | - Flutter
39 | - SDWebImage (5.9.5):
40 | - SDWebImage/Core (= 5.9.5)
41 | - SDWebImage/Core (5.9.5)
42 | - SwiftyGif (5.3.0)
43 | - video_player (0.0.1):
44 | - Flutter
45 | - wakelock (0.0.1):
46 | - Flutter
47 |
48 | DEPENDENCIES:
49 | - file_picker (from `.symlinks/plugins/file_picker/ios`)
50 | - Flutter (from `Flutter`)
51 | - meedu_player (from `.symlinks/plugins/meedu_player/ios`)
52 | - video_player (from `.symlinks/plugins/video_player/ios`)
53 | - wakelock (from `.symlinks/plugins/wakelock/ios`)
54 |
55 | SPEC REPOS:
56 | trunk:
57 | - DKImagePickerController
58 | - DKPhotoGallery
59 | - SDWebImage
60 | - SwiftyGif
61 |
62 | EXTERNAL SOURCES:
63 | file_picker:
64 | :path: ".symlinks/plugins/file_picker/ios"
65 | Flutter:
66 | :path: Flutter
67 | meedu_player:
68 | :path: ".symlinks/plugins/meedu_player/ios"
69 | video_player:
70 | :path: ".symlinks/plugins/video_player/ios"
71 | wakelock:
72 | :path: ".symlinks/plugins/wakelock/ios"
73 |
74 | SPEC CHECKSUMS:
75 | DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
76 | DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
77 | file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
78 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
79 | meedu_player: 443190e5cf1247367384643b594defbffc094b81
80 | SDWebImage: 0b2ba0d56479bf6a45ecddbfd5558bea93150d25
81 | SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
82 | video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
83 | wakelock: 0d4a70faf8950410735e3f61fb15d517c8a6efc4
84 |
85 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
86 |
87 | COCOAPODS: 1.10.0
88 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/plugin/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 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/plugin/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 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/plugin/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.
--------------------------------------------------------------------------------
/plugin/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 |
--------------------------------------------------------------------------------
/plugin/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 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | player_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | NSAppTransportSecurity
45 |
46 | NSAllowsArbitraryLoads
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/plugin/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/plugin/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:player_example/pages/basic_example_page.dart';
3 | import 'package:player_example/pages/change_quality_example_page.dart';
4 | import 'package:player_example/pages/custom_icons_example.dart';
5 | import 'package:player_example/pages/disabled_buttons_example_page.dart';
6 | import 'package:player_example/pages/fullscreen_example_page.dart';
7 | import 'package:player_example/pages/listview_example.dart';
8 | import 'package:player_example/pages/network_with_subtitle_page.dart';
9 | import 'package:player_example/pages/one_page_to_other_page_example.dart';
10 | import 'package:player_example/pages/pick_file_page_example.dart';
11 | import 'package:player_example/pages/playback_speed_example_page.dart';
12 | import 'package:player_example/pages/player_with_header_page.dart';
13 |
14 | void main() {
15 | runApp(MyApp());
16 | }
17 |
18 | class MyApp extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return MaterialApp(
22 | home: HomePage(),
23 | routes: {
24 | "basic": (_) => BasicExamplePage(),
25 | "fullscreen": (_) => FullscreenExamplePage(),
26 | "with-header": (_) => PlayerWithHeaderPage(),
27 | "subtitles": (_) => NetworkWithSubtitlesPage(),
28 | "playback-speed": (_) => PlayBackSpeedExamplePage(),
29 | "quality-change": (_) => ChangeQualityExamplePage(),
30 | "one-page-to-other": (_) => OnePageExample(),
31 | "pick-file": (_) => PickFileExamplePage(),
32 | "custom-icons": (_) => CustomIconsExamplePage(),
33 | "disabled-buttons": (_) => DisabledButtonsExample(),
34 | "listview": (_) => ListViewExample(),
35 | },
36 | );
37 | }
38 | }
39 |
40 | class HomePage extends StatefulWidget {
41 | const HomePage({Key? key}) : super(key: key);
42 |
43 | @override
44 | _HomePageState createState() => _HomePageState();
45 | }
46 |
47 | class _HomePageState extends State {
48 | @override
49 | void initState() {
50 | super.initState();
51 | // SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
52 | }
53 |
54 | @override
55 | Widget build(BuildContext context) {
56 | return Scaffold(
57 | body: ListView(
58 | children: [
59 | TextButton(
60 | onPressed: () {
61 | Navigator.pushNamed(context, 'basic');
62 | },
63 | child: Text("Basic Network example"),
64 | ),
65 | TextButton(
66 | onPressed: () {
67 | Navigator.pushNamed(context, 'fullscreen');
68 | },
69 | child: Text("Fullscreen example"),
70 | ),
71 | TextButton(
72 | onPressed: () {
73 | Navigator.pushNamed(context, 'with-header');
74 | },
75 | child: Text("With header example"),
76 | ),
77 | TextButton(
78 | onPressed: () {
79 | Navigator.pushNamed(context, 'subtitles');
80 | },
81 | child: Text("With subtitles example"),
82 | ),
83 | TextButton(
84 | onPressed: () {
85 | Navigator.pushNamed(context, 'playback-speed');
86 | },
87 | child: Text("Playback speed example"),
88 | ),
89 | TextButton(
90 | onPressed: () {
91 | Navigator.pushNamed(context, 'quality-change');
92 | },
93 | child: Text("Quality Change example"),
94 | ),
95 | TextButton(
96 | onPressed: () {
97 | Navigator.pushNamed(context, 'one-page-to-other');
98 | },
99 | child: Text("One Page to other"),
100 | ),
101 | TextButton(
102 | onPressed: () {
103 | Navigator.pushNamed(context, 'pick-file');
104 | },
105 | child: Text("Pick file"),
106 | ),
107 | TextButton(
108 | onPressed: () {
109 | Navigator.pushNamed(context, 'custom-icons');
110 | },
111 | child: Text("Custom Icons"),
112 | ),
113 | TextButton(
114 | onPressed: () {
115 | Navigator.pushNamed(context, 'disabled-buttons');
116 | },
117 | child: Text("Disabled Buttons"),
118 | ),
119 | TextButton(
120 | onPressed: () {
121 | Navigator.pushNamed(context, 'listview');
122 | },
123 | child: Text("ListView"),
124 | )
125 | ],
126 | ),
127 | );
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/basic_example_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:meedu_player/meedu_player.dart';
5 | import 'package:wakelock/wakelock.dart';
6 |
7 | class BasicExamplePage extends StatefulWidget {
8 | BasicExamplePage({Key? key}) : super(key: key);
9 |
10 | @override
11 | _BasicExamplePageState createState() => _BasicExamplePageState();
12 | }
13 |
14 | class _BasicExamplePageState extends State {
15 | // read the documentation https://the-meedu-app.github.io/flutter-meedu-player/#/picture-in-picture
16 | // to enable the pip (picture in picture) support on Android
17 | final _meeduPlayerController = MeeduPlayerController(
18 | controlsStyle: ControlsStyle.primary,
19 | pipEnabled: true, // enable pip on android
20 | showPipButton: true, // use false to hide pip button in the player
21 | );
22 |
23 | StreamSubscription? _playerEventSubs;
24 |
25 | @override
26 | void initState() {
27 | super.initState();
28 | // The following line will enable the Android and iOS wakelock.
29 | _playerEventSubs = _meeduPlayerController.onPlayerStatusChanged.listen(
30 | (PlayerStatus status) {
31 | if (status == PlayerStatus.playing) {
32 | Wakelock.enable();
33 | } else {
34 | Wakelock.disable();
35 | }
36 | },
37 | );
38 |
39 | WidgetsBinding.instance!.addPostFrameCallback((_) {
40 | _init();
41 | });
42 | }
43 |
44 | @override
45 | void dispose() {
46 | // The next line disables the wakelock again.
47 | _playerEventSubs?.cancel();
48 | Wakelock.disable();
49 | _meeduPlayerController.dispose();
50 | super.dispose();
51 | }
52 |
53 | _init() {
54 | _meeduPlayerController.setDataSource(
55 | DataSource(
56 | type: DataSourceType.network,
57 | source: "https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4",
58 | ),
59 | autoplay: true,
60 | );
61 | }
62 |
63 | @override
64 | Widget build(BuildContext context) {
65 | return Scaffold(
66 | appBar: AppBar(),
67 | body: SafeArea(
68 | child: AspectRatio(
69 | aspectRatio: 16 / 9,
70 | child: MeeduVideoPlayer(
71 | controller: _meeduPlayerController,
72 | ),
73 | ),
74 | ),
75 | );
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/change_quality_example_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:meedu_player/meedu_player.dart';
6 |
7 | class Quality {
8 | final String url, label;
9 | Quality({
10 | required this.url,
11 | required this.label,
12 | });
13 | }
14 |
15 | class ChangeQualityExamplePage extends StatefulWidget {
16 | ChangeQualityExamplePage({Key? key}) : super(key: key);
17 |
18 | @override
19 | _ChangeQualityExamplePageState createState() => _ChangeQualityExamplePageState();
20 | }
21 |
22 | class _ChangeQualityExamplePageState extends State {
23 | final _controller = MeeduPlayerController(
24 | screenManager: ScreenManager(
25 | forceLandScapeInFullscreen: false,
26 | ),
27 | );
28 |
29 | final _qualities = [
30 | Quality(
31 | url:
32 | "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h480p.mov",
33 | label: "480p",
34 | ),
35 | Quality(
36 | url:
37 | "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h720p.mov",
38 | label: "720p",
39 | ),
40 | Quality(
41 | url:
42 | "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h1080p.mov",
43 | label: "1080p",
44 | ),
45 | ];
46 |
47 | /// listener for the video quality
48 | ValueNotifier _quality = ValueNotifier(null);
49 |
50 | Duration _currentPosition = Duration.zero; // to save the video position
51 |
52 | /// subscription to listen the video position changes
53 | StreamSubscription? _currentPositionSubs;
54 |
55 | @override
56 | void initState() {
57 | super.initState();
58 | _quality.value = _qualities[0]; // set the default video quality (480p)
59 |
60 | // listen the video position
61 | _currentPositionSubs = _controller.onPositionChanged.listen(
62 | (Duration position) {
63 | _currentPosition = position; // save the video position
64 | },
65 | );
66 | WidgetsBinding.instance!.addPostFrameCallback((_) {
67 | _setDataSource();
68 | });
69 | }
70 |
71 | @override
72 | void dispose() {
73 | _currentPositionSubs?.cancel(); // cancel the subscription
74 | _controller.dispose();
75 | super.dispose();
76 | }
77 |
78 | void _onChangeVideoQuality() {
79 | showCupertinoModalPopup(
80 | context: context,
81 | builder: (_) => CupertinoActionSheet(
82 | actions: List.generate(
83 | _qualities.length,
84 | (index) {
85 | final quality = _qualities[index];
86 | return CupertinoActionSheetAction(
87 | child: Text("${quality.label}"),
88 | onPressed: () {
89 | _quality.value = quality; // change the video quality
90 | _setDataSource(); // update the datasource
91 | Navigator.pop(_);
92 | },
93 | );
94 | },
95 | ),
96 | cancelButton: CupertinoActionSheetAction(
97 | onPressed: () => Navigator.pop(_),
98 | child: Text("Cancel"),
99 | isDestructiveAction: true,
100 | ),
101 | ),
102 | );
103 | }
104 |
105 | Future _setDataSource() async {
106 | // set the data source and play the video in the last video position
107 | await _controller.setDataSource(
108 | DataSource(
109 | type: DataSourceType.network,
110 | source: _quality.value!.url,
111 | ),
112 | autoplay: true,
113 | seekTo: _currentPosition,
114 | );
115 | }
116 |
117 | @override
118 | Widget build(BuildContext context) {
119 | return Scaffold(
120 | appBar: AppBar(),
121 | body: AspectRatio(
122 | aspectRatio: 16 / 9,
123 | child: MeeduVideoPlayer(
124 | controller: _controller,
125 | bottomRight: (ctx, controller, responsive) {
126 | // creates a responsive fontSize using the size of video container
127 | final double fontSize = responsive.ip(3);
128 |
129 | return CupertinoButton(
130 | padding: EdgeInsets.all(5),
131 | minSize: 25,
132 | child: ValueListenableBuilder(
133 | valueListenable: this._quality,
134 | builder: (context, Quality? quality, child) {
135 | return Text(
136 | "${quality!.label}",
137 | style: TextStyle(
138 | fontSize: fontSize > 18 ? 18 : fontSize,
139 | color: Colors.white,
140 | ),
141 | );
142 | },
143 | ),
144 | onPressed: _onChangeVideoQuality,
145 | );
146 | },
147 | ),
148 | ),
149 | );
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/custom_icons_example.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:meedu_player/meedu_player.dart';
5 | import 'package:wakelock/wakelock.dart';
6 |
7 | class CustomIconsExamplePage extends StatefulWidget {
8 | CustomIconsExamplePage({Key? key}) : super(key: key);
9 |
10 | @override
11 | _CustomIconsExamplePageState createState() => _CustomIconsExamplePageState();
12 | }
13 |
14 | class _CustomIconsExamplePageState extends State {
15 | // read the documentation https://the-meedu-app.github.io/flutter-meedu-player/#/picture-in-picture
16 | // to enable the pip (picture in picture) support on Android
17 | final _meeduPlayerController = MeeduPlayerController(
18 | controlsStyle: ControlsStyle.primary,
19 | pipEnabled: true, // enable pip on android
20 | showPipButton: true, // use false to hide pip button in the player
21 | );
22 |
23 | StreamSubscription? _playerEventSubs;
24 |
25 | @override
26 | void initState() {
27 | super.initState();
28 | // The following line will enable the Android and iOS wakelock.
29 | _playerEventSubs = _meeduPlayerController.onPlayerStatusChanged.listen(
30 | (PlayerStatus status) {
31 | if (status == PlayerStatus.playing) {
32 | Wakelock.enable();
33 | } else {
34 | Wakelock.disable();
35 | }
36 | },
37 | );
38 |
39 | WidgetsBinding.instance!.addPostFrameCallback((_) {
40 | _init();
41 | });
42 | }
43 |
44 | @override
45 | void dispose() {
46 | // The next line disables the wakelock again.
47 | _playerEventSubs?.cancel();
48 | Wakelock.disable();
49 | _meeduPlayerController.dispose();
50 | super.dispose();
51 | }
52 |
53 | _init() {
54 | _meeduPlayerController.setDataSource(
55 | DataSource(
56 | type: DataSourceType.network,
57 | source: "https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4",
58 | ),
59 | autoplay: true,
60 | );
61 | }
62 |
63 | @override
64 | Widget build(BuildContext context) {
65 | return Scaffold(
66 | appBar: AppBar(),
67 | body: SafeArea(
68 | child: AspectRatio(
69 | aspectRatio: 16 / 9,
70 | child: MeeduVideoPlayer(
71 | controller: _meeduPlayerController,
72 | customIcons: (responsive) {
73 | final iconSize = responsive.ip(15);
74 | final miniIconSize = responsive.ip(7);
75 | return CustomIcons(
76 | play: Container(
77 | padding: EdgeInsets.all(iconSize * 0.2),
78 | child: Icon(
79 | Icons.play_arrow,
80 | size: iconSize,
81 | color: Colors.redAccent,
82 | ),
83 | ),
84 | pause: Container(
85 | padding: EdgeInsets.all(iconSize * 0.2),
86 | child: Icon(
87 | Icons.pause,
88 | size: iconSize,
89 | color: Colors.redAccent,
90 | ),
91 | ),
92 | rewind: Container(
93 | padding: EdgeInsets.all(iconSize * 0.1),
94 | child: Icon(
95 | Icons.fast_rewind_rounded,
96 | size: iconSize * 0.8,
97 | color: Colors.redAccent,
98 | ),
99 | ),
100 | fastForward: Container(
101 | padding: EdgeInsets.all(iconSize * 0.1),
102 | child: Icon(
103 | Icons.fast_forward_rounded,
104 | size: iconSize * 0.8,
105 | color: Colors.redAccent,
106 | ),
107 | ),
108 | mute: Container(
109 | padding: EdgeInsets.all(miniIconSize * 0.2),
110 | child: Icon(
111 | Icons.volume_off_rounded,
112 | size: miniIconSize,
113 | color: Colors.redAccent,
114 | ),
115 | ),
116 | sound: Container(
117 | padding: EdgeInsets.all(miniIconSize * 0.2),
118 | child: Icon(
119 | Icons.volume_up_rounded,
120 | size: miniIconSize,
121 | color: Colors.redAccent,
122 | ),
123 | ),
124 | fullscreen: Container(
125 | padding: EdgeInsets.all(miniIconSize * 0.2),
126 | child: Icon(
127 | Icons.fullscreen_rounded,
128 | size: miniIconSize,
129 | color: Colors.redAccent,
130 | ),
131 | ),
132 | minimize: Container(
133 | padding: EdgeInsets.all(miniIconSize * 0.2),
134 | child: Icon(
135 | Icons.fullscreen_exit_rounded,
136 | size: miniIconSize,
137 | color: Colors.redAccent,
138 | ),
139 | ),
140 | pip: Container(
141 | padding: EdgeInsets.all(miniIconSize * 0.2),
142 | child: Icon(
143 | Icons.picture_in_picture_alt_rounded,
144 | size: miniIconSize * 0.8,
145 | color: Colors.redAccent,
146 | ),
147 | ),
148 | );
149 | },
150 | ),
151 | ),
152 | ),
153 | );
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/disabled_buttons_example_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:meedu_player/meedu_player.dart';
5 | import 'package:wakelock/wakelock.dart';
6 |
7 | class DisabledButtonsExample extends StatefulWidget {
8 | DisabledButtonsExample({Key? key}) : super(key: key);
9 |
10 | @override
11 | _DisabledButtonsExampleState createState() => _DisabledButtonsExampleState();
12 | }
13 |
14 | class _DisabledButtonsExampleState extends State {
15 | // read the documentation https://the-meedu-app.github.io/flutter-meedu-player/#/picture-in-picture
16 | // to enable the pip (picture in picture) support on Android
17 | final _meeduPlayerController = MeeduPlayerController(
18 | controlsStyle: ControlsStyle.primary,
19 | pipEnabled: true, // enable pip on android
20 | showPipButton: true, // use false to hide pip button in the player
21 | enabledButtons: EnabledButtons(
22 | fullscreen: false,
23 | pip: false,
24 | ),
25 | );
26 |
27 | StreamSubscription? _playerEventSubs;
28 |
29 | @override
30 | void initState() {
31 | super.initState();
32 | // The following line will enable the Android and iOS wakelock.
33 | _playerEventSubs = _meeduPlayerController.onPlayerStatusChanged.listen(
34 | (PlayerStatus status) {
35 | if (status == PlayerStatus.playing) {
36 | Wakelock.enable();
37 | } else {
38 | Wakelock.disable();
39 | }
40 | },
41 | );
42 |
43 | WidgetsBinding.instance!.addPostFrameCallback((_) {
44 | _init();
45 | });
46 | }
47 |
48 | @override
49 | void dispose() {
50 | // The next line disables the wakelock again.
51 | _playerEventSubs?.cancel();
52 | Wakelock.disable();
53 | _meeduPlayerController.dispose();
54 | super.dispose();
55 | }
56 |
57 | _init() {
58 | _meeduPlayerController.setDataSource(
59 | DataSource(
60 | type: DataSourceType.network,
61 | source: "https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4",
62 | ),
63 | autoplay: true,
64 | );
65 | }
66 |
67 | @override
68 | Widget build(BuildContext context) {
69 | return Scaffold(
70 | appBar: AppBar(),
71 | body: SafeArea(
72 | child: AspectRatio(
73 | aspectRatio: 16 / 9,
74 | child: MeeduVideoPlayer(
75 | controller: _meeduPlayerController,
76 | ),
77 | ),
78 | ),
79 | );
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/fullscreen_example_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:meedu_player/meedu_player.dart';
6 |
7 | const videos = [
8 | 'https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h720p.mov',
9 | 'https://movietrailers.apple.com/movies/independent/bill-ted-face-the-music/bill-and-ted-face-the-music-trailer-1_h720p.mov',
10 | 'https://movietrailers.apple.com/movies/roadsideattractions/words-on-bathroom-walls/words-on-bathroom-walls-trailer-1_h720p.mov',
11 | 'https://movietrailers.apple.com/movies/independent/alone/alone-trailer-1_h720p.mov',
12 | 'https://movietrailers.apple.com/movies/fox/the-new-mutants/the-new-mutants-trailer-1_h720p.mov',
13 | ];
14 |
15 | class FullscreenExamplePage extends StatefulWidget {
16 | FullscreenExamplePage({Key? key}) : super(key: key);
17 |
18 | @override
19 | _FullscreenExamplePageState createState() => _FullscreenExamplePageState();
20 | }
21 |
22 | class _FullscreenExamplePageState extends State {
23 | // final MeeduPlayerController _meeduPlayerController = MeeduPlayerController(
24 | // screenManager: ScreenManager(
25 | // forceLandScapeInFullscreen: false,
26 | // orientations: [
27 | // DeviceOrientation.landscapeLeft,
28 | // DeviceOrientation.landscapeRight,
29 | // DeviceOrientation.portraitDown,
30 | // DeviceOrientation.portraitUp,
31 | // ],
32 | // ),
33 | // );
34 |
35 | final MeeduPlayerController _meeduPlayerController = MeeduPlayerController(
36 | colorTheme: Colors.blue,
37 | );
38 | ValueNotifier currentIndex = ValueNotifier(0);
39 | DataSource? _dataSource;
40 |
41 | StreamSubscription? _subscription;
42 |
43 | @override
44 | void initState() {
45 | super.initState();
46 | _subscription = _meeduPlayerController.onFullscreenChanged.listen(
47 | (bool isFullscreen) {
48 | if (!isFullscreen) {
49 | // if the fullscreen page was closed
50 | _dataSource = null;
51 | }
52 | },
53 | );
54 | }
55 |
56 | @override
57 | void dispose() {
58 | _subscription?.cancel();
59 | _meeduPlayerController.dispose();
60 | super.dispose();
61 | }
62 |
63 | Widget get nextButton {
64 | return ValueListenableBuilder(
65 | valueListenable: currentIndex,
66 | builder: (_, int index, __) {
67 | final hasNext = index < videos.length - 1;
68 | return TextButton(
69 | onPressed: hasNext
70 | ? () {
71 | currentIndex.value++;
72 | this._set();
73 | }
74 | : null,
75 | child: Text(
76 | "NEXT VIDEO",
77 | style: TextStyle(
78 | color: Colors.white.withOpacity(hasNext ? 1 : 0.2),
79 | ),
80 | ),
81 | );
82 | },
83 | );
84 | }
85 |
86 | Widget get header {
87 | return ValueListenableBuilder(
88 | valueListenable: currentIndex,
89 | builder: (_, int index, __) {
90 | return Container(
91 | padding: EdgeInsets.all(10),
92 | child: Row(
93 | children: [
94 | CupertinoButton(
95 | child: Icon(
96 | Icons.arrow_back,
97 | color: Colors.white,
98 | ),
99 | onPressed: () {
100 | // close the fullscreen
101 | Navigator.pop(context);
102 | },
103 | ),
104 | Expanded(
105 | child: Text(
106 | videos[index],
107 | style: TextStyle(
108 | color: Colors.white,
109 | ),
110 | ),
111 | ),
112 | ],
113 | ),
114 | );
115 | },
116 | );
117 | }
118 |
119 | Future _set() async {
120 | final index = currentIndex.value;
121 | if (_dataSource == null) {
122 | // if the player is not launched yet
123 | _dataSource = DataSource(
124 | source: videos[index],
125 | type: DataSourceType.network,
126 | );
127 |
128 | // launch the player in fullscreen mode
129 | await this._meeduPlayerController.launchAsFullscreen(
130 | context,
131 | dataSource: _dataSource!,
132 | autoplay: true,
133 | header: header,
134 | bottomRight: nextButton,
135 | );
136 | } else {
137 | // update the player with new datasource and it doesn't re-launch the player
138 | await this._meeduPlayerController.setDataSource(
139 | _dataSource!.copyWith(
140 | source: videos[index],
141 | ),
142 | seekTo: Duration.zero,
143 | );
144 | }
145 | }
146 |
147 | @override
148 | Widget build(BuildContext context) {
149 | return Scaffold(
150 | appBar: AppBar(),
151 | body: ListView(
152 | children: List.generate(
153 | videos.length,
154 | (index) => ListTile(
155 | onTap: () {
156 | currentIndex.value = index;
157 | this._set();
158 | },
159 | title: Text("video ${index + 1}"),
160 | ),
161 | ),
162 | ),
163 | );
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/listview_example.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:visibility_detector/visibility_detector.dart';
5 |
6 | class ListViewExample extends StatefulWidget {
7 | ListViewExample({Key? key}) : super(key: key);
8 |
9 | @override
10 | _ListViewExampleState createState() => _ListViewExampleState();
11 | }
12 |
13 | class _ListViewExampleState extends State with AutomaticKeepAliveClientMixin {
14 | @override
15 | Widget build(BuildContext context) {
16 | super.build(context);
17 | return Scaffold(
18 | appBar: AppBar(),
19 | body: ListView.builder(
20 | itemBuilder: (_, index) => VideoItem(
21 | uniqueKey: "$index",
22 | ),
23 | itemCount: 50,
24 | ),
25 | );
26 | }
27 |
28 | @override
29 | bool get wantKeepAlive => true;
30 | }
31 |
32 | class VideoItem extends StatefulWidget {
33 | final String uniqueKey;
34 | VideoItem({Key? key, required this.uniqueKey}) : super(key: key);
35 |
36 | @override
37 | _VideoItemState createState() => _VideoItemState();
38 | }
39 |
40 | class _VideoItemState extends State with AutomaticKeepAliveClientMixin {
41 | MeeduPlayerController _controller = MeeduPlayerController(
42 | screenManager: ScreenManager(orientations: [
43 | DeviceOrientation.portraitUp,
44 | ]),
45 | );
46 |
47 | ValueNotifier _visible = ValueNotifier(true);
48 |
49 | @override
50 | void initState() {
51 | super.initState();
52 | _controller.setDataSource(
53 | DataSource(
54 | source: 'https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4',
55 | type: DataSourceType.network,
56 | ),
57 | autoplay: false,
58 | );
59 | }
60 |
61 | @override
62 | void dispose() {
63 | _controller.dispose();
64 |
65 | print("❌ dispose video player");
66 | super.dispose();
67 | }
68 |
69 | @override
70 | Widget build(BuildContext context) {
71 | super.build(context);
72 | return VisibilityDetector(
73 | key: Key(widget.uniqueKey),
74 | onVisibilityChanged: (info) {
75 | final visible = info.visibleFraction > 0;
76 | if (_visible.value != visible) {
77 | _visible.value = visible;
78 | if (!visible && _controller.videoPlayerController!.value.isPlaying) {
79 | _controller.pause();
80 | }
81 | }
82 | },
83 | child: AspectRatio(
84 | aspectRatio: 16 / 9,
85 | child: ValueListenableBuilder(
86 | valueListenable: _visible,
87 | builder: (_, visible, child) {
88 | return visible
89 | ? MeeduVideoPlayer(
90 | controller: _controller,
91 | )
92 | : child!;
93 | },
94 | child: Container(),
95 | ),
96 | ),
97 | );
98 | }
99 |
100 | @override
101 | bool get wantKeepAlive => true;
102 | }
103 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/network_with_subtitle_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 |
5 | class NetworkWithSubtitlesPage extends StatefulWidget {
6 | @override
7 | _NetworkWithSubtitlesPageState createState() => _NetworkWithSubtitlesPageState();
8 | }
9 |
10 | class _NetworkWithSubtitlesPageState extends State {
11 | late MeeduPlayerController _controller;
12 |
13 | ValueNotifier _subtitlesEnabled = ValueNotifier(true);
14 |
15 | @override
16 | void initState() {
17 | super.initState();
18 | _controller = MeeduPlayerController(
19 | controlsStyle: ControlsStyle.primary,
20 | );
21 | this._setDataSource();
22 | }
23 |
24 | @override
25 | void dispose() {
26 | this._controller.dispose();
27 | super.dispose();
28 | }
29 |
30 | _setDataSource() async {
31 | await _controller.setDataSource(
32 | DataSource(
33 | source: 'https://thepaciellogroup.github.io/AT-browser-tests/video/ElephantsDream.mp4',
34 | type: DataSourceType.network,
35 | closedCaptionFile: this._loadCaptions(),
36 | ),
37 | autoplay: true,
38 | );
39 | _controller.onClosedCaptionEnabled(true);
40 | }
41 |
42 | Future _loadCaptions() async {
43 | // you can get the fileContents as string from a remote str file using a http client like dio or http
44 | // or you can load from assets
45 | /*
46 | final String fileContents = await DefaultAssetBundle.of(context)
47 | .loadString('assets/captions.srt');
48 | */
49 | // in srt format
50 | final String fileContents = '''
51 | 0
52 | 00:00:02,170 --> 00:00:04,136
53 | Emo, close your eyes
54 |
55 | 1
56 | 00:00:04,136 --> 00:00:05,597
57 | Why?
58 | NOW!
59 |
60 | 2
61 | 00:00:05,597 --> 00:00:07,405
62 | Ok
63 |
64 | 3
65 | 00:00:07,405 --> 00:00:08,803
66 | Good
67 |
68 | 4
69 | 00:00:08,803 --> 00:00:11,541
70 | What do you see at your left side Emo?
71 |
72 | 5
73 | 00:00:11,541 --> 00:00:13,287
74 | Well?
75 |
76 | 6
77 | 00:00:13,287 --> 00:00:16,110
78 | Er nothing?
79 | Really?
80 |
81 | 7
82 | 00:00:16,110 --> 00:00:18,514
83 | No, nothing at all!
84 |
85 | 8
86 | 00:00:18,514 --> 00:00:22,669
87 | Really? and at your right? What do you see at your right side Emo?
88 |
89 | 9
90 | 00:00:22,669 --> 00:00:26,111
91 | Umm, the same Proog
92 |
93 | 10
94 | 00:00:26,111 --> 00:00:28,646
95 | Exactly the same! Nothing!
96 |
97 | 11
98 | 00:00:28,646 --> 00:00:30,794
99 | Great
100 | ''';
101 | return SubRipCaptionFile(fileContents);
102 | }
103 |
104 | @override
105 | Widget build(BuildContext context) {
106 | return Scaffold(
107 | appBar: AppBar(),
108 | body: SafeArea(
109 | child: AspectRatio(
110 | aspectRatio: 16 / 9,
111 | child: MeeduVideoPlayer(
112 | controller: this._controller,
113 | bottomRight: (ctx, controller, responsive) {
114 | // creates a responsive fontSize using the size of video container
115 | final double fontSize = responsive.ip(3);
116 |
117 | return CupertinoButton(
118 | padding: EdgeInsets.all(5),
119 | minSize: 25,
120 | child: ValueListenableBuilder(
121 | valueListenable: this._subtitlesEnabled,
122 | builder: (BuildContext context, bool enabled, _) {
123 | return Text(
124 | "CC",
125 | style: TextStyle(
126 | fontSize: fontSize > 18 ? 18 : fontSize,
127 | color: Colors.white.withOpacity(
128 | enabled ? 1 : 0.4,
129 | ),
130 | ),
131 | );
132 | },
133 | ),
134 | onPressed: () {
135 | _subtitlesEnabled.value = !_subtitlesEnabled.value;
136 | this._controller.onClosedCaptionEnabled(_subtitlesEnabled.value);
137 | },
138 | );
139 | },
140 | ),
141 | ),
142 | ),
143 | );
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/one_page_to_other_page_example.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:meedu_player/meedu_player.dart';
5 | import 'package:wakelock/wakelock.dart';
6 |
7 | class OnePageExample extends StatefulWidget {
8 | @override
9 | _OnePageExampleState createState() => _OnePageExampleState();
10 | }
11 |
12 | class _OnePageExampleState extends State {
13 | // read the documentation https://the-meedu-app.github.io/flutter-meedu-player/#/picture-in-picture
14 | // to enable the pip (picture in picture) support on Android
15 | MeeduPlayerController? _meeduPlayerController = MeeduPlayerController(
16 | controlsStyle: ControlsStyle.secondary,
17 | pipEnabled: true, // enable pip on android
18 | showPipButton: true, // use false to hide pip button in the player
19 | );
20 |
21 | StreamSubscription? _playerEventSubs;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | // The following line will enable the Android and iOS wakelock.
27 | _playerEventSubs = _meeduPlayerController!.onPlayerStatusChanged.listen(
28 | (PlayerStatus status) {
29 | if (status == PlayerStatus.playing) {
30 | Wakelock.enable();
31 | } else {
32 | Wakelock.disable();
33 | }
34 | },
35 | );
36 |
37 | WidgetsBinding.instance!.addPostFrameCallback((_) {
38 | _init();
39 | });
40 | }
41 |
42 | @override
43 | void dispose() {
44 | _meeduDispose();
45 | super.dispose();
46 | }
47 |
48 | Future _meeduDispose() async {
49 | if (_meeduPlayerController != null) {
50 | _playerEventSubs?.cancel();
51 | await _meeduPlayerController!.dispose();
52 | _meeduPlayerController = null;
53 | // The next line disables the wakelock again.
54 | await Wakelock.disable();
55 | }
56 | }
57 |
58 | _init() {
59 | _meeduPlayerController!.setDataSource(
60 | DataSource(
61 | type: DataSourceType.network,
62 | source: "https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4",
63 | ),
64 | autoplay: true,
65 | );
66 | }
67 |
68 | Future _gotTo() async {
69 | final route = MaterialPageRoute(
70 | builder: (_) => PageTwo(),
71 | );
72 | Navigator.pushReplacement(context, route);
73 | }
74 |
75 | @override
76 | Widget build(BuildContext context) {
77 | return Scaffold(
78 | appBar: AppBar(
79 | title: Text("Page 1"),
80 | ),
81 | body: SafeArea(
82 | child: Column(
83 | children: [
84 | AspectRatio(
85 | aspectRatio: 16 / 9,
86 | child: MeeduVideoPlayer(
87 | controller: _meeduPlayerController!,
88 | ),
89 | ),
90 | SizedBox(height: 2),
91 | TextButton(
92 | onPressed: this._gotTo,
93 | child: Text("Page 2"),
94 | ),
95 | ],
96 | ),
97 | ),
98 | );
99 | }
100 | }
101 |
102 | class PageTwo extends StatefulWidget {
103 | PageTwo({Key? key}) : super(key: key);
104 |
105 | @override
106 | _PageTwoState createState() => _PageTwoState();
107 | }
108 |
109 | class _PageTwoState extends State {
110 | // read the documentation https://the-meedu-app.github.io/flutter-meedu-player/#/picture-in-picture
111 | // to enable the pip (picture in picture) support on Android
112 | final _meeduPlayerController = MeeduPlayerController(
113 | controlsStyle: ControlsStyle.primary,
114 | pipEnabled: true, // enable pip on android
115 | showPipButton: true, // use false to hide pip button in the player
116 | );
117 |
118 | StreamSubscription? _playerEventSubs;
119 |
120 | @override
121 | void initState() {
122 | super.initState();
123 | // The following line will enable the Android and iOS wakelock.
124 | _playerEventSubs = _meeduPlayerController.onPlayerStatusChanged.listen(
125 | (PlayerStatus status) {
126 | if (status == PlayerStatus.playing) {
127 | Wakelock.enable();
128 | } else {
129 | Wakelock.disable();
130 | }
131 | },
132 | );
133 |
134 | WidgetsBinding.instance!.addPostFrameCallback((_) {
135 | _init();
136 | });
137 | }
138 |
139 | @override
140 | void dispose() {
141 | // The next line disables the wakelock again.
142 | _playerEventSubs?.cancel();
143 | Wakelock.disable();
144 | _meeduPlayerController.dispose();
145 | super.dispose();
146 | }
147 |
148 | _init() {
149 | _meeduPlayerController.setDataSource(
150 | DataSource(
151 | type: DataSourceType.network,
152 | source: "https://movietrailers.apple.com/movies/fox/the-new-mutants/the-new-mutants-trailer-1_h720p.mov",
153 | ),
154 | autoplay: true,
155 | );
156 | }
157 |
158 | @override
159 | Widget build(BuildContext context) {
160 | return Scaffold(
161 | appBar: AppBar(
162 | title: Text("Page 2"),
163 | ),
164 | body: SafeArea(
165 | child: AspectRatio(
166 | aspectRatio: 16 / 9,
167 | child: MeeduVideoPlayer(
168 | controller: _meeduPlayerController,
169 | ),
170 | ),
171 | ),
172 | );
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/pick_file_page_example.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:file_picker/file_picker.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:meedu_player/meedu_player.dart';
5 |
6 | class PickFileExamplePage extends StatefulWidget {
7 | PickFileExamplePage({Key? key}) : super(key: key);
8 |
9 | @override
10 | _PickFileExamplePageState createState() => _PickFileExamplePageState();
11 | }
12 |
13 | class _PickFileExamplePageState extends State {
14 | final _controller = MeeduPlayerController(
15 | screenManager: ScreenManager(forceLandScapeInFullscreen: false),
16 | );
17 |
18 | @override
19 | void dispose() {
20 | _controller.dispose();
21 | super.dispose();
22 | }
23 |
24 | _onPickFile() async {
25 | FilePickerResult? result = await FilePicker.platform.pickFiles(
26 | type: FileType.custom,
27 | allowedExtensions: ['mov', 'avi', 'mp4'],
28 | );
29 |
30 | if (result != null) {
31 | File file = File(result.files.single.path!);
32 | _controller.launchAsFullscreen(
33 | context,
34 | autoplay: true,
35 | dataSource: DataSource(
36 | file: file,
37 | type: DataSourceType.file,
38 | ),
39 | );
40 | } else {
41 | // User canceled the picker
42 | }
43 | }
44 |
45 | @override
46 | Widget build(BuildContext context) {
47 | return Scaffold(
48 | body: Center(
49 | child: TextButton(
50 | child: Text("Pick video file"),
51 | onPressed: this._onPickFile,
52 | ),
53 | ),
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/playback_speed_example_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 |
5 | class PlayBackSpeedExamplePage extends StatefulWidget {
6 | PlayBackSpeedExamplePage({Key? key}) : super(key: key);
7 |
8 | @override
9 | _PlayBackSpeedExamplePageState createState() => _PlayBackSpeedExamplePageState();
10 | }
11 |
12 | class _PlayBackSpeedExamplePageState extends State {
13 | final _controller = MeeduPlayerController(
14 | screenManager: ScreenManager(
15 | forceLandScapeInFullscreen: false,
16 | ),
17 | );
18 |
19 | ValueNotifier _playbackSpeed = ValueNotifier(1);
20 |
21 | void _onPlaybackSpeed() {
22 | final options = [0.2, 0.5, 1.0, 2.0, 4.0];
23 | showCupertinoModalPopup(
24 | context: context,
25 | builder: (_) => CupertinoActionSheet(
26 | actions: List.generate(
27 | options.length,
28 | (index) => CupertinoActionSheetAction(
29 | child: Text("${options[index]}x"),
30 | onPressed: () {
31 | _playbackSpeed.value = options[index];
32 | // change the playback speed
33 | _controller.setPlaybackSpeed(
34 | _playbackSpeed.value,
35 | );
36 | // hide the modal
37 | Navigator.pop(_);
38 | },
39 | ),
40 | ),
41 | cancelButton: CupertinoActionSheetAction(
42 | onPressed: () => Navigator.pop(_),
43 | child: Text("Cancel"),
44 | isDestructiveAction: true,
45 | ),
46 | ),
47 | );
48 | }
49 |
50 | @override
51 | void initState() {
52 | super.initState();
53 | WidgetsBinding.instance!.addPostFrameCallback((_) {
54 | _init();
55 | });
56 | }
57 |
58 | @override
59 | void dispose() {
60 | _controller.dispose();
61 | super.dispose();
62 | }
63 |
64 | _init() {
65 | _controller.setDataSource(
66 | DataSource(
67 | type: DataSourceType.network,
68 | source: "https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4",
69 | ),
70 | autoplay: true,
71 | );
72 | }
73 |
74 | @override
75 | Widget build(BuildContext context) {
76 | return Scaffold(
77 | appBar: AppBar(),
78 | body: AspectRatio(
79 | aspectRatio: 16 / 9,
80 | child: MeeduVideoPlayer(
81 | controller: _controller,
82 | bottomRight: (ctx, controller, responsive) {
83 | // creates a responsive fontSize using the size of video container
84 | final double fontSize = responsive.ip(3);
85 |
86 | return CupertinoButton(
87 | padding: EdgeInsets.all(5),
88 | minSize: 25,
89 | child: ValueListenableBuilder(
90 | valueListenable: this._playbackSpeed,
91 | builder: (context, double speed, child) {
92 | return Text(
93 | "$speed x",
94 | style: TextStyle(
95 | fontSize: fontSize > 18 ? 18 : fontSize,
96 | color: Colors.white,
97 | ),
98 | );
99 | },
100 | ),
101 | onPressed: _onPlaybackSpeed,
102 | );
103 | },
104 | ),
105 | ),
106 | );
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/plugin/example/lib/pages/player_with_header_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:wakelock/wakelock.dart';
5 |
6 | class PlayerWithHeaderPage extends StatefulWidget {
7 | PlayerWithHeaderPage({Key? key}) : super(key: key);
8 |
9 | @override
10 | _PlayerWithHeaderPageState createState() => _PlayerWithHeaderPageState();
11 | }
12 |
13 | class _PlayerWithHeaderPageState extends State {
14 | MeeduPlayerController _meeduPlayerController = MeeduPlayerController(
15 | controlsStyle: ControlsStyle.secondary,
16 | );
17 |
18 | @override
19 | void initState() {
20 | super.initState();
21 |
22 | // The following line will enable the Android and iOS wakelock.
23 | Wakelock.enable();
24 | WidgetsBinding.instance!.addPostFrameCallback((_) {
25 | _init();
26 | });
27 | }
28 |
29 | @override
30 | void dispose() {
31 | // The next line disables the wakelock again.
32 | Wakelock.disable();
33 | _meeduPlayerController.dispose();
34 | super.dispose();
35 | }
36 |
37 | _init() {
38 | _meeduPlayerController.setDataSource(
39 | DataSource(
40 | type: DataSourceType.network,
41 | source: "https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4",
42 | ),
43 | autoplay: true,
44 | );
45 | }
46 |
47 | @override
48 | Widget build(BuildContext context) {
49 | return Scaffold(
50 | appBar: AppBar(),
51 | body: SafeArea(
52 | child: AspectRatio(
53 | aspectRatio: 16 / 9,
54 | child: MeeduVideoPlayer(
55 | header: (ctx, controller, responsive) {
56 | // creates a responsive fontSize using the size of video container
57 | final double fontSize = responsive.ip(3);
58 |
59 | return Container(
60 | padding: EdgeInsets.only(left: 10),
61 | color: Colors.black12,
62 | child: Row(
63 | crossAxisAlignment: CrossAxisAlignment.center,
64 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
65 | children: [
66 | Text(
67 | "Insert you title 3",
68 | style: TextStyle(
69 | color: Colors.white,
70 | fontSize: fontSize > 17 ? 17 : fontSize,
71 | ),
72 | ),
73 | CupertinoButton(
74 | padding: EdgeInsets.all(5),
75 | child: Icon(
76 | CupertinoIcons.share,
77 | color: Colors.white,
78 | ),
79 | onPressed: () {},
80 | )
81 | ],
82 | ),
83 | );
84 | },
85 | controller: _meeduPlayerController,
86 | ),
87 | ),
88 | ),
89 | );
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/plugin/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.5.0"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.2.0"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | cupertino_icons:
47 | dependency: "direct main"
48 | description:
49 | name: cupertino_icons
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.0.2"
53 | fake_async:
54 | dependency: transitive
55 | description:
56 | name: fake_async
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.2.0"
60 | ffi:
61 | dependency: transitive
62 | description:
63 | name: ffi
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "1.0.0"
67 | file_picker:
68 | dependency: "direct main"
69 | description:
70 | name: file_picker
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "3.0.0"
74 | flutter:
75 | dependency: "direct main"
76 | description: flutter
77 | source: sdk
78 | version: "0.0.0"
79 | flutter_meedu:
80 | dependency: transitive
81 | description:
82 | name: flutter_meedu
83 | url: "https://pub.dartlang.org"
84 | source: hosted
85 | version: "2.0.1"
86 | flutter_plugin_android_lifecycle:
87 | dependency: transitive
88 | description:
89 | name: flutter_plugin_android_lifecycle
90 | url: "https://pub.dartlang.org"
91 | source: hosted
92 | version: "2.0.0"
93 | flutter_spinkit:
94 | dependency: transitive
95 | description:
96 | name: flutter_spinkit
97 | url: "https://pub.dartlang.org"
98 | source: hosted
99 | version: "5.0.0"
100 | flutter_test:
101 | dependency: "direct dev"
102 | description: flutter
103 | source: sdk
104 | version: "0.0.0"
105 | flutter_web_plugins:
106 | dependency: transitive
107 | description: flutter
108 | source: sdk
109 | version: "0.0.0"
110 | js:
111 | dependency: transitive
112 | description:
113 | name: js
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "0.6.3"
117 | matcher:
118 | dependency: transitive
119 | description:
120 | name: matcher
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "0.12.10"
124 | meedu:
125 | dependency: transitive
126 | description:
127 | name: meedu
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "2.0.1"
131 | meedu_player:
132 | dependency: "direct dev"
133 | description:
134 | path: ".."
135 | relative: true
136 | source: path
137 | version: "0.5.0-nullsafety.0"
138 | meta:
139 | dependency: transitive
140 | description:
141 | name: meta
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "1.3.0"
145 | nested:
146 | dependency: transitive
147 | description:
148 | name: nested
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "1.0.0"
152 | path:
153 | dependency: transitive
154 | description:
155 | name: path
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "1.8.0"
159 | plugin_platform_interface:
160 | dependency: transitive
161 | description:
162 | name: plugin_platform_interface
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "2.0.0"
166 | provider:
167 | dependency: transitive
168 | description:
169 | name: provider
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "5.0.0"
173 | sky_engine:
174 | dependency: transitive
175 | description: flutter
176 | source: sdk
177 | version: "0.0.99"
178 | source_span:
179 | dependency: transitive
180 | description:
181 | name: source_span
182 | url: "https://pub.dartlang.org"
183 | source: hosted
184 | version: "1.8.0"
185 | stack_trace:
186 | dependency: transitive
187 | description:
188 | name: stack_trace
189 | url: "https://pub.dartlang.org"
190 | source: hosted
191 | version: "1.10.0"
192 | stream_channel:
193 | dependency: transitive
194 | description:
195 | name: stream_channel
196 | url: "https://pub.dartlang.org"
197 | source: hosted
198 | version: "2.1.0"
199 | string_scanner:
200 | dependency: transitive
201 | description:
202 | name: string_scanner
203 | url: "https://pub.dartlang.org"
204 | source: hosted
205 | version: "1.1.0"
206 | term_glyph:
207 | dependency: transitive
208 | description:
209 | name: term_glyph
210 | url: "https://pub.dartlang.org"
211 | source: hosted
212 | version: "1.2.0"
213 | test_api:
214 | dependency: transitive
215 | description:
216 | name: test_api
217 | url: "https://pub.dartlang.org"
218 | source: hosted
219 | version: "0.2.19"
220 | typed_data:
221 | dependency: transitive
222 | description:
223 | name: typed_data
224 | url: "https://pub.dartlang.org"
225 | source: hosted
226 | version: "1.3.0"
227 | vector_math:
228 | dependency: transitive
229 | description:
230 | name: vector_math
231 | url: "https://pub.dartlang.org"
232 | source: hosted
233 | version: "2.1.0"
234 | video_player:
235 | dependency: transitive
236 | description:
237 | name: video_player
238 | url: "https://pub.dartlang.org"
239 | source: hosted
240 | version: "2.0.0"
241 | video_player_platform_interface:
242 | dependency: transitive
243 | description:
244 | name: video_player_platform_interface
245 | url: "https://pub.dartlang.org"
246 | source: hosted
247 | version: "4.0.0"
248 | video_player_web:
249 | dependency: transitive
250 | description:
251 | name: video_player_web
252 | url: "https://pub.dartlang.org"
253 | source: hosted
254 | version: "2.0.0"
255 | visibility_detector:
256 | dependency: "direct main"
257 | description:
258 | name: visibility_detector
259 | url: "https://pub.dartlang.org"
260 | source: hosted
261 | version: "0.2.0-nullsafety.1"
262 | wakelock:
263 | dependency: "direct main"
264 | description:
265 | name: wakelock
266 | url: "https://pub.dartlang.org"
267 | source: hosted
268 | version: "0.5.0+2"
269 | wakelock_macos:
270 | dependency: transitive
271 | description:
272 | name: wakelock_macos
273 | url: "https://pub.dartlang.org"
274 | source: hosted
275 | version: "0.1.0"
276 | wakelock_platform_interface:
277 | dependency: transitive
278 | description:
279 | name: wakelock_platform_interface
280 | url: "https://pub.dartlang.org"
281 | source: hosted
282 | version: "0.2.0"
283 | wakelock_web:
284 | dependency: transitive
285 | description:
286 | name: wakelock_web
287 | url: "https://pub.dartlang.org"
288 | source: hosted
289 | version: "0.2.0"
290 | wakelock_windows:
291 | dependency: transitive
292 | description:
293 | name: wakelock_windows
294 | url: "https://pub.dartlang.org"
295 | source: hosted
296 | version: "0.1.0"
297 | win32:
298 | dependency: transitive
299 | description:
300 | name: win32
301 | url: "https://pub.dartlang.org"
302 | source: hosted
303 | version: "2.0.0"
304 | sdks:
305 | dart: ">=2.12.0 <3.0.0"
306 | flutter: ">=2.0.0"
307 |
--------------------------------------------------------------------------------
/plugin/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: player_example
2 | description: Demonstrates how to use the player plugin.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | environment:
9 | sdk: ">=2.12.0 <3.0.0"
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 | wakelock: ^0.5.0+2
15 | file_picker: ^3.0.0
16 |
17 | # The following adds the Cupertino Icons font to your application.
18 | # Use with the CupertinoIcons class for iOS style icons.
19 | cupertino_icons: ^1.0.2
20 | visibility_detector: ^0.2.0-nullsafety.1
21 |
22 |
23 | dev_dependencies:
24 | flutter_test:
25 | sdk: flutter
26 | meedu_player:
27 | path: ../
28 |
29 | # For information on the generic Dart part of this file, see the
30 | # following page: https://dart.dev/tools/pub/pubspec
31 |
32 | # The following section is specific to Flutter.
33 | flutter:
34 |
35 | # The following line ensures that the Material Icons font is
36 | # included with your application, so that you can use the icons in
37 | # the material Icons class.
38 | uses-material-design: true
39 |
40 | # To add assets to your application, add an assets section, like this:
41 | # assets:
42 | # - images/a_dot_burr.jpeg
43 | # - images/a_dot_ham.jpeg
44 |
45 | # An image asset can refer to one or more resolution-specific "variants", see
46 | # https://flutter.dev/assets-and-images/#resolution-aware.
47 |
48 | # For details regarding adding assets from package dependencies, see
49 | # https://flutter.dev/assets-and-images/#from-packages
50 |
51 | # To add custom fonts to your application, add a fonts section here,
52 | # in this "flutter" section. Each entry in this list should have a
53 | # "family" key with the font family name, and a "fonts" key with a
54 | # list giving the asset and other descriptors for the font. For
55 | # example:
56 | # fonts:
57 | # - family: Schyler
58 | # fonts:
59 | # - asset: fonts/Schyler-Regular.ttf
60 | # - asset: fonts/Schyler-Italic.ttf
61 | # style: italic
62 | # - family: Trajan Pro
63 | # fonts:
64 | # - asset: fonts/TrajanPro.ttf
65 | # - asset: fonts/TrajanPro_Bold.ttf
66 | # weight: 700
67 | #
68 | # For details regarding fonts from package dependencies,
69 | # see https://flutter.dev/custom-fonts/#from-packages
70 |
--------------------------------------------------------------------------------
/plugin/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | void main() {}
9 |
--------------------------------------------------------------------------------
/plugin/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/flutter_export_environment.sh
--------------------------------------------------------------------------------
/plugin/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darwin-morocho/flutter-meedu-player/007d2f536693718a05d6a5caac17cc70d23d2c4b/plugin/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/plugin/ios/Classes/MeeduPlayerPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface MeeduPlayerPlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/plugin/ios/Classes/MeeduPlayerPlugin.m:
--------------------------------------------------------------------------------
1 | #import "MeeduPlayerPlugin.h"
2 | #if __has_include()
3 | #import
4 | #else
5 | // Support project import fallback if the generated compatibility header
6 | // is not copied when this plugin is created as a library.
7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
8 | #import "meedu_player-Swift.h"
9 | #endif
10 |
11 | @implementation MeeduPlayerPlugin
12 | + (void)registerWithRegistrar:(NSObject*)registrar {
13 | [SwiftMeeduPlayerPlugin registerWithRegistrar:registrar];
14 | }
15 | @end
16 |
--------------------------------------------------------------------------------
/plugin/ios/Classes/SwiftMeeduPlayerPlugin.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 |
4 | public class SwiftMeeduPlayerPlugin: NSObject, FlutterPlugin {
5 | public static func register(with registrar: FlutterPluginRegistrar) {
6 | let channel = FlutterMethodChannel(name: "meedu_player", binaryMessenger: registrar.messenger())
7 | let instance = SwiftMeeduPlayerPlugin()
8 | registrar.addMethodCallDelegate(instance, channel: channel)
9 | }
10 |
11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
12 | result("iOS " + UIDevice.current.systemVersion)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/plugin/ios/meedu_player.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint player.podspec' to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'meedu_player'
7 | s.version = '0.3.4'
8 | s.summary = 'A new Flutter UI video controls for the flutter video_player plugin.'
9 | s.description = <<-DESC
10 | A new Flutter UI video controls for the flutter video_player plugin..
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, '9.0'
19 |
20 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
22 | s.swift_version = '5.0'
23 | end
24 |
--------------------------------------------------------------------------------
/plugin/lib/meedu_player.dart:
--------------------------------------------------------------------------------
1 | export 'src/controller.dart';
2 | export 'src/widgets/meedu_video_player.dart';
3 | export 'src/helpers/data_source.dart';
4 | export 'src/helpers/player_data_status.dart';
5 | export 'src/helpers/meedu_player_status.dart';
6 | export 'src/helpers/screen_manager.dart';
7 | export 'src/helpers/custom_icons.dart';
8 | export 'src/helpers/enabled_buttons.dart';
9 | export 'package:video_player/video_player.dart';
10 |
--------------------------------------------------------------------------------
/plugin/lib/src/helpers/custom_icons.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// this class help you to change the default player icons
4 | class CustomIcons {
5 | final Widget? play,
6 | pause,
7 | repeat,
8 | rewind,
9 | fastForward,
10 | sound,
11 | mute,
12 | videoFit,
13 | pip,
14 | minimize,
15 | fullscreen;
16 |
17 | const CustomIcons({
18 | this.play,
19 | this.pause,
20 | this.repeat,
21 | this.rewind,
22 | this.fastForward,
23 | this.sound,
24 | this.mute,
25 | this.videoFit,
26 | this.pip,
27 | this.minimize,
28 | this.fullscreen,
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/plugin/lib/src/helpers/data_source.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:video_player/video_player.dart'
3 | show VideoFormat, DataSourceType, ClosedCaptionFile;
4 |
5 | class DataSource {
6 | final File? file;
7 | final String? source, package;
8 | final DataSourceType type;
9 | final VideoFormat? formatHint;
10 | final Future? closedCaptionFile; // for subtiles
11 |
12 | DataSource({
13 | this.file,
14 | this.source,
15 | required this.type,
16 | this.formatHint,
17 | this.package,
18 | this.closedCaptionFile,
19 | }) : assert((type == DataSourceType.file && file != null) || source != null);
20 |
21 | DataSource copyWith({
22 | File? file,
23 | String? source,
24 | String? package,
25 | DataSourceType? type,
26 | VideoFormat? formatHint,
27 | Future? closedCaptionFile,
28 | }) {
29 | return DataSource(
30 | file: file ?? this.file,
31 | source: source ?? this.source,
32 | type: type ?? this.type,
33 | package: package ?? this.package,
34 | formatHint: formatHint ?? this.formatHint,
35 | closedCaptionFile: closedCaptionFile ?? this.closedCaptionFile,
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/plugin/lib/src/helpers/enabled_buttons.dart:
--------------------------------------------------------------------------------
1 | /// this class helps you to hide some player buttons
2 | class EnabledButtons {
3 | final bool playPauseAndRepeat,
4 | rewindAndfastForward,
5 | videoFit,
6 | muteAndSound,
7 | pip,
8 | fullscreen;
9 |
10 | const EnabledButtons({
11 | this.playPauseAndRepeat = true,
12 | this.rewindAndfastForward = true,
13 | this.videoFit = true,
14 | this.muteAndSound = true,
15 | this.pip = true,
16 | this.fullscreen = true,
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/plugin/lib/src/helpers/meedu_player_status.dart:
--------------------------------------------------------------------------------
1 | import 'package:meedu/rx.dart';
2 |
3 | enum PlayerStatus { stopped, playing, paused }
4 |
5 | class MeeduPlayerStatus {
6 | Rx status = Rx(PlayerStatus.paused);
7 |
8 | bool get playing {
9 | return status.value == PlayerStatus.playing;
10 | }
11 |
12 | bool get paused {
13 | return status.value == PlayerStatus.paused;
14 | }
15 |
16 | bool get stopped {
17 | return status.value == PlayerStatus.stopped;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/plugin/lib/src/helpers/player_data_status.dart:
--------------------------------------------------------------------------------
1 | import 'package:meedu/rx.dart';
2 |
3 | enum DataStatus { none, loading, loaded, error }
4 |
5 | class MeeduPlayerDataStatus {
6 | Rx status = Rx(DataStatus.none);
7 |
8 | bool get none => status.value == DataStatus.none;
9 | bool get loading => status.value == DataStatus.loading;
10 | bool get loaded => status.value == DataStatus.loaded;
11 | bool get error => status.value == DataStatus.error;
12 | }
13 |
--------------------------------------------------------------------------------
/plugin/lib/src/helpers/responsive.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math' as math;
2 |
3 | class Responsive {
4 | final double width, height;
5 | late double inch;
6 | Responsive(this.width, this.height) {
7 | inch = math.sqrt((width * width) + (height * height));
8 | }
9 |
10 | double ip(double percent) {
11 | return inch * percent / 100;
12 | }
13 |
14 | double wp(double percent) {
15 | return width * percent / 100;
16 | }
17 |
18 | double hp(double percent) {
19 | return height * percent / 100;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/plugin/lib/src/helpers/screen_manager.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 |
3 | class ScreenManager {
4 | /// [orientations] the device orientation after exit of the fullscreen
5 | final List orientations;
6 |
7 | /// [overlays] the device overlays after exit of the fullscreen
8 | final List overlays;
9 |
10 | /// when the player is in fullscreen mode if forceLandScapeInFullscreen the player only show the landscape mode
11 | final bool forceLandScapeInFullscreen;
12 |
13 | const ScreenManager({
14 | this.orientations = DeviceOrientation.values,
15 | this.overlays = SystemUiOverlay.values,
16 | this.forceLandScapeInFullscreen = true,
17 | });
18 |
19 | /// set the default orientations and overlays after exit of fullscreen
20 | Future setDefaultOverlaysAndOrientations() async {
21 | await SystemChrome.setPreferredOrientations(this.orientations);
22 | await SystemChrome.setEnabledSystemUIOverlays(this.overlays);
23 | }
24 |
25 | /// hide the statusBar and the navigation bar, set only landscape mode only if forceLandScapeInFullscreen is true
26 | Future setFullScreenOverlaysAndOrientations({
27 | hideOverLays = true,
28 | }) async {
29 | await SystemChrome.setPreferredOrientations(this.forceLandScapeInFullscreen
30 | ? [
31 | DeviceOrientation.landscapeLeft,
32 | DeviceOrientation.landscapeRight,
33 | ]
34 | : this.orientations);
35 |
36 | if (hideOverLays) {
37 | await SystemChrome.setEnabledSystemUIOverlays([]);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/plugin/lib/src/helpers/utils.dart:
--------------------------------------------------------------------------------
1 | String printDuration(Duration? duration) {
2 | if (duration == null) return "--:--";
3 |
4 | String twoDigits(int n) {
5 | if (n >= 10) return "$n";
6 | return "0$n";
7 | }
8 |
9 | String twoDigitMinutes = twoDigits(duration.inMinutes);
10 | String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
11 | return "$twoDigitMinutes:$twoDigitSeconds";
12 | }
13 |
14 | String printDurationWithHours(Duration? duration) {
15 | if (duration == null) return "--:--:--";
16 |
17 | String twoDigits(int n) {
18 | if (n >= 10) return "$n";
19 | return "0$n";
20 | }
21 |
22 | String twoDigitHours = twoDigits(duration.inHours);
23 | String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
24 | String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
25 | return "$twoDigitHours:$twoDigitMinutes:$twoDigitSeconds";
26 | }
27 |
--------------------------------------------------------------------------------
/plugin/lib/src/native/pip_manager.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io' show Platform;
3 |
4 | import 'package:flutter/services.dart';
5 | import 'package:meedu/rx.dart';
6 |
7 | class PipManager {
8 | final _channel = MethodChannel("app.meedu.player");
9 |
10 | Completer _osVersion = Completer();
11 | Completer _pipAvailable = Completer();
12 |
13 | Rx isInPipMode = false.obs;
14 |
15 | PipManager() {
16 | _channel.setMethodCallHandler((call) async {
17 | if (call.method == 'onPictureInPictureModeChanged') {
18 | isInPipMode.value = call.arguments;
19 | }
20 | });
21 | }
22 |
23 | Future get osVersion async {
24 | return _osVersion.future;
25 | }
26 |
27 | Future get pipAvailable async {
28 | return _pipAvailable.future;
29 | }
30 |
31 | Future _getOSVersion() async {
32 | final _os = await _channel.invokeMethod('osVersion');
33 | final os = double.parse(_os!);
34 | this._osVersion.complete(os);
35 | }
36 |
37 | Future enterPip() async {
38 | await _channel.invokeMethod('enterPip');
39 | }
40 |
41 | Future checkPipAvailable() async {
42 | bool available = false;
43 | if (Platform.isAndroid) {
44 | await this._channel.invokeMethod('initPipConfiguration');
45 | await _getOSVersion();
46 | final osVersion = await _osVersion.future;
47 | // check the OS version
48 | if (osVersion >= 7) {
49 | return true;
50 | }
51 | }
52 | this._pipAvailable.complete(available);
53 | return available;
54 | }
55 |
56 | Future dispose() {
57 | return isInPipMode.close();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/closed_caption_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 |
4 | import 'package:meedu_player/meedu_player.dart';
5 | import 'package:meedu_player/src/helpers/responsive.dart';
6 |
7 | class ClosedCaptionView extends StatelessWidget {
8 | final Responsive responsive;
9 | const ClosedCaptionView({Key? key, required this.responsive})
10 | : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | final _ = MeeduPlayerController.of(context);
15 | return RxBuilder(
16 | observables: [_.closedCaptionEnabled],
17 | builder: (__) {
18 | if (!_.closedCaptionEnabled.value) return Container();
19 |
20 | return StreamBuilder(
21 | initialData: Duration.zero,
22 | stream: _.onPositionChanged,
23 | builder: (__, snapshot) {
24 | if (snapshot.hasError) {
25 | return Container();
26 | }
27 |
28 | final strSubtitle = _.videoPlayerController!.value.caption.text;
29 |
30 | return Positioned(
31 | left: 60,
32 | right: 60,
33 | bottom: 0,
34 | child: ClosedCaption(
35 | text: strSubtitle,
36 | textStyle: TextStyle(
37 | color: Colors.white,
38 | fontSize: responsive.ip(2),
39 | ),
40 | ),
41 | );
42 | },
43 | );
44 | });
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/fullscreen_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 |
5 | import 'player_button.dart';
6 |
7 | class FullscreenButton extends StatelessWidget {
8 | final double size;
9 | const FullscreenButton({Key? key, this.size = 30}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | final _ = MeeduPlayerController.of(context);
14 | return RxBuilder(
15 | observables: [_.fullscreen],
16 | builder: (__) {
17 | String iconPath = 'assets/icons/minimize.png';
18 | Widget? customIcon = _.customIcons.minimize;
19 |
20 | if (!_.fullscreen.value) {
21 | iconPath = 'assets/icons/fullscreen.png';
22 | customIcon = _.customIcons.fullscreen;
23 | }
24 | return PlayerButton(
25 | size: size,
26 | circle: false,
27 | backgrounColor: Colors.transparent,
28 | iconColor: Colors.white,
29 | iconPath: iconPath,
30 | customIcon: customIcon,
31 | onPressed: () {
32 | if (_.fullscreen.value) {
33 | // exit to fullscreen
34 | Navigator.pop(context);
35 | } else {
36 | _.goToFullscreen(context);
37 | }
38 | },
39 | );
40 | },
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/fullscreen_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 |
5 | class MeeduPlayerFullscreenPage extends StatelessWidget {
6 | final MeeduPlayerController controller;
7 |
8 | const MeeduPlayerFullscreenPage({Key? key, required this.controller})
9 | : super(key: key);
10 | @override
11 | Widget build(BuildContext context) {
12 | final _size = MediaQuery.of(context).size;
13 | return Scaffold(
14 | backgroundColor: Colors.black,
15 | body: RxBuilder(
16 | observables: [controller.videoFit],
17 | builder: (__) {
18 | return Container(
19 | width: double.infinity,
20 | height: double.infinity,
21 | child: FittedBox(
22 | fit: controller.videoFit.value,
23 | child: SizedBox(
24 | width: _size.width,
25 | height: _size.height,
26 | child: MeeduVideoPlayer(
27 | controller: this.controller,
28 | ),
29 | ),
30 | ),
31 | );
32 | },
33 | ),
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/meedu_video_player.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:meedu_player/src/controller.dart';
5 | import 'package:meedu_player/src/helpers/responsive.dart';
6 | import 'package:meedu_player/src/widgets/closed_caption_view.dart';
7 | import 'package:meedu_player/src/widgets/styles/primary/primary_player_controls.dart';
8 | import 'package:meedu_player/src/widgets/styles/secondary/secondary_player_controls.dart';
9 | import 'package:video_player/video_player.dart';
10 |
11 | class MeeduVideoPlayer extends StatefulWidget {
12 | final MeeduPlayerController controller;
13 |
14 | final Widget Function(
15 | BuildContext context,
16 | MeeduPlayerController controller,
17 | Responsive responsive,
18 | )? header;
19 |
20 | final Widget Function(
21 | BuildContext context,
22 | MeeduPlayerController controller,
23 | Responsive responsive,
24 | )? bottomRight;
25 |
26 | final CustomIcons Function(
27 | Responsive responsive,
28 | )? customIcons;
29 |
30 | MeeduVideoPlayer({
31 | Key? key,
32 | required this.controller,
33 | this.header,
34 | this.bottomRight,
35 | this.customIcons,
36 | }) : super(key: key);
37 |
38 | @override
39 | _MeeduVideoPlayerState createState() => _MeeduVideoPlayerState();
40 | }
41 |
42 | class _MeeduVideoPlayerState extends State {
43 | Widget _getView(MeeduPlayerController _) {
44 | if (_.dataStatus.none) return Container();
45 | if (_.dataStatus.loading) {
46 | return Center(
47 | child: _.loadingWidget,
48 | );
49 | }
50 | if (_.dataStatus.error) {
51 | return Center(
52 | child: Text(
53 | _.errorText ?? 'Error',
54 | style: TextStyle(color: Colors.white),
55 | ),
56 | );
57 | }
58 |
59 | return LayoutBuilder(
60 | builder: (ctx, constraints) {
61 | final responsive = Responsive(
62 | constraints.maxWidth,
63 | constraints.maxHeight,
64 | );
65 |
66 | if (widget.customIcons != null) {
67 | _.customIcons = this.widget.customIcons!(responsive);
68 | }
69 |
70 | if (widget.header != null) {
71 | _.header = this.widget.header!(context, _, responsive);
72 | }
73 |
74 | if (widget.bottomRight != null) {
75 | _.bottomRight = this.widget.bottomRight!(context, _, responsive);
76 | }
77 | return Stack(
78 | alignment: Alignment.center,
79 | children: [
80 | RxBuilder(
81 | observables: [_.videoFit],
82 | builder: (__) {
83 | return SizedBox.expand(
84 | child: FittedBox(
85 | fit: widget.controller.videoFit.value,
86 | child: SizedBox(
87 | width: _.videoPlayerController!.value.size.width,
88 | height: _.videoPlayerController!.value.size.height,
89 | child: VideoPlayer(_.videoPlayerController!),
90 | ),
91 | ),
92 | );
93 | }),
94 | ClosedCaptionView(responsive: responsive),
95 | if (_.controlsEnabled && _.controlsStyle == ControlsStyle.primary)
96 | PrimaryVideoPlayerControls(
97 | responsive: responsive,
98 | ),
99 | if (_.controlsEnabled && _.controlsStyle == ControlsStyle.secondary)
100 | SecondaryVideoPlayerControls(
101 | responsive: responsive,
102 | ),
103 | ],
104 | );
105 | },
106 | );
107 | }
108 |
109 | @override
110 | Widget build(BuildContext context) {
111 | return MeeduPlayerProvider(
112 | child: Container(
113 | color: Colors.black,
114 | width: 0.0,
115 | height: 0.0,
116 | child: RxBuilder(
117 | observables: [
118 | widget.controller.showControls,
119 | widget.controller.dataStatus.status
120 | ],
121 | builder: (__) => _getView(widget.controller),
122 | ),
123 | ),
124 | controller: widget.controller,
125 | );
126 | }
127 | }
128 |
129 | class MeeduPlayerProvider extends InheritedWidget {
130 | final MeeduPlayerController controller;
131 |
132 | MeeduPlayerProvider({
133 | Key? key,
134 | required Widget child,
135 | required this.controller,
136 | }) : super(key: key, child: child);
137 |
138 | @override
139 | bool updateShouldNotify(covariant InheritedWidget oldWidget) {
140 | return false;
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/mute_sound_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:meedu_player/src/helpers/responsive.dart';
5 |
6 | import 'player_button.dart';
7 |
8 | class MuteSoundButton extends StatelessWidget {
9 | final Responsive responsive;
10 | const MuteSoundButton({Key? key, required this.responsive}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | final _ = MeeduPlayerController.of(context);
15 | return RxBuilder(
16 | observables: [_.mute, _.fullscreen],
17 | builder: (__) {
18 | String iconPath = 'assets/icons/mute.png';
19 | Widget? customIcon = _.customIcons.mute;
20 |
21 | if (!_.mute.value) {
22 | iconPath = 'assets/icons/sound.png';
23 | customIcon = _.customIcons.sound;
24 | }
25 |
26 | return PlayerButton(
27 | size: responsive.ip(_.fullscreen.value ? 5 : 7),
28 | circle: false,
29 | backgrounColor: Colors.transparent,
30 | iconColor: Colors.white,
31 | iconPath: iconPath,
32 | customIcon: customIcon,
33 | onPressed: () {
34 | _.setMute(!_.mute.value);
35 | },
36 | );
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/pip_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:meedu_player/src/helpers/responsive.dart';
5 |
6 | import 'player_button.dart';
7 |
8 | class PipButton extends StatelessWidget {
9 | final Responsive responsive;
10 | const PipButton({Key? key, required this.responsive}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | final _ = MeeduPlayerController.of(context);
15 | return RxBuilder(
16 | observables: [
17 | _.pipAvailable,
18 | _.fullscreen,
19 | ],
20 | builder: (__) {
21 | if (!_.pipAvailable.value || !_.showPipButton) return Container();
22 | return PlayerButton(
23 | size: responsive.ip(_.fullscreen.value ? 5 : 7),
24 | circle: false,
25 | backgrounColor: Colors.transparent,
26 | iconColor: Colors.white,
27 | iconPath: 'assets/icons/picture-in-picture.png',
28 | customIcon: _.customIcons.pip,
29 | onPressed: () => _.enterPip(context),
30 | );
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/play_pause_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_meedu/rx.dart';
4 | import 'package:meedu_player/meedu_player.dart';
5 |
6 | import 'player_button.dart';
7 |
8 | class PlayPauseButton extends StatelessWidget {
9 | final double size;
10 | const PlayPauseButton({Key? key, this.size = 40}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | final _ = MeeduPlayerController.of(context);
15 | return RxBuilder(
16 | observables: [
17 | _.playerStatus.status,
18 | _.buffered,
19 | _.isBuffering,
20 | _.position
21 | ],
22 | builder: (__) {
23 | if (_.isBuffering.value) {
24 | return CupertinoButton(child: _.loadingWidget!, onPressed: _.pause);
25 | }
26 |
27 | String iconPath = 'assets/icons/repeat.png';
28 | Widget? customIcon = _.customIcons.repeat;
29 | if (_.playerStatus.playing) {
30 | iconPath = 'assets/icons/pause.png';
31 | customIcon = _.customIcons.pause;
32 | } else if (_.playerStatus.paused) {
33 | iconPath = 'assets/icons/play.png';
34 | customIcon = _.customIcons.play;
35 | }
36 | return PlayerButton(
37 | backgrounColor: Colors.transparent,
38 | iconColor: Colors.white,
39 | onPressed: () {
40 | if (_.playerStatus.playing) {
41 | _.pause();
42 | } else if (_.playerStatus.paused) {
43 | _.play();
44 | } else {
45 | _.play(repeat: true);
46 | }
47 | },
48 | size: size,
49 | iconPath: iconPath,
50 | customIcon: customIcon,
51 | );
52 | },
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/player_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class PlayerButton extends StatelessWidget {
5 | final double size;
6 | final String iconPath;
7 | final VoidCallback onPressed;
8 | final Color backgrounColor, iconColor;
9 | final bool circle;
10 | final Widget? customIcon;
11 |
12 | const PlayerButton({
13 | Key? key,
14 | this.size = 40,
15 | required this.iconPath,
16 | required this.onPressed,
17 | this.circle = true,
18 | this.backgrounColor = Colors.white54,
19 | this.iconColor = Colors.black,
20 | this.customIcon,
21 | }) : super(key: key);
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | return CupertinoButton(
26 | padding: EdgeInsets.zero,
27 | minSize: 20,
28 | child: customIcon ??
29 | Container(
30 | width: this.size,
31 | height: this.size,
32 | padding: EdgeInsets.all(this.size * 0.25),
33 | child: Image.asset(
34 | this.iconPath,
35 | color: this.iconColor,
36 | package: 'meedu_player',
37 | ),
38 | decoration: BoxDecoration(
39 | color: this.backgrounColor,
40 | shape: this.circle ? BoxShape.circle : BoxShape.rectangle,
41 | ),
42 | ),
43 | onPressed: this.onPressed,
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/player_slider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:meedu_player/src/helpers/utils.dart';
5 |
6 | class PlayerSlider extends StatelessWidget {
7 | const PlayerSlider({Key? key}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | final _ = MeeduPlayerController.of(context);
12 | return Stack(
13 | alignment: Alignment.centerLeft,
14 | children: [
15 | Container(
16 | child: LayoutBuilder(builder: (ctx, constraints) {
17 | return RxBuilder(
18 | observables: [_.buffered, _.duration],
19 | builder: (__) {
20 | // convert the bufferedLoaded to a percent using the video duration as a 100%
21 | double percent = 0;
22 | if (_.buffered.value.isNotEmpty) {
23 | final loaded = _.buffered.value.last.end;
24 | percent = loaded.inSeconds / _.duration.value.inSeconds;
25 | }
26 | // draw the bufferedLoaded as a container
27 | return AnimatedContainer(
28 | duration: Duration(milliseconds: 300),
29 | color: Colors.white30,
30 | width: constraints.maxWidth * percent,
31 | height: 3,
32 | );
33 | },
34 | );
35 | }),
36 | ),
37 | RxBuilder(
38 | observables: [_.sliderPosition, _.duration],
39 | builder: (__) {
40 | final int value = _.sliderPosition.value.inSeconds;
41 | final double max = _.duration.value.inSeconds.toDouble();
42 | if (value > max || max <= 0) {
43 | return Container();
44 | }
45 | return Container(
46 | constraints: BoxConstraints(
47 | maxHeight: 30,
48 | ),
49 | padding: EdgeInsets.only(bottom: 8),
50 | alignment: Alignment.center,
51 | child: SliderTheme(
52 | data: SliderThemeData(
53 | trackShape: MSliderTrackShape(),
54 | thumbColor: _.colorTheme,
55 | activeTrackColor: _.colorTheme,
56 | trackHeight: 10,
57 | thumbShape: RoundSliderThumbShape(enabledThumbRadius: 4.0),
58 | ),
59 | child: Slider(
60 | min: 0,
61 | divisions: _.duration.value.inSeconds,
62 | value: value.toDouble(),
63 | onChangeStart: (v) {
64 | _.onChangedSliderStart();
65 | },
66 | onChangeEnd: (v) {
67 | _.onChangedSliderEnd();
68 | _.seekTo(
69 | Duration(seconds: v.floor()),
70 | );
71 | },
72 | label: printDuration(_.sliderPosition.value),
73 | max: max,
74 | onChanged: _.onChangedSlider,
75 | ),
76 | ),
77 | );
78 | },
79 | )
80 | ],
81 | );
82 | }
83 | }
84 |
85 | class MSliderTrackShape extends RoundedRectSliderTrackShape {
86 | @override
87 | Rect getPreferredRect({
88 | required RenderBox parentBox,
89 | Offset offset = Offset.zero,
90 | SliderThemeData? sliderTheme,
91 | bool isEnabled = false,
92 | bool isDiscrete = false,
93 | }) {
94 | final double trackHeight = 1;
95 | final double trackLeft = offset.dx;
96 | final double trackTop =
97 | offset.dy + (parentBox.size.height - trackHeight) / 2 + 4;
98 | final double trackWidth = parentBox.size.width;
99 | return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/styles/controls_container.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/src/controller.dart';
4 |
5 | class ControlsContainer extends StatelessWidget {
6 | final Widget child;
7 | const ControlsContainer({Key? key, required this.child}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | final _ = MeeduPlayerController.of(context);
12 | return Positioned.fill(
13 | child: RxBuilder(
14 | observables: [_.showControls],
15 | builder: (__) => GestureDetector(
16 | onTap: () => _.controls = !_.showControls.value,
17 | child: AnimatedOpacity(
18 | opacity: _.showControls.value ? 1 : 0,
19 | duration: Duration(milliseconds: 300),
20 | child: AnimatedContainer(
21 | duration: Duration(milliseconds: 300),
22 | color: _.showControls.value ? Colors.black38 : Colors.transparent,
23 | child: AbsorbPointer(
24 | absorbing: !_.showControls.value,
25 | child: this.child,
26 | ),
27 | ),
28 | ),
29 | ),
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/styles/primary/bottom_controls.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:meedu_player/src/helpers/responsive.dart';
5 | import 'package:meedu_player/src/helpers/utils.dart';
6 | import 'package:meedu_player/src/widgets/fullscreen_button.dart';
7 | import 'package:meedu_player/src/widgets/mute_sound_button.dart';
8 | import 'package:meedu_player/src/widgets/pip_button.dart';
9 | import 'package:meedu_player/src/widgets/player_slider.dart';
10 | import 'package:meedu_player/src/widgets/video_fit_button.dart';
11 |
12 | class PrimaryBottomControls extends StatelessWidget {
13 | final Responsive responsive;
14 | const PrimaryBottomControls({Key? key, required this.responsive})
15 | : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | final _ = MeeduPlayerController.of(context);
20 | final fontSize = responsive.ip(2.5);
21 | final textStyle = TextStyle(
22 | color: Colors.white,
23 | fontSize: fontSize > 17 ? 17 : fontSize,
24 | );
25 | return Positioned(
26 | left: 5,
27 | right: 0,
28 | bottom: 0,
29 | child: Row(
30 | crossAxisAlignment: CrossAxisAlignment.center,
31 | children: [
32 | // START VIDEO POSITION
33 | RxBuilder(
34 | observables: [_.duration, _.position],
35 | builder: (__) => Text(
36 | _.duration.value.inMinutes >= 60
37 | ? printDurationWithHours(_.position.value)
38 | : printDuration(_.position.value),
39 | style: textStyle,
40 | ),
41 | ),
42 | // END VIDEO POSITION
43 | SizedBox(width: 10),
44 | Expanded(
45 | child: PlayerSlider(),
46 | ),
47 | SizedBox(width: 10),
48 | // START VIDEO DURATION
49 | RxBuilder(
50 | observables: [_.duration],
51 | builder: (__) => Text(
52 | _.duration.value.inMinutes >= 60
53 | ? printDurationWithHours(_.duration.value)
54 | : printDuration(_.duration.value),
55 | style: textStyle,
56 | ),
57 | ),
58 | // END VIDEO DURATION
59 | SizedBox(width: 15),
60 | if (_.bottomRight != null) ...[_.bottomRight!, SizedBox(width: 5)],
61 |
62 | if (_.enabledButtons.pip) PipButton(responsive: responsive),
63 |
64 | if (_.enabledButtons.videoFit) VideoFitButton(responsive: responsive),
65 | if (_.enabledButtons.muteAndSound)
66 | MuteSoundButton(responsive: responsive),
67 |
68 | if (_.enabledButtons.fullscreen)
69 | FullscreenButton(
70 | size: responsive.ip(_.fullscreen.value ? 5 : 7),
71 | )
72 | ],
73 | ),
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/styles/primary/primary_player_controls.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:meedu_player/meedu_player.dart';
3 |
4 | import 'package:meedu_player/src/helpers/responsive.dart';
5 | import 'package:meedu_player/src/widgets/play_pause_button.dart';
6 | import 'package:meedu_player/src/widgets/styles/controls_container.dart';
7 | import 'package:meedu_player/src/widgets/styles/primary/bottom_controls.dart';
8 | import '../../player_button.dart';
9 |
10 | class PrimaryVideoPlayerControls extends StatelessWidget {
11 | final Responsive responsive;
12 | const PrimaryVideoPlayerControls({Key? key, required this.responsive})
13 | : super(key: key);
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | final _ = MeeduPlayerController.of(context);
18 |
19 | return ControlsContainer(
20 | child: Stack(
21 | alignment: Alignment.center,
22 | children: [
23 | // RENDER A CUSTOM HEADER
24 | if (_.header != null)
25 | Positioned(
26 | child: _.header!,
27 | left: 0,
28 | right: 0,
29 | top: 0,
30 | ),
31 |
32 | Row(
33 | mainAxisSize: MainAxisSize.min,
34 | children: [
35 | if (_.enabledButtons.rewindAndfastForward) ...[
36 | PlayerButton(
37 | onPressed: _.rewind,
38 | size: responsive.ip(_.fullscreen.value ? 8 : 12),
39 | iconColor: Colors.white,
40 | backgrounColor: Colors.transparent,
41 | iconPath: 'assets/icons/rewind.png',
42 | customIcon: _.customIcons.rewind,
43 | ),
44 | SizedBox(width: 10),
45 | ],
46 | if (_.enabledButtons.playPauseAndRepeat)
47 | PlayPauseButton(
48 | size: responsive.ip(_.fullscreen.value ? 10 : 15),
49 | ),
50 | if (_.enabledButtons.rewindAndfastForward) ...[
51 | SizedBox(width: 10),
52 | PlayerButton(
53 | onPressed: _.fastForward,
54 | iconColor: Colors.white,
55 | backgrounColor: Colors.transparent,
56 | size: responsive.ip(_.fullscreen.value ? 8 : 12),
57 | iconPath: 'assets/icons/fast-forward.png',
58 | customIcon: _.customIcons.fastForward,
59 | ),
60 | ]
61 | ],
62 | ),
63 |
64 | PrimaryBottomControls(
65 | responsive: responsive,
66 | ),
67 | ],
68 | ),
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/styles/secondary/secondary_bottom_controls.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:meedu_player/src/helpers/responsive.dart';
5 | import 'package:meedu_player/src/helpers/utils.dart';
6 | import 'package:meedu_player/src/widgets/fullscreen_button.dart';
7 | import 'package:meedu_player/src/widgets/mute_sound_button.dart';
8 | import 'package:meedu_player/src/widgets/pip_button.dart';
9 | import 'package:meedu_player/src/widgets/play_pause_button.dart';
10 | import 'package:meedu_player/src/widgets/player_slider.dart';
11 |
12 | import '../../video_fit_button.dart';
13 |
14 | class SecondaryBottomControls extends StatelessWidget {
15 | final Responsive responsive;
16 | const SecondaryBottomControls({Key? key, required this.responsive})
17 | : super(key: key);
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | final _ = MeeduPlayerController.of(context);
22 | final fontSize = responsive.ip(2);
23 | final textStyle = TextStyle(
24 | color: Colors.white,
25 | fontSize: fontSize > 17 ? 17 : fontSize,
26 | );
27 |
28 | final tmp = responsive.ip(8);
29 | final buttonsSize = tmp < 45.0 ? tmp : 45.0;
30 |
31 | return Positioned(
32 | left: 0,
33 | right: 0,
34 | bottom: 0,
35 | child: Column(
36 | mainAxisSize: MainAxisSize.min,
37 | crossAxisAlignment: CrossAxisAlignment.end,
38 | children: [
39 | Transform.translate(
40 | offset: Offset(0, 4),
41 | child: PlayerSlider(),
42 | ),
43 | Row(
44 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
45 | children: [
46 | Row(
47 | children: [
48 | SizedBox(width: 5),
49 | PlayPauseButton(
50 | size: buttonsSize,
51 | ),
52 | SizedBox(width: 5),
53 | RxBuilder(
54 | observables: [_.duration, _.position],
55 | builder: (__) {
56 | String text = "";
57 | if (_.duration.value.inMinutes >= 60) {
58 | // if the duration is >= 1 hour
59 | text =
60 | "${printDurationWithHours(_.position.value)} / ${printDurationWithHours(_.duration.value)}";
61 | } else {
62 | text =
63 | "${printDuration(_.position.value)} / ${printDuration(_.duration.value)}";
64 | }
65 | return Padding(
66 | padding: EdgeInsets.only(right: 5),
67 | child: Text(
68 | text,
69 | style: textStyle,
70 | ),
71 | );
72 | },
73 | ),
74 | // PlayerButton(
75 | // onPressed: _.rewind,
76 | // size: buttonsSize,
77 | // iconColor: Colors.white,
78 | // backgrounColor: Colors.transparent,
79 | // iconPath: 'assets/icons/rewind.png',
80 | // ),
81 | // PlayerButton(
82 | // onPressed: _.fastForward,
83 | // iconColor: Colors.white,
84 | // backgrounColor: Colors.transparent,
85 | // size: buttonsSize,
86 | // iconPath: 'assets/icons/fast-forward.png',
87 | // ),
88 | SizedBox(width: 5),
89 | ],
90 | ),
91 | Row(
92 | children: [
93 | if (_.bottomRight != null) ...[
94 | _.bottomRight!,
95 | SizedBox(width: 10)
96 | ],
97 | if (_.enabledButtons.pip) PipButton(responsive: responsive),
98 | if (_.enabledButtons.videoFit)
99 | VideoFitButton(responsive: responsive),
100 | if (_.enabledButtons.muteAndSound)
101 | MuteSoundButton(responsive: responsive),
102 | if (_.enabledButtons.fullscreen) ...[
103 | FullscreenButton(
104 | size: buttonsSize,
105 | ),
106 | SizedBox(width: 5),
107 | ],
108 | ],
109 | )
110 | ],
111 | ),
112 | ],
113 | ),
114 | );
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/styles/secondary/secondary_player_controls.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:meedu_player/src/controller.dart';
3 | import 'package:meedu_player/src/helpers/responsive.dart';
4 |
5 | import 'package:meedu_player/src/widgets/styles/controls_container.dart';
6 | import 'package:meedu_player/src/widgets/styles/secondary/secondary_bottom_controls.dart';
7 |
8 | class SecondaryVideoPlayerControls extends StatelessWidget {
9 | final Responsive responsive;
10 | const SecondaryVideoPlayerControls({Key? key, required this.responsive})
11 | : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | final _ = MeeduPlayerController.of(context);
16 | return ControlsContainer(
17 | child: Stack(
18 | children: [
19 | // RENDER A CUSTOM HEADER
20 | if (_.header != null)
21 | Positioned(
22 | child: _.header!,
23 | left: 0,
24 | right: 0,
25 | top: 0,
26 | ),
27 | SecondaryBottomControls(
28 | responsive: responsive,
29 | ),
30 | ],
31 | ),
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugin/lib/src/widgets/video_fit_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_meedu/rx.dart';
3 | import 'package:meedu_player/meedu_player.dart';
4 | import 'package:meedu_player/src/helpers/responsive.dart';
5 |
6 | import 'player_button.dart';
7 |
8 | class VideoFitButton extends StatelessWidget {
9 | final Responsive responsive;
10 | const VideoFitButton({Key? key, required this.responsive}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | final _ = MeeduPlayerController.of(context);
15 | return RxBuilder(
16 | observables: [_.fullscreen],
17 | builder: (__) {
18 | String iconPath = 'assets/icons/fit.png';
19 | Widget? customIcon = _.customIcons.videoFit;
20 |
21 | return PlayerButton(
22 | size: responsive.ip(_.fullscreen.value ? 5 : 7),
23 | circle: false,
24 | backgrounColor: Colors.transparent,
25 | iconColor: Colors.white,
26 | iconPath: iconPath,
27 | customIcon: customIcon,
28 | onPressed: () {
29 | _.toggleVideoFit();
30 | },
31 | );
32 | });
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/plugin/player.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/plugin/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.5.0"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.2.0"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | fake_async:
47 | dependency: transitive
48 | description:
49 | name: fake_async
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.2.0"
53 | flutter:
54 | dependency: "direct main"
55 | description: flutter
56 | source: sdk
57 | version: "0.0.0"
58 | flutter_meedu:
59 | dependency: "direct main"
60 | description:
61 | name: flutter_meedu
62 | url: "https://pub.dartlang.org"
63 | source: hosted
64 | version: "2.0.1"
65 | flutter_spinkit:
66 | dependency: "direct main"
67 | description:
68 | name: flutter_spinkit
69 | url: "https://pub.dartlang.org"
70 | source: hosted
71 | version: "5.0.0"
72 | flutter_test:
73 | dependency: "direct dev"
74 | description: flutter
75 | source: sdk
76 | version: "0.0.0"
77 | flutter_web_plugins:
78 | dependency: transitive
79 | description: flutter
80 | source: sdk
81 | version: "0.0.0"
82 | js:
83 | dependency: transitive
84 | description:
85 | name: js
86 | url: "https://pub.dartlang.org"
87 | source: hosted
88 | version: "0.6.3"
89 | matcher:
90 | dependency: transitive
91 | description:
92 | name: matcher
93 | url: "https://pub.dartlang.org"
94 | source: hosted
95 | version: "0.12.10"
96 | meedu:
97 | dependency: "direct main"
98 | description:
99 | name: meedu
100 | url: "https://pub.dartlang.org"
101 | source: hosted
102 | version: "2.0.1"
103 | meta:
104 | dependency: "direct main"
105 | description:
106 | name: meta
107 | url: "https://pub.dartlang.org"
108 | source: hosted
109 | version: "1.3.0"
110 | nested:
111 | dependency: transitive
112 | description:
113 | name: nested
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "1.0.0"
117 | path:
118 | dependency: transitive
119 | description:
120 | name: path
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "1.8.0"
124 | provider:
125 | dependency: transitive
126 | description:
127 | name: provider
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "5.0.0"
131 | sky_engine:
132 | dependency: transitive
133 | description: flutter
134 | source: sdk
135 | version: "0.0.99"
136 | source_span:
137 | dependency: transitive
138 | description:
139 | name: source_span
140 | url: "https://pub.dartlang.org"
141 | source: hosted
142 | version: "1.8.0"
143 | stack_trace:
144 | dependency: transitive
145 | description:
146 | name: stack_trace
147 | url: "https://pub.dartlang.org"
148 | source: hosted
149 | version: "1.10.0"
150 | stream_channel:
151 | dependency: transitive
152 | description:
153 | name: stream_channel
154 | url: "https://pub.dartlang.org"
155 | source: hosted
156 | version: "2.1.0"
157 | string_scanner:
158 | dependency: transitive
159 | description:
160 | name: string_scanner
161 | url: "https://pub.dartlang.org"
162 | source: hosted
163 | version: "1.1.0"
164 | term_glyph:
165 | dependency: transitive
166 | description:
167 | name: term_glyph
168 | url: "https://pub.dartlang.org"
169 | source: hosted
170 | version: "1.2.0"
171 | test_api:
172 | dependency: transitive
173 | description:
174 | name: test_api
175 | url: "https://pub.dartlang.org"
176 | source: hosted
177 | version: "0.2.19"
178 | typed_data:
179 | dependency: transitive
180 | description:
181 | name: typed_data
182 | url: "https://pub.dartlang.org"
183 | source: hosted
184 | version: "1.3.0"
185 | vector_math:
186 | dependency: transitive
187 | description:
188 | name: vector_math
189 | url: "https://pub.dartlang.org"
190 | source: hosted
191 | version: "2.1.0"
192 | video_player:
193 | dependency: "direct main"
194 | description:
195 | name: video_player
196 | url: "https://pub.dartlang.org"
197 | source: hosted
198 | version: "2.0.0"
199 | video_player_platform_interface:
200 | dependency: transitive
201 | description:
202 | name: video_player_platform_interface
203 | url: "https://pub.dartlang.org"
204 | source: hosted
205 | version: "4.0.0"
206 | video_player_web:
207 | dependency: transitive
208 | description:
209 | name: video_player_web
210 | url: "https://pub.dartlang.org"
211 | source: hosted
212 | version: "2.0.0"
213 | sdks:
214 | dart: ">=2.12.0 <3.0.0"
215 | flutter: ">=2.0.0"
216 |
--------------------------------------------------------------------------------
/plugin/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: meedu_player
2 | description: A new Flutter UI video controls for the flutter video_player plugin.
3 | version: 0.5.0-nullsafety.0
4 | homepage: https://github.com/the-meedu-app/flutter-meedu-player/tree/master/plugin
5 |
6 | environment:
7 | sdk: '>=2.12.0 <3.0.0'
8 | flutter: ">=2.0.0"
9 |
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 | video_player: ^2.0.0
15 | meedu: ^2.0.1
16 | flutter_meedu: ^2.0.1
17 | flutter_spinkit: ^5.0.0
18 | meta: ^1.3.0
19 |
20 |
21 | dev_dependencies:
22 | flutter_test:
23 | sdk: flutter
24 |
25 | # For information on the generic Dart part of this file, see the
26 | # following page: https://dart.dev/tools/pub/pubspec
27 |
28 | # The following section is specific to Flutter.
29 | flutter:
30 | # This section identifies this Flutter project as a plugin project.
31 | # The 'pluginClass' and Android 'package' identifiers should not ordinarily
32 | # be modified. They are used by the tooling to maintain consistency when
33 | # adding or updating assets for this project.
34 | plugin:
35 | platforms:
36 | android:
37 | package: app.meedu.player
38 | pluginClass: MeeduPlayerPlugin
39 | ios:
40 | pluginClass: MeeduPlayerPlugin
41 |
42 | assets:
43 | - assets/icons/fast-forward.png
44 | - assets/icons/fullscreen.png
45 | - assets/icons/minimize.png
46 | - assets/icons/mute.png
47 | - assets/icons/play.png
48 | - assets/icons/pause.png
49 | - assets/icons/repeat.png
50 | - assets/icons/rewind.png
51 | - assets/icons/sound.png
52 | - assets/icons/picture-in-picture.png
53 | - assets/icons/fit.png
54 | #
55 | # For details regarding assets in packages, see
56 | # https://flutter.dev/assets-and-images/#from-packages
57 | #
58 | # An image asset can refer to one or more resolution-specific "variants", see
59 | # https://flutter.dev/assets-and-images/#resolution-aware.
60 |
61 | # To add custom fonts to your plugin package, add a fonts section here,
62 | # in this "flutter" section. Each entry in this list should have a
63 | # "family" key with the font family name, and a "fonts" key with a
64 | # list giving the asset and other descriptors for the font. For
65 | # example:
66 | # fonts:
67 | # - family: Schyler
68 | # fonts:
69 | # - asset: fonts/Schyler-Regular.ttf
70 | # - asset: fonts/Schyler-Italic.ttf
71 | # style: italic
72 | # - family: Trajan Pro
73 | # fonts:
74 | # - asset: fonts/TrajanPro.ttf
75 | # - asset: fonts/TrajanPro_Bold.ttf
76 | # weight: 700
77 | #
78 | # For details regarding fonts in packages, see
79 | # https://flutter.dev/custom-fonts/#from-packages
--------------------------------------------------------------------------------