Skip to content

Commit 7b3b839

Browse files
authored
Merge pull request #623 from henri2h/temporized-buffering-status
[Android] Add a delay before displaying progress indicator
2 parents fde2eca + 491becd commit 7b3b839

File tree

6 files changed

+174
-5
lines changed

6 files changed

+174
-5
lines changed

example/lib/app/app.dart

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:io';
2+
13
import 'package:chewie/chewie.dart';
24
import 'package:chewie_example/app/theme.dart';
35
import 'package:flutter/material.dart';
@@ -23,6 +25,7 @@ class _ChewieDemoState extends State<ChewieDemo> {
2325
late VideoPlayerController _videoPlayerController1;
2426
late VideoPlayerController _videoPlayerController2;
2527
ChewieController? _chewieController;
28+
int? bufferDelay;
2629

2730
@override
2831
void initState() {
@@ -39,6 +42,7 @@ class _ChewieDemoState extends State<ChewieDemo> {
3942
}
4043

4144
List<String> srcs = [
45+
"https://assets.mixkit.co/videos/preview/mixkit-spinning-around-the-earth-29351-large.mp4",
4246
"https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4",
4347
"https://assets.mixkit.co/videos/preview/mixkit-a-girl-blowing-a-bubble-gum-at-an-amusement-park-1226-large.mp4"
4448
];
@@ -110,6 +114,8 @@ class _ChewieDemoState extends State<ChewieDemo> {
110114
videoPlayerController: _videoPlayerController1,
111115
autoPlay: true,
112116
looping: true,
117+
progressIndicatorDelay:
118+
bufferDelay != null ? Duration(milliseconds: bufferDelay!) : null,
113119

114120
additionalOptions: (context) {
115121
return <OptionItem>[
@@ -155,7 +161,10 @@ class _ChewieDemoState extends State<ChewieDemo> {
155161

156162
Future<void> toggleVideo() async {
157163
await _videoPlayerController1.pause();
158-
currPlayIndex = currPlayIndex == 0 ? 1 : 0;
164+
currPlayIndex += 1;
165+
if (currPlayIndex >= srcs.length) {
166+
currPlayIndex = 0;
167+
}
159168
await initializePlayer();
160169
}
161170

@@ -302,9 +311,74 @@ class _ChewieDemoState extends State<ChewieDemo> {
302311
),
303312
],
304313
),
314+
if (Platform.isAndroid)
315+
ListTile(
316+
title: const Text("Delay"),
317+
subtitle: DelaySlider(
318+
delay:
319+
_chewieController?.progressIndicatorDelay?.inMilliseconds,
320+
onSave: (delay) async {
321+
if (delay != null) {
322+
bufferDelay = delay == 0 ? null : delay;
323+
await initializePlayer();
324+
}
325+
},
326+
),
327+
)
305328
],
306329
),
307330
),
308331
);
309332
}
310333
}
334+
335+
class DelaySlider extends StatefulWidget {
336+
const DelaySlider({Key? key, required this.delay, required this.onSave})
337+
: super(key: key);
338+
339+
final int? delay;
340+
final void Function(int?) onSave;
341+
@override
342+
State<DelaySlider> createState() => _DelaySliderState();
343+
}
344+
345+
class _DelaySliderState extends State<DelaySlider> {
346+
int? delay;
347+
bool saved = false;
348+
349+
@override
350+
void initState() {
351+
super.initState();
352+
delay = widget.delay;
353+
}
354+
355+
@override
356+
Widget build(BuildContext context) {
357+
const int max = 1000;
358+
return ListTile(
359+
title: Text(
360+
"Progress indicator delay ${delay != null ? "${delay.toString()} MS" : ""}",
361+
),
362+
subtitle: Slider(
363+
value: delay != null ? (delay! / max) : 0,
364+
onChanged: (value) async {
365+
delay = (value * max).toInt();
366+
setState(() {
367+
saved = false;
368+
});
369+
},
370+
),
371+
trailing: IconButton(
372+
icon: const Icon(Icons.save),
373+
onPressed: saved
374+
? null
375+
: () {
376+
widget.onSave(delay);
377+
setState(() {
378+
saved = true;
379+
});
380+
},
381+
),
382+
);
383+
}
384+
}

