diff --git a/go/main/controller/client.go b/go/main/controller/client.go index 0c3148f..fb119aa 100644 --- a/go/main/controller/client.go +++ b/go/main/controller/client.go @@ -44,7 +44,7 @@ var switchAddresses = map[int]string{ } var switchAddress = 1 -var switchAddressPattern, _ = regexp.Compile("^.+pica" + "comic\\.com:\\d+$") +var switchAddressPattern, _ = regexp.Compile("^.+picacomic\\.com:\\d+$") func switchAddressContext(ctx context.Context, network, addr string) (net.Conn, error) { if sAddr, ok := switchAddresses[switchAddress]; ok { diff --git a/lib/basic/const.dart b/lib/basic/const.dart new file mode 100644 index 0000000..cea4a12 --- /dev/null +++ b/lib/basic/const.dart @@ -0,0 +1,5 @@ + +import 'package:flutter/material.dart'; + +var readerAppbarColor = Color(0xff1e202c); +var readerAppbarColor2 = readerAppbarColor.withAlpha(225); diff --git a/lib/screens/ComicInfoScreen.dart b/lib/screens/ComicInfoScreen.dart index a76b52b..a33685d 100644 --- a/lib/screens/ComicInfoScreen.dart +++ b/lib/screens/ComicInfoScreen.dart @@ -332,7 +332,7 @@ class _ComicInfoScreenState extends State with RouteAware { comicInfo: comicInfo, epList: epList, currentEpOrder: order, - initPictureRank: rank, + initPicturePosition: rank, ), ), ); diff --git a/lib/screens/ComicReaderScreen.dart b/lib/screens/ComicReaderScreen.dart index 2ac8f0d..f68f2f5 100644 --- a/lib/screens/ComicReaderScreen.dart +++ b/lib/screens/ComicReaderScreen.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:pikapika/basic/Common.dart'; import 'package:pikapika/basic/Entities.dart'; import 'package:pikapika/basic/Method.dart'; import 'package:pikapika/basic/config/AutoFullScreen.dart'; @@ -9,6 +10,7 @@ import 'package:pikapika/basic/config/FullScreenUI.dart'; import 'package:pikapika/basic/config/Quality.dart'; import 'package:pikapika/basic/config/ReaderDirection.dart'; import 'package:pikapika/basic/config/ReaderType.dart'; +import 'package:pikapika/basic/const.dart'; import 'package:pikapika/screens/components/ContentBuilder.dart'; import 'components/ImageReader.dart'; @@ -17,7 +19,7 @@ class ComicReaderScreen extends StatefulWidget { final ComicInfo comicInfo; final List epList; final currentEpOrder; - final int? initPictureRank; + final int? initPicturePosition; final ReaderType pagerType = currentReaderType(); final ReaderDirection pagerDirection = gReaderDirection; late final bool autoFullScreen; @@ -27,7 +29,7 @@ class ComicReaderScreen extends StatefulWidget { required this.comicInfo, required this.epList, required this.currentEpOrder, - this.initPictureRank, + this.initPicturePosition, bool? autoFullScreen, }) : super(key: key) { this.autoFullScreen = autoFullScreen ?? currentAutoFullScreen(); @@ -45,8 +47,8 @@ class _ComicReaderScreenState extends State { bool _replacement = false; Future> _load() async { - if (widget.initPictureRank == null) { - await method.storeViewEp(widget.comicInfo.id, _ep.order, _ep.title, 1); + if (widget.initPicturePosition == null) { + await method.storeViewEp(widget.comicInfo.id, _ep.order, _ep.title, 0); } List list = []; var _needLoadPage = 0; @@ -70,11 +72,13 @@ class _ComicReaderScreenState extends State { } Future _onPositionChange(int position) async { - _lastChangeRank = position + 1; + _lastChangeRank = position; return method.storeViewEp( - widget.comicInfo.id, _ep.order, _ep.title, position + 1); + widget.comicInfo.id, _ep.order, _ep.title, position); } + FutureOr Function() _previousAction = () => null; + String _nextText = ""; FutureOr Function() _nextAction = () => null; @@ -85,6 +89,23 @@ class _ComicReaderScreenState extends State { widget.epList.forEach((element) { orderMap[element.order] = element; }); + if (orderMap.containsKey(widget.currentEpOrder - 1)) { + _previousAction = () { + _replacement = true; + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => ComicReaderScreen( + comicInfo: widget.comicInfo, + epList: widget.epList, + currentEpOrder: widget.currentEpOrder - 1, + autoFullScreen: _fullScreen, + ), + ), + ); + }; + } else { + _previousAction = () => defaultToast(context, "已经到头了"); + } if (orderMap.containsKey(widget.currentEpOrder + 1)) { _nextText = "下一章"; _nextAction = () { @@ -135,6 +156,7 @@ class _ComicReaderScreenState extends State { appBar: _fullScreen ? null : AppBar( + backgroundColor: readerAppbarColor, title: Text("${_ep.title} - ${widget.comicInfo.title}"), actions: [ IconButton( @@ -164,8 +186,8 @@ class _ComicReaderScreenState extends State { _future = _load(); }); }, - successBuilder: - (BuildContext context, AsyncSnapshot> snapshot) { + successBuilder: (BuildContext context, + AsyncSnapshot> snapshot) { return ImageReader( ImageReaderStruct( images: snapshot.data! @@ -182,11 +204,10 @@ class _ComicReaderScreenState extends State { fullScreen: _fullScreen, onFullScreenChange: _onFullScreenChange, onNextText: _nextText, + onPreviousAction: _previousAction, onNextAction: _nextAction, onPositionChange: _onPositionChange, - initPosition: widget.initPictureRank == null - ? null - : widget.initPictureRank! - 1, + initPosition: widget.initPicturePosition, pagerType: widget.pagerType, pagerDirection: widget.pagerDirection, ), @@ -212,7 +233,7 @@ class _ComicReaderScreenState extends State { comicInfo: widget.comicInfo, epList: widget.epList, currentEpOrder: widget.currentEpOrder, - initPictureRank: _lastChangeRank ?? widget.initPictureRank, + initPicturePosition: _lastChangeRank ?? widget.initPicturePosition, // maybe null autoFullScreen: _fullScreen, ), diff --git a/lib/screens/DownloadInfoScreen.dart b/lib/screens/DownloadInfoScreen.dart index 2bc5ba2..2c54dc5 100644 --- a/lib/screens/DownloadInfoScreen.dart +++ b/lib/screens/DownloadInfoScreen.dart @@ -176,7 +176,7 @@ class _DownloadInfoScreenState extends State comicInfo: _task, epList: _epList, currentEpOrder: epOrder, - initPictureRank: rank, + initPicturePosition: rank, ), ), ); diff --git a/lib/screens/DownloadReaderScreen.dart b/lib/screens/DownloadReaderScreen.dart index 55df1f2..679a910 100644 --- a/lib/screens/DownloadReaderScreen.dart +++ b/lib/screens/DownloadReaderScreen.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:pikapika/basic/Common.dart'; import 'package:pikapika/basic/Entities.dart'; import 'package:pikapika/basic/config/AutoFullScreen.dart'; import 'package:pikapika/basic/config/FullScreenUI.dart'; @@ -16,7 +17,7 @@ class DownloadReaderScreen extends StatefulWidget { final DownloadComic comicInfo; final List epList; final int currentEpOrder; - final int? initPictureRank; + final int? initPicturePosition; final ReaderType pagerType = currentReaderType(); final ReaderDirection pagerDirection = gReaderDirection; late final bool autoFullScreen; @@ -26,7 +27,7 @@ class DownloadReaderScreen extends StatefulWidget { required this.comicInfo, required this.epList, required this.currentEpOrder, - this.initPictureRank, + this.initPicturePosition, bool? autoFullScreen, }) : super(key: key) { this.autoFullScreen = autoFullScreen ?? currentAutoFullScreen(); @@ -45,8 +46,8 @@ class _DownloadReaderScreenState extends State { bool _replacement = false; Future _load() async { - if (widget.initPictureRank == null) { - await method.storeViewEp(widget.comicInfo.id, _ep.epOrder, _ep.title, 1); + if (widget.initPicturePosition == null) { + await method.storeViewEp(widget.comicInfo.id, _ep.epOrder, _ep.title, 0); } pictures.clear(); for (var ep in widget.epList) { @@ -63,11 +64,13 @@ class _DownloadReaderScreenState extends State { } Future _onPositionChange(int position) async { - _lastChangeRank = position + 1; + _lastChangeRank = position; return method.storeViewEp( - widget.comicInfo.id, _ep.epOrder, _ep.title, position + 1); + widget.comicInfo.id, _ep.epOrder, _ep.title, position); } + FutureOr Function() _previousAction = () => null; + String _nextText = ""; FutureOr Function() _nextAction = () => null; @@ -78,6 +81,23 @@ class _DownloadReaderScreenState extends State { widget.epList.forEach((element) { orderMap[element.epOrder] = element; }); + if (orderMap.containsKey(widget.currentEpOrder - 1)) { + _previousAction = () { + _replacement = true; + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => DownloadReaderScreen( + comicInfo: widget.comicInfo, + epList: widget.epList, + currentEpOrder: widget.currentEpOrder - 1, + autoFullScreen: _fullScreen, + ), + ), + ); + }; + } else { + _previousAction = () => defaultToast(context, "已经到头了"); + } if (orderMap.containsKey(widget.currentEpOrder + 1)) { _nextText = "下一章"; _nextAction = () { @@ -168,11 +188,10 @@ class _DownloadReaderScreenState extends State { fullScreen: _fullScreen, onFullScreenChange: _onFullScreenChange, onNextText: _nextText, + onPreviousAction: _previousAction, onNextAction: _nextAction, onPositionChange: _onPositionChange, - initPosition: widget.initPictureRank == null - ? null - : widget.initPictureRank! - 1, + initPosition: widget.initPicturePosition, pagerType: widget.pagerType, pagerDirection: widget.pagerDirection, ), @@ -199,7 +218,7 @@ class _DownloadReaderScreenState extends State { comicInfo: widget.comicInfo, epList: widget.epList, currentEpOrder: widget.currentEpOrder, - initPictureRank: _lastChangeRank ?? widget.initPictureRank, + initPicturePosition: _lastChangeRank ?? widget.initPicturePosition, // maybe null autoFullScreen: _fullScreen, ), diff --git a/lib/screens/components/ContinueReadButton.dart b/lib/screens/components/ContinueReadButton.dart index 685d4a4..748bfe6 100644 --- a/lib/screens/components/ContinueReadButton.dart +++ b/lib/screens/components/ContinueReadButton.dart @@ -37,7 +37,7 @@ class _ContinueReadButtonState extends State { snapshot.data?.lastViewPictureRank, ); text = - '继续阅读 ${snapshot.data?.lastViewEpTitle} P. ${snapshot.data?.lastViewPictureRank}'; + '继续阅读 ${snapshot.data?.lastViewEpTitle} P. ${(snapshot.data?.lastViewPictureRank ?? 0) + 1}'; } else { onPressed = () => widget.onChoose(null, null); text = '开始阅读'; diff --git a/lib/screens/components/ImageReader.dart b/lib/screens/components/ImageReader.dart index e003a84..483ef7f 100644 --- a/lib/screens/components/ImageReader.dart +++ b/lib/screens/components/ImageReader.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:another_xlider/another_xlider.dart'; import 'package:event/event.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:photo_view/photo_view_gallery.dart'; @@ -11,11 +12,11 @@ import 'package:pikapika/basic/Cross.dart'; import 'package:pikapika/basic/Entities.dart'; import 'package:pikapika/basic/Method.dart'; import 'package:pikapika/basic/config/FullScreenAction.dart'; -import 'package:pikapika/basic/config/GalleryPreloadCount.dart'; import 'package:pikapika/basic/config/KeyboardController.dart'; import 'package:pikapika/basic/config/NoAnimation.dart'; import 'package:pikapika/basic/config/ReaderDirection.dart'; import 'package:pikapika/basic/config/ReaderType.dart'; +import 'package:pikapika/basic/const.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../FilePhotoViewScreen.dart'; import 'gesture_zoom_box.dart'; @@ -69,7 +70,7 @@ var _volumeListenCount = 0; EventChannel volumeButtonChannel = EventChannel("volume_button"); StreamSubscription? volumeS; -addVolumeListen() { +void addVolumeListen() { _volumeListenCount++; if (_volumeListenCount == 1) { volumeS = @@ -77,7 +78,7 @@ addVolumeListen() { } } -delVolumeListen() { +void delVolumeListen() { _volumeListenCount--; if (_volumeListenCount == 0) { volumeS?.cancel(); @@ -106,6 +107,7 @@ class ImageReaderStruct { final bool fullScreen; final FutureOr Function(bool fullScreen) onFullScreenChange; final String onNextText; + final FutureOr Function() onPreviousAction; final FutureOr Function() onNextAction; final FutureOr Function(int) onPositionChange; final int? initPosition; @@ -117,6 +119,7 @@ class ImageReaderStruct { required this.fullScreen, required this.onFullScreenChange, required this.onNextText, + required this.onPreviousAction, required this.onNextAction, required this.onPositionChange, this.initPosition, @@ -127,123 +130,216 @@ class ImageReaderStruct { // -class ImageReader extends StatelessWidget { +class ImageReader extends StatefulWidget { final ImageReaderStruct struct; const ImageReader(this.struct); @override - Widget build(BuildContext context) { - late Widget reader; + State createState() { switch (struct.pagerType) { case ReaderType.WEB_TOON: - reader = _WebToonReader(struct); - break; + return _WebToonReaderState(); case ReaderType.WEB_TOON_ZOOM: - reader = _WebToonZoomReader(struct); - break; + return _WebToonZoomReaderState(); case ReaderType.GALLERY: - reader = _GalleryReader(struct); - break; + return _GalleryReaderState(); default: - reader = Container(); - break; + throw Exception("ERROR READER TYPE"); } - switch (currentFullScreenAction()) { - case FullScreenAction.CONTROLLER: - reader = Stack( - children: [ - reader, - _buildFullScreenController( - struct.fullScreen, - struct.onFullScreenChange, - ), - ], - ); - break; - case FullScreenAction.TOUCH_ONCE: - reader = GestureDetector( - onTap: () => struct.onFullScreenChange(!struct.fullScreen), - child: reader, - ); - break; - case FullScreenAction.THREE_AREA: - reader = Stack( - children: [ - reader, - LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - var up = Expanded( - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - _readerControllerEvent - .broadcast(_ReaderControllerEventArgs("UP")); - }, - child: Container(), - ), - ); - var down = Expanded( - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - _readerControllerEvent - .broadcast(_ReaderControllerEventArgs("DOWN")); - }, - child: Container( - ), - ), - ); - var fullScreen = Expanded( - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => struct.onFullScreenChange(!struct.fullScreen), - child: Container(), - ), - ); - late Widget child; - switch (struct.pagerDirection) { - case ReaderDirection.TOP_TO_BOTTOM: - child = Column(children: [ - up, - fullScreen, - down, - ]); - break; - case ReaderDirection.LEFT_TO_RIGHT: - child = Row(children: [ - up, - fullScreen, - down, - ]); - break; - case ReaderDirection.RIGHT_TO_LEFT: - child = Row(children: [ - down, - fullScreen, - up, - ]); - break; - } - return Container( - width: constraints.maxWidth, - height: constraints.maxHeight, - child: child, - ); - }, - ), - ], - ); - break; - } - // - return reader; + } +} + +abstract class _ImageReaderState extends State { + // 阅读器 + Widget _buildViewer(); + + // 键盘, 音量键 等事件 + void _needJumpTo(int index, bool animation); + + @override + void initState() { + _initCurrent(); + _readerControllerEvent.subscribe(_onPageControl); + super.initState(); } - Widget _buildFullScreenController( - bool fullScreen, - FutureOr Function(bool fullScreen) onFullScreenChange, - ) { + @override + void dispose() { + _readerControllerEvent.unsubscribe(_onPageControl); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + _buildViewer(), + _buildControllerAndBar(), + ], + ); + } + + void _onPageControl(_ReaderControllerEventArgs? args) { + if (args != null) { + var event = args.key; + switch ("$event") { + case "UP": + if (_current > 0) { + _needJumpTo(_current - 1, true); + } + break; + case "DOWN": + if (_current < widget.struct.images.length - 1) { + _needJumpTo(_current + 1, true); + } + break; + } + } + } + + late int _startIndex; + late int _current; + late int _slider; + + void _initCurrent() { + if (widget.struct.initPosition != null && + widget.struct.images.length > widget.struct.initPosition!) { + _startIndex = widget.struct.initPosition!; + } else { + _startIndex = 0; + } + _current = _startIndex; + _slider = _startIndex; + } + + void _onCurrentChange(int index) { + if (index != _current) { + setState(() { + _current = index; + _slider = index; + widget.struct.onPositionChange(index); + }); + } + } + + Widget _buildControllerAndBar() { + if (widget.struct.fullScreen) { + return _buildController(); + } + return SafeArea( + child: Column( + children: [ + Expanded( + child: _buildController(hiddenFullScreen: true), + ), + Container( + color: readerAppbarColor2, + child: Row( + children: [ + Container(width: 15), + IconButton( + icon: Icon(Icons.fullscreen), + color: Colors.white, + onPressed: () { + widget.struct.onFullScreenChange(!widget.struct.fullScreen); + }, + ), + Container(width: 10), + Expanded( + child: Column( + children: [ + Container(height: 3), + Container( + height: 25, + child: FlutterSlider( + axis: Axis.horizontal, + values: [_slider.toDouble()], + min: 0, + max: widget.struct.images.length.toDouble(), + onDragging: (handlerIndex, lowerValue, upperValue) { + _slider = (lowerValue.toInt()); + }, + onDragCompleted: + (handlerIndex, lowerValue, upperValue) { + _slider = (lowerValue.toInt()); + if (_slider != _current) { + _needJumpTo(_slider, false); + } + }, + trackBar: FlutterSliderTrackBar( + inactiveTrackBar: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: Colors.grey.shade300, + ), + activeTrackBar: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: Theme.of(context).colorScheme.secondary, + ), + ), + step: FlutterSliderStep( + step: 1, + isPercentRange: false, + ), + tooltip: FlutterSliderTooltip(custom: (value) { + double a = value + 1; + return Container( + padding: EdgeInsets.all(8), + decoration: ShapeDecoration( + color: Colors.black.withAlpha(0xCC), + shape: RoundedRectangleBorder( + borderRadius: + BorderRadiusDirectional.circular(3)), + ), + child: Text( + '${a.toInt()}', + style: TextStyle( + color: Colors.white, + fontSize: 18, + ), + ), + ); + }), + ), + ), + Container(height: 3), + ], + ), + ), + Container(width: 10), + IconButton( + icon: Icon(Icons.skip_next_outlined), + color: Colors.white, + onPressed: () { + widget.struct.onNextAction(); + }, + ), + Container(width: 15), + ], + ), + ), + ], + ), + ); + } + + Widget _buildController({bool hiddenFullScreen = false}) { + switch (currentFullScreenAction()) { + case FullScreenAction.CONTROLLER: + if (hiddenFullScreen) { + return Container(); + } + return _buildFullScreenController(); + case FullScreenAction.TOUCH_ONCE: + return _buildTouchOnceController(); + case FullScreenAction.THREE_AREA: + return _buildThreeAreaController(); + default: + return Container(); + } + } + + Widget _buildFullScreenController() { return Align( alignment: Alignment.bottomLeft, child: Material( @@ -260,10 +356,12 @@ class ImageReader extends StatelessWidget { ), child: GestureDetector( onTap: () { - onFullScreenChange(!fullScreen); + widget.struct.onFullScreenChange(!widget.struct.fullScreen); }, child: Icon( - fullScreen ? Icons.fullscreen_exit : Icons.fullscreen_outlined, + widget.struct.fullScreen + ? Icons.fullscreen_exit + : Icons.fullscreen_outlined, size: 30, color: Colors.white, ), @@ -272,40 +370,89 @@ class ImageReader extends StatelessWidget { ), ); } + + Widget _buildTouchOnceController() { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + widget.struct.onFullScreenChange(!widget.struct.fullScreen); + }, + child: Container(), + ); + } + + Widget _buildThreeAreaController() { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + var up = Expanded( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + _readerControllerEvent + .broadcast(_ReaderControllerEventArgs("UP")); + }, + child: Container(), + ), + ); + var down = Expanded( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + _readerControllerEvent + .broadcast(_ReaderControllerEventArgs("DOWN")); + }, + child: Container(), + ), + ); + var fullScreen = Expanded( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => + widget.struct.onFullScreenChange(!widget.struct.fullScreen), + child: Container(), + ), + ); + late Widget child; + switch (widget.struct.pagerDirection) { + case ReaderDirection.TOP_TO_BOTTOM: + child = Column(children: [ + up, + fullScreen, + down, + ]); + break; + case ReaderDirection.LEFT_TO_RIGHT: + child = Row(children: [ + up, + fullScreen, + down, + ]); + break; + case ReaderDirection.RIGHT_TO_LEFT: + child = Row(children: [ + down, + fullScreen, + up, + ]); + break; + } + return Container( + width: constraints.maxWidth, + height: constraints.maxHeight, + child: child, + ); + }, + ); + } } /////////////////////////////////////////////////////////////////////////////// -class _WebToonReader extends StatefulWidget { - final ImageReaderStruct struct; - - const _WebToonReader(this.struct); - - @override - State createState() => _WebToonReaderState(); -} - -class _WebToonReaderState extends State<_WebToonReader> { +class _WebToonReaderState extends _ImageReaderState { + var _controllerTime = DateTime.now().millisecondsSinceEpoch + 400; late final List _trueSizes = []; late final ItemScrollController _itemScrollController; late final ItemPositionsListener _itemPositionsListener; - late final int _initialPosition; - - var _current = 1; - var _slider = 1; - - void _onCurrentChange() { - var to = _itemPositionsListener.itemPositions.value.first.index + 1; - if (_current != to) { - setState(() { - _current = to; - _slider = to; - if (to - 1 < widget.struct.images.length) { - widget.struct.onPositionChange(to - 1); - } - }); - } - } @override void initState() { @@ -318,81 +465,49 @@ class _WebToonReaderState extends State<_WebToonReader> { }); _itemScrollController = ItemScrollController(); _itemPositionsListener = ItemPositionsListener.create(); - _itemPositionsListener.itemPositions.addListener(_onCurrentChange); - if (widget.struct.initPosition != null && - widget.struct.images.length > widget.struct.initPosition!) { - _initialPosition = widget.struct.initPosition!; - } else { - _initialPosition = 0; - } - _readerControllerEvent.subscribe(_onPageControllerEvent); + _itemPositionsListener.itemPositions.addListener(_onListCurrentChange); super.initState(); } @override void dispose() { - _itemPositionsListener.itemPositions.removeListener(_onCurrentChange); - _readerControllerEvent.unsubscribe(_onPageControllerEvent); + _itemPositionsListener.itemPositions.removeListener(_onListCurrentChange); super.dispose(); } - void _onPageControllerEvent(_ReaderControllerEventArgs? args) { - if (args != null) { - var event = args.key; - print("EVENT : $event"); - switch ("$event") { - case "UP": - if (_current > 1) { - if (noAnimation()) { - _itemScrollController.jumpTo( - index: _current - 2, // 减1 当前position 再减少1 前一个 - ); - } else { - if (DateTime.now().millisecondsSinceEpoch < _controllerTime) { - return; - } - _controllerTime = DateTime.now().millisecondsSinceEpoch + 400; - _itemScrollController.scrollTo( - index: _current - 2, // 减1 当前position 再减少1 前一个 - duration: Duration(milliseconds: 400), - ); - } - } - break; - case "DOWN": - if (_current < widget.struct.images.length) { - if (noAnimation()) { - _itemScrollController.jumpTo(index: _current); - } else { - if (DateTime.now().millisecondsSinceEpoch < _controllerTime) { - return; - } - _controllerTime = DateTime.now().millisecondsSinceEpoch + 400; - _itemScrollController.scrollTo( - index: _current, - duration: Duration(milliseconds: 400), - ); - } - } - break; - } + void _onListCurrentChange() { + var to = _itemPositionsListener.itemPositions.value.first.index; + // 包含一个下一章, 假设5张图片 0,1,2,3,4 length=5, 下一章=5 + if (to >= 0 && to < widget.struct.images.length) { + super._onCurrentChange(to); } } - var _controllerTime = DateTime.now().millisecondsSinceEpoch + 400; + @override + void _needJumpTo(int index, bool animation) { + if (noAnimation() || animation == false) { + _itemScrollController.jumpTo( + index: index, + ); + } else { + if (DateTime.now().millisecondsSinceEpoch < _controllerTime) { + return; + } + _controllerTime = DateTime.now().millisecondsSinceEpoch + 400; + _itemScrollController.scrollTo( + index: index, // 减1 当前position 再减少1 前一个 + duration: Duration(milliseconds: 400), + ); + } + } @override - Widget build(BuildContext context) { + Widget _buildViewer() { return Container( decoration: BoxDecoration( color: Colors.black, ), - child: Stack( - children: [ - _buildList(), - ..._buildControllers(), - ], - ), + child: _buildList(), ); } @@ -459,20 +574,22 @@ class _WebToonReaderState extends State<_WebToonReader> { } } return ScrollablePositionedList.builder( - initialScrollIndex: _initialPosition, + initialScrollIndex: super._startIndex, scrollDirection: widget.struct.pagerDirection == ReaderDirection.TOP_TO_BOTTOM ? Axis.vertical : Axis.horizontal, reverse: widget.struct.pagerDirection == ReaderDirection.RIGHT_TO_LEFT, - padding: widget.struct.fullScreen && - widget.struct.pagerDirection == ReaderDirection.TOP_TO_BOTTOM - ? EdgeInsets.only( - top: scaffold.appBarMaxHeight ?? 0, - bottom: scaffold.appBarMaxHeight ?? 0, - ) - : null, + padding: EdgeInsets.only( + top: widget.struct.fullScreen ? (scaffold.appBarMaxHeight ?? 0) : 0, + bottom: + widget.struct.pagerDirection == ReaderDirection.TOP_TO_BOTTOM + ? 130 + : (widget.struct.fullScreen + ? (scaffold.appBarMaxHeight ?? 0) + : 0), + ), itemScrollController: _itemScrollController, itemPositionsListener: _itemPositionsListener, itemCount: widget.struct.images.length + 1, @@ -487,27 +604,6 @@ class _WebToonReaderState extends State<_WebToonReader> { ); } - List _buildControllers() { - if (widget.struct.fullScreen) { - return []; - } - return [ - _buildImageCount(context, "$_current / ${widget.struct.images.length}"), - _buildScrollController( - context, - _current, - _slider, - widget.struct.images.length, - (value) => _slider = value, - () { - if (_slider != _current && _slider > 0) { - _itemScrollController.jumpTo(index: _slider - 1); - } - }, - ), - ]; - } - Widget _buildNextEp() { return Container( padding: EdgeInsets.all(20), @@ -649,15 +745,6 @@ class _WebToonReaderImageState extends State<_WebToonReaderImage> { /////////////////////////////////////////////////////////////////////////////// -class _WebToonZoomReader extends _WebToonReader { - const _WebToonZoomReader( - ImageReaderStruct struct, - ) : super(struct); - - @override - State createState() => _WebToonZoomReaderState(); -} - class _WebToonZoomReaderState extends _WebToonReaderState { @override Widget _buildList() { @@ -667,81 +754,41 @@ class _WebToonZoomReaderState extends _WebToonReaderState { /////////////////////////////////////////////////////////////////////////////// -class _GalleryReader extends StatefulWidget { - final ImageReaderStruct struct; - - _GalleryReader(this.struct); - - @override - State createState() => _GalleryReaderState(); -} - -class _GalleryReaderState extends State<_GalleryReader> { - late int _current = (widget.struct.initPosition ?? 0) + 1; - late int _slider = (widget.struct.initPosition ?? 0) + 1; - late PageController _pageController = - PageController(initialPage: widget.struct.initPosition ?? 0); +class _GalleryReaderState extends _ImageReaderState { + late PageController _pageController; @override void initState() { - _readerControllerEvent.subscribe(_onPageControllerEvent); + _pageController = PageController(initialPage: super._startIndex); super.initState(); } @override void dispose() { _pageController.dispose(); - _readerControllerEvent.unsubscribe(_onPageControllerEvent); super.dispose(); } - void _onPageControllerEvent(_ReaderControllerEventArgs? args) { - if (args != null) { - var event = args.key; - print("EVENT : $event"); - switch ("$event") { - case "UP": - if (_current > 1) { - if (noAnimation()) { - _pageController.previousPage( - duration: Duration(milliseconds: 1), - curve: Curves.ease, - ); - } else { - _pageController.previousPage( - duration: Duration(milliseconds: 400), - curve: Curves.ease, - ); - } - } - break; - case "DOWN": - if (_current < widget.struct.images.length) { - if (noAnimation()) { - _pageController.nextPage( - duration: Duration(milliseconds: 1), - curve: Curves.ease, - ); - } else { - _pageController.nextPage( - duration: Duration(milliseconds: 400), - curve: Curves.ease, - ); - } - } - break; - } + @override + void _needJumpTo(int index, bool animation) { + if (noAnimation() || animation == false) { + _pageController.jumpToPage( + index, + ); + } else { + _pageController.animateToPage( + index, + duration: Duration(milliseconds: 400), + curve: Curves.ease, + ); } } - @override - Widget build(BuildContext context) { - return Stack( - children: [ - _buildViewer(), - ..._buildControllers(), - ], - ); + void _onGalleryPageChange(int to) { + // 包含一个下一章, 假设5张图片 0,1,2,3,4 length=5, 下一章=5 + if (to >= 0 && to < widget.struct.images.length) { + super._onCurrentChange(to); + } } Widget _buildViewer() { @@ -758,34 +805,7 @@ class _GalleryReaderState extends State<_GalleryReader> { }, ), pageController: _pageController, - onPageChanged: (value) { - setState(() { - _current = value + 1; - _slider = value + 1; - widget.struct.onPositionChange(value); - if (galleryPrePreloadCount > 0) { - for (var count = 1; - count <= galleryPrePreloadCount && value - count >= 0; - count++) { - var target = widget.struct.images[value - count]; - if (target.downloadLocalPath == null) { - method.remoteImagePreload(target.fileServer, target.path); - } - } - } - if (galleryPreloadCount > 0) { - for (var count = 1; - count <= galleryPreloadCount && - value + count < widget.struct.images.length; - count++) { - var target = widget.struct.images[value + count]; - if (target.downloadLocalPath == null) { - method.remoteImagePreload(target.fileServer, target.path); - } - } - } - }); - }, + onPageChanged: _onGalleryPageChange, itemCount: widget.struct.images.length, builder: (BuildContext context, int index) { var item = widget.struct.images[index]; @@ -821,10 +841,9 @@ class _GalleryReaderState extends State<_GalleryReader> { return GestureDetector( child: gallery, onLongPress: () async { - var index = _current - 1; - if (index >= 0 && _current < widget.struct.images.length) { + if (_current >= 0 && _current < widget.struct.images.length) { Future Function() load = () async { - var item = widget.struct.images[index]; + var item = widget.struct.images[_current]; if (item.downloadLocalPath != null) { return method.downloadImagePath(item.downloadLocalPath!); } @@ -858,155 +877,9 @@ class _GalleryReaderState extends State<_GalleryReader> { ); } - List _buildControllers() { - var controllers = []; - if (!widget.struct.fullScreen) { - controllers.addAll([ - _buildImageCount(context, "$_current / ${widget.struct.images.length}"), - _buildScrollController( - context, - _current, - _slider, - widget.struct.images.length, - (value) => _slider = value, - () { - if (_slider != _current && _slider > 0) { - _pageController.jumpToPage(_slider - 1); - } - }, - ), - ]); - } - if (_current == widget.struct.images.length) { - controllers.add(_buildNextEpController( - widget.struct.onNextAction, - widget.struct.onNextText, - )); - } - return controllers; + Widget _buildNextEpButton() { + return Container(); } } /////////////////////////////////////////////////////////////////////////////// - -Widget _buildImageCount(BuildContext context, String info) { - return Align( - alignment: Alignment.topRight, - child: Material( - color: Color(0x0), - child: Container( - margin: EdgeInsets.only(top: 10), - padding: EdgeInsets.only(left: 10, right: 10, top: 4, bottom: 4), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10), - ), - color: Color(0x88000000), - ), - child: GestureDetector( - onTap: () { - // TODO 输入跳转页数 - }, - child: Text("$info", style: TextStyle(color: Colors.white)), - ), - ), - ), - ); -} - -Widget _buildScrollController( - BuildContext context, - int current, - int slider, - int total, - Function(int) onSliderChange, - Function() onSliderDown, -) { - if (total == 0) { - return Container(); - } - var theme = Theme.of(context); - return Align( - alignment: Alignment.centerRight, - child: Material( - color: Color(0x0), - child: Container( - width: 35, - height: 300, - decoration: BoxDecoration( - color: Color(0x66000000), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10), - ), - ), - padding: EdgeInsets.only(top: 10, bottom: 10, left: 5, right: 6), - child: Center( - child: FlutterSlider( - axis: Axis.vertical, - values: [(slider > total ? total : slider).toDouble()], - max: total.toDouble(), - min: 1, - onDragging: (handlerIndex, lowerValue, upperValue) { - onSliderChange(lowerValue.toInt()); - }, - onDragCompleted: (handlerIndex, lowerValue, upperValue) { - onSliderChange(lowerValue.toInt()); - onSliderDown(); - }, - trackBar: FlutterSliderTrackBar( - inactiveTrackBar: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: Colors.grey.shade300, - ), - activeTrackBar: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: theme.colorScheme.secondary, - ), - ), - step: FlutterSliderStep( - step: 1, - isPercentRange: false, - ), - tooltip: FlutterSliderTooltip(custom: (value) { - double a = value; - return Container( - padding: EdgeInsets.all(5), - color: Colors.white, - child: - Text('${a.toInt()}', style: TextStyle(color: Colors.black)), - ); - }), - ), - ), - ), - ), - ); -} - -Widget _buildNextEpController(Function() next, String text) { - return Align( - alignment: Alignment.bottomRight, - child: Material( - color: Color(0x0), - child: Container( - margin: EdgeInsets.only(bottom: 10), - padding: EdgeInsets.only(left: 10, right: 10, top: 4, bottom: 4), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10), - ), - color: Color(0x88000000), - ), - child: GestureDetector( - onTap: () { - next(); - }, - child: Text(text, style: TextStyle(color: Colors.white)), - ), - ), - ), - ); -}