Skip to content

Commit

Permalink
feat(Widgets): Add overlay widgets to trailer video player
Browse files Browse the repository at this point in the history
Signed-off-by: arafaysaleem <a.rafaysaleem@gmail.com>
  • Loading branch information
arafaysaleem committed Jun 9, 2021
1 parent d110b14 commit 190403f
Show file tree
Hide file tree
Showing 4 changed files with 332 additions and 0 deletions.
65 changes: 65 additions & 0 deletions lib/views/widgets/trailer/overlay_back_button.dart
@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:better_player/better_player.dart';
import 'package:auto_route/auto_route.dart';

class OverlayBackButton extends StatefulWidget {
final BetterPlayerController betterPlayerController;

const OverlayBackButton({
Key? key,
required this.betterPlayerController,
}) : super(key: key);

@override
_OverlayBackButtonState createState() => _OverlayBackButtonState();
}

class _OverlayBackButtonState extends State<OverlayBackButton> {
bool _isVisible = false;

BetterPlayerController get _betterPlayerController => widget.betterPlayerController;

@override
void initState() {
super.initState();
_betterPlayerController.addEventsListener(_handlePlayerEventChanges);
}

/// Listens to all events sent by [_betterPlayerController]
/// and handles them with the appropriate response.
void _handlePlayerEventChanges(BetterPlayerEvent event) {
final eventType = event.betterPlayerEventType;
//handle events if initialized
final controlsVisible = eventType == BetterPlayerEventType.controlsVisible;
final controlsHidden = eventType == BetterPlayerEventType.controlsHidden;
if (controlsVisible || controlsHidden) { //if overlay controls toggled
setState(() {
_isVisible = controlsVisible;
});
}
}

@override
Widget build(BuildContext context) {
if (!_isVisible) {
return const SizedBox.shrink();
}
return InkWell(
onTap: () => context.router.pop(),
child: const Padding(
padding: EdgeInsets.all(15),
child: Icon(
Icons.arrow_back_sharp,
color: Colors.white,
size: 28,
),
),
);
}

@override
void dispose() {
_betterPlayerController.removeEventsListener(_handlePlayerEventChanges);
super.dispose();
}
}
62 changes: 62 additions & 0 deletions lib/views/widgets/trailer/overlay_black_header.dart
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:better_player/better_player.dart';

class OverlayBlackHeader extends StatefulWidget {
final BetterPlayerController betterPlayerController;

const OverlayBlackHeader({
Key? key,
required this.betterPlayerController,
}) : super(key: key);

@override
_OverlayBlackHeaderState createState() => _OverlayBlackHeaderState();
}

class _OverlayBlackHeaderState extends State<OverlayBlackHeader> {
bool _isVisible = false;

BetterPlayerController get _betterPlayerController => widget.betterPlayerController;

@override
void initState() {
super.initState();
_betterPlayerController.addEventsListener(_handlePlayerEventChanges);
}

/// Listens to all events sent by [_betterPlayerController]
/// and handles them with the appropriate response.
void _handlePlayerEventChanges(BetterPlayerEvent event) {
final eventType = event.betterPlayerEventType;
//handle events if initialized
final controlsVisible = eventType == BetterPlayerEventType.controlsVisible;
final controlsHidden = eventType == BetterPlayerEventType.controlsHidden;
if (controlsVisible || controlsHidden) { //if overlay controls toggled
setState(() {
_isVisible = controlsVisible;
});
}
}

@override
Widget build(BuildContext context) {
if (!_isVisible) {
return const SizedBox.shrink();
}
return const IgnorePointer(
child: SizedBox(
width: double.infinity,
height: 55,
child: ColoredBox(
color: Colors.black54,
),
),
);
}

@override
void dispose() {
_betterPlayerController.removeEventsListener(_handlePlayerEventChanges);
super.dispose();
}
}
69 changes: 69 additions & 0 deletions lib/views/widgets/trailer/overlay_movie_title.dart
@@ -0,0 +1,69 @@
import 'package:better_player/better_player.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

//Helpers
import '../../../helper/extensions/context_extensions.dart';

//Providers
import '../../../providers/movies_provider.dart';

class OverlayMovieTitle extends StatefulHookWidget {
final BetterPlayerController betterPlayerController;

const OverlayMovieTitle({
Key? key,
required this.betterPlayerController,
}) : super(key: key);

@override
_OverlayMovieTitleState createState() => _OverlayMovieTitleState();
}

