improved fullscreen with notch, top overlay buttons

This commit is contained in:
Thibault Deckers 2019-08-01 23:54:27 +09:00
parent a25f81f359
commit 09dedaa604
4 changed files with 113 additions and 54 deletions

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowTranslucentNavigation">@bool/translucentNavBar</item> <!-- API19+, tinted background & request the SYSTEM_UI_FLAG_LAYOUT_STABLE and SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flags -->
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <!-- API28+, draws next to the notch in fullscreen -->
</style>
</resources>

View file

@ -0,0 +1,35 @@
import 'dart:ui';
import 'package:flutter/material.dart';
class BlurredRect extends StatelessWidget {
final Widget child;
const BlurredRect({Key key, this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4),
child: child,
),
);
}
}
class BlurredOval extends StatelessWidget {
final Widget child;
const BlurredOval({Key key, this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return ClipOval(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4),
child: child,
),
);
}
}

View file

@ -5,6 +5,7 @@ import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/fullscreen/info_page.dart'; import 'package:aves/widgets/fullscreen/info_page.dart';
import 'package:aves/widgets/fullscreen/overlay.dart'; import 'package:aves/widgets/fullscreen/overlay.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart'; import 'package:photo_view/photo_view_gallery.dart';
@ -69,7 +70,11 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
ImagePage( ImagePage(
entries: entries, entries: entries,
pageController: _horizontalPager, pageController: _horizontalPager,
onTap: () => _overlayVisible.value = !_overlayVisible.value, onTap: () {
final visible = !_overlayVisible.value;
_overlayVisible.value = visible;
SystemChrome.setEnabledSystemUIOverlays(visible ? []: SystemUiOverlay.values);
},
onPageChanged: (page) => setState(() => _currentHorizontalPage = page), onPageChanged: (page) => setState(() => _currentHorizontalPage = page),
onScaleChanged: (state) => setState(() => _isInitialScale = state == PhotoViewScaleState.initial), onScaleChanged: (state) => setState(() => _isInitialScale = state == PhotoViewScaleState.initial),
), ),

View file

@ -4,27 +4,12 @@ import 'dart:ui';
import 'package:aves/model/android_app_service.dart'; import 'package:aves/model/android_app_service.dart';
import 'package:aves/model/image_entry.dart'; import 'package:aves/model/image_entry.dart';
import 'package:aves/model/metadata_service.dart'; import 'package:aves/model/metadata_service.dart';
import 'package:aves/widgets/common/blurred.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
const kOverlayBackground = Colors.black26; const kOverlayBackground = Colors.black26;
class Blurred extends StatelessWidget {
final Widget child;
const Blurred({Key key, this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4),
child: child,
),
);
}
}
class FullscreenTopOverlay extends StatelessWidget { class FullscreenTopOverlay extends StatelessWidget {
final List<ImageEntry> entries; final List<ImageEntry> entries;
final int index; final int index;
@ -35,23 +20,23 @@ class FullscreenTopOverlay extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Blurred( return SafeArea(
child: SafeArea( child: Padding(
child: Container( padding: EdgeInsets.all(8.0),
height: kToolbarHeight, child: Row(
child: AppBar( children: [
title: Text('${index + 1}/${entries.length}'), OverlayButton(
actions: [ child: BackButton(),
// IconButton(icon: Icon(Icons.delete), onPressed: delete), ),
IconButton( Spacer(),
OverlayButton(
child: IconButton(
icon: Icon(Icons.share), icon: Icon(Icons.share),
onPressed: share, onPressed: share,
tooltip: 'Share', tooltip: 'Share',
), ),
], ),
elevation: 0, ],
backgroundColor: kOverlayBackground,
),
), ),
), ),
); );
@ -102,28 +87,31 @@ class _FullscreenBottomOverlayState extends State<FullscreenBottomOverlay> {
final innerPadding = EdgeInsets.all(8.0); final innerPadding = EdgeInsets.all(8.0);
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
final overlayContentMaxWidth = mediaQuery.size.width - mediaQuery.viewPadding.horizontal - innerPadding.horizontal; final overlayContentMaxWidth = mediaQuery.size.width - mediaQuery.viewPadding.horizontal - innerPadding.horizontal;
return Blurred( return BlurredRect(
child: IgnorePointer( child: Container(
child: Padding( color: kOverlayBackground,
padding: mediaQuery.viewInsets + mediaQuery.viewPadding.copyWith(top: 0), child: IgnorePointer(
child: Container( child: Padding(
padding: innerPadding, padding: mediaQuery.viewInsets + mediaQuery.viewPadding.copyWith(top: 0),
color: kOverlayBackground, child: Container(
child: FutureBuilder( padding: innerPadding,
future: _detailLoader, child: FutureBuilder(
builder: (futureContext, AsyncSnapshot<Map> snapshot) { future: _detailLoader,
if (snapshot.connectionState == ConnectionState.done && !snapshot.hasError) { builder: (futureContext, AsyncSnapshot<Map> snapshot) {
_lastDetails = snapshot.data; if (snapshot.connectionState == ConnectionState.done && !snapshot.hasError) {
_lastEntry = entry; _lastDetails = snapshot.data;
} _lastEntry = entry;
return _lastEntry == null }
? SizedBox.shrink() return _lastEntry == null
: _FullscreenBottomOverlayContent( ? SizedBox.shrink()
entry: _lastEntry, : _FullscreenBottomOverlayContent(
details: _lastDetails, entry: _lastEntry,
maxWidth: overlayContentMaxWidth, details: _lastDetails,
); position: '${widget.index + 1}/${widget.entries.length}',
}, maxWidth: overlayContentMaxWidth,
);
},
),
), ),
), ),
), ),
@ -135,9 +123,10 @@ class _FullscreenBottomOverlayState extends State<FullscreenBottomOverlay> {
class _FullscreenBottomOverlayContent extends StatelessWidget { class _FullscreenBottomOverlayContent extends StatelessWidget {
final ImageEntry entry; final ImageEntry entry;
final Map details; final Map details;
final String position;
final double maxWidth; final double maxWidth;
_FullscreenBottomOverlayContent({this.entry, this.details, this.maxWidth}); _FullscreenBottomOverlayContent({this.entry, this.details, this.position, this.maxWidth});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -159,7 +148,7 @@ class _FullscreenBottomOverlayContent extends StatelessWidget {
SizedBox( SizedBox(
width: maxWidth, width: maxWidth,
child: Text( child: Text(
entry.title, '$position ${entry.title}',
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
@ -196,3 +185,26 @@ class _FullscreenBottomOverlayContent extends StatelessWidget {
); );
} }
} }
class OverlayButton extends StatelessWidget {
final Widget child;
const OverlayButton({this.child});
@override
Widget build(BuildContext context) {
return BlurredOval(
child: Material(
type: MaterialType.circle,
color: kOverlayBackground,
child: Ink(
decoration: BoxDecoration(
border: Border.all(color: Colors.white30, width: 0.5),
shape: BoxShape.circle,
),
child: child,
),
),
);
}
}