new reader slider

This commit is contained in:
niuhuan 2021-11-30 18:49:51 +08:00
parent c95771e637
commit b5868f3fe9
8 changed files with 415 additions and 497 deletions

View File

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

5
lib/basic/const.dart Normal file
View File

@ -0,0 +1,5 @@
import 'package:flutter/material.dart';
var readerAppbarColor = Color(0xff1e202c);
var readerAppbarColor2 = readerAppbarColor.withAlpha(225);

View File

@ -332,7 +332,7 @@ class _ComicInfoScreenState extends State<ComicInfoScreen> with RouteAware {
comicInfo: comicInfo,
epList: epList,
currentEpOrder: order,
initPictureRank: rank,
initPicturePosition: rank,
),
),
);

View File

@ -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<Ep> 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<ComicReaderScreen> {
bool _replacement = false;
Future<List<RemoteImageInfo>> _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<RemoteImageInfo> list = [];
var _needLoadPage = 0;
@ -70,11 +72,13 @@ class _ComicReaderScreenState extends State<ComicReaderScreen> {
}
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<dynamic> Function() _previousAction = () => null;
String _nextText = "";
FutureOr<dynamic> Function() _nextAction = () => null;
@ -85,6 +89,23 @@ class _ComicReaderScreenState extends State<ComicReaderScreen> {
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<ComicReaderScreen> {
appBar: _fullScreen
? null
: AppBar(
backgroundColor: readerAppbarColor,
title: Text("${_ep.title} - ${widget.comicInfo.title}"),
actions: [
IconButton(
@ -164,8 +186,8 @@ class _ComicReaderScreenState extends State<ComicReaderScreen> {
_future = _load();
});
},
successBuilder:
(BuildContext context, AsyncSnapshot<List<RemoteImageInfo>> snapshot) {
successBuilder: (BuildContext context,
AsyncSnapshot<List<RemoteImageInfo>> snapshot) {
return ImageReader(
ImageReaderStruct(
images: snapshot.data!
@ -182,11 +204,10 @@ class _ComicReaderScreenState extends State<ComicReaderScreen> {
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<ComicReaderScreen> {
comicInfo: widget.comicInfo,
epList: widget.epList,
currentEpOrder: widget.currentEpOrder,
initPictureRank: _lastChangeRank ?? widget.initPictureRank,
initPicturePosition: _lastChangeRank ?? widget.initPicturePosition,
// maybe null
autoFullScreen: _fullScreen,
),

View File

@ -176,7 +176,7 @@ class _DownloadInfoScreenState extends State<DownloadInfoScreen>
comicInfo: _task,
epList: _epList,
currentEpOrder: epOrder,
initPictureRank: rank,
initPicturePosition: rank,
),
),
);

View File

@ -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<DownloadEp> 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<DownloadReaderScreen> {
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<DownloadReaderScreen> {
}
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<dynamic> Function() _previousAction = () => null;
String _nextText = "";
FutureOr<dynamic> Function() _nextAction = () => null;
@ -78,6 +81,23 @@ class _DownloadReaderScreenState extends State<DownloadReaderScreen> {
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<DownloadReaderScreen> {
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<DownloadReaderScreen> {
comicInfo: widget.comicInfo,
epList: widget.epList,
currentEpOrder: widget.currentEpOrder,
initPictureRank: _lastChangeRank ?? widget.initPictureRank,
initPicturePosition: _lastChangeRank ?? widget.initPicturePosition,
// maybe null
autoFullScreen: _fullScreen,
),

View File

@ -37,7 +37,7 @@ class _ContinueReadButtonState extends State<ContinueReadButton> {
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 = '开始阅读';

View File

@ -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<dynamic> Function(bool fullScreen) onFullScreenChange;
final String onNextText;
final FutureOr<dynamic> Function() onPreviousAction;
final FutureOr<dynamic> Function() onNextAction;
final FutureOr<dynamic> 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<StatefulWidget> 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<ImageReader> {
//
Widget _buildViewer();
// ,
void _needJumpTo(int index, bool animation);
@override
void initState() {
_initCurrent();
_readerControllerEvent.subscribe(_onPageControl);
super.initState();
}
Widget _buildFullScreenController(
bool fullScreen,
FutureOr<dynamic> 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<StatefulWidget> createState() => _WebToonReaderState();
}
class _WebToonReaderState extends State<_WebToonReader> {
class _WebToonReaderState extends _ImageReaderState {
var _controllerTime = DateTime.now().millisecondsSinceEpoch + 400;
late final List<Size?> _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<Widget> _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<StatefulWidget> 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<StatefulWidget> 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<String> 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<Widget> _buildControllers() {
var controllers = <Widget>[];
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)),
),
),
),
);
}