class _OverlayMovieTitleState extends State<OverlayMovieTitle> {
bool _isVisible = false;

BetterPlayerController get _betterPlayerController => widget.betterPlayerController;

@override
void initState() {
super.initState();
_betterPlayerController.addEventsListener(_handlePlayerEventChanges);
}

/// Listens to all events sent by [_betterPlayerController]
/// and handles them with the appropriate response.
void _handlePlayerEventChanges(BetterPlayerEvent event) {
final eventType = event.betterPlayerEventType;
//handle events if initialized
final controlsVisible = eventType == BetterPlayerEventType.controlsVisible;
final controlsHidden = eventType == BetterPlayerEventType.controlsHidden;
if (controlsVisible || controlsHidden) { //if overlay controls toggled
setState(() {
_isVisible = controlsVisible;
});
}
}

@override
Widget build(BuildContext context) {
if (!_isVisible) {
return const SizedBox.shrink();
}
final title = useProvider(selectedMovieProvider.select(
(value) => value.state.title,
));
return Text(
title,
maxLines: 1,
style: context.headline3.copyWith(fontSize: 22),
);
}

@override
void dispose() {
_betterPlayerController.removeEventsListener(_handlePlayerEventChanges);
super.dispose();
}
}
136 changes: 136 additions & 0 deletions lib/views/widgets/trailer/overlay_play_pause_button.dart
@@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:better_player/better_player.dart';

class OverlayPlayPauseButton extends StatefulWidget {
final BetterPlayerController betterPlayerController;

const OverlayPlayPauseButton({
Key? key,
required this.betterPlayerController,
}) : super(key: key);

@override
_OverlayPlayPauseButtonState createState() => _OverlayPlayPauseButtonState();
}

class _OverlayPlayPauseButtonState extends State<OverlayPlayPauseButton>
with SingleTickerProviderStateMixin {
late bool _initialized;
bool _isVisible = false;
late final AnimationController _animController;

BetterPlayerController get _betterPlayerController => widget.betterPlayerController;

@override
void initState() {
super.initState();
/// Hide visibility if buffering or uninitialized
_initialized = _betterPlayerController.isPlaying() ?? false;
_betterPlayerController.addEventsListener(_handlePlayerEventChanges);
_animController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 250),
);
}

/// Checks and sets the initialization flag to true when the
/// buffering is complete.
///
/// All [BetterPlayerEventType] events are ignored until [_initialized]
/// is set to true.
void checkInitialization() {
final bufferingComplete = _betterPlayerController.isPlaying()!;
if (bufferingComplete) {
setState(() {
_initialized = bufferingComplete;
});
}
}

/// Listens to all events sent by [_betterPlayerController]
/// and handles them with the appropriate response.
void _handlePlayerEventChanges(BetterPlayerEvent event) {
final eventType = event.betterPlayerEventType;
if (!_initialized) {
//if not initialized
checkInitialization();
} else if (eventType == BetterPlayerEventType.finished) {
setState(() {
_isVisible = false;
_initialized = false;
});
} else {
//handle events if initialized
final controlsVisible =
eventType == BetterPlayerEventType.controlsVisible;
final controlsHidden = eventType == BetterPlayerEventType.controlsHidden;
if (controlsVisible || controlsHidden) {
//if overlay controls toggled
setState(() {
_isVisible = controlsVisible;
});
} else if (_isVisible) {
//if other events, check control visibility
_handlePlayPauseEvent(eventType);
}
}
}

/// Animates the overlay play icon to pause and vice versa.
///
/// Called when the play/pause event is dispatched, that is, after the
/// [_handlePlayPauseTap] is called.
void _handlePlayPauseEvent(BetterPlayerEventType eventType) {
final isPlay = eventType == BetterPlayerEventType.play;
final isPause = eventType == BetterPlayerEventType.pause;
if (isPlay) {
_animController.reverse();
} else if (isPause) {
_animController.forward();
}
}

/// Pauses or resumes the video using [_betterPlayerController].
///
/// Called when the play/pause overlay button is pressed.
///
/// The controller's methods send a pause/play event to the
/// listener [_handlePlayerEventChanges].
void _handlePlayPauseTap() {
if (_betterPlayerController.isPlaying()!) {
_betterPlayerController.pause();
} else {
_betterPlayerController.play();
}
}

@override
Widget build(BuildContext context) {
if (!_isVisible) {
return const SizedBox.shrink();
}
return InkWell(
onTap: _handlePlayPauseTap,
child: Container(
padding: const EdgeInsets.all(15),
decoration: const BoxDecoration(
color: Colors.black54,
shape: BoxShape.circle,
),
child: AnimatedIcon(
icon: AnimatedIcons.pause_play,
color: Colors.white,
size: 32,
progress: _animController,
),
),
);
}

@override
void dispose() {
_betterPlayerController.removeEventsListener(_handlePlayerEventChanges);
_animController.dispose();
super.dispose();
}
}

0 comments on commit 190403f

Please sign in to comment.