pikapika/lib/screens/components/ComicPager.dart

499 lines
15 KiB
Dart
Raw Permalink Normal View History

import 'package:event/event.dart';
2021-09-29 23:57:09 +00:00
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
2021-11-11 03:00:38 +00:00
import 'package:pikapika/basic/Entities.dart';
import 'package:pikapika/basic/config/PagerAction.dart';
2021-11-24 13:22:22 +00:00
import 'package:pikapika/basic/config/ShadowCategoriesEvent.dart';
2021-11-11 03:00:38 +00:00
import 'package:pikapika/basic/enum/Sort.dart';
import 'package:pikapika/screens/components/ComicList.dart';
import 'package:pikapika/screens/components/ContentError.dart';
import 'package:pikapika/screens/components/FitButton.dart';
2022-07-12 07:28:47 +00:00
import '../../basic/Common.dart';
import '../../basic/config/IsPro.dart';
2021-09-29 23:57:09 +00:00
import 'ContentLoading.dart';
// 漫画列页
class ComicPager extends StatefulWidget {
final ComicListController? comicListController;
2021-09-29 23:57:09 +00:00
final Future<ComicsPage> Function(String sort, int page) fetchPage;
const ComicPager({
required this.fetchPage,
Key? key,
// required
this.comicListController,
}) : super(key: key);
2021-09-29 23:57:09 +00:00
@override
State<StatefulWidget> createState() => _ComicPagerState();
}
class _ComicPagerState extends State<ComicPager> {
@override
void initState() {
shadowCategoriesEvent.subscribe(_onShadowChange);
super.initState();
}
@override
void dispose() {
shadowCategoriesEvent.unsubscribe(_onShadowChange);
super.dispose();
}
void _onShadowChange(EventArgs? args) {
2022-03-19 04:12:27 +00:00
setState(() {});
}
2021-09-29 23:57:09 +00:00
@override
Widget build(BuildContext context) {
2021-11-04 05:56:25 +00:00
switch (currentPagerAction()) {
2021-09-29 23:57:09 +00:00
case PagerAction.CONTROLLER:
return ControllerComicPager(
fetchPage: widget.fetchPage,
comicListController: widget.comicListController,
);
2021-09-29 23:57:09 +00:00
case PagerAction.STREAM:
return StreamComicPager(
fetchPage: widget.fetchPage,
comicListController: widget.comicListController,
);
2021-09-29 23:57:09 +00:00
default:
return Container();
}
}
}
class ControllerComicPager extends StatefulWidget {
final ComicListController? comicListController;
2021-09-29 23:57:09 +00:00
final Future<ComicsPage> Function(String sort, int page) fetchPage;
const ControllerComicPager({
Key? key,
required this.fetchPage,
required this.comicListController,
2021-09-29 23:57:09 +00:00
}) : super(key: key);
@override
State<StatefulWidget> createState() => _ControllerComicPagerState();
}
class _ControllerComicPagerState extends State<ControllerComicPager> {
final TextEditingController _textEditController =
TextEditingController(text: '');
late String _currentSort = SORT_DEFAULT;
late int _currentPage = 1;
late Future<ComicsPage> _pageFuture;
Future<dynamic> _load() async {
setState(() {
_pageFuture = widget.fetchPage(_currentSort, _currentPage);
});
}
@override
void initState() {
_load();
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _pageFuture,
builder: (BuildContext context, AsyncSnapshot<ComicsPage> snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
2022-03-19 04:12:27 +00:00
return const Text('初始化');
2021-09-29 23:57:09 +00:00
}
if (snapshot.connectionState != ConnectionState.done) {
2022-03-19 04:12:27 +00:00
return const ContentLoading(label: '加载中');
2021-09-29 23:57:09 +00:00
}
if (snapshot.hasError) {
return ContentError(
error: snapshot.error,
stackTrace: snapshot.stackTrace,
onRefresh: _load,
);
}
var comicsPage = snapshot.data!;
return Scaffold(
appBar: _buildAppBar(comicsPage, context),
body: ComicList(
comicsPage.docs,
appendWidget: _buildNextButton(comicsPage),
listController: widget.comicListController,
2021-09-29 23:57:09 +00:00
),
);
},
);
}
PreferredSize _buildAppBar(ComicsPage comicsPage, BuildContext context) {
return PreferredSize(
2022-03-19 04:12:27 +00:00
preferredSize: const Size.fromHeight(40),
2021-09-29 23:57:09 +00:00
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: .5,
style: BorderStyle.solid,
color: Colors.grey[200]!,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(width: 10),
DropdownButton(
items: items,
value: _currentSort,
onChanged: (String? value) {
if (value != null) {
_currentPage = 1;
_currentSort = value;
_load();
}
},
),
],
),
InkWell(
onTap: () {
_textEditController.clear();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Card(
2022-03-17 03:31:25 +00:00
child: TextField(
controller: _textEditController,
decoration: const InputDecoration(
labelText: "请输入页数:",
2021-09-29 23:57:09 +00:00
),
2022-03-17 03:31:25 +00:00
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp(r'\d+')),
],
2021-09-29 23:57:09 +00:00
),
),
actions: <Widget>[
MaterialButton(
onPressed: () {
Navigator.pop(context);
},
2022-03-19 04:12:27 +00:00
child: const Text('取消'),
2021-09-29 23:57:09 +00:00
),
MaterialButton(
onPressed: () {
Navigator.pop(context);
var text = _textEditController.text;
2022-03-17 03:31:25 +00:00
if (text.isEmpty || text.length > 5) {
2021-09-29 23:57:09 +00:00
return;
}
var num = int.parse(text);
if (num == 0 || num > comicsPage.pages) {
return;
}
2022-10-27 07:21:46 +00:00
if (num > 10 && !isPro) {
defaultToast(context, "发电以后才能看10页以后的内容");
2022-07-12 07:28:47 +00:00
return;
}
2021-09-29 23:57:09 +00:00
_currentPage = num;
_load();
},
2022-03-19 04:12:27 +00:00
child: const Text('确定'),
2021-09-29 23:57:09 +00:00
),
],
);
},
);
},
child: Row(
children: [
Text("${comicsPage.page} / ${comicsPage.pages}"),
],
),
),
Row(
children: [
MaterialButton(
minWidth: 0,
onPressed: () {
if (comicsPage.page > 1) {
_currentPage = comicsPage.page - 1;
_load();
}
},
2022-03-19 04:12:27 +00:00
child: const Text('上一页'),
2021-09-29 23:57:09 +00:00
),
MaterialButton(
minWidth: 0,
onPressed: () {
if (comicsPage.page < comicsPage.pages) {
2022-10-27 07:21:46 +00:00
if (_currentPage >= 10 && !isPro) {
defaultToast(context, "发电以后才能看10页以后的内容");
2022-07-12 07:28:47 +00:00
return;
}
2021-09-29 23:57:09 +00:00
_currentPage = comicsPage.page + 1;
_load();
}
},
2022-03-19 04:12:27 +00:00
child: const Text('下一页'),
2021-09-29 23:57:09 +00:00
)
],
),
],
),
),
);
}
Widget? _buildNextButton(ComicsPage comicsPage) {
if (comicsPage.page < comicsPage.pages) {
return FitButton(
onPressed: () {
2022-10-27 07:21:46 +00:00
if (_currentPage >= 10 && !isPro) {
defaultToast(context, "发电以后才能看10页以后的内容");
2022-07-12 07:28:47 +00:00
return;
}
2021-09-29 23:57:09 +00:00
_currentPage = comicsPage.page + 1;
_load();
},
text: '下一页',
);
}
2022-03-17 03:31:25 +00:00
return null;
2021-09-29 23:57:09 +00:00
}
}
class StreamComicPager extends StatefulWidget {
final ComicListController? comicListController;
2021-09-29 23:57:09 +00:00
final Future<ComicsPage> Function(String sort, int page) fetchPage;
const StreamComicPager({
Key? key,
required this.fetchPage,
required this.comicListController,
2021-09-29 23:57:09 +00:00
}) : super(key: key);
@override
State<StatefulWidget> createState() => _StreamComicPagerState();
}
class _StreamComicPagerState extends State<StreamComicPager> {
2022-07-09 07:11:26 +00:00
final TextEditingController _textEditController =
2022-07-12 07:28:47 +00:00
TextEditingController(text: '');
2021-09-29 23:57:09 +00:00
final _scrollController = ScrollController();
late String _currentSort = SORT_DEFAULT;
late int _currentPage = 1;
late int _maxPage = 0;
late List<ComicSimple> _list = [];
late bool _loading = false;
late bool _over = false;
late bool _error = false;
2022-07-12 07:28:47 +00:00
late bool _noPro = false;
// late Future<dynamic> _pageFuture;
2021-09-29 23:57:09 +00:00
2022-07-09 07:11:26 +00:00
_onSetOffset(int i) {
_list.clear();
_currentPage = i;
_load();
}
2021-09-29 23:57:09 +00:00
void _onScroll() {
2022-07-12 07:28:47 +00:00
if (_over || _error || _loading || _noPro) {
2021-09-29 23:57:09 +00:00
return;
}
if (_scrollController.offset + MediaQuery.of(context).size.height / 2 <
_scrollController.position.maxScrollExtent) {
return;
}
_load();
}
Future<dynamic> _load() async {
setState(() {
//_pageFuture =
_fetch();
2021-09-29 23:57:09 +00:00
});
}
Future<dynamic> _fetch() async {
_error = false;
setState(() {
_loading = true;
});
try {
var page = await widget.fetchPage(_currentSort, _currentPage);
setState(() {
_currentPage++;
_maxPage = page.pages;
_list.addAll(page.docs);
_over = page.page >= page.pages;
2022-10-27 07:21:46 +00:00
_noPro = _currentPage > 10 && !isPro;
2021-09-29 23:57:09 +00:00
});
} catch (e, s) {
_error = true;
print("$e\n$s");
2022-03-19 04:12:27 +00:00
rethrow;
2021-09-29 23:57:09 +00:00
} finally {
setState(() {
_loading = false;
});
}
}
@override
void initState() {
_load();
_scrollController.addListener(_onScroll);
super.initState();
}
@override
void dispose() {
_scrollController.removeListener(_onScroll);
_scrollController.dispose();
2022-07-09 07:11:26 +00:00
_textEditController.dispose();
2021-09-29 23:57:09 +00:00
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppBar(context),
body: ComicList(
_list,
scrollController: _scrollController,
2021-09-29 23:57:09 +00:00
appendWidget: _buildLoadingCell(),
listController: widget.comicListController,
2021-09-29 23:57:09 +00:00
),
);
}
PreferredSize _buildAppBar(BuildContext context) {
return PreferredSize(
2022-03-19 04:12:27 +00:00
preferredSize: const Size.fromHeight(40),
2021-09-29 23:57:09 +00:00
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: .5,
style: BorderStyle.solid,
color: Colors.grey[200]!,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(width: 10),
DropdownButton(
items: items,
value: _currentSort,
onChanged: (String? value) {
if (value != null) {
_list = [];
_currentPage = 1;
_currentSort = value;
_load();
}
},
),
],
),
Row(
children: [
2022-07-09 07:11:26 +00:00
InkWell(
onTap: () {
_textEditController.clear();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Card(
child: TextField(
controller: _textEditController,
decoration: const InputDecoration(
labelText: "请输入页数:",
),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
2022-07-12 07:28:47 +00:00
FilteringTextInputFormatter.allow(
RegExp(r'\d+')),
2022-07-09 07:11:26 +00:00
],
),
),
actions: <Widget>[
MaterialButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('取消'),
),
MaterialButton(
onPressed: () {
Navigator.pop(context);
var text = _textEditController.text;
if (text.isEmpty || text.length > 5) {
return;
}
var num = int.parse(text);
if (num == 0 || num > _maxPage) {
return;
}
2022-10-27 07:21:46 +00:00
if (_currentPage >= 10 && !isPro) {
defaultToast(context, "发电以后才能看10页以后的内容");
2022-07-12 07:28:47 +00:00
return;
}
2022-07-09 07:11:26 +00:00
_currentPage = num;
_onSetOffset(num);
},
child: const Text('确定'),
),
],
);
},
);
},
child: Row(
children: [
Text("已经加载 ${_currentPage - 1} / $_maxPage"),
],
),
),
2021-09-29 23:57:09 +00:00
],
),
],
),
),
);
}
Widget? _buildLoadingCell() {
2022-07-12 07:28:47 +00:00
if (_noPro) {
2023-02-16 07:33:09 +00:00
return FitButton(onPressed: () {}, text: '发电以后才能看10页以后的内容');
2022-07-12 07:28:47 +00:00
}
2021-09-29 23:57:09 +00:00
if (_error) {
return FitButton(
onPressed: () {
setState(() {
_error = false;
});
_load();
},
text: '网络错误 / 点击刷新');
}
if (_loading) {
return FitButton(onPressed: () {}, text: '加载中');
}
2022-03-17 03:31:25 +00:00
return null;
2021-09-29 23:57:09 +00:00
}
}