Closed b2nil closed 4 weeks ago
i am facing the same exact problem in my code too
can you provide a working example so i can test with and let you know of a fix?
@abdelaziz-mahdy hi, I already provided a fully working sample above, just click to expand to copy the code.
@abdelaziz-mahdy hi, I already provided a fully working sample above, just click to expand to copy the code.
oh i am sorry. i didnt see it, i will test it when i got free time, and if i have a fix i will let you know
@abdelaziz-mahdy hi, I already provided a fully working sample above, just click to expand to copy the code.
oh i am sorry. i didnt see it, i will test it when i got free time, and if i have a fix i will let you know
No problem and take your time.
the problem is the context of the modal sheet, is wrong the code cant find the full screen widget
so the fix should be not using modalsheet, couldnt find a way to pass the right context to the modal sheet, it cant find it anywhere
this is the updated example
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
runApp(const TVApp());
}
class TVApp extends StatelessWidget {
const TVApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'IPTV Player',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 47, 99, 89)),
useMaterial3: true,
),
home: const DemoPlayer(title: 'Demo Mediakit Player'),
);
}
}
class DemoPlayer extends StatefulWidget {
const DemoPlayer({super.key, required this.title});
final String title;
@override
State<DemoPlayer> createState() => _DemoPlayerState();
}
class _DemoPlayerState extends State<DemoPlayer> {
late final GlobalKey<VideoState> key = GlobalKey<VideoState>();
late final player = Player();
late var controller = VideoController(player);
bool playing = false;
bool buffering = false;
bool isFullscreen = false;
ValueNotifier<bool> showControls = ValueNotifier(false);
@override
void initState() {
super.initState();
if (mounted) {
controller = VideoController(player);
player.open(Media("https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"));
setState(() {
isFullscreen = key.currentState?.isFullscreen() ?? false;
});
}
}
// exitFullscreen not working as expected
void _toggleFullscreen(VoidCallback onToggle) async {
await key.currentState?.toggleFullscreen();
// if (!isFullscreen) {
// await key.currentState?.enterFullscreen();
// } else {
// await key.currentState?.exitFullscreen();
// }
// tried to set the `isFullscreen` with and without a callback function
onToggle();
// setState(() {
// isFullscreen = !isFullscreen;
// });
}
void playPause() {
setState(() {
controller.player.playOrPause();
playing = !playing;
});
}
Widget _showVideoControls(BuildContext context) {
const controlsColor = Colors.white;
// Using StatefulBuilder here to get access to the inner `setState` method
// so that the icons updates accordingly with the changes of
// the states `playing` and `isFullscreen`
return ValueListenableBuilder(
valueListenable: showControls,
builder: (context, value, child) {
if (!value) {
return Container();
}
return Container(
color: Colors.black38,
child: StatefulBuilder(
builder: (ctx, innerSetState) => LayoutBuilder(
builder: (layoutCtx, constraints) => SizedBox(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
innerSetState(() {
playPause();
});
},
icon: Icon(
playing ? Icons.pause : Icons.play_arrow,
color: controlsColor,
),
),
const Expanded(
child: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
LinearProgressIndicator(),
],
),
),
),
IconButton(
// The issue is here:
// - okay to enter fullscreen when the button is clicked
// - NOT okay to exit fullscreen when the button is clicked
onPressed: () {
_toggleFullscreen(() {
innerSetState(() {
isFullscreen = !isFullscreen;
});
});
},
icon: Icon(
// The icon toggles as expected when the button is clicked
isFullscreen
? Icons.fullscreen_exit
: Icons.fullscreen,
color: controlsColor,
),
),
],
),
),
),
),
),
);
});
}
@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width * 9.0 / 16.0,
child: Video(
key: key,
controller: controller,
controls: (state) {
state.widget.controller.player.stream.buffering.listen((event) {
if (buffering != event) {
setState(() {
buffering = event;
});
}
});
state.widget.controller.player.stream.playing.listen((event) {
if (playing != event) {
setState(() {
playing = event;
});
}
});
return Scaffold(
backgroundColor: Colors.transparent,
body: Builder(
builder: (ctx) => GestureDetector(
// _toggleFullscreen here works with double tap
// okay to enter and exit fullscreen as expected
onDoubleTap: () {
_toggleFullscreen(() {
setState(() {
isFullscreen = !isFullscreen;
});
});
},
onTap: () {
showControls.value = !showControls.value;
},
child: Stack(
alignment: Alignment.bottomCenter,
children: [
_buildLoadingIndicator(),
Positioned.fill(
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: 60,
child: _showVideoControls(ctx),
))),
],
),
),
),
);
},
),
),
);
}
// for demo only
Widget _buildLoadingIndicator() {
return Stack(
fit: StackFit.expand,
children: [
Positioned.fill(
child: DecoratedBox(
decoration: const BoxDecoration(color: Colors.transparent),
child: Center(
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 0.0, end: buffering ? 1.0 : 0.0),
duration: const Duration(milliseconds: 150),
builder: (ctx, value, child) {
if (value > 0.0) {
return Opacity(
opacity: value,
child: child ?? const CircularProgressIndicator(),
);
}
return const SizedBox.shrink();
},
child: const CircularProgressIndicator(),
),
),
),
),
],
);
}
}
@abdelaziz-mahdy Thanks for the effort, it is really appreciated. Value notifier works for me.
I tried to implement a simple video controls using the provided custom builder for video controls, and used
showModalBottomSheet
to implement the bottom bar for housing the play/pause button, the seekbar and the fullscreen button. To control the fullscreen state, I used a global key as suggested in the docs.The issue I met is that, inside the bottom sheet created using
showModalBottomSheet
, when I clicked the fullscreen button, the player can enter fullscreen mode, but cannot exit when clicking the fullscreen button again. I used the builtintoggleFullscreen
method through the global key, but I didn't use theStreamBuilder
for the Icon implementation.Here is the
_toggleFullscreen
and_showVideoControls
implementation using a bottom sheet:The thing that puzzles me is that I can toggle the fullscreen mode as expected by double tap on the player, and I can enter fullscreen by just clicking the fullscreen icon button, but I cannot exit fullscreen mode by just clicking the fullscreen icon button. The
_toggleFullscreen
funtion fires as expected at debugging.The builtin
MaterialVideoControls
works as expectd. So I guess maybe I did something incorrectly which led to this issue, but I failed to spot. Would you guys please kindly have a look at my code and let me known what went wrong?Here is my development environment:
And here below is a fullly runnable code to reproduce the issue:
Click to expand!
```dart import 'package:flutter/material.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); MediaKit.ensureInitialized(); runApp(const TVApp()); } class TVApp extends StatelessWidget { const TVApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'IPTV Player', theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: const Color.fromARGB(255, 47, 99, 89)), useMaterial3: true, ), home: const DemoPlayer(title: 'Demo Mediakit Player'), ); } } class DemoPlayer extends StatefulWidget { const DemoPlayer({super.key, required this.title}); final String title; @override State