mirror of
https://github.com/nrop19/weiman_app.git
synced 2025-08-02 23:05:48 +08:00
1.0.6
This commit is contained in:
parent
b86adbb991
commit
4c88153171
@ -11,7 +11,6 @@ class ActivityBook extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BookState extends State<ActivityBook> {
|
class BookState extends State<ActivityBook> {
|
||||||
static BoxDecoration _border;
|
|
||||||
final GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
final GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
||||||
ScrollController _scrollController;
|
ScrollController _scrollController;
|
||||||
|
|
||||||
@ -26,7 +25,8 @@ class BookState extends State<ActivityBook> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
isFavorite = widget.book.isFavorite();
|
isFavorite = widget.book.isFavorite();
|
||||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
_refresh.currentState.show();
|
_refresh.currentState
|
||||||
|
.show(notificationDragOffset: SliverPullToRefreshHeader.height);
|
||||||
});
|
});
|
||||||
_scrollController = ScrollController();
|
_scrollController = ScrollController();
|
||||||
}
|
}
|
||||||
@ -56,12 +56,13 @@ class BookState extends State<ActivityBook> {
|
|||||||
if (isFavorite) Data.addFavorite(book);
|
if (isFavorite) Data.addFavorite(book);
|
||||||
|
|
||||||
_scrollToRead();
|
_scrollToRead();
|
||||||
|
isLoading = false;
|
||||||
isSuccess = true;
|
isSuccess = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
isLoading = false;
|
||||||
isSuccess = false;
|
isSuccess = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
isLoading = false;
|
|
||||||
print('刷新 $book');
|
print('刷新 $book');
|
||||||
setState(() {});
|
setState(() {});
|
||||||
return true;
|
return true;
|
||||||
@ -121,7 +122,7 @@ class BookState extends State<ActivityBook> {
|
|||||||
final isRead = chapter.cid == book.history?.cid;
|
final isRead = chapter.cid == book.history?.cid;
|
||||||
if (index < chapters.length - 1) {
|
if (index < chapters.length - 1) {
|
||||||
return DecoratedBox(
|
return DecoratedBox(
|
||||||
decoration: _border,
|
decoration: _Main._border,
|
||||||
child: WidgetChapter(
|
child: WidgetChapter(
|
||||||
chapter: chapter,
|
chapter: chapter,
|
||||||
onTap: _openChapter,
|
onTap: _openChapter,
|
||||||
@ -138,10 +139,6 @@ class BookState extends State<ActivityBook> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_border == null)
|
|
||||||
_border = BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
bottom: Divider.createBorderSide(context, color: Colors.grey)));
|
|
||||||
Color color = isFavorite ? Colors.red : Colors.white;
|
Color color = isFavorite ? Colors.red : Colors.white;
|
||||||
IconData icon = isFavorite ? Icons.favorite : Icons.favorite_border;
|
IconData icon = isFavorite ? Icons.favorite : Icons.favorite_border;
|
||||||
final book = this.book ?? widget.book;
|
final book = this.book ?? widget.book;
|
||||||
@ -211,8 +208,8 @@ class BookState extends State<ActivityBook> {
|
|||||||
),
|
),
|
||||||
PullToRefreshContainer((info) => SliverPullToRefreshHeader(
|
PullToRefreshContainer((info) => SliverPullToRefreshHeader(
|
||||||
info: info,
|
info: info,
|
||||||
onTap: () => _refresh.currentState
|
onTap: () => _refresh.currentState.show(
|
||||||
.show(notificationDragOffset: kToolbarHeight * 2),
|
notificationDragOffset: SliverPullToRefreshHeader.height),
|
||||||
)),
|
)),
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
@ -1,24 +1,5 @@
|
|||||||
part of '../main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
enum LoadState {
|
|
||||||
Loading,
|
|
||||||
Finish,
|
|
||||||
Timeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoadMoreListSource extends LoadingMoreBase<int> {
|
|
||||||
@override
|
|
||||||
Future<bool> loadData([bool isloadMoreAction = false]) {
|
|
||||||
return Future.delayed(Duration(seconds: 1), () {
|
|
||||||
for (var i = 0; i < 10; i++) {
|
|
||||||
this.add(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ActivityChapter extends StatefulWidget {
|
class ActivityChapter extends StatefulWidget {
|
||||||
final Book book;
|
final Book book;
|
||||||
final Chapter chapter;
|
final Chapter chapter;
|
||||||
@ -77,14 +58,6 @@ class ChapterState extends State<ActivityChapter> {
|
|||||||
chapter: widget.book.chapters[index],
|
chapter: widget.book.chapters[index],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
// floatingActionButton: FloatingActionButton(
|
|
||||||
// child: Text('下一章'),
|
|
||||||
// onPressed: () {
|
|
||||||
// if (hasNextChapter)
|
|
||||||
// return openChapter(widget.book.chapters[chapterIndex + 1]);
|
|
||||||
// Fluttertoast.showToast(msg: '已经是最后一章了');
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,11 +83,9 @@ class _ChapterDrawer extends State<ChapterDrawer> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_controller = ScrollController();
|
|
||||||
updateRead();
|
updateRead();
|
||||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
_controller =
|
||||||
_controller.jumpTo(WidgetChapter.height * read);
|
ScrollController(initialScrollOffset: WidgetChapter.height * read);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -187,68 +158,68 @@ class _ChapterContentView extends State<ChapterContentView> {
|
|||||||
BoxDecoration _decoration =
|
BoxDecoration _decoration =
|
||||||
BoxDecoration(color: Colors.black.withOpacity(0.4));
|
BoxDecoration(color: Colors.black.withOpacity(0.4));
|
||||||
|
|
||||||
int chapterIndex = -1;
|
bool loading = true;
|
||||||
bool hasNextChapter = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
chapterIndex = widget.book.chapters.indexOf(widget.chapter);
|
|
||||||
hasNextChapter = widget.book.chapters.last != widget.chapter;
|
|
||||||
Data.addHistory(widget.book, widget.chapter);
|
Data.addHistory(widget.book, widget.chapter);
|
||||||
SchedulerBinding.instance.addPostFrameCallback((_) => _refresh?.currentState
|
SchedulerBinding.instance.addPostFrameCallback((_) => _refresh?.currentState
|
||||||
?.show(notificationDragOffset: kToolbarHeight * 2));
|
?.show(notificationDragOffset: SliverPullToRefreshHeader.height));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> fetchImages() async {
|
Future<bool> fetchImages() async {
|
||||||
print('fetchImages');
|
print('fetchImages');
|
||||||
setState(() {});
|
if (mounted) setState(() {});
|
||||||
|
loading = true;
|
||||||
images.clear();
|
images.clear();
|
||||||
try {
|
try {
|
||||||
images.addAll(await UserAgentClient.instance
|
images.addAll(await UserAgentClient.instance
|
||||||
.getImages(aid: widget.book.aid, cid: widget.chapter.cid)
|
.getImages(aid: widget.book.aid, cid: widget.chapter.cid)
|
||||||
.timeout(const Duration(seconds: 5)));
|
.timeout(Duration(seconds: 5)));
|
||||||
if (images.length < 5) {
|
if (images.length < 5) {
|
||||||
// print('图片 前:' + images.toString());
|
// print('图片 前:' + images.toString());
|
||||||
var list = await checkImage(images.last);
|
final list =
|
||||||
|
await checkImage(images.last).timeout(Duration(seconds: 15));
|
||||||
images.addAll(list);
|
images.addAll(list);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('错误');
|
print('错误 $e');
|
||||||
|
showToastWidget(
|
||||||
|
GestureDetector(
|
||||||
|
child: Container(
|
||||||
|
child: Text('读取章节内容出现错误\n点击复制错误内容'),
|
||||||
|
color: Colors.black.withOpacity(0.5),
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
await Clipboard.setData(ClipboardData(text: e.toString()));
|
||||||
|
final content = await Clipboard.getData(Clipboard.kTextPlain);
|
||||||
|
print('粘贴板 ${content.text}');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
duration: Duration(seconds: 5),
|
||||||
|
handleTouch: true,
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
// throw(e);
|
// throw(e);
|
||||||
}
|
}
|
||||||
|
loading = false;
|
||||||
// print('所有图片:' + images.toString());
|
// print('所有图片:' + images.toString());
|
||||||
setState(() {});
|
if (mounted) setState(() {});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> list = [];
|
final list = <Widget>[];
|
||||||
for (var i = 0; i < images.length; i++) {
|
if (!loading && images.length < 20) {
|
||||||
list.add(SliverStickyHeader(
|
list.add(SliverToBoxAdapter(
|
||||||
overlapsContent: true,
|
child: Padding(
|
||||||
header: SafeArea(
|
padding: EdgeInsets.all(5),
|
||||||
top: true,
|
child: Text('只读取到少于20张图片,友情提示:\n'
|
||||||
bottom: false,
|
'由于能力有限,可能没有办法识别出本章的所有图片,\n'
|
||||||
child: Row(
|
'敬请谅解。'))));
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.all(5),
|
|
||||||
decoration: _decoration,
|
|
||||||
child: Text(
|
|
||||||
'${i + 1} / ${images.length}',
|
|
||||||
style: _style,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
sliver:
|
|
||||||
SliverToBoxAdapter(child: Image(image: NetworkImageSSL(images[i]))),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
return PullToRefreshNotification(
|
return PullToRefreshNotification(
|
||||||
key: _refresh,
|
key: _refresh,
|
||||||
@ -266,71 +237,132 @@ class _ChapterContentView extends State<ChapterContentView> {
|
|||||||
PullToRefreshContainer(
|
PullToRefreshContainer(
|
||||||
(info) => SliverPullToRefreshHeader(
|
(info) => SliverPullToRefreshHeader(
|
||||||
info: info,
|
info: info,
|
||||||
onTap: () => _refresh.currentState
|
onTap: () => _refresh.currentState.show(
|
||||||
.show(notificationDragOffset: kToolbarHeight * 2),
|
notificationDragOffset: SliverPullToRefreshHeader.height),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...list,
|
...list,
|
||||||
|
SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(ctx, i) {
|
||||||
|
print('item $i');
|
||||||
|
return StickyHeader(
|
||||||
|
overlapHeaders: true,
|
||||||
|
header: SafeArea(
|
||||||
|
top: true,
|
||||||
|
bottom: false,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
decoration: _decoration,
|
||||||
|
child: Text(
|
||||||
|
'${i + 1} / ${images.length}',
|
||||||
|
style: _style,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
content: ExtendedImage(
|
||||||
|
image: NetworkImageSSL(images[i]),
|
||||||
|
enableLoadState: true,
|
||||||
|
enableMemoryCache: true,
|
||||||
|
fit: BoxFit.fitWidth,
|
||||||
|
loadStateChanged: (state) {
|
||||||
|
switch (state.extendedImageLoadState) {
|
||||||
|
case LoadState.loading:
|
||||||
|
return SizedBox(
|
||||||
|
height: 300,
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case LoadState.failed:
|
||||||
|
return SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 300,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text('图片读取失败'),
|
||||||
|
RaisedButton(
|
||||||
|
child: Text('重试'),
|
||||||
|
onPressed: state.reLoadImage,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ExtendedRawImage(
|
||||||
|
image: state.extendedImageInfo?.image,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// content: Image(
|
||||||
|
// image: NetworkImageSSL(images[i]),
|
||||||
|
// loadingBuilder: (_, child, loadingProgress) {
|
||||||
|
// if (loadingProgress == null) return child;
|
||||||
|
// return SizedBox(
|
||||||
|
// height: 400,
|
||||||
|
// child: Center(
|
||||||
|
// child: CircularProgressIndicator(
|
||||||
|
// value: loadingProgress.expectedTotalBytes != null
|
||||||
|
// ? loadingProgress.cumulativeBytesLoaded /
|
||||||
|
// loadingProgress.expectedTotalBytes
|
||||||
|
// : null,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: images.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> checkImage(String last) async {
|
Future<List<String>> checkImage(String last) async {
|
||||||
final response = new ReceivePort();
|
final uri = Uri.parse(last);
|
||||||
await Isolate.spawn(_checkImage, response.sendPort);
|
// print({'scheme': uri.scheme, 'host': uri.host, 'path': uri.path});
|
||||||
final sendPort = await response.first as SendPort;
|
final a = uri.scheme + '://' + uri.host;
|
||||||
//接收消息的ReceivePort
|
final b = uri.pathSegments.take(uri.pathSegments.length - 1).join('/');
|
||||||
final answer = new ReceivePort();
|
// print({'a': a, 'b': b});
|
||||||
//发送数据
|
//网址最后的图片文件名
|
||||||
sendPort.send([answer.sendPort, last]);
|
final file = uri.pathSegments.last.split('.');
|
||||||
return answer.first;
|
final fileName = file[0];
|
||||||
}
|
// 图片格式
|
||||||
|
final fileFormat = file[1];
|
||||||
void _checkImage(SendPort initialReplyTo) {
|
final List<String> list = [];
|
||||||
UserAgentClient.instance = UserAgentClient('chrome');
|
int plus = 1;
|
||||||
final port = new ReceivePort();
|
//print('最后的图片:' + last);
|
||||||
initialReplyTo.send(port.sendPort);
|
while (true) {
|
||||||
port.listen((message) async {
|
final String file1 = getFileName(name: fileName, divider: '_', plus: plus),
|
||||||
// 获取数据并解析
|
file2 = getFileName(name: fileName, divider: '_', plus: plus + 1);
|
||||||
final send = message[0] as SendPort;
|
var url1 = '$a/$b/$file1.$fileFormat', url2 = '$a/$b/$file2.$fileFormat';
|
||||||
final last = message[1] as String;
|
// print('正在测试:\n' + url1 + '\n' + url2);
|
||||||
// 返回结果
|
final res = await Future.wait([
|
||||||
final uri = Uri.parse(last);
|
UserAgentClient.instance.head(url1),
|
||||||
// print({'scheme': uri.scheme, 'host': uri.host, 'path': uri.path});
|
UserAgentClient.instance.head(url2)
|
||||||
final a = uri.scheme + '://' + uri.host;
|
]);
|
||||||
final b = uri.pathSegments.take(uri.pathSegments.length - 1).join('/');
|
if (res[0].statusCode != 200) break;
|
||||||
// print({'a': a, 'b': b});
|
list.add(url1);
|
||||||
//网址最后的图片文件名
|
if (res[1].statusCode != 200) {
|
||||||
final file = uri.pathSegments.last.split('.');
|
break;
|
||||||
final fileName = file[0];
|
|
||||||
// 图片格式
|
|
||||||
final fileFormat = file[1];
|
|
||||||
final list = <String>[];
|
|
||||||
int plus = 1;
|
|
||||||
//print('最后的图片:' + last);
|
|
||||||
while (true) {
|
|
||||||
final String file1 =
|
|
||||||
getFileName(name: fileName, divider: '_', plus: plus),
|
|
||||||
file2 = getFileName(name: fileName, divider: '_', plus: plus + 1);
|
|
||||||
var url1 = '$a/$b/$file1.$fileFormat', url2 = '$a/$b/$file2.$fileFormat';
|
|
||||||
// print('正在测试:\n' + url1 + '\n' + url2);
|
|
||||||
final res = await Future.wait([
|
|
||||||
UserAgentClient.instance.head(url1),
|
|
||||||
UserAgentClient.instance.head(url2)
|
|
||||||
]);
|
|
||||||
if (res[0].statusCode != 200) break;
|
|
||||||
list.add(url1);
|
|
||||||
if (res[1].statusCode != 200) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
list.add(url2);
|
|
||||||
plus += 2;
|
|
||||||
}
|
}
|
||||||
// print('最后的图片数量: ' + number.toString());
|
list.add(url2);
|
||||||
send.send(list);
|
plus += 2;
|
||||||
});
|
}
|
||||||
|
// print('最后的图片数量: ' + number.toString());
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getFileName(
|
String getFileName(
|
||||||
|
@ -79,12 +79,7 @@ class _State extends State<ActivityCheckData> {
|
|||||||
maxLines: 8,
|
maxLines: 8,
|
||||||
controller: _outputController,
|
controller: _outputController,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Fluttertoast.showToast(
|
showToast('已经复制');
|
||||||
msg: '已经复制',
|
|
||||||
toastLength: Toast.LENGTH_SHORT,
|
|
||||||
gravity: ToastGravity.BOTTOM,
|
|
||||||
timeInSecForIos: 1,
|
|
||||||
);
|
|
||||||
Clipboard.setData(ClipboardData(text: _outputController.text));
|
Clipboard.setData(ClipboardData(text: _outputController.text));
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
@ -32,9 +32,8 @@ class HomeState extends State<ActivityHome> {
|
|||||||
.where((int updatedChapters) => updatedChapters > 0)
|
.where((int updatedChapters) => updatedChapters > 0)
|
||||||
.length;
|
.length;
|
||||||
if (updated > 0)
|
if (updated > 0)
|
||||||
Fluttertoast.showToast(
|
showToast(
|
||||||
msg: '$updated 本藏书有更新',
|
'$updated 本藏书有更新',
|
||||||
gravity: ToastGravity.CENTER,
|
|
||||||
backgroundColor: Colors.black.withOpacity(0.5),
|
backgroundColor: Colors.black.withOpacity(0.5),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -61,7 +60,7 @@ class HomeState extends State<ActivityHome> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
settings: RouteSettings(name: '/activity_recommend/'),
|
settings: RouteSettings(name: '/activity_recommend/'),
|
||||||
builder: (_) => ActivityRecommend(),
|
builder: (_) => ActivityRank(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
part of '../main.dart';
|
|
||||||
|
|
||||||
class ActivityRecommend extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
_ActivityRecommend createState() => _ActivityRecommend();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ActivityRecommend extends State<ActivityRecommend> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text('月排行榜'),
|
|
||||||
),
|
|
||||||
body: BookList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BookList extends StatefulWidget {
|
|
||||||
const BookList({Key key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_BookList createState() => _BookList();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BookList extends State<BookList> {
|
|
||||||
final GlobalKey<RefreshIndicatorState> _refresh = GlobalKey();
|
|
||||||
final List<Book> books = [];
|
|
||||||
bool loadFail = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
|
||||||
_refresh.currentState.show();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> loadBooks() async {
|
|
||||||
loadFail = false;
|
|
||||||
try {
|
|
||||||
final books = await UserAgentClient.instance
|
|
||||||
.getMonthList()
|
|
||||||
.timeout(Duration(seconds: 5));
|
|
||||||
this.books
|
|
||||||
..clear()
|
|
||||||
..addAll(books);
|
|
||||||
} catch (e) {
|
|
||||||
loadFail = true;
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return RefreshIndicator(
|
|
||||||
key: _refresh,
|
|
||||||
onRefresh: loadBooks,
|
|
||||||
child: loadFail
|
|
||||||
? CustomScrollView(
|
|
||||||
slivers: [
|
|
||||||
SliverFillRemaining(
|
|
||||||
child: Center(child: Text('读取失败,下拉刷新')),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: ListView(
|
|
||||||
children: ListTile.divideTiles(
|
|
||||||
context: context,
|
|
||||||
tiles: books.map((book) => WidgetBook(
|
|
||||||
book,
|
|
||||||
subtitle: book.author,
|
|
||||||
))).toList(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,121 +14,109 @@ class Search extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum _SearchState {
|
|
||||||
None,
|
|
||||||
Searching,
|
|
||||||
Done,
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
class SearchState extends State<Search> {
|
class SearchState extends State<Search> {
|
||||||
Future<List<Book>> search;
|
|
||||||
TextEditingController _controller = TextEditingController();
|
TextEditingController _controller = TextEditingController();
|
||||||
CancelableOperation _searcher;
|
GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
||||||
_SearchState _state = _SearchState.None;
|
|
||||||
final List<Book> _books = [];
|
final List<Book> _books = [];
|
||||||
|
bool loading;
|
||||||
|
|
||||||
void startSearch() {
|
void submit() {
|
||||||
|
_refresh.currentState
|
||||||
|
.show(notificationDragOffset: SliverPullToRefreshHeader.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> startSearch() async {
|
||||||
print('搜索漫画: ' + _controller.text);
|
print('搜索漫画: ' + _controller.text);
|
||||||
if (_searcher != null) _searcher.cancel();
|
|
||||||
_books.clear();
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_state = _SearchState.Searching;
|
loading = true;
|
||||||
});
|
|
||||||
_searcher = CancelableOperation.fromFuture(
|
|
||||||
UserAgentClient.instance.searchBook(_controller.text))
|
|
||||||
.then((books) {
|
|
||||||
setState(() {
|
|
||||||
print('搜索完成: ' + books.length.toString());
|
|
||||||
_books.addAll(books);
|
|
||||||
_state = _SearchState.Done;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
_books.clear();
|
||||||
|
try {
|
||||||
|
final List<Book> books = await UserAgentClient.instance
|
||||||
|
.searchBook(_controller.text)
|
||||||
|
.timeout(Duration(seconds: 5));
|
||||||
|
_books.addAll(books);
|
||||||
|
loading = false;
|
||||||
|
} catch (e) {
|
||||||
|
loading = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
search = null;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
body: PullToRefreshNotification(
|
||||||
title: Text('搜索漫画'),
|
key: _refresh,
|
||||||
),
|
onRefresh: startSearch,
|
||||||
body: Column(
|
child: CustomScrollView(slivers: [
|
||||||
children: <Widget>[
|
SliverAppBar(
|
||||||
Row(
|
pinned: true,
|
||||||
children: <Widget>[
|
title: RawKeyboardListener(
|
||||||
Expanded(
|
focusNode: FocusNode(),
|
||||||
child: RawKeyboardListener(
|
onKey: (RawKeyEvent event) {
|
||||||
focusNode: FocusNode(),
|
print(
|
||||||
onKey: (RawKeyEvent event) {
|
'is enter: ${LogicalKeyboardKey.enter == event.logicalKey}');
|
||||||
if (event.runtimeType == RawKeyUpEvent &&
|
if (_controller.text.isEmpty) return;
|
||||||
event.logicalKey.debugName.toLowerCase() == 'enter') {
|
if (event.runtimeType == RawKeyUpEvent &&
|
||||||
if (_controller.text.isEmpty) return;
|
LogicalKeyboardKey.enter == event.logicalKey) {
|
||||||
startSearch();
|
print('回车键搜索');
|
||||||
}
|
submit();
|
||||||
},
|
|
||||||
child: TextField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: '搜索书名',
|
|
||||||
prefixIcon: IconButton(
|
|
||||||
onPressed: startSearch,
|
|
||||||
icon: Icon(Icons.search),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
controller: _controller,
|
|
||||||
autofocus: true,
|
|
||||||
textInputAction: TextInputAction.search,
|
|
||||||
onSubmitted: (String name) {
|
|
||||||
print('onSubmitted');
|
|
||||||
startSearch();
|
|
||||||
},
|
|
||||||
keyboardType: TextInputType.text,
|
|
||||||
onEditingComplete: () {
|
|
||||||
print('onEditingComplete');
|
|
||||||
startSearch();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
|
||||||
switch (_state) {
|
|
||||||
case _SearchState.Searching:
|
|
||||||
return Center(child: CircularProgressIndicator());
|
|
||||||
case _SearchState.None:
|
|
||||||
return Center(
|
|
||||||
child: Icon(
|
|
||||||
Icons.search,
|
|
||||||
color: Colors.grey,
|
|
||||||
));
|
|
||||||
default:
|
|
||||||
if (_books.length == 0)
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
'一本也找不到',
|
|
||||||
style: TextStyle(color: Colors.blueGrey),
|
|
||||||
));
|
|
||||||
List<Widget> list = _books
|
|
||||||
.map((book) => WidgetBook(
|
|
||||||
book,
|
|
||||||
subtitle: book.author,
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
return ListView(
|
|
||||||
children:
|
|
||||||
ListTile.divideTiles(context: context, tiles: list)
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
child: TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: '搜索书名',
|
||||||
|
prefixIcon: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_refresh.currentState.show(
|
||||||
|
notificationDragOffset:
|
||||||
|
SliverPullToRefreshHeader.height);
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.search),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
controller: _controller,
|
||||||
|
autofocus: true,
|
||||||
|
textInputAction: TextInputAction.search,
|
||||||
|
onSubmitted: (String name) {
|
||||||
|
print('onSubmitted');
|
||||||
|
submit();
|
||||||
|
},
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
onEditingComplete: () {
|
||||||
|
print('onEditingComplete');
|
||||||
|
submit();
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
PullToRefreshContainer((info) => SliverPullToRefreshHeader(
|
||||||
|
info: info,
|
||||||
|
onTap: submit,
|
||||||
|
)),
|
||||||
|
SliverLayoutBuilder(
|
||||||
|
builder: (_, __) {
|
||||||
|
if (loading == null)
|
||||||
|
return SliverFillRemaining(
|
||||||
|
child: Center(child: Text('输入关键词搜索')));
|
||||||
|
if (loading) return SliverToBoxAdapter();
|
||||||
|
if (_books.length == 0) {
|
||||||
|
return SliverFillRemaining(child: Center(child: Text('一本也没有')));
|
||||||
|
}
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate((_, i) {
|
||||||
|
return WidgetBook(
|
||||||
|
_books[i],
|
||||||
|
subtitle: _books[i].author,
|
||||||
|
);
|
||||||
|
}, childCount: _books.length),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,15 @@ part of '../main.dart';
|
|||||||
const domain = '';
|
const domain = '';
|
||||||
|
|
||||||
class UserAgentClient extends http.BaseClient {
|
class UserAgentClient extends http.BaseClient {
|
||||||
final String userAgent;
|
http.Client _inner = http.Client();
|
||||||
http.Client _inner;
|
|
||||||
String lastKey;
|
String lastKey;
|
||||||
int lastKeyTime = 0;
|
int lastKeyTime = 0;
|
||||||
|
|
||||||
static UserAgentClient instance;
|
static UserAgentClient instance;
|
||||||
|
|
||||||
UserAgentClient(this.userAgent) {
|
// UserAgentClient(this.userAgent);
|
||||||
|
|
||||||
|
UserAgentClient(String userAgent, ByteData data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getKey() async {
|
Future<String> getKey() async {
|
||||||
@ -21,6 +22,7 @@ class UserAgentClient extends http.BaseClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> getImages(
|
Future<List<String>> getImages(
|
||||||
|
{@required String aid, @required String cid}) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Book> getBook({String aid}) async {
|
Future<Book> getBook({String aid}) async {
|
||||||
@ -32,7 +34,7 @@ class UserAgentClient extends http.BaseClient {
|
|||||||
Future<List<Book>> searchBook(String name) async {
|
Future<List<Book>> searchBook(String name) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init(String userAgent) {
|
static void init() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<http.Response> _get(url, {Map<String, String> headers}) async {
|
Future<http.Response> _get(url, {Map<String, String> headers}) async {
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import 'dart:async';
|
part of '../main.dart';
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'dart:ui' as ui;
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/painting.dart' as image_provider;
|
|
||||||
|
|
||||||
/// The dart:io implementation of [image_provider.NetworkImage].
|
/// The dart:io implementation of [image_provider.NetworkImage].
|
||||||
class NetworkImageSSL
|
class NetworkImageSSL
|
||||||
@ -26,6 +20,8 @@ class NetworkImageSSL
|
|||||||
@override
|
@override
|
||||||
final Map<String, String> headers;
|
final Map<String, String> headers;
|
||||||
|
|
||||||
|
static void init(ByteData data) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<NetworkImageSSL> obtainKey(
|
Future<NetworkImageSSL> obtainKey(
|
||||||
image_provider.ImageConfiguration configuration) {
|
image_provider.ImageConfiguration configuration) {
|
||||||
@ -59,18 +55,12 @@ class NetworkImageSSL
|
|||||||
// We set `autoUncompress` to false to ensure that we can trust the value of
|
// We set `autoUncompress` to false to ensure that we can trust the value of
|
||||||
// the `Content-Length` HTTP header. We automatically uncompress the content
|
// the `Content-Length` HTTP header. We automatically uncompress the content
|
||||||
// in our call to [consolidateHttpClientResponseBytes].
|
// in our call to [consolidateHttpClientResponseBytes].
|
||||||
static final HttpClient _sharedHttpClient = HttpClient()
|
static HttpClient _sharedHttpClient = HttpClient()
|
||||||
..autoUncompress = false
|
..autoUncompress = false
|
||||||
..badCertificateCallback = (_, __, ___) => true;
|
..badCertificateCallback = (_, __, ___) => true;
|
||||||
|
|
||||||
static HttpClient get _httpClient {
|
static HttpClient get _httpClient {
|
||||||
HttpClient client = _sharedHttpClient;
|
return _sharedHttpClient;
|
||||||
assert(() {
|
|
||||||
if (image_provider.debugNetworkImageHttpClientProvider != null)
|
|
||||||
client = image_provider.debugNetworkImageHttpClientProvider();
|
|
||||||
return true;
|
|
||||||
}());
|
|
||||||
return client;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ui.Codec> _loadAsync(
|
Future<ui.Codec> _loadAsync(
|
||||||
@ -82,7 +72,8 @@ class NetworkImageSSL
|
|||||||
assert(key == this);
|
assert(key == this);
|
||||||
|
|
||||||
final Uri resolved = Uri.base.resolve(key.url);
|
final Uri resolved = Uri.base.resolve(key.url);
|
||||||
final HttpClientRequest request = await _httpClient.getUrl(resolved);
|
final HttpClientRequest request =
|
||||||
|
await _httpClient.getUrl(resolved).timeout(Duration(seconds: 5));
|
||||||
headers?.forEach((String name, String value) {
|
headers?.forEach((String name, String value) {
|
||||||
request.headers.add(name, value);
|
request.headers.add(name, value);
|
||||||
});
|
});
|
||||||
|
@ -1,31 +1,38 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:async/async.dart';
|
|
||||||
import 'package:draggable_container/draggable_container.dart';
|
import 'package:draggable_container/draggable_container.dart';
|
||||||
import 'package:dynamic_theme/dynamic_theme.dart';
|
import 'package:dynamic_theme/dynamic_theme.dart';
|
||||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||||
|
import 'package:extended_image/extended_image.dart';
|
||||||
|
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
||||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||||
import 'package:firebase_analytics/observer.dart';
|
import 'package:firebase_analytics/observer.dart';
|
||||||
import 'package:flutter/material.dart' hide NestedScrollView;
|
import 'package:flutter/material.dart' hide NestedScrollView;
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:html/parser.dart' as html;
|
import 'package:html/parser.dart' as html;
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:http/io_client.dart';
|
import 'package:http/io_client.dart';
|
||||||
import 'package:loading_more_list/loading_more_list.dart';
|
import 'package:oktoast/oktoast.dart';
|
||||||
import 'package:package_info/package_info.dart';
|
import 'package:package_info/package_info.dart';
|
||||||
import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart'
|
import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart'
|
||||||
hide CircularProgressIndicator;
|
hide CircularProgressIndicator;
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:sticky_headers/sticky_headers/widget.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'classes/networkImageSSL.dart';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/painting.dart' as image_provider;
|
||||||
|
|
||||||
part './activities/book.dart';
|
part './activities/book.dart';
|
||||||
|
|
||||||
@ -35,7 +42,7 @@ part './activities/checkData.dart';
|
|||||||
|
|
||||||
part './activities/home.dart';
|
part './activities/home.dart';
|
||||||
|
|
||||||
part './activities/recommend.dart';
|
part './activities/rank.dart';
|
||||||
|
|
||||||
part './activities/search.dart';
|
part './activities/search.dart';
|
||||||
|
|
||||||
@ -47,6 +54,8 @@ part './classes/data.dart';
|
|||||||
|
|
||||||
part './classes/http.dart';
|
part './classes/http.dart';
|
||||||
|
|
||||||
|
part './classes/networkImageSSL.dart';
|
||||||
|
|
||||||
part './widgets/book.dart';
|
part './widgets/book.dart';
|
||||||
|
|
||||||
part './widgets/favorites.dart';
|
part './widgets/favorites.dart';
|
||||||
@ -69,6 +78,7 @@ FirebaseAnalyticsObserver observer;
|
|||||||
const bool isDevMode = !bool.fromEnvironment('dart.vm.product');
|
const bool isDevMode = !bool.fromEnvironment('dart.vm.product');
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
|
FlutterError.onError = (FlutterErrorDetails details) {};
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -82,8 +92,8 @@ void main() async {
|
|||||||
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
|
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
|
||||||
]);
|
]);
|
||||||
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||||
UserAgentClient.init(
|
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36');
|
UserAgentClient.init();
|
||||||
runApp(Main(packageInfo: packageInfo));
|
runApp(Main(packageInfo: packageInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,32 +107,27 @@ class Main extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _Main extends State<Main> with WidgetsBindingObserver {
|
class _Main extends State<Main> with WidgetsBindingObserver {
|
||||||
|
static BoxDecoration _border;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (_border == null)
|
||||||
|
_border = BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: Divider.createBorderSide(context, color: Colors.grey)));
|
||||||
return DynamicTheme(
|
return DynamicTheme(
|
||||||
defaultBrightness: Brightness.dark,
|
defaultBrightness: Brightness.dark,
|
||||||
data: (brightness) => new ThemeData(
|
data: (brightness) => new ThemeData(
|
||||||
brightness: brightness,
|
brightness: brightness,
|
||||||
),
|
),
|
||||||
themedWidgetBuilder: (context, theme) {
|
themedWidgetBuilder: (context, theme) {
|
||||||
return new MaterialApp(
|
return OKToast(
|
||||||
title: 'Flutter Demo',
|
child: MaterialApp(
|
||||||
theme: theme,
|
title: 'Flutter Demo',
|
||||||
home: ActivityHome(widget.packageInfo),
|
theme: theme,
|
||||||
|
home: ActivityHome(widget.packageInfo),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
// return MaterialApp(
|
|
||||||
// title: '微漫',
|
|
||||||
// theme: ThemeData(
|
|
||||||
// brightness: Brightness.light,
|
|
||||||
// ),
|
|
||||||
// darkTheme: ThemeData(
|
|
||||||
// brightness: Brightness.dark,
|
|
||||||
// ),
|
|
||||||
// themeMode: ThemeMode.system,
|
|
||||||
// debugShowCheckedModeBanner: false,
|
|
||||||
// navigatorObservers: [observer],
|
|
||||||
// home: ActivityHome(widget.packageInfo),
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,9 +44,8 @@ class _FavoriteList extends State<FavoriteList> {
|
|||||||
if (all.isNotEmpty) {
|
if (all.isNotEmpty) {
|
||||||
if (showTip == false) {
|
if (showTip == false) {
|
||||||
showTip = true;
|
showTip = true;
|
||||||
Fluttertoast.showToast(
|
showToast(
|
||||||
msg: '下拉列表可以检查漫画更新',
|
'下拉列表可以检查漫画更新',
|
||||||
gravity: ToastGravity.CENTER,
|
|
||||||
backgroundColor: Colors.black.withOpacity(0.5),
|
backgroundColor: Colors.black.withOpacity(0.5),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -174,7 +173,9 @@ class FBookItem extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () => onTap(book),
|
onTap: () => onTap(book),
|
||||||
leading: Hero(tag: 'fb ${book.aid}', child: Image(image:NetworkImageSSL(book.avatar))),
|
leading: Hero(
|
||||||
|
tag: 'fb ${book.aid}',
|
||||||
|
child: Image(image: NetworkImageSSL(book.avatar))),
|
||||||
title: Text(book.name, style: Theme.of(context).textTheme.body1),
|
title: Text(book.name, style: Theme.of(context).textTheme.body1),
|
||||||
subtitle: RichText(text: subtitle),
|
subtitle: RichText(text: subtitle),
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
part of '../main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class SliverPullToRefreshHeader extends StatelessWidget {
|
class SliverPullToRefreshHeader extends StatelessWidget {
|
||||||
|
static final double height = kToolbarHeight * 2;
|
||||||
final PullToRefreshScrollNotificationInfo info;
|
final PullToRefreshScrollNotificationInfo info;
|
||||||
final void Function() onTap;
|
final void Function() onTap;
|
||||||
final double fontSize;
|
final double fontSize;
|
||||||
@ -24,7 +25,7 @@ class SliverPullToRefreshHeader extends StatelessWidget {
|
|||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
baseline: TextBaseline.alphabetic,
|
baseline: TextBaseline.alphabetic,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
child: Image.asset("images/logo.png", height: 20),
|
child: Image.asset("assets/logo.png", height: 20),
|
||||||
padding: EdgeInsets.only(right: 5),
|
padding: EdgeInsets.only(right: 5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -32,7 +33,7 @@ class SliverPullToRefreshHeader extends StatelessWidget {
|
|||||||
if (info.mode == RefreshIndicatorMode.error) {
|
if (info.mode == RefreshIndicatorMode.error) {
|
||||||
text.children.addAll([
|
text.children.addAll([
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '读取失败\n当失败次数太多请检查网络情况\n有些很旧的章节会看不到,请见谅\n',
|
text: '读取失败\n当失败次数太多可能是网络出现问题\n',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
),
|
),
|
||||||
|
@ -5,8 +5,10 @@ class QuickBook extends DraggableItem {
|
|||||||
Widget child;
|
Widget child;
|
||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
final Book book;
|
final Book book;
|
||||||
|
final double width, height;
|
||||||
|
|
||||||
QuickBook({@required this.book, @required this.context}) {
|
QuickBook(this.width, this.height,
|
||||||
|
{@required this.book, @required this.context}) {
|
||||||
child = GestureDetector(
|
child = GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@ -19,9 +21,16 @@ class QuickBook extends DraggableItem {
|
|||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Hero(
|
SizedBox(
|
||||||
tag: '$heroTag ${book.aid}',
|
width: width,
|
||||||
child: Image(image: NetworkImageSSL(book.avatar)),
|
height: height,
|
||||||
|
child: Hero(
|
||||||
|
tag: '$heroTag ${book.aid}',
|
||||||
|
child: Image(
|
||||||
|
image: NetworkImageSSL(book.avatar),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
left: 0,
|
||||||
@ -68,6 +77,7 @@ class QuickState extends State<Quick> {
|
|||||||
DraggableItem _addButton;
|
DraggableItem _addButton;
|
||||||
GlobalKey<DraggableContainerState> _key = GlobalKey();
|
GlobalKey<DraggableContainerState> _key = GlobalKey();
|
||||||
final List<String> id = [];
|
final List<String> id = [];
|
||||||
|
double width = 0, height = 0;
|
||||||
|
|
||||||
void exit() {
|
void exit() {
|
||||||
_key.currentState.draggableMode = false;
|
_key.currentState.draggableMode = false;
|
||||||
@ -134,8 +144,8 @@ class QuickState extends State<Quick> {
|
|||||||
final book = await _showSelectBookDialog();
|
final book = await _showSelectBookDialog();
|
||||||
print('选择了 $book');
|
print('选择了 $book');
|
||||||
if (book == null) return;
|
if (book == null) return;
|
||||||
_key.currentState.insteadOfIndex(
|
_key.currentState.insteadOfIndex(buttonIndex,
|
||||||
buttonIndex, QuickBook(book: book, context: context),
|
QuickBook(width, height, book: book, context: context),
|
||||||
force: true);
|
force: true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -151,9 +161,11 @@ class QuickState extends State<Quick> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
width = widget.width / 4 - 10;
|
||||||
|
height = (width / 0.7).roundToDouble();
|
||||||
_draggableItems.addAll(Data.quickList().map((book) {
|
_draggableItems.addAll(Data.quickList().map((book) {
|
||||||
id.add(book.aid);
|
id.add(book.aid);
|
||||||
return QuickBook(book: book, context: context);
|
return QuickBook(width, height, book: book, context: context);
|
||||||
}));
|
}));
|
||||||
if (_draggableItems.length < count) _draggableItems.add(_addButton);
|
if (_draggableItems.length < count) _draggableItems.add(_addButton);
|
||||||
for (var i = count - _draggableItems.length; i > 0; i--) {
|
for (var i = count - _draggableItems.length; i > 0; i--) {
|
||||||
@ -163,8 +175,6 @@ class QuickState extends State<Quick> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final width = widget.width / 4 - 10;
|
|
||||||
final height = (width / 0.7).roundToDouble();
|
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user