#81 viewer: padding below overlay controls to avoid system gesture area, fixed video progress bar height to match interaction standards
This commit is contained in:
parent
db6b47b351
commit
35929fa7ab
3 changed files with 91 additions and 63 deletions
|
@ -11,6 +11,7 @@ import 'package:aves/theme/format.dart';
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/media_query.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/bottom/multipage.dart';
|
||||
|
@ -92,14 +93,20 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
|
|||
final viewPadding = widget.viewPadding ?? mqViewPadding;
|
||||
final availableWidth = mqWidth - viewPadding.horizontal;
|
||||
|
||||
return Container(
|
||||
color: hasEdgeContent ? overlayBackgroundColor(blurred: blurred) : Colors.transparent,
|
||||
padding: EdgeInsets.only(
|
||||
left: max(viewInsets.left, viewPadding.left),
|
||||
top: 0,
|
||||
right: max(viewInsets.right, viewPadding.right),
|
||||
bottom: max(viewInsets.bottom, viewPadding.bottom),
|
||||
),
|
||||
return Selector<MediaQueryData, double>(
|
||||
selector: (context, mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom),
|
||||
builder: (context, mqPaddingBottom, child) {
|
||||
return Container(
|
||||
color: hasEdgeContent ? overlayBackgroundColor(blurred: blurred) : Colors.transparent,
|
||||
padding: EdgeInsets.only(
|
||||
left: max(viewInsets.left, viewPadding.left),
|
||||
top: 0,
|
||||
right: max(viewInsets.right, viewPadding.right),
|
||||
bottom: mqPaddingBottom,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: FutureBuilder<OverlayMetadata?>(
|
||||
future: _detailLoader,
|
||||
builder: (context, snapshot) {
|
||||
|
|
|
@ -119,6 +119,7 @@ class _VideoControlOverlayState extends State<VideoControlOverlay> with SingleTi
|
|||
Widget _buildProgressBar() {
|
||||
const progressBarBorderRadius = 123.0;
|
||||
final blurred = settings.enableOverlayBlurEffect;
|
||||
const textStyle = TextStyle(shadows: Constants.embossShadows);
|
||||
return SizeTransition(
|
||||
sizeFactor: scale,
|
||||
child: BlurredRRect(
|
||||
|
@ -138,50 +139,62 @@ class _VideoControlOverlayState extends State<VideoControlOverlay> with SingleTi
|
|||
onHorizontalDragEnd: (details) {
|
||||
if (_playingOnDragStart) controller!.play();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16) + const EdgeInsets.only(bottom: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: overlayBackgroundColor(blurred: blurred),
|
||||
border: AvesBorder.border,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(progressBarBorderRadius)),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: kMinInteractiveDimension,
|
||||
),
|
||||
child: Column(
|
||||
key: _progressBarKey,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
StreamBuilder<int>(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: overlayBackgroundColor(blurred: blurred),
|
||||
border: AvesBorder.border,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(progressBarBorderRadius)),
|
||||
),
|
||||
child: Column(
|
||||
key: _progressBarKey,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
StreamBuilder<int>(
|
||||
stream: positionStream,
|
||||
builder: (context, snapshot) {
|
||||
// do not use stream snapshot because it is obsolete when switching between videos
|
||||
final position = controller?.currentPosition.floor() ?? 0;
|
||||
return Text(
|
||||
formatFriendlyDuration(Duration(milliseconds: position)),
|
||||
style: textStyle,
|
||||
);
|
||||
}),
|
||||
const Spacer(),
|
||||
Text(
|
||||
entry.durationText,
|
||||
style: textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
child: StreamBuilder<int>(
|
||||
stream: positionStream,
|
||||
builder: (context, snapshot) {
|
||||
// do not use stream snapshot because it is obsolete when switching between videos
|
||||
final position = controller?.currentPosition.floor() ?? 0;
|
||||
return Text(
|
||||
formatFriendlyDuration(Duration(milliseconds: position)),
|
||||
style: const TextStyle(shadows: Constants.embossShadows),
|
||||
var progress = controller?.progress ?? 0.0;
|
||||
if (!progress.isFinite) progress = 0.0;
|
||||
return LinearProgressIndicator(
|
||||
value: progress,
|
||||
backgroundColor: Colors.grey.shade700,
|
||||
);
|
||||
}),
|
||||
const Spacer(),
|
||||
Text(
|
||||
entry.durationText,
|
||||
style: const TextStyle(shadows: Constants.embossShadows),
|
||||
),
|
||||
],
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
child: StreamBuilder<int>(
|
||||
stream: positionStream,
|
||||
builder: (context, snapshot) {
|
||||
// do not use stream snapshot because it is obsolete when switching between videos
|
||||
var progress = controller?.progress ?? 0.0;
|
||||
if (!progress.isFinite) progress = 0.0;
|
||||
return LinearProgressIndicator(
|
||||
value: progress,
|
||||
backgroundColor: Colors.grey.shade700,
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Text(
|
||||
// fake text below to match the height of the text above and center the whole thing
|
||||
'',
|
||||
style: textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/entry.dart';
|
||||
import 'package:aves/model/entry_images.dart';
|
||||
import 'package:aves/model/panorama.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/basic/insets.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/media_query.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/common.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -70,7 +73,7 @@ class _PanoramaPageState extends State<PanoramaPage> {
|
|||
croppedFullWidth: info.hasCroppedArea ? info.fullPanoSize!.width : 1.0,
|
||||
croppedFullHeight: info.hasCroppedArea ? info.fullPanoSize!.height : 1.0,
|
||||
onTap: (longitude, latitude, tilt) => _overlayVisible.value = !_overlayVisible.value,
|
||||
child: child as Image?,
|
||||
child: child as Image,
|
||||
);
|
||||
},
|
||||
child: Image(
|
||||
|
@ -78,8 +81,8 @@ class _PanoramaPageState extends State<PanoramaPage> {
|
|||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: TooltipTheme(
|
||||
data: TooltipTheme.of(context).copyWith(
|
||||
preferBelow: false,
|
||||
|
@ -89,24 +92,29 @@ class _PanoramaPageState extends State<PanoramaPage> {
|
|||
builder: (context, overlayVisible, child) {
|
||||
return Visibility(
|
||||
visible: overlayVisible,
|
||||
child: Selector<MediaQueryData, EdgeInsets>(
|
||||
selector: (context, mq) => mq.viewPadding + mq.viewInsets,
|
||||
builder: (context, mqPadding, child) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8) + EdgeInsets.only(right: mqPadding.right, bottom: mqPadding.bottom),
|
||||
child: OverlayButton(
|
||||
child: ValueListenableBuilder<SensorControl>(
|
||||
valueListenable: _sensorControl,
|
||||
builder: (context, sensorControl, child) {
|
||||
return IconButton(
|
||||
icon: Icon(sensorControl == SensorControl.None ? AIcons.sensorControl : AIcons.sensorControlOff),
|
||||
onPressed: _toggleSensor,
|
||||
tooltip: sensorControl == SensorControl.None ? context.l10n.panoramaEnableSensorControl : context.l10n.panoramaDisableSensorControl,
|
||||
);
|
||||
}),
|
||||
child: Selector<MediaQueryData, double>(
|
||||
selector: (context, mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom),
|
||||
builder: (context, mqPaddingBottom, child) {
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8) + EdgeInsets.only(bottom: mqPaddingBottom),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: OverlayButton(
|
||||
child: ValueListenableBuilder<SensorControl>(
|
||||
valueListenable: _sensorControl,
|
||||
builder: (context, sensorControl, child) {
|
||||
return IconButton(
|
||||
icon: Icon(sensorControl == SensorControl.None ? AIcons.sensorControl : AIcons.sensorControlOff),
|
||||
onPressed: _toggleSensor,
|
||||
tooltip: sensorControl == SensorControl.None ? context.l10n.panoramaEnableSensorControl : context.l10n.panoramaDisableSensorControl,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue