1- import 'package:flutter/foundation.dart' ;
21import 'package:flutter/material.dart' ;
3- import 'package:theoplayer/theoplayer.dart' ;
4- import 'package:theoplayer_example/player_widgets/current_time_widget.dart' ;
5- import 'package:theoplayer_example/player_widgets/player_ui_widget.dart' ;
6- import 'package:theoplayer_example/player_widgets/texture_widgets/aspect_ratio_chromeless_widget.dart' ;
7- import 'package:theoplayer_example/player_widgets/texture_widgets/aspect_ratio_custom_fullscreen_widget.dart' ;
8-
9- // use your THEOplayer Flutter license here from https://portal.theoplayer.com
10- // without a license the player only accepts URLs from 'localhost' or 'theoplayer.com' domains
11- const PLAYER_LICENSE = "" ;
2+ import 'package:theoplayer_example/player.dart' ;
3+ import 'package:theoplayer_example/weather_page.dart' ;
124
135void main () {
146 runApp (const MyApp ());
@@ -22,347 +14,35 @@ class MyApp extends StatefulWidget {
2214}
2315
2416class _MyAppState extends State <MyApp > {
25- UniqueKey key1 = UniqueKey ();
26- UniqueKey key2 = UniqueKey ();
27- late THEOplayer player;
28-
29- final _messengerKey = GlobalKey <ScaffoldMessengerState >();
30-
31- @override
32- void initState () {
33- super .initState ();
34-
35- player = THEOplayer (
36- fullscreenBuilder: (BuildContext context, THEOplayer player) {
37- // default, chromeless behaviour, same as not specifying a fullscreenBuilder.
38- // return FullscreenStatefulWidget(theoplayer: player, fullscreenConfig: player.theoPlayerConfig.fullscreenConfig),
39-
40- // example on how to pass a custom UI with the player
41- return Scaffold (
42- body: Stack (
43- alignment: Alignment .center,
44- children: [
45- // for hybrid composition:
46- //FullscreenStatefulWidget(theoplayer: player, fullscreenConfig: player.theoPlayerConfig.fullscreenConfig),
47-
48- // for Texture-based composition:
49- AspectRatioCustomFullscreenWidget (theoplayer: player, fullscreenConfig: player.theoPlayerConfig.fullscreenConfiguration),
50- PlayerUI (player: player),
51- ],
52- ),
53- );
54- },
55- theoPlayerConfig: THEOplayerConfig (
56- license: PLAYER_LICENSE ,
57- // Extra THEOlive configuration:
58- //theolive: TheoLiveConfiguration(externalSessionId: "mySessionID"),
59- webConfiguration: WebConfig (libraryLocation: "/theoplayer" )
60- ),
61- onCreate: () {
62- print ("main - THEOplayer - onCreate" );
63- player.autoplay = true ;
64- player.allowBackgroundPlayback = true ;
65- player.allowAutomaticPictureInPicture = true ;
66- // print errors
67- player.addEventListener (PlayerEventTypes .ERROR , (errorEvent) {
68- var error = errorEvent as ErrorEvent ;
69- _messengerKey.currentState? .showSnackBar (
70- SnackBar (
71- duration: const Duration (milliseconds: 6000 ),
72- backgroundColor: Colors .red,
73- content: Text (error.error),
74- action: SnackBarAction (
75- label: 'OK' ,
76- onPressed: () {
77- // Code to execute.
78- },
79- ),
80- ),
81- );
82- });
83-
84- player.addEventListener (PlayerEventTypes .PRESENTATIONMODECHANGE , (pmEvent) {
85- var pmd = pmEvent as PresentationModeChangeEvent ;
86- print ("New presentation mode: ${pmd .presentationMode }" );
87- });
88-
89- });
90- }
91-
92- @override
93- void dispose () {
94- player.dispose ();
95- super .dispose ();
96- }
17+ int _selectedIndex = 0 ;
9718
9819 @override
9920 Widget build (BuildContext context) {
10021 return MaterialApp (
101- scaffoldMessengerKey: _messengerKey,
10222 home: Scaffold (
10323 appBar: AppBar (
10424 title: const Text ('THEOplayer example app' ),
10525 ),
106- body: Builder (builder: (context) {
107- return Center (
108- child: Column (
109- children: [
110- SizedBox (
111- width: 400 ,
112- height: 300 ,
113- child: Stack (
114- alignment: Alignment .center,
115- children: [
116- //for hybrid compositon.
117- // ChromelessPlayerView(player: player),
118-
119- //for Texture-based composition:
120- AspectRatioChromelessPlayerView (player: player, continuouslyFollowAspectRatioChanges: true ,),
121- PlayerUI (player: player),
122- ],
123- ),
124- ),
125- Expanded (
126- child: ListView (
127- children: [
128- Column (
129- children: [
130- CurrentTimeWidget (player: player),
131- const SizedBox (
132- height: 16 ,
133- ),
134- const Text ("API calls" ),
135- Row (
136- mainAxisSize: MainAxisSize .max,
137- mainAxisAlignment: MainAxisAlignment .center,
138- children: [
139- FilledButton (
140- onPressed: () => {player.play ()},
141- child: const Text ("PLAY" ),
142- ),
143- FilledButton (
144- onPressed: () => {player.pause ()},
145- child: const Text ("PAUSE" ),
146- ),
147- ],
148- ),
149- FilledButton (
150- onPressed: () => {logApiCalls ()},
151- child: const Text ("API LOGGER" ),
152- ),
153- FilledButton (
154- onPressed: () {
155- player.setPresentationMode (PresentationMode .FULLSCREEN );
156- },
157- child: const Text ("Open Fullscreen" )),
158- FilledButton (
159- onPressed: () {
160- if (kIsWeb) {
161- player.setPresentationMode (PresentationMode .PIP );
162- } else {
163- player.allowAutomaticPictureInPicture = ! player.allowAutomaticPictureInPicture;
164- }
165- },
166- child: const Text ("Open PiP (web) / Flip automatic PiP mode (native)" )),
167- FilledButton (
168- onPressed: () {
169- player.setPresentationMode (PresentationMode .INLINE );
170- },
171- child: const Text ("INLINE" )),
172- FilledButton (
173- onPressed: () {
174- player.getVideoTracks ().first.targetQuality = player.getVideoTracks ().first.qualities.first;
175- },
176- child: const Text ("set video target quality" )),
177- Column (
178- children: [
179- const Text ("Sources" ),
180- FilledButton (
181- onPressed: () {
182- _licenseConfigCheckDialog (context);
183- player.source = SourceDescription (sources: [
184- TypedSource (
185- src: "https://cdn.theoplayer.com/video/big_buck_bunny/big_buck_bunny.m3u8" ,
186- type: "application/x-mpegurl"
187- ),
188- ]);
189- },
190- child: const Text ("Basic source" ),
191- ),
192- FilledButton (
193- onPressed: () {
194- _licenseConfigCheckDialog (context);
195-
196- /**
197- * register for theolive events, if interested
198- *
199- *
200- player.theoLive?.addEventListener(THEOliveApiEventTypes.DISTRIBUTIONLOADSTART, (e) {
201- print("DISTRIBUTIONLOADSTART");
202- });
203- player.theoLive?.addEventListener(THEOliveApiEventTypes.DISTRIBUTIONOFFLINE, (e) {
204- print("DISTRIBUTIONOFFLINE");
205- });
206- player.theoLive?.addEventListener(THEOliveApiEventTypes.ENDPOINTLOADED, (e) {
207- print("ENDPOINTLOADED");
208- });
209- player.theoLive?.addEventListener(THEOliveApiEventTypes.INTENTTOFALLBACK, (e) {
210- print("INTENTTOFALLBACK");
211- });
212- player.theoLive?.addEventListener(THEOliveApiEventTypes.ENTERBADNETWORKMODE, (e) {
213- print("ENTERBADNETWORKMODE");
214- });
215- player.theoLive?.addEventListener(THEOliveApiEventTypes.EXITBADNETWORKMODE, (e) {
216- print("EXITBADNETWORKMODE");
217- });
218- */
219-
220- /**
221- * preload channels for faster startup
222- *
223- player.theoLive?.preloadChannels(["38yyniscxeglzr8n0lbku57b0"] );
224- */
225- player.source = SourceDescription (sources: [
226- TheoLiveSource (src: "38yyniscxeglzr8n0lbku57b0" ),
227- ]);
228- },
229- child: const Text ("THEOlive source" ),
230- ),
231- FilledButton (
232- onPressed: () {
233- _licenseConfigCheckDialog (context);
234- player.source = SourceDescription (sources: [
235- TypedSource (
236- src: "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd" ,
237- type: "application/dash+xml" ,
238- drm: DRMConfiguration (
239- widevine: WidevineDRMConfiguration (
240- licenseAcquisitionURL: "https://proxy.uat.widevine.com/proxy?provider=widevine_test" ),
241- )),
242- ]);
243- },
244- child: const Text ("Widevine source" ),
245- ),
246- FilledButton (
247- onPressed: () {
248- _licenseConfigCheckDialog (context);
249- player.source = SourceDescription (sources: [
250- TypedSource (
251- src: "https://fps.ezdrm.com/demo/video/ezdrm.m3u8" ,
252- type: "application/x-mpegurl" ,
253- drm: DRMConfiguration (
254- customIntegrationId: "EzdrmDRMIntegration" ,
255- fairplay: FairPlayDRMConfiguration (
256- licenseAcquisitionURL: "https://fps.ezdrm.com/api/licenses/09cc0377-6dd4-40cb-b09d-b582236e70fe" ,
257- certificateURL: "https://fps.ezdrm.com/demo/video/eleisure.cer" ,
258- headers: null ,
259- ),
260- )),
261- ]);
262- },
263- child: const Text ("Fairplay EZDRM source - iOS" ),
264- ),
265- FilledButton (
266- onPressed: () {
267- _licenseConfigCheckDialog (context);
268- player.source = SourceDescription (sources: [
269- TypedSource (
270- src:
271- "https://d2jl6e4h8300i8.cloudfront.net/netflix_meridian/4k-18.5!9/keyos-logo/g180-avc_a2.0-vbr-aac-128k/r30/dash-wv-pr/stream.mpd" ,
272- drm: DRMConfiguration (
273- customIntegrationId: "KeyOSDRMIntegration" ,
274- integrationParameters: {
275- "x-keyos-authorization" :
276- "PEtleU9TQXV0aGVudGljYXRpb25YTUw+PERhdGE+PEdlbmVyYXRpb25UaW1lPjIwMTYtMTEtMTkgMDk6MzQ6MDEuOTkyPC9HZW5lcmF0aW9uVGltZT48RXhwaXJhdGlvblRpbWU+MjAyNi0xMS0xOSAwOTozNDowMS45OTI8L0V4cGlyYXRpb25UaW1lPjxVbmlxdWVJZD4wZmZmMTk3YWQzMzQ0ZTMyOWU0MTA0OTIwMmQ5M2VlYzwvVW5pcXVlSWQ+PFJTQVB1YktleUlkPjdlMTE0MDBjN2RjY2QyOWQwMTc0YzY3NDM5N2Q5OWRkPC9SU0FQdWJLZXlJZD48V2lkZXZpbmVQb2xpY3kgZmxfQ2FuUGxheT0idHJ1ZSIgZmxfQ2FuUGVyc2lzdD0iZmFsc2UiIC8+PFdpZGV2aW5lQ29udGVudEtleVNwZWMgVHJhY2tUeXBlPSJIRCI+PFNlY3VyaXR5TGV2ZWw+MTwvU2VjdXJpdHlMZXZlbD48L1dpZGV2aW5lQ29udGVudEtleVNwZWM+PEZhaXJQbGF5UG9saWN5IC8+PExpY2Vuc2UgdHlwZT0ic2ltcGxlIiAvPjwvRGF0YT48U2lnbmF0dXJlPk1sNnhkcU5xc1VNalNuMDdicU8wME15bHhVZUZpeERXSHB5WjhLWElBYlAwOE9nN3dnRUFvMTlYK1c3MDJOdytRdmEzNFR0eDQydTlDUlJPU1NnREQzZTM4aXE1RHREcW9HelcwS2w2a0JLTWxHejhZZGRZOWhNWmpPTGJkNFVkRnJUbmxxU21raC9CWnNjSFljSmdaUm5DcUZIbGI1Y0p0cDU1QjN4QmtxMUREZUEydnJUNEVVcVJiM3YyV1NueUhGeVZqWDhCR3o0ZWFwZmVFeDlxSitKbWI3dUt3VjNqVXN2Y0Fab1ozSHh4QzU3WTlySzRqdk9Wc1I0QUd6UDlCc3pYSXhKd1ZSZEk3RXRoMjhZNXVEQUVZVi9hZXRxdWZiSXIrNVZOaE9yQ2JIVjhrR2praDhHRE43dC9nYWh6OWhVeUdOaXRqY2NCekJvZHRnaXdSUT09PC9TaWduYXR1cmU+PC9LZXlPU0F1dGhlbnRpY2F0aW9uWE1MPg==",
277- },
278- widevine: WidevineDRMConfiguration (
279- licenseAcquisitionURL: "https://wv-keyos.licensekeyserver.com" ,
280- ),
281- )),
282- ]);
283- },
284- child: const Text ("Widevine KeyOS source - Android" ),
285- ),
286- ],
287- )
288- ],
289- ),
290- ],
291- ),
292- )
293- ],
294- ),
295- );
296- }),
297- ),
298- );
299- }
300-
301- Future <void > logApiCalls () async {
302- print ("source: ${player .source }" );
303- print ("isAutoplay: ${player .isAutoplay }" );
304- print ("isPaused: ${player .isPaused }" );
305- print ("currentTime: ${player .currentTime }" );
306- print ("currentProgramDateTIme: ${player .currentProgramDateTime }" );
307- print ("duration: ${player .duration }" );
308- print ("playbackRate: ${player .playbackRate }" );
309- print ("volume: ${player .volume }" );
310- print ("isMuted: ${player .isMuted }" );
311- print ("preload: ${player .preload }" );
312- print ("readyState: ${player .readyState }" );
313- print ("isSeeking: ${player .isSeeking }" );
314- print ("isEnded: ${player .isEnded }" );
315- print ("videoHeight: ${player .videoHeight }" );
316- print ("videoWidth: ${player .videoWidth }" );
317- print ("buffered: ${player .buffered }" );
318- print ("seekable: ${player .seekable }" );
319- print ("played: ${(player .played )}" );
320- print ("error: ${player .error }" );
321- print ("audio target quality: ${player .audioTracks .first .targetQuality ?.uid }" );
322- print ("audio active quality: ${player .audioTracks .first .activeQuality ?.uid }" );
323- print ("video target quality: ${player .videoTracks .first .targetQuality ?.uid }" );
324- print ("video active quality: ${player .videoTracks .first .activeQuality ?.uid }" );
325- print ("allowBackgroundPlayback: ${player .allowBackgroundPlayback }" );
326-
327- if (kIsWeb) {
328- print ("theolive distributionState: ${player .theoLive ?.distributionState }" );
329- print ("theolive badnetwork: ${player .theoLive ?.badNetworkMode }" );
330- }
331- }
332-
333- Future <void > _licenseConfigCheckDialog (BuildContext context) async {
334- if (PLAYER_LICENSE != "" ) {
335- //ok
336- return ;
337- }
338-
339- return showDialog <void >(
340- context: context,
341- barrierDismissible: false , // user must tap button!
342- builder: (BuildContext context) {
343- return AlertDialog (
344- title: const Text ('License configuration needed!' ),
345- content: const SingleChildScrollView (
346- child: ListBody (
347- children: < Widget > [
348- Text ('Your forgot to configure your license!' ),
349- SizedBox (height: 8 ,),
350- Text ('Without a license, THEOplayer can only play sources from `theoplayer.com`' ),
351- SizedBox (height: 8 ,),
352- Text ('Get your license from `https://portal.theoplayer.com!`' ),
353- ],
26+ body: _selectedIndex == 0 ? const PlayerPage () : const WeatherPage (),
27+ bottomNavigationBar: BottomNavigationBar (
28+ items: const < BottomNavigationBarItem > [
29+ BottomNavigationBarItem (
30+ icon: Icon (Icons .ondemand_video),
31+ label: 'Player' ,
35432 ),
355- ),
356- actions: < Widget > [
357- TextButton (
358- child: const Text ('OK' ),
359- onPressed: () {
360- Navigator .of (context).pop ();
361- },
33+ BottomNavigationBarItem (
34+ icon: Icon (Icons .wb_sunny),
35+ label: 'Weather' ,
36236 ),
36337 ],
364- );
365- },
38+ currentIndex: _selectedIndex,
39+ onTap: (int index) {
40+ setState (() {
41+ _selectedIndex = index;
42+ });
43+ },
44+ ),
45+ ),
36646 );
36747 }
36848}
0 commit comments