#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:
Thibault Deckers 2021-10-05 19:11:37 +09:00
parent db6b47b351
commit 35929fa7ab
3 changed files with 91 additions and 63 deletions

View file

@ -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) {

View file

@ -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,
),
],
),
),
),
),

View file

@ -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,
);
},
),
),
),
);
},