Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Add a delay before displaying progress indicator #623

Merged
76 changes: 75 additions & 1 deletion example/lib/app/app.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:io';

import 'package:chewie/chewie.dart';
import 'package:chewie_example/app/theme.dart';
import 'package:flutter/material.dart';
Expand All @@ -23,6 +25,7 @@ class _ChewieDemoState extends State<ChewieDemo> {
late VideoPlayerController _videoPlayerController1;
late VideoPlayerController _videoPlayerController2;
ChewieController? _chewieController;
int? bufferDelay;

@override
void initState() {
Expand All @@ -39,6 +42,7 @@ class _ChewieDemoState extends State<ChewieDemo> {
}

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

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

Future<void> toggleVideo() async {
await _videoPlayerController1.pause();
currPlayIndex = currPlayIndex == 0 ? 1 : 0;
currPlayIndex += 1;
if (currPlayIndex >= srcs.length) {
currPlayIndex = 0;
}
await initializePlayer();
}

Expand Down Expand Up @@ -302,9 +311,74 @@ class _ChewieDemoState extends State<ChewieDemo> {
),
],
),
if (Platform.isAndroid)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not using it for iOS and Android?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any apple device so I can't test on iOS and don't want to modify things that I'm not sure it properly works / is needed.

ListTile(
title: const Text("Delay"),
subtitle: DelaySlider(
delay:
_chewieController?.progressIndicatorDelay?.inMilliseconds,
onSave: (delay) async {
if (delay != null) {
bufferDelay = delay == 0 ? null : delay;
await initializePlayer();
}
},
),
)
],
),
),
);
}
}

class DelaySlider extends StatefulWidget {
const DelaySlider({Key? key, required this.delay, required this.onSave})
: super(key: key);

final int? delay;
final void Function(int?) onSave;
@override
State<DelaySlider> createState() => _DelaySliderState();
}

