mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-25 10:58:50 +01:00
Refactoring: extract page viewer logic
This commit is contained in:
parent
80a64b35bc
commit
ed19b1bd82
2 changed files with 280 additions and 212 deletions
253
lib/widget/horizontal_page_viewer.dart
Normal file
253
lib/widget/horizontal_page_viewer.dart
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:nc_photos/k.dart' as k;
|
||||||
|
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||||
|
|
||||||
|
class HorizontalPageViewer extends StatefulWidget {
|
||||||
|
HorizontalPageViewer({
|
||||||
|
Key? key,
|
||||||
|
required this.pageCount,
|
||||||
|
required this.pageBuilder,
|
||||||
|
this.initialPage = 0,
|
||||||
|
HorizontalPageViewerController? controller,
|
||||||
|
this.viewportFraction = 1,
|
||||||
|
this.canSwitchPage = true,
|
||||||
|
}) : controller = controller ?? HorizontalPageViewerController(),
|
||||||
|
super(key: key) {
|
||||||
|
this.controller._pageController = PageController(
|
||||||
|
initialPage: initialPage,
|
||||||
|
viewportFraction: viewportFraction,
|
||||||
|
keepPage: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
createState() => _HorizontalPageViewerState();
|
||||||
|
|
||||||
|
final int initialPage;
|
||||||
|
final int pageCount;
|
||||||
|
final Widget Function(BuildContext context, int index) pageBuilder;
|
||||||
|
final HorizontalPageViewerController controller;
|
||||||
|
final double viewportFraction;
|
||||||
|
final bool canSwitchPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HorizontalPageViewerState extends State<HorizontalPageViewer> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_pageFocus.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
build(BuildContext context) {
|
||||||
|
if (!_hasInit) {
|
||||||
|
_updateNavigationState(widget.initialPage);
|
||||||
|
_hasInit = true;
|
||||||
|
}
|
||||||
|
return platform_k.isWeb
|
||||||
|
? _buildWebContent(context)
|
||||||
|
: _buildContent(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildWebContent(BuildContext context) {
|
||||||
|
assert(platform_k.isWeb);
|
||||||
|
// support switching pages with keyboard on web
|
||||||
|
return RawKeyboardListener(
|
||||||
|
onKey: (ev) {
|
||||||
|
if (!widget.canSwitchPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.isKeyPressed(LogicalKeyboardKey.arrowLeft)) {
|
||||||
|
_switchToLeft();
|
||||||
|
} else if (ev.isKeyPressed(LogicalKeyboardKey.arrowRight)) {
|
||||||
|
_switchToRight();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
focusNode: _pageFocus,
|
||||||
|
child: _buildContent(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildContent(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
PageView.builder(
|
||||||
|
controller: widget.controller._pageController,
|
||||||
|
itemCount: widget.pageCount,
|
||||||
|
itemBuilder: widget.pageBuilder,
|
||||||
|
physics: !platform_k.isWeb && widget.canSwitchPage
|
||||||
|
? null
|
||||||
|
: const NeverScrollableScrollPhysics(),
|
||||||
|
),
|
||||||
|
if (platform_k.isWeb) ..._buildNavigationButtons(context),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildNavigationButtons(BuildContext context) {
|
||||||
|
return [
|
||||||
|
if (_canSwitchRight)
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: Visibility(
|
||||||
|
visible: widget.canSwitchPage,
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity: _isShowRight ? 1.0 : 0.0,
|
||||||
|
duration: k.animationDurationShort,
|
||||||
|
child: MouseRegion(
|
||||||
|
onEnter: (details) {
|
||||||
|
setState(() {
|
||||||
|
_isShowRight = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onExit: (details) {
|
||||||
|
setState(() {
|
||||||
|
_isShowRight = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24, vertical: 36),
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_forward_ios_outlined,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
onPressed: _switchToRight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_canSwitchLeft)
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: Visibility(
|
||||||
|
visible: widget.canSwitchPage,
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity: _isShowLeft ? 1.0 : 0.0,
|
||||||
|
duration: k.animationDurationShort,
|
||||||
|
child: MouseRegion(
|
||||||
|
onEnter: (details) {
|
||||||
|
setState(() {
|
||||||
|
_isShowLeft = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onExit: (details) {
|
||||||
|
setState(() {
|
||||||
|
_isShowLeft = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24, vertical: 36),
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_back_ios_outlined,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
onPressed: _switchToLeft,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Switch to the previous image in the stream
|
||||||
|
void _switchToPrev() {
|
||||||
|
widget.controller._pageController
|
||||||
|
.previousPage(
|
||||||
|
duration: k.animationDurationNormal, curve: Curves.easeInOut)
|
||||||
|
.whenComplete(() => _updateNavigationState(
|
||||||
|
widget.controller._pageController.page!.round()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Switch to the next image in the stream
|
||||||
|
void _switchToNext() {
|
||||||
|
widget.controller._pageController
|
||||||
|
.nextPage(duration: k.animationDurationNormal, curve: Curves.easeInOut)
|
||||||
|
.whenComplete(() => _updateNavigationState(
|
||||||
|
widget.controller._pageController.page!.round()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Switch to the image on the "left", what that means depend on the current
|
||||||
|
/// text direction
|
||||||
|
void _switchToLeft() {
|
||||||
|
if (Directionality.of(context) == TextDirection.ltr) {
|
||||||
|
_switchToPrev();
|
||||||
|
} else {
|
||||||
|
_switchToNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Switch to the image on the "right", what that means depend on the current
|
||||||
|
/// text direction
|
||||||
|
void _switchToRight() {
|
||||||
|
if (Directionality.of(context) == TextDirection.ltr) {
|
||||||
|
_switchToNext();
|
||||||
|
} else {
|
||||||
|
_switchToPrev();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the navigation state for [page]
|
||||||
|
void _updateNavigationState(int page) {
|
||||||
|
// currently useless to run on non-web platform
|
||||||
|
if (!platform_k.isWeb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final hasNext = page < widget.pageCount - 1;
|
||||||
|
final hasPrev = page > 0;
|
||||||
|
final hasLeft =
|
||||||
|
Directionality.of(context) == TextDirection.ltr ? hasPrev : hasNext;
|
||||||
|
if (_canSwitchLeft != hasLeft) {
|
||||||
|
setState(() {
|
||||||
|
_canSwitchLeft = hasLeft;
|
||||||
|
if (!_canSwitchLeft) {
|
||||||
|
_isShowLeft = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
final hasRight =
|
||||||
|
Directionality.of(context) == TextDirection.ltr ? hasNext : hasPrev;
|
||||||
|
if (_canSwitchRight != hasRight) {
|
||||||
|
setState(() {
|
||||||
|
_canSwitchRight = hasRight;
|
||||||
|
if (!_canSwitchRight) {
|
||||||
|
_isShowRight = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _hasInit = false;
|
||||||
|
|
||||||
|
var _canSwitchRight = true;
|
||||||
|
var _canSwitchLeft = true;
|
||||||
|
var _isShowRight = false;
|
||||||
|
var _isShowLeft = false;
|
||||||
|
|
||||||
|
/// used to gain focus on web for keyboard support
|
||||||
|
final _pageFocus = FocusNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
class HorizontalPageViewerController {
|
||||||
|
int get currentPage => _pageController.hasClients
|
||||||
|
? _pageController.page!.round()
|
||||||
|
: _pageController.initialPage;
|
||||||
|
|
||||||
|
late PageController _pageController;
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/use_case/remove.dart';
|
import 'package:nc_photos/use_case/remove.dart';
|
||||||
import 'package:nc_photos/widget/animated_visibility.dart';
|
import 'package:nc_photos/widget/animated_visibility.dart';
|
||||||
|
import 'package:nc_photos/widget/horizontal_page_viewer.dart';
|
||||||
import 'package:nc_photos/widget/image_viewer.dart';
|
import 'package:nc_photos/widget/image_viewer.dart';
|
||||||
import 'package:nc_photos/widget/video_viewer.dart';
|
import 'package:nc_photos/widget/video_viewer.dart';
|
||||||
import 'package:nc_photos/widget/viewer_bottom_app_bar.dart';
|
import 'package:nc_photos/widget/viewer_bottom_app_bar.dart';
|
||||||
|
@ -67,28 +68,13 @@ class Viewer extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ViewerState extends State<Viewer> {
|
class _ViewerState extends State<Viewer> {
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_pageController = PageController(
|
|
||||||
initialPage: widget.startIndex,
|
|
||||||
viewportFraction: 1.05,
|
|
||||||
keepPage: false);
|
|
||||||
_pageFocus.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
build(BuildContext context) {
|
build(BuildContext context) {
|
||||||
if (!_hasInit) {
|
|
||||||
_updateNavigationState(widget.startIndex);
|
|
||||||
_hasInit = true;
|
|
||||||
}
|
|
||||||
return AppTheme(
|
return AppTheme(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: Builder(
|
body: Builder(
|
||||||
builder: (context) => platform_k.isWeb
|
builder: _buildContent,
|
||||||
? _buildWebContent(context)
|
),
|
||||||
: _buildContent(context)),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -99,25 +85,6 @@ class _ViewerState extends State<Viewer> {
|
||||||
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
|
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildWebContent(BuildContext context) {
|
|
||||||
assert(platform_k.isWeb);
|
|
||||||
// support switching pages with keyboard on web
|
|
||||||
return RawKeyboardListener(
|
|
||||||
onKey: (ev) {
|
|
||||||
if (!_canSwitchPage()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ev.isKeyPressed(LogicalKeyboardKey.arrowLeft)) {
|
|
||||||
_switchToLeftImage();
|
|
||||||
} else if (ev.isKeyPressed(LogicalKeyboardKey.arrowRight)) {
|
|
||||||
_switchToRightImage();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
focusNode: _pageFocus,
|
|
||||||
child: _buildContent(context),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildContent(BuildContext context) {
|
Widget _buildContent(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -128,21 +95,20 @@ class _ViewerState extends State<Viewer> {
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Container(color: Colors.black),
|
Container(color: Colors.black),
|
||||||
if (!_pageController.hasClients ||
|
if (!_isViewerLoaded ||
|
||||||
!_pageStates[_pageController.page!.round()]!.hasLoaded)
|
!_pageStates[_viewerController.currentPage]!.hasLoaded)
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: const CircularProgressIndicator(),
|
child: const CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
PageView.builder(
|
HorizontalPageViewer(
|
||||||
controller: _pageController,
|
pageCount: widget.streamFiles.length,
|
||||||
itemCount: widget.streamFiles.length,
|
pageBuilder: _buildPage,
|
||||||
itemBuilder: _buildPage,
|
initialPage: widget.startIndex,
|
||||||
physics: !platform_k.isWeb && _canSwitchPage()
|
controller: _viewerController,
|
||||||
? null
|
viewportFraction: _viewportFraction,
|
||||||
: const NeverScrollableScrollPhysics(),
|
canSwitchPage: _canSwitchPage(),
|
||||||
),
|
),
|
||||||
if (platform_k.isWeb) ..._buildNavigationButtons(context),
|
|
||||||
_buildBottomAppBar(context),
|
_buildBottomAppBar(context),
|
||||||
_buildAppBar(context),
|
_buildAppBar(context),
|
||||||
],
|
],
|
||||||
|
@ -150,85 +116,6 @@ class _ViewerState extends State<Viewer> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildNavigationButtons(BuildContext context) {
|
|
||||||
return [
|
|
||||||
if (_canSwitchRight)
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: Visibility(
|
|
||||||
visible: _canSwitchPage(),
|
|
||||||
child: AnimatedOpacity(
|
|
||||||
opacity: _isShowRight ? 1.0 : 0.0,
|
|
||||||
duration: k.animationDurationShort,
|
|
||||||
child: MouseRegion(
|
|
||||||
onEnter: (details) {
|
|
||||||
setState(() {
|
|
||||||
_isShowRight = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onExit: (details) {
|
|
||||||
setState(() {
|
|
||||||
_isShowRight = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 24, vertical: 36),
|
|
||||||
child: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.arrow_forward_ios_outlined,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onPressed: _switchToRightImage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (_canSwitchLeft)
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: Visibility(
|
|
||||||
visible: _canSwitchPage(),
|
|
||||||
child: AnimatedOpacity(
|
|
||||||
opacity: _isShowLeft ? 1.0 : 0.0,
|
|
||||||
duration: k.animationDurationShort,
|
|
||||||
child: MouseRegion(
|
|
||||||
onEnter: (details) {
|
|
||||||
setState(() {
|
|
||||||
_isShowLeft = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onExit: (details) {
|
|
||||||
setState(() {
|
|
||||||
_isShowLeft = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 24, vertical: 36),
|
|
||||||
child: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.arrow_back_ios_outlined,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onPressed: _switchToLeftImage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAppBar(BuildContext context) {
|
Widget _buildAppBar(BuildContext context) {
|
||||||
return Wrap(
|
return Wrap(
|
||||||
children: [
|
children: [
|
||||||
|
@ -311,7 +198,7 @@ class _ViewerState extends State<Viewer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return FractionallySizedBox(
|
return FractionallySizedBox(
|
||||||
widthFactor: 1 / _pageController.viewportFraction,
|
widthFactor: 1 / _viewportFraction,
|
||||||
child: NotificationListener<ScrollNotification>(
|
child: NotificationListener<ScrollNotification>(
|
||||||
onNotification: (notif) => _onPageContentScrolled(notif, index),
|
onNotification: (notif) => _onPageContentScrolled(notif, index),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
@ -419,14 +306,14 @@ class _ViewerState extends State<Viewer> {
|
||||||
// upward, open the pane to its minimal size
|
// upward, open the pane to its minimal size
|
||||||
Future.delayed(Duration.zero, () {
|
Future.delayed(Duration.zero, () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_openDetailPane(_pageController.page!.toInt(),
|
_openDetailPane(_viewerController.currentPage,
|
||||||
shouldAnimate: true);
|
shouldAnimate: true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (scrollPos.userScrollDirection == ScrollDirection.forward) {
|
} else if (scrollPos.userScrollDirection == ScrollDirection.forward) {
|
||||||
// downward, close the pane
|
// downward, close the pane
|
||||||
Future.delayed(Duration.zero, () {
|
Future.delayed(Duration.zero, () {
|
||||||
_closeDetailPane(_pageController.page!.toInt(),
|
_closeDetailPane(_viewerController.currentPage,
|
||||||
shouldAnimate: true);
|
shouldAnimate: true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -438,7 +325,7 @@ class _ViewerState extends State<Viewer> {
|
||||||
void _onImageLoaded(int index) {
|
void _onImageLoaded(int index) {
|
||||||
// currently pageview doesn't pre-load pages, we do it manually
|
// currently pageview doesn't pre-load pages, we do it manually
|
||||||
// don't pre-load if user already navigated away
|
// don't pre-load if user already navigated away
|
||||||
if (_pageController.page!.round() == index &&
|
if (_viewerController.currentPage == index &&
|
||||||
!_pageStates[index]!.hasLoaded) {
|
!_pageStates[index]!.hasLoaded) {
|
||||||
_log.info("[_onImageLoaded] Pre-loading nearby images");
|
_log.info("[_onImageLoaded] Pre-loading nearby images");
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
|
@ -455,15 +342,17 @@ class _ViewerState extends State<Viewer> {
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_pageStates[index]!.hasLoaded = true;
|
_pageStates[index]!.hasLoaded = true;
|
||||||
|
_isViewerLoaded = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onVideoLoaded(int index) {
|
void _onVideoLoaded(int index) {
|
||||||
if (_pageController.page!.round() == index &&
|
if (_viewerController.currentPage == index &&
|
||||||
!_pageStates[index]!.hasLoaded) {
|
!_pageStates[index]!.hasLoaded) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_pageStates[index]!.hasLoaded = true;
|
_pageStates[index]!.hasLoaded = true;
|
||||||
|
_isViewerLoaded = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -508,19 +397,19 @@ class _ViewerState extends State<Viewer> {
|
||||||
void _onDetailsPressed() {
|
void _onDetailsPressed() {
|
||||||
if (!_isDetailPaneActive) {
|
if (!_isDetailPaneActive) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_openDetailPane(_pageController.page!.toInt(), shouldAnimate: true);
|
_openDetailPane(_viewerController.currentPage, shouldAnimate: true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSharePressed(BuildContext context) {
|
void _onSharePressed(BuildContext context) {
|
||||||
assert(platform_k.isAndroid);
|
assert(platform_k.isAndroid);
|
||||||
final file = widget.streamFiles[_pageController.page!.round()];
|
final file = widget.streamFiles[_viewerController.currentPage];
|
||||||
ShareHandler().shareFiles(context, widget.account, [file]);
|
ShareHandler().shareFiles(context, widget.account, [file]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDownloadPressed(BuildContext context) async {
|
void _onDownloadPressed(BuildContext context) async {
|
||||||
final file = widget.streamFiles[_pageController.page!.round()];
|
final file = widget.streamFiles[_viewerController.currentPage];
|
||||||
_log.info("[_onDownloadPressed] Downloading file: ${file.path}");
|
_log.info("[_onDownloadPressed] Downloading file: ${file.path}");
|
||||||
var controller = SnackBarManager().showSnackBar(SnackBar(
|
var controller = SnackBarManager().showSnackBar(SnackBar(
|
||||||
content: Text(L10n.of(context).downloadProcessingNotification),
|
content: Text(L10n.of(context).downloadProcessingNotification),
|
||||||
|
@ -582,7 +471,7 @@ class _ViewerState extends State<Viewer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDeletePressed(BuildContext context) async {
|
void _onDeletePressed(BuildContext context) async {
|
||||||
final file = widget.streamFiles[_pageController.page!.round()];
|
final file = widget.streamFiles[_viewerController.currentPage];
|
||||||
_log.info("[_onDeletePressed] Removing file: ${file.path}");
|
_log.info("[_onDeletePressed] Removing file: ${file.path}");
|
||||||
var controller = SnackBarManager().showSnackBar(SnackBar(
|
var controller = SnackBarManager().showSnackBar(SnackBar(
|
||||||
content: Text(L10n.of(context).deleteProcessingNotification),
|
content: Text(L10n.of(context).deleteProcessingNotification),
|
||||||
|
@ -687,99 +576,25 @@ class _ViewerState extends State<Viewer> {
|
||||||
_isClosingDetailPane = false;
|
_isClosingDetailPane = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Switch to the previous image in the stream
|
|
||||||
void _switchToPrevImage() {
|
|
||||||
_pageController
|
|
||||||
.previousPage(
|
|
||||||
duration: k.animationDurationNormal, curve: Curves.easeInOut)
|
|
||||||
.whenComplete(
|
|
||||||
() => _updateNavigationState(_pageController.page!.round()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Switch to the next image in the stream
|
|
||||||
void _switchToNextImage() {
|
|
||||||
_pageController
|
|
||||||
.nextPage(duration: k.animationDurationNormal, curve: Curves.easeInOut)
|
|
||||||
.whenComplete(
|
|
||||||
() => _updateNavigationState(_pageController.page!.round()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Switch to the image on the "left", what that means depend on the current
|
|
||||||
/// text direction
|
|
||||||
void _switchToLeftImage() {
|
|
||||||
if (Directionality.of(context) == TextDirection.ltr) {
|
|
||||||
_switchToPrevImage();
|
|
||||||
} else {
|
|
||||||
_switchToNextImage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Switch to the image on the "right", what that means depend on the current
|
|
||||||
/// text direction
|
|
||||||
void _switchToRightImage() {
|
|
||||||
if (Directionality.of(context) == TextDirection.ltr) {
|
|
||||||
_switchToNextImage();
|
|
||||||
} else {
|
|
||||||
_switchToPrevImage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the navigation state for [page]
|
|
||||||
void _updateNavigationState(int page) {
|
|
||||||
// currently useless to run on non-web platform
|
|
||||||
if (!platform_k.isWeb) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final hasNext = page < widget.streamFiles.length - 1;
|
|
||||||
final hasPrev = page > 0;
|
|
||||||
final hasLeft =
|
|
||||||
Directionality.of(context) == TextDirection.ltr ? hasPrev : hasNext;
|
|
||||||
if (_canSwitchLeft != hasLeft) {
|
|
||||||
setState(() {
|
|
||||||
_canSwitchLeft = hasLeft;
|
|
||||||
if (!_canSwitchLeft) {
|
|
||||||
_isShowLeft = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
final hasRight =
|
|
||||||
Directionality.of(context) == TextDirection.ltr ? hasNext : hasPrev;
|
|
||||||
if (_canSwitchRight != hasRight) {
|
|
||||||
setState(() {
|
|
||||||
_canSwitchRight = hasRight;
|
|
||||||
if (!_canSwitchRight) {
|
|
||||||
_isShowRight = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _canSwitchPage() => !_isZoomed;
|
bool _canSwitchPage() => !_isZoomed;
|
||||||
bool _canOpenDetailPane() => !_isZoomed;
|
bool _canOpenDetailPane() => !_isZoomed;
|
||||||
bool _canZoom() => !_isDetailPaneActive;
|
bool _canZoom() => !_isDetailPaneActive;
|
||||||
|
|
||||||
var _hasInit = false;
|
|
||||||
|
|
||||||
var _isShowAppBar = true;
|
var _isShowAppBar = true;
|
||||||
|
|
||||||
var _isShowDetailPane = false;
|
var _isShowDetailPane = false;
|
||||||
var _isDetailPaneActive = false;
|
var _isDetailPaneActive = false;
|
||||||
var _isClosingDetailPane = false;
|
var _isClosingDetailPane = false;
|
||||||
|
|
||||||
var _canSwitchRight = true;
|
|
||||||
var _canSwitchLeft = true;
|
|
||||||
var _isShowRight = false;
|
|
||||||
var _isShowLeft = false;
|
|
||||||
|
|
||||||
var _isZoomed = false;
|
var _isZoomed = false;
|
||||||
|
|
||||||
late PageController _pageController;
|
final _viewerController = HorizontalPageViewerController();
|
||||||
|
bool _isViewerLoaded = false;
|
||||||
final _pageStates = <int, _PageState>{};
|
final _pageStates = <int, _PageState>{};
|
||||||
|
|
||||||
/// used to gain focus on web for keyboard support
|
|
||||||
final _pageFocus = FocusNode();
|
|
||||||
|
|
||||||
static final _log = Logger("widget.viewer._ViewerState");
|
static final _log = Logger("widget.viewer._ViewerState");
|
||||||
|
|
||||||
|
static const _viewportFraction = 1.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PageState {
|
class _PageState {
|
||||||
|
|
Loading…
Reference in a new issue