diff --git a/lib/activities/book.dart b/lib/activities/book.dart index 3872f9b..45b15ea 100644 --- a/lib/activities/book.dart +++ b/lib/activities/book.dart @@ -11,7 +11,6 @@ class ActivityBook extends StatefulWidget { } class BookState extends State { - static BoxDecoration _border; final GlobalKey _refresh = GlobalKey(); ScrollController _scrollController; @@ -26,7 +25,8 @@ class BookState extends State { 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 { 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 { 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 { @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 { ), PullToRefreshContainer((info) => SliverPullToRefreshHeader( info: info, - onTap: () => _refresh.currentState - .show(notificationDragOffset: kToolbarHeight * 2), + onTap: () => _refresh.currentState.show( + notificationDragOffset: SliverPullToRefreshHeader.height), )), SliverList( delegate: SliverChildBuilderDelegate( diff --git a/lib/activities/chapter.dart b/lib/activities/chapter.dart index 45942de..9af93a4 100644 --- a/lib/activities/chapter.dart +++ b/lib/activities/chapter.dart @@ -1,24 +1,5 @@ part of '../main.dart'; -enum LoadState { - Loading, - Finish, - Timeout, -} - -class LoadMoreListSource extends LoadingMoreBase { - @override - Future 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 { 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 { @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 { 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 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 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 = []; + 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 { 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 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 = []; - 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> 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 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( diff --git a/lib/activities/checkData.dart b/lib/activities/checkData.dart index 1d12faa..849d665 100644 --- a/lib/activities/checkData.dart +++ b/lib/activities/checkData.dart @@ -79,12 +79,7 @@ class _State extends State { maxLines: 8, controller: _outputController, onTap: () { - Fluttertoast.showToast( - msg: '已经复制', - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - timeInSecForIos: 1, - ); + showToast('已经复制'); Clipboard.setData(ClipboardData(text: _outputController.text)); }, )); diff --git a/lib/activities/home.dart b/lib/activities/home.dart index 892e8d4..9998c57 100644 --- a/lib/activities/home.dart +++ b/lib/activities/home.dart @@ -32,9 +32,8 @@ class HomeState extends State { .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 { context, MaterialPageRoute( settings: RouteSettings(name: '/activity_recommend/'), - builder: (_) => ActivityRecommend(), + builder: (_) => ActivityRank(), )); } diff --git a/lib/activities/recommend.dart b/lib/activities/recommend.dart deleted file mode 100644 index b6dec28..0000000 --- a/lib/activities/recommend.dart +++ /dev/null @@ -1,83 +0,0 @@ -part of '../main.dart'; - -class ActivityRecommend extends StatefulWidget { - @override - _ActivityRecommend createState() => _ActivityRecommend(); -} - -class _ActivityRecommend extends State { - @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 { - final GlobalKey _refresh = GlobalKey(); - final List books = []; - bool loadFail = false; - - @override - void initState() { - super.initState(); - SchedulerBinding.instance.addPostFrameCallback((_) { - _refresh.currentState.show(); - }); - } - - Future 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(), - ), - ); - } -} diff --git a/lib/activities/search.dart b/lib/activities/search.dart index 2c76937..261841f 100644 --- a/lib/activities/search.dart +++ b/lib/activities/search.dart @@ -14,121 +14,109 @@ class Search extends StatefulWidget { } } -enum _SearchState { - None, - Searching, - Done, - Error, -} - class SearchState extends State { - Future> search; TextEditingController _controller = TextEditingController(); - CancelableOperation _searcher; - _SearchState _state = _SearchState.None; + GlobalKey _refresh = GlobalKey(); final List _books = []; + bool loading; - void startSearch() { + void submit() { + _refresh.currentState + .show(notificationDragOffset: SliverPullToRefreshHeader.height); + } + + Future 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 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: [ - Row( - children: [ - 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 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), + ); + }, + ), + ]), ), ); } diff --git a/lib/classes/http.dart b/lib/classes/http.dart index df7f973..37caa79 100644 --- a/lib/classes/http.dart +++ b/lib/classes/http.dart @@ -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 getKey() async { @@ -21,6 +22,7 @@ class UserAgentClient extends http.BaseClient { } Future> getImages( + {@required String aid, @required String cid}) async { } Future getBook({String aid}) async { @@ -32,7 +34,7 @@ class UserAgentClient extends http.BaseClient { Future> searchBook(String name) async { } - static void init(String userAgent) { + static void init() async { } Future _get(url, {Map headers}) async { diff --git a/lib/classes/networkImageSSL.dart b/lib/classes/networkImageSSL.dart index 56fe42d..65003e8 100644 --- a/lib/classes/networkImageSSL.dart +++ b/lib/classes/networkImageSSL.dart @@ -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 headers; + static void init(ByteData data) {} + @override Future 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 _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); }); diff --git a/lib/main.dart b/lib/main.dart index 6f69b90..601707f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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
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), -// ); } } diff --git a/lib/widgets/favorites.dart b/lib/widgets/favorites.dart index ce57d94..57b5169 100644 --- a/lib/widgets/favorites.dart +++ b/lib/widgets/favorites.dart @@ -44,9 +44,8 @@ class _FavoriteList extends State { 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), ); diff --git a/lib/widgets/pullToRefreshHeader.dart b/lib/widgets/pullToRefreshHeader.dart index ea35b82..4a9c31f 100644 --- a/lib/widgets/pullToRefreshHeader.dart +++ b/lib/widgets/pullToRefreshHeader.dart @@ -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, ), diff --git a/lib/widgets/quick.dart b/lib/widgets/quick.dart index 1a64e90..63ba157 100644 --- a/lib/widgets/quick.dart +++ b/lib/widgets/quick.dart @@ -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: [ - 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 { DraggableItem _addButton; GlobalKey _key = GlobalKey(); final List id = []; + double width = 0, height = 0; void exit() { _key.currentState.draggableMode = false; @@ -134,8 +144,8 @@ class QuickState extends State { 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 { 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 { @override Widget build(BuildContext context) { - final width = widget.width / 4 - 10; - final height = (width / 0.7).roundToDouble(); return Column( children: [ Container(