You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
6.6 KiB
Dart
239 lines
6.6 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:after_layout/after_layout.dart';
|
|
import 'package:diplomaticquarterapp/pages/conference/widgets/circle_button.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class ConferenceButtonBar extends StatefulWidget {
|
|
final VoidCallback onVideoEnabled;
|
|
final VoidCallback onAudioEnabled;
|
|
final VoidCallback onHangup;
|
|
final VoidCallback onSwitchCamera;
|
|
final VoidCallback onPersonAdd;
|
|
final VoidCallback onPersonRemove;
|
|
final void Function(double) onHeight;
|
|
final VoidCallback onHide;
|
|
final VoidCallback onShow;
|
|
final Stream<bool> videoEnabled;
|
|
final Stream<bool> audioEnabled;
|
|
|
|
const ConferenceButtonBar({
|
|
Key key,
|
|
this.onVideoEnabled,
|
|
this.onAudioEnabled,
|
|
this.onHangup,
|
|
this.onSwitchCamera,
|
|
this.onPersonAdd,
|
|
this.onPersonRemove,
|
|
@required this.videoEnabled,
|
|
@required this.audioEnabled,
|
|
this.onHeight,
|
|
this.onHide,
|
|
this.onShow,
|
|
}) : assert(videoEnabled != null),
|
|
assert(audioEnabled != null),
|
|
super(key: key);
|
|
|
|
@override
|
|
_ConferenceButtonBarState createState() => _ConferenceButtonBarState();
|
|
}
|
|
|
|
class _ConferenceButtonBarState extends State<ConferenceButtonBar> with AfterLayoutMixin<ConferenceButtonBar> {
|
|
var _bottom = -100.0;
|
|
Timer _timer;
|
|
int _remaining;
|
|
var _videoEnabled = true;
|
|
var _audioEnabled = true;
|
|
double _hidden;
|
|
double _visible;
|
|
final _keyButtonBarHeight = GlobalKey();
|
|
|
|
final Duration timeout = const Duration(seconds: 5);
|
|
final Duration ms = const Duration(milliseconds: 1);
|
|
final Duration periodicDuration = const Duration(milliseconds: 100);
|
|
|
|
Timer startTimeout([int milliseconds]) {
|
|
final duration = milliseconds == null ? timeout : ms * milliseconds;
|
|
_remaining = duration.inMilliseconds;
|
|
return Timer.periodic(periodicDuration, (Timer timer) {
|
|
_remaining -= periodicDuration.inMilliseconds;
|
|
if (_remaining <= 0) {
|
|
timer.cancel();
|
|
_toggleBar();
|
|
}
|
|
});
|
|
}
|
|
|
|
void _pauseTimer() {
|
|
if (_timer == null) {
|
|
return;
|
|
}
|
|
_timer.cancel();
|
|
_timer = null;
|
|
}
|
|
|
|
void _resumeTimer() {
|
|
// resume the timer only when there is no timer active or when
|
|
// the bar is not already hidden.
|
|
if ((_timer != null && _timer.isActive) || _bottom == _hidden) {
|
|
return;
|
|
}
|
|
_timer = startTimeout(_remaining);
|
|
}
|
|
|
|
void _toggleBar() {
|
|
setState(() {
|
|
_bottom = _bottom == _visible ? _hidden : _visible;
|
|
if (_bottom == _visible && widget.onShow != null) {
|
|
widget.onShow();
|
|
}
|
|
if (_bottom == _hidden && widget.onHide != null) {
|
|
widget.onHide();
|
|
}
|
|
});
|
|
}
|
|
|
|
void _toggleBarOnEnd() {
|
|
if (_timer != null) {
|
|
if (_timer.isActive) {
|
|
_timer.cancel();
|
|
}
|
|
_timer = null;
|
|
}
|
|
if (_bottom == 0) {
|
|
_timer = startTimeout();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_timer = startTimeout();
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
_visible = MediaQuery.of(context).viewPadding.bottom;
|
|
super.didChangeDependencies();
|
|
}
|
|
|
|
@override
|
|
void afterFirstLayout(BuildContext context) {
|
|
final RenderBox renderBoxButtonBar = _keyButtonBarHeight.currentContext.findRenderObject();
|
|
final heightButtonBar = renderBoxButtonBar.size.height;
|
|
// Because the `didChangeDependencies` fires before the `afterFirstLayout`, we can use the `_visible` property here.
|
|
_hidden = -(heightButtonBar + _visible);
|
|
widget.onHeight(heightButtonBar);
|
|
_toggleBar();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
if (_timer != null && _timer.isActive) {
|
|
_timer.cancel();
|
|
_timer = null;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Positioned(
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
child: GestureDetector(
|
|
key: Key('show-hide-button-bar-gesture'),
|
|
behavior: HitTestBehavior.translucent,
|
|
onTapDown: (_) => _pauseTimer(),
|
|
onTapUp: (_) => _toggleBar(),
|
|
onTapCancel: () => _resumeTimer(),
|
|
child: Stack(
|
|
children: <Widget>[
|
|
AnimatedPositioned(
|
|
key: Key('button-bar'),
|
|
bottom: _bottom,
|
|
left: 0,
|
|
right: 0,
|
|
duration: const Duration(milliseconds: 300),
|
|
curve: Curves.linear,
|
|
child: _buildRow(context),
|
|
onEnd: _toggleBarOnEnd,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _onPressed(VoidCallback callback) {
|
|
if (callback != null) {
|
|
callback();
|
|
}
|
|
if (_timer != null && _timer.isActive) {
|
|
_timer.cancel();
|
|
}
|
|
_timer = startTimeout();
|
|
}
|
|
|
|
Widget _buildRow(BuildContext context) {
|
|
return Padding(
|
|
key: _keyButtonBarHeight,
|
|
padding: const EdgeInsets.only(bottom: 8.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
children: <Widget>[
|
|
CircleButton(
|
|
child: StreamBuilder<bool>(
|
|
stream: widget.videoEnabled,
|
|
initialData: _videoEnabled,
|
|
builder: (context, snapshot) {
|
|
_videoEnabled = snapshot.data;
|
|
return Icon(
|
|
_videoEnabled ? Icons.videocam : Icons.videocam_off,
|
|
color: Colors.white,
|
|
);
|
|
}),
|
|
key: Key('camera-button'),
|
|
onPressed: () => _onPressed(widget.onVideoEnabled),
|
|
),
|
|
CircleButton(
|
|
child: StreamBuilder<bool>(
|
|
stream: widget.audioEnabled,
|
|
initialData: _audioEnabled,
|
|
builder: (context, snapshot) {
|
|
_audioEnabled = snapshot.data;
|
|
return Icon(
|
|
_audioEnabled ? Icons.mic : Icons.mic_off,
|
|
color: Colors.white,
|
|
);
|
|
}),
|
|
key: Key('microphone-button'),
|
|
onPressed: () => _onPressed(widget.onAudioEnabled),
|
|
),
|
|
CircleButton(
|
|
child: const Icon(Icons.switch_camera, color: Colors.white),
|
|
key: Key('switch-camera-button'),
|
|
onPressed: () => _onPressed(widget.onSwitchCamera),
|
|
),
|
|
CircleButton(
|
|
radius: 35,
|
|
child: const RotationTransition(
|
|
turns: AlwaysStoppedAnimation<double>(135 / 360),
|
|
child: Icon(
|
|
Icons.phone,
|
|
color: Colors.white,
|
|
size: 40,
|
|
),
|
|
),
|
|
color: Colors.red.withAlpha(200),
|
|
key: Key('hangup-button'),
|
|
onPressed: () => _onPressed(widget.onHangup),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|