class _DelaySliderState extends State<DelaySlider> {
int? delay;
bool saved = false;

@override
void initState() {
super.initState();
delay = widget.delay;
}

@override
Widget build(BuildContext context) {
const int max = 1000;
return ListTile(
title: Text(
"Progress indicator delay ${delay != null ? "${delay.toString()} MS" : ""}",
),
subtitle: Slider(
value: delay != null ? (delay! / max) : 0,
onChanged: (value) async {
delay = (value * max).toInt();
setState(() {
saved = false;
});
},
),
trailing: IconButton(
icon: const Icon(Icons.save),
onPressed: saved
? null
: () {
widget.onSave(delay);
setState(() {
saved = true;
});
},
),
);
}
}
7 changes: 7 additions & 0 deletions lib/src/chewie_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ class ChewieController extends ChangeNotifier {
this.systemOverlaysAfterFullScreen = SystemUiOverlay.values,
this.deviceOrientationsAfterFullScreen = DeviceOrientation.values,
this.routePageBuilder,
this.progressIndicatorDelay,
this.hideControlsTimer = defaultHideControlsTimer,
}) : assert(
playbackSpeeds.every((speed) => speed > 0),
Expand Down Expand Up @@ -324,6 +325,7 @@ class ChewieController extends ChangeNotifier {
List<DeviceOrientation>? deviceOrientationsOnEnterFullScreen,
List<SystemUiOverlay>? systemOverlaysAfterFullScreen,
List<DeviceOrientation>? deviceOrientationsAfterFullScreen,
Duration? progressIndicatorDelay,
Widget Function(
BuildContext,
Animation<double>,
Expand Down Expand Up @@ -377,6 +379,8 @@ class ChewieController extends ChangeNotifier {
this.deviceOrientationsAfterFullScreen,
routePageBuilder: routePageBuilder ?? this.routePageBuilder,
hideControlsTimer: hideControlsTimer ?? this.hideControlsTimer,
progressIndicatorDelay:
progressIndicatorDelay ?? this.progressIndicatorDelay,
);
}

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

/// Defines a delay in milliseconds between entering buffering state and displaying the loading spinner. Set null (default) to disable it.
final Duration? progressIndicatorDelay;

static ChewieController of(BuildContext context) {
final chewieControllerProvider =
context.dependOnInheritedWidgetOfExactType<ChewieControllerProvider>()!;
Expand Down
28 changes: 27 additions & 1 deletion lib/src/cupertino/cupertino_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class _CupertinoControlsState extends State<CupertinoControls>
bool _dragging = false;
Duration? _subtitlesPosition;
bool _subtitleOn = false;
Timer? _bufferingDisplayTimer;
bool _displayBufferingIndicator = false;

late VideoPlayerController controller;

Expand Down Expand Up @@ -91,7 +93,7 @@ class _CupertinoControlsState extends State<CupertinoControls>
absorbing: notifier.hideStuff,
child: Stack(
children: [
if (_latestValue.isBuffering)
if (_displayBufferingIndicator)
const Center(
child: CircularProgressIndicator(),
)
Expand Down Expand Up @@ -769,8 +771,32 @@ class _CupertinoControlsState extends State<CupertinoControls>
});
}

void _bufferingTimerTimeout() {
_displayBufferingIndicator = true;
if (mounted) {
setState(() {});
}
}

void _updateState() {
if (!mounted) return;

// display the progress bar indicator only after the buffering delay if it has been set
if (chewieController.progressIndicatorDelay != null) {
if (controller.value.isBuffering) {
_bufferingDisplayTimer ??= Timer(
chewieController.progressIndicatorDelay!,
_bufferingTimerTimeout,
);
} else {
_bufferingDisplayTimer?.cancel();
_bufferingDisplayTimer = null;
_displayBufferingIndicator = false;
}
} else {
_displayBufferingIndicator = controller.value.isBuffering;
}

setState(() {
_latestValue = controller.value;
_subtitlesPosition = controller.value.position;
Expand Down
28 changes: 27 additions & 1 deletion lib/src/material/material_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class _MaterialControlsState extends State<MaterialControls>
Timer? _showAfterExpandCollapseTimer;
bool _dragging = false;
bool _displayTapped = false;
Timer? _bufferingDisplayTimer;
bool _displayBufferingIndicator = false;

final barHeight = 48.0 * 1.5;
final marginSize = 5.0;
Expand Down Expand Up @@ -82,7 +84,7 @@ class _MaterialControlsState extends State<MaterialControls>
absorbing: notifier.hideStuff,
child: Stack(
children: [
if (_latestValue.isBuffering)
if (_displayBufferingIndicator)
const Center(
child: CircularProgressIndicator(),
)
Expand Down Expand Up @@ -550,8 +552,32 @@ class _MaterialControlsState extends State<MaterialControls>
});
}

void _bufferingTimerTimeout() {
_displayBufferingIndicator = true;
if (mounted) {
setState(() {});
}
}

void _updateState() {
if (!mounted) return;

// display the progress bar indicator only after the buffering delay if it has been set
if (chewieController.progressIndicatorDelay != null) {
if (controller.value.isBuffering) {
_bufferingDisplayTimer ??= Timer(
chewieController.progressIndicatorDelay!,
_bufferingTimerTimeout,
);
} else {
_bufferingDisplayTimer?.cancel();
_bufferingDisplayTimer = null;
_displayBufferingIndicator = false;
}
} else {
_displayBufferingIndicator = controller.value.isBuffering;
}

setState(() {
_latestValue = controller.value;
_subtitlesPosition = controller.value.position;
Expand Down
28 changes: 27 additions & 1 deletion lib/src/material/material_desktop_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
Timer? _showAfterExpandCollapseTimer;
bool _dragging = false;
bool _displayTapped = false;
Timer? _bufferingDisplayTimer;
bool _displayBufferingIndicator = false;

final barHeight = 48.0 * 1.5;
final marginSize = 5.0;
Expand Down Expand Up @@ -83,7 +85,7 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
absorbing: notifier.hideStuff,
child: Stack(
children: [
if (_latestValue.isBuffering)
if (_displayBufferingIndicator)
const Center(
child: CircularProgressIndicator(),
)
Expand Down Expand Up @@ -530,8 +532,32 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
});
}

void _bufferingTimerTimeout() {
_displayBufferingIndicator = true;
if (mounted) {
setState(() {});
}
}

void _updateState() {
if (!mounted) return;

// display the progress bar indicator only after the buffering delay if it has been set
if (chewieController.progressIndicatorDelay != null) {
if (controller.value.isBuffering) {
_bufferingDisplayTimer ??= Timer(
chewieController.progressIndicatorDelay!,
_bufferingTimerTimeout,
);
} else {
_bufferingDisplayTimer?.cancel();
_bufferingDisplayTimer = null;
_displayBufferingIndicator = false;
}
} else {
_displayBufferingIndicator = controller.value.isBuffering;
}

setState(() {
_latestValue = controller.value;
_subtitlesPosition = controller.value.position;
Expand Down
12 changes: 11 additions & 1 deletion lib/src/progress_bar.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:io';

import 'package:chewie/src/chewie_progress_colors.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
Expand Down Expand Up @@ -81,7 +83,15 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
if (!controller.value.isInitialized) {
return;
}
_seekToRelativePosition(details.globalPosition);
// Should only seek if it's not running on Android, or if it is,
// then the VideoPlayerController cannot be buffering.
// On Android, we need to let the player buffer when scrolling
// in order to let the player buffer. https://github.com/flutter/flutter/issues/101409
final shouldSeekToRelativePosition =
!Platform.isAndroid || !controller.value.isBuffering;
if (shouldSeekToRelativePosition) {
_seekToRelativePosition(details.globalPosition);
}
henri2h marked this conversation as resolved.
Show resolved Hide resolved

widget.onDragUpdate?.call();
},
Expand Down