lib/src/chewie_player.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ class ChewieController extends ChangeNotifier {
280280
this.systemOverlaysAfterFullScreen = SystemUiOverlay.values,
281281
this.deviceOrientationsAfterFullScreen = DeviceOrientation.values,
282282
this.routePageBuilder,
283+
this.progressIndicatorDelay,
283284
this.hideControlsTimer = defaultHideControlsTimer,
284285
}) : assert(
285286
playbackSpeeds.every((speed) => speed > 0),
@@ -324,6 +325,7 @@ class ChewieController extends ChangeNotifier {
324325
List<DeviceOrientation>? deviceOrientationsOnEnterFullScreen,
325326
List<SystemUiOverlay>? systemOverlaysAfterFullScreen,
326327
List<DeviceOrientation>? deviceOrientationsAfterFullScreen,
328+
Duration? progressIndicatorDelay,
327329
Widget Function(
328330
BuildContext,
329331
Animation<double>,
@@ -377,6 +379,8 @@ class ChewieController extends ChangeNotifier {
377379
this.deviceOrientationsAfterFullScreen,
378380
routePageBuilder: routePageBuilder ?? this.routePageBuilder,
379381
hideControlsTimer: hideControlsTimer ?? this.hideControlsTimer,
382+
progressIndicatorDelay:
383+
progressIndicatorDelay ?? this.progressIndicatorDelay,
380384
);
381385
}
382386

@@ -513,6 +517,9 @@ class ChewieController extends ChangeNotifier {
513517
/// Defines a custom RoutePageBuilder for the fullscreen
514518
final ChewieRoutePageBuilder? routePageBuilder;
515519

520+
/// Defines a delay in milliseconds between entering buffering state and displaying the loading spinner. Set null (default) to disable it.
521+
final Duration? progressIndicatorDelay;
522+
516523
static ChewieController of(BuildContext context) {
517524
final chewieControllerProvider =
518525
context.dependOnInheritedWidgetOfExactType<ChewieControllerProvider>()!;

lib/src/cupertino/cupertino_controls.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class _CupertinoControlsState extends State<CupertinoControls>
4747
bool _dragging = false;
4848
Duration? _subtitlesPosition;
4949
bool _subtitleOn = false;
50+
Timer? _bufferingDisplayTimer;
51+
bool _displayBufferingIndicator = false;
5052

5153
late VideoPlayerController controller;
5254

@@ -91,7 +93,7 @@ class _CupertinoControlsState extends State<CupertinoControls>
9193
absorbing: notifier.hideStuff,
9294
child: Stack(
9395
children: [
94-
if (_latestValue.isBuffering)
96+
if (_displayBufferingIndicator)
9597
const Center(
9698
child: CircularProgressIndicator(),
9799
)
@@ -769,8 +771,32 @@ class _CupertinoControlsState extends State<CupertinoControls>
769771
});
770772
}
771773

774+
void _bufferingTimerTimeout() {
775+
_displayBufferingIndicator = true;
776+
if (mounted) {
777+
setState(() {});
778+
}
779+
}
780+
772781
void _updateState() {
773782
if (!mounted) return;
783+
784+
// display the progress bar indicator only after the buffering delay if it has been set
785+
if (chewieController.progressIndicatorDelay != null) {
786+
if (controller.value.isBuffering) {
787+
_bufferingDisplayTimer ??= Timer(
788+
chewieController.progressIndicatorDelay!,
789+
_bufferingTimerTimeout,
790+
);
791+
} else {
792+
_bufferingDisplayTimer?.cancel();
793+
_bufferingDisplayTimer = null;
794+
_displayBufferingIndicator = false;
795+
}
796+
} else {
797+
_displayBufferingIndicator = controller.value.isBuffering;
798+
}
799+
774800
setState(() {
775801
_latestValue = controller.value;
776802
_subtitlesPosition = controller.value.position;

lib/src/material/material_controls.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class _MaterialControlsState extends State<MaterialControls>
4040
Timer? _showAfterExpandCollapseTimer;
4141
bool _dragging = false;
4242
bool _displayTapped = false;
43+
Timer? _bufferingDisplayTimer;
44+
bool _displayBufferingIndicator = false;
4345

4446
final barHeight = 48.0 * 1.5;
4547
final marginSize = 5.0;
@@ -82,7 +84,7 @@ class _MaterialControlsState extends State<MaterialControls>
8284
absorbing: notifier.hideStuff,
8385
child: Stack(
8486
children: [
85-
if (_latestValue.isBuffering)
87+
if (_displayBufferingIndicator)
8688
const Center(
8789
child: CircularProgressIndicator(),
8890
)
@@ -550,8 +552,32 @@ class _MaterialControlsState extends State<MaterialControls>
550552
});
551553
}
552554

555+
void _bufferingTimerTimeout() {
556+
_displayBufferingIndicator = true;
557+
if (mounted) {
558+
setState(() {});
559+
}
560+
}
561+
553562
void _updateState() {
554563
if (!mounted) return;
564+
565+
// display the progress bar indicator only after the buffering delay if it has been set
566+
if (chewieController.progressIndicatorDelay != null) {
567+
if (controller.value.isBuffering) {
568+
_bufferingDisplayTimer ??= Timer(
569+
chewieController.progressIndicatorDelay!,
570+
_bufferingTimerTimeout,
571+
);
572+
} else {
573+
_bufferingDisplayTimer?.cancel();
574+
_bufferingDisplayTimer = null;
575+
_displayBufferingIndicator = false;
576+
}
577+
} else {
578+
_displayBufferingIndicator = controller.value.isBuffering;
579+
}
580+
555581
setState(() {
556582
_latestValue = controller.value;
557583
_subtitlesPosition = controller.value.position;

lib/src/material/material_desktop_controls.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
4141
Timer? _showAfterExpandCollapseTimer;
4242
bool _dragging = false;
4343
bool _displayTapped = false;
44+
Timer? _bufferingDisplayTimer;
45+
bool _displayBufferingIndicator = false;
4446

4547
final barHeight = 48.0 * 1.5;
4648
final marginSize = 5.0;
@@ -83,7 +85,7 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
8385
absorbing: notifier.hideStuff,
8486
child: Stack(
8587
children: [
86-
if (_latestValue.isBuffering)
88+
if (_displayBufferingIndicator)
8789
const Center(
8890
child: CircularProgressIndicator(),
8991
)
@@ -530,8 +532,32 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
530532
});
531533
}
532534

535+
void _bufferingTimerTimeout() {
536+
_displayBufferingIndicator = true;
537+
if (mounted) {
538+
setState(() {});
539+
}
540+
}
541+
533542
void _updateState() {
534543
if (!mounted) return;
544+
545+
// display the progress bar indicator only after the buffering delay if it has been set
546+
if (chewieController.progressIndicatorDelay != null) {
547+
if (controller.value.isBuffering) {
548+
_bufferingDisplayTimer ??= Timer(
549+
chewieController.progressIndicatorDelay!,
550+
_bufferingTimerTimeout,
551+
);
552+
} else {
553+
_bufferingDisplayTimer?.cancel();
554+
_bufferingDisplayTimer = null;
555+
_displayBufferingIndicator = false;
556+
}
557+
} else {
558+
_displayBufferingIndicator = controller.value.isBuffering;
559+
}
560+
535561
setState(() {
536562
_latestValue = controller.value;
537563
_subtitlesPosition = controller.value.position;

lib/src/progress_bar.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:io';
2+
13
import 'package:chewie/src/chewie_progress_colors.dart';
24
import 'package:flutter/material.dart';
35
import 'package:video_player/video_player.dart';
@@ -81,7 +83,15 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
8183
if (!controller.value.isInitialized) {
8284
return;
8385
}
84-
_seekToRelativePosition(details.globalPosition);
86+
// Should only seek if it's not running on Android, or if it is,
87+
// then the VideoPlayerController cannot be buffering.
88+
// On Android, we need to let the player buffer when scrolling
89+
// in order to let the player buffer. https://github.com/flutter/flutter/issues/101409
90+
final shouldSeekToRelativePosition =
91+
!Platform.isAndroid || !controller.value.isBuffering;
92+
if (shouldSeekToRelativePosition) {
93+
_seekToRelativePosition(details.globalPosition);
94+
}
8595

8696
widget.onDragUpdate?.call();
8797
},

0 commit comments

Comments
 (0)