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> {
|
||||
static BoxDecoration _border;
|
||||
final GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
||||
ScrollController _scrollController;
|
||||
|
||||
@ -26,7 +25,8 @@ class BookState extends State<ActivityBook> {
|
||||
super.initState();
|
||||
isFavorite = widget.book.isFavorite();
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
_refresh.currentState.show();
|
||||
_refresh.currentState
|
||||
.show(notificationDragOffset: SliverPullToRefreshHeader.height);
|
||||
});
|
||||
_scrollController = ScrollController();
|
||||
}
|
||||
@ -56,12 +56,13 @@ class BookState extends State<ActivityBook> {
|
||||
if (isFavorite) Data.addFavorite(book);
|
||||
|
||||
_scrollToRead();
|
||||
isLoading = false;
|
||||
isSuccess = true;
|
||||
} catch (e) {
|
||||
isLoading = false;
|
||||
isSuccess = false;
|
||||
return false;
|
||||
}
|
||||
isLoading = false;
|
||||
print('刷新 $book');
|
||||
setState(() {});
|
||||
return true;
|
||||
@ -121,7 +122,7 @@ class BookState extends State<ActivityBook> {
|
||||
final isRead = chapter.cid == book.history?.cid;
|
||||
if (index < chapters.length - 1) {
|
||||
return DecoratedBox(
|
||||
decoration: _border,
|
||||
decoration: _Main._border,
|
||||
child: WidgetChapter(
|
||||
chapter: chapter,
|
||||
onTap: _openChapter,
|
||||
@ -138,10 +139,6 @@ class BookState extends State<ActivityBook> {
|
||||
|
||||
@override
|
||||
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;
|
||||
IconData icon = isFavorite ? Icons.favorite : Icons.favorite_border;
|
||||
final book = this.book ?? widget.book;
|
||||
@ -211,8 +208,8 @@ class BookState extends State<ActivityBook> {
|
||||
),
|
||||
PullToRefreshContainer((info) => SliverPullToRefreshHeader(
|
||||
info: info,
|
||||
onTap: () => _refresh.currentState
|
||||
.show(notificationDragOffset: kToolbarHeight * 2),
|
||||
onTap: () => _refresh.currentState.show(
|
||||
notificationDragOffset: SliverPullToRefreshHeader.height),
|
||||
)),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
|
@ -1,24 +1,5 @@
|
||||
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 {
|
||||
final Book book;
|
||||
final Chapter chapter;
|
||||
@ -77,14 +58,6 @@ class ChapterState extends State<ActivityChapter> {
|
||||
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
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = ScrollController();
|
||||
updateRead();
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
_controller.jumpTo(WidgetChapter.height * read);
|
||||
});
|
||||
_controller =
|
||||
ScrollController(initialScrollOffset: WidgetChapter.height * read);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -187,68 +158,68 @@ class _ChapterContentView extends State<ChapterContentView> {
|
||||
BoxDecoration _decoration =
|
||||
BoxDecoration(color: Colors.black.withOpacity(0.4));
|
||||
|
||||
int chapterIndex = -1;
|
||||
bool hasNextChapter = false;
|
||||
bool loading = true;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
|
||||
chapterIndex = widget.book.chapters.indexOf(widget.chapter);
|
||||
hasNextChapter = widget.book.chapters.last != widget.chapter;
|
||||
Data.addHistory(widget.book, widget.chapter);
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) => _refresh?.currentState
|
||||
?.show(notificationDragOffset: kToolbarHeight * 2));
|
||||
?.show(notificationDragOffset: SliverPullToRefreshHeader.height));
|
||||
}
|
||||
|
||||
Future<bool> fetchImages() async {
|
||||
print('fetchImages');
|
||||
setState(() {});
|
||||
if (mounted) setState(() {});
|
||||
loading = true;
|
||||
images.clear();
|
||||
try {
|
||||
images.addAll(await UserAgentClient.instance
|
||||
.getImages(aid: widget.book.aid, cid: widget.chapter.cid)
|
||||
.timeout(const Duration(seconds: 5)));
|
||||
.timeout(Duration(seconds: 5)));
|
||||
if (images.length < 5) {
|
||||
// print('图片 前:' + images.toString());
|
||||
var list = await checkImage(images.last);
|
||||
final list =
|
||||
await checkImage(images.last).timeout(Duration(seconds: 15));
|
||||
images.addAll(list);
|
||||
}
|
||||
} 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;
|
||||
// throw(e);
|
||||
}
|
||||
loading = false;
|
||||
// print('所有图片:' + images.toString());
|
||||
setState(() {});
|
||||
if (mounted) setState(() {});
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> list = [];
|
||||
for (var i = 0; i < images.length; i++) {
|
||||
list.add(SliverStickyHeader(
|
||||
overlapsContent: 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
sliver:
|
||||
SliverToBoxAdapter(child: Image(image: NetworkImageSSL(images[i]))),
|
||||
));
|
||||
final list = <Widget>[];
|
||||
if (!loading && images.length < 20) {
|
||||
list.add(SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: Text('只读取到少于20张图片,友情提示:\n'
|
||||
'由于能力有限,可能没有办法识别出本章的所有图片,\n'
|
||||
'敬请谅解。'))));
|
||||
}
|
||||
return PullToRefreshNotification(
|
||||
key: _refresh,
|
||||
@ -266,71 +237,132 @@ class _ChapterContentView extends State<ChapterContentView> {
|
||||
PullToRefreshContainer(
|
||||
(info) => SliverPullToRefreshHeader(
|
||||
info: info,
|
||||
onTap: () => _refresh.currentState
|
||||
.show(notificationDragOffset: kToolbarHeight * 2),
|
||||
onTap: () => _refresh.currentState.show(
|
||||
notificationDragOffset: SliverPullToRefreshHeader.height),
|
||||
),
|
||||
),
|
||||
...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 {
|
||||
final response = new ReceivePort();
|
||||
await Isolate.spawn(_checkImage, response.sendPort);
|
||||
final sendPort = await response.first as SendPort;
|
||||
//接收消息的ReceivePort
|
||||
final answer = new ReceivePort();
|
||||
//发送数据
|
||||
sendPort.send([answer.sendPort, last]);
|
||||
return answer.first;
|
||||
}
|
||||
|
||||
void _checkImage(SendPort initialReplyTo) {
|
||||
UserAgentClient.instance = UserAgentClient('chrome');
|
||||
final port = new ReceivePort();
|
||||
initialReplyTo.send(port.sendPort);
|
||||
port.listen((message) async {
|
||||
// 获取数据并解析
|
||||
final send = message[0] as SendPort;
|
||||
final last = message[1] as String;
|
||||
// 返回结果
|
||||
final uri = Uri.parse(last);
|
||||
// print({'scheme': uri.scheme, 'host': uri.host, 'path': uri.path});
|
||||
final a = uri.scheme + '://' + uri.host;
|
||||
final b = uri.pathSegments.take(uri.pathSegments.length - 1).join('/');
|
||||
// print({'a': a, 'b': b});
|
||||
//网址最后的图片文件名
|
||||
final file = uri.pathSegments.last.split('.');
|
||||
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;
|
||||
Future<List<String>> checkImage(String last) async {
|
||||
final uri = Uri.parse(last);
|
||||
// print({'scheme': uri.scheme, 'host': uri.host, 'path': uri.path});
|
||||
final a = uri.scheme + '://' + uri.host;
|
||||
final b = uri.pathSegments.take(uri.pathSegments.length - 1).join('/');
|
||||
// print({'a': a, 'b': b});
|
||||
//网址最后的图片文件名
|
||||
final file = uri.pathSegments.last.split('.');
|
||||
final fileName = file[0];
|
||||
// 图片格式
|
||||
final fileFormat = file[1];
|
||||
final List<String> list = [];
|
||||
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;
|
||||
}
|
||||
// print('最后的图片数量: ' + number.toString());
|
||||
send.send(list);
|
||||
});
|
||||
list.add(url2);
|
||||
plus += 2;
|
||||
}
|
||||
// print('最后的图片数量: ' + number.toString());
|
||||
return list;
|
||||
}
|
||||
|
||||
String getFileName(
|
||||
|
@ -79,12 +79,7 @@ class _State extends State<ActivityCheckData> {
|
||||
maxLines: 8,
|
||||
controller: _outputController,
|
||||
onTap: () {
|
||||
Fluttertoast.showToast(
|
||||
msg: '已经复制',
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
timeInSecForIos: 1,
|
||||
);
|
||||
showToast('已经复制');
|
||||
Clipboard.setData(ClipboardData(text: _outputController.text));
|
||||
},
|
||||
));
|
||||
|
@ -32,9 +32,8 @@ class HomeState extends State<ActivityHome> {
|
||||
.where((int updatedChapters) => updatedChapters > 0)
|
||||
.length;
|
||||
if (updated > 0)
|
||||
Fluttertoast.showToast(
|
||||
msg: '$updated 本藏书有更新',
|
||||
gravity: ToastGravity.CENTER,
|
||||
showToast(
|
||||
'$updated 本藏书有更新',
|
||||
backgroundColor: Colors.black.withOpacity(0.5),
|
||||
);
|
||||
});
|
||||
@ -61,7 +60,7 @@ class HomeState extends State<ActivityHome> {
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
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> {
|
||||
Future<List<Book>> search;
|
||||
TextEditingController _controller = TextEditingController();
|
||||
CancelableOperation _searcher;
|
||||
_SearchState _state = _SearchState.None;
|
||||
GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
||||
final List<Book> _books = [];
|
||||
bool loading;
|
||||
|
||||
void startSearch() {
|
||||
void submit() {
|
||||
_refresh.currentState
|
||||
.show(notificationDragOffset: SliverPullToRefreshHeader.height);
|
||||
}
|
||||
|
||||
Future<bool> startSearch() async {
|
||||
print('搜索漫画: ' + _controller.text);
|
||||
if (_searcher != null) _searcher.cancel();
|
||||
_books.clear();
|
||||
setState(() {
|
||||
_state = _SearchState.Searching;
|
||||
});
|
||||
_searcher = CancelableOperation.fromFuture(
|
||||
UserAgentClient.instance.searchBook(_controller.text))
|
||||
.then((books) {
|
||||
setState(() {
|
||||
print('搜索完成: ' + books.length.toString());
|
||||
_books.addAll(books);
|
||||
_state = _SearchState.Done;
|
||||
});
|
||||
loading = true;
|
||||
});
|
||||
_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
|
||||
Widget build(BuildContext context) {
|
||||
search = null;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('搜索漫画'),
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: RawKeyboardListener(
|
||||
focusNode: FocusNode(),
|
||||
onKey: (RawKeyEvent event) {
|
||||
if (event.runtimeType == RawKeyUpEvent &&
|
||||
event.logicalKey.debugName.toLowerCase() == 'enter') {
|
||||
if (_controller.text.isEmpty) return;
|
||||
startSearch();
|
||||
}
|
||||
},
|
||||
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(),
|
||||
);
|
||||
body: PullToRefreshNotification(
|
||||
key: _refresh,
|
||||
onRefresh: startSearch,
|
||||
child: CustomScrollView(slivers: [
|
||||
SliverAppBar(
|
||||
pinned: true,
|
||||
title: RawKeyboardListener(
|
||||
focusNode: FocusNode(),
|
||||
onKey: (RawKeyEvent event) {
|
||||
print(
|
||||
'is enter: ${LogicalKeyboardKey.enter == event.logicalKey}');
|
||||
if (_controller.text.isEmpty) return;
|
||||
if (event.runtimeType == RawKeyUpEvent &&
|
||||
LogicalKeyboardKey.enter == event.logicalKey) {
|
||||
print('回车键搜索');
|
||||
submit();
|
||||
}
|
||||
},
|
||||
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 = '';
|
||||
|
||||
class UserAgentClient extends http.BaseClient {
|
||||
final String userAgent;
|
||||
http.Client _inner;
|
||||
http.Client _inner = http.Client();
|
||||
String lastKey;
|
||||
int lastKeyTime = 0;
|
||||
|
||||
static UserAgentClient instance;
|
||||
|
||||
UserAgentClient(this.userAgent) {
|
||||
// UserAgentClient(this.userAgent);
|
||||
|
||||
UserAgentClient(String userAgent, ByteData data) {
|
||||
}
|
||||
|
||||
Future<String> getKey() async {
|
||||
@ -21,6 +22,7 @@ class UserAgentClient extends http.BaseClient {
|
||||
}
|
||||
|
||||
Future<List<String>> getImages(
|
||||
{@required String aid, @required String cid}) async {
|
||||
}
|
||||
|
||||
Future<Book> getBook({String aid}) async {
|
||||
@ -32,7 +34,7 @@ class UserAgentClient extends http.BaseClient {
|
||||
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 {
|
||||
|
@ -1,10 +1,4 @@
|
||||
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 of '../main.dart';
|
||||
|
||||
/// The dart:io implementation of [image_provider.NetworkImage].
|
||||
class NetworkImageSSL
|
||||
@ -26,6 +20,8 @@ class NetworkImageSSL
|
||||
@override
|
||||
final Map<String, String> headers;
|
||||
|
||||
static void init(ByteData data) {}
|
||||
|
||||
@override
|
||||
Future<NetworkImageSSL> obtainKey(
|
||||
image_provider.ImageConfiguration configuration) {
|
||||
@ -59,18 +55,12 @@ class NetworkImageSSL
|
||||
// We set `autoUncompress` to false to ensure that we can trust the value of
|
||||
// the `Content-Length` HTTP header. We automatically uncompress the content
|
||||
// in our call to [consolidateHttpClientResponseBytes].
|
||||
static final HttpClient _sharedHttpClient = HttpClient()
|
||||
static HttpClient _sharedHttpClient = HttpClient()
|
||||
..autoUncompress = false
|
||||
..badCertificateCallback = (_, __, ___) => true;
|
||||
|
||||
static HttpClient get _httpClient {
|
||||
HttpClient client = _sharedHttpClient;
|
||||
assert(() {
|
||||
if (image_provider.debugNetworkImageHttpClientProvider != null)
|
||||
client = image_provider.debugNetworkImageHttpClientProvider();
|
||||
return true;
|
||||
}());
|
||||
return client;
|
||||
return _sharedHttpClient;
|
||||
}
|
||||
|
||||
Future<ui.Codec> _loadAsync(
|
||||
@ -82,7 +72,8 @@ class NetworkImageSSL
|
||||
assert(key == this);
|
||||
|
||||
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) {
|
||||
request.headers.add(name, value);
|
||||
});
|
||||
|
@ -1,31 +1,38 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:draggable_container/draggable_container.dart';
|
||||
import 'package:dynamic_theme/dynamic_theme.dart';
|
||||
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/observer.dart';
|
||||
import 'package:flutter/material.dart' hide NestedScrollView;
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.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:html/parser.dart' as html;
|
||||
import 'package:http/http.dart' as http;
|
||||
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:pull_to_refresh_notification/pull_to_refresh_notification.dart'
|
||||
hide CircularProgressIndicator;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:sticky_headers/sticky_headers/widget.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';
|
||||
|
||||
@ -35,7 +42,7 @@ part './activities/checkData.dart';
|
||||
|
||||
part './activities/home.dart';
|
||||
|
||||
part './activities/recommend.dart';
|
||||
part './activities/rank.dart';
|
||||
|
||||
part './activities/search.dart';
|
||||
|
||||
@ -47,6 +54,8 @@ part './classes/data.dart';
|
||||
|
||||
part './classes/http.dart';
|
||||
|
||||
part './classes/networkImageSSL.dart';
|
||||
|
||||
part './widgets/book.dart';
|
||||
|
||||
part './widgets/favorites.dart';
|
||||
@ -69,6 +78,7 @@ FirebaseAnalyticsObserver observer;
|
||||
const bool isDevMode = !bool.fromEnvironment('dart.vm.product');
|
||||
|
||||
void main() async {
|
||||
FlutterError.onError = (FlutterErrorDetails details) {};
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
try {
|
||||
@ -82,8 +92,8 @@ void main() async {
|
||||
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
|
||||
]);
|
||||
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));
|
||||
}
|
||||
|
||||
@ -97,32 +107,27 @@ class Main extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _Main extends State<Main> with WidgetsBindingObserver {
|
||||
static BoxDecoration _border;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_border == null)
|
||||
_border = BoxDecoration(
|
||||
border: Border(
|
||||
bottom: Divider.createBorderSide(context, color: Colors.grey)));
|
||||
return DynamicTheme(
|
||||
defaultBrightness: Brightness.dark,
|
||||
data: (brightness) => new ThemeData(
|
||||
brightness: brightness,
|
||||
),
|
||||
themedWidgetBuilder: (context, theme) {
|
||||
return new MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: theme,
|
||||
home: ActivityHome(widget.packageInfo),
|
||||
return OKToast(
|
||||
child: MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
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 (showTip == false) {
|
||||
showTip = true;
|
||||
Fluttertoast.showToast(
|
||||
msg: '下拉列表可以检查漫画更新',
|
||||
gravity: ToastGravity.CENTER,
|
||||
showToast(
|
||||
'下拉列表可以检查漫画更新',
|
||||
backgroundColor: Colors.black.withOpacity(0.5),
|
||||
);
|
||||
}
|
||||
@ -174,7 +173,9 @@ class FBookItem extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
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),
|
||||
subtitle: RichText(text: subtitle),
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class SliverPullToRefreshHeader extends StatelessWidget {
|
||||
static final double height = kToolbarHeight * 2;
|
||||
final PullToRefreshScrollNotificationInfo info;
|
||||
final void Function() onTap;
|
||||
final double fontSize;
|
||||
@ -24,7 +25,7 @@ class SliverPullToRefreshHeader extends StatelessWidget {
|
||||
WidgetSpan(
|
||||
baseline: TextBaseline.alphabetic,
|
||||
child: Padding(
|
||||
child: Image.asset("images/logo.png", height: 20),
|
||||
child: Image.asset("assets/logo.png", height: 20),
|
||||
padding: EdgeInsets.only(right: 5),
|
||||
),
|
||||
),
|
||||
@ -32,7 +33,7 @@ class SliverPullToRefreshHeader extends StatelessWidget {
|
||||
if (info.mode == RefreshIndicatorMode.error) {
|
||||
text.children.addAll([
|
||||
TextSpan(
|
||||
text: '读取失败\n当失败次数太多请检查网络情况\n有些很旧的章节会看不到,请见谅\n',
|
||||
text: '读取失败\n当失败次数太多可能是网络出现问题\n',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
),
|
||||
|
@ -5,8 +5,10 @@ class QuickBook extends DraggableItem {
|
||||
Widget child;
|
||||
final BuildContext context;
|
||||
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(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
@ -19,9 +21,16 @@ class QuickBook extends DraggableItem {
|
||||
},
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Hero(
|
||||
tag: '$heroTag ${book.aid}',
|
||||
child: Image(image: NetworkImageSSL(book.avatar)),
|
||||
SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: Hero(
|
||||
tag: '$heroTag ${book.aid}',
|
||||
child: Image(
|
||||
image: NetworkImageSSL(book.avatar),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
@ -68,6 +77,7 @@ class QuickState extends State<Quick> {
|
||||
DraggableItem _addButton;
|
||||
GlobalKey<DraggableContainerState> _key = GlobalKey();
|
||||
final List<String> id = [];
|
||||
double width = 0, height = 0;
|
||||
|
||||
void exit() {
|
||||
_key.currentState.draggableMode = false;
|
||||
@ -134,8 +144,8 @@ class QuickState extends State<Quick> {
|
||||
final book = await _showSelectBookDialog();
|
||||
print('选择了 $book');
|
||||
if (book == null) return;
|
||||
_key.currentState.insteadOfIndex(
|
||||
buttonIndex, QuickBook(book: book, context: context),
|
||||
_key.currentState.insteadOfIndex(buttonIndex,
|
||||
QuickBook(width, height, book: book, context: context),
|
||||
force: true);
|
||||
}
|
||||
},
|
||||
@ -151,9 +161,11 @@ class QuickState extends State<Quick> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
width = widget.width / 4 - 10;
|
||||
height = (width / 0.7).roundToDouble();
|
||||
_draggableItems.addAll(Data.quickList().map((book) {
|
||||
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);
|
||||
for (var i = count - _draggableItems.length; i > 0; i--) {
|
||||
@ -163,8 +175,6 @@ class QuickState extends State<Quick> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final width = widget.width / 4 - 10;
|
||||
final height = (width / 0.7).roundToDouble();
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
|
Loading…
x
Reference in New Issue
Block a user