> _getChapterAllTags(Element body) async{
328 | return body.getElementsByTagName("a");
329 | }
330 |
331 | static String _removeChapterName(String chapterName, String beautifulFormat) {
332 | try {
333 | chapterName = chapterName.replaceAll(" ", "");
334 | return beautifulFormat.replaceAll(RegExp(".*$chapterName.*"), "");
335 | } catch(_) {
336 | return beautifulFormat;
337 | }
338 | }
339 |
340 | static String _getNextPageUrl(String url, String nextPageUrl) {
341 | var uri = Uri.parse(url);
342 | /// //aaa/cvvv
343 | if (nextPageUrl.startsWith("//")) {
344 | return uri.scheme + ":" + nextPageUrl;
345 | }
346 | /// /aaa/cvvv
347 | if (nextPageUrl.startsWith("/")) {
348 | return uri.origin + nextPageUrl;
349 | }
350 | /// http://aaa/cvvv
351 | if (nextPageUrl.startsWith("http")) {
352 | return nextPageUrl;
353 | }
354 | if (nextPageUrl.startsWith("www")) {
355 | return uri.scheme + ":" + "//" + nextPageUrl;
356 | }
357 | if (!url.endsWith("/")) {
358 | url += "/";
359 | }
360 | return url + nextPageUrl;
361 | }
362 | }
363 |
364 | String _trim(String text) {
365 | return text
366 | .replaceAll(" ", "")
367 | .replaceAll(">", "")
368 | .replaceAll(" ", "");
369 | }
370 | String _beautyBrAndP(String text) {
371 | return text.replaceAll(RegExp(r"]*>"), "")
372 | .replaceAll("
", "
").replaceAll(RegExp(r"]*>"), "")
373 | .replaceAll(RegExp(r"]*>"), "").replaceAll("
", "")
374 | .replaceAll("", "
").replaceAll("
", "\n")
375 | ;
376 | }
377 | String _beautyScript(String text) {
378 | return text.replaceAll(RegExp(r"<[a-zA-Z]+.*?>([\s\S]*?)[a-zA-Z]+.*?>"), "");
379 | }
380 | String _beautyNotes(String text) {
381 | return text.replaceAll(RegExp(r""), "");
382 | }
383 | String _beautyUnknownTag(String text) {
384 | return text.replaceAll(RegExp(r"<\.*>|<.*>"), "");
385 | }
386 | String _beautifulFormat(String str) {
387 | var strList = str.split('\n');
388 | List newStr = [];
389 | for (var element in strList) {
390 | if (element.isEmpty) {
391 | continue;
392 | }
393 | var match = chinese.allMatches(element);
394 | if (match.isEmpty || match.length <= 1) {
395 | continue;
396 | }
397 | if (element.contains(contentFilter)) {
398 | continue;
399 | }
400 | newStr.add(element.replaceAll(" ", "").replaceAll("\u3000", ""));
401 | }
402 | return newStr.join('\n').trim();
403 | }
--------------------------------------------------------------------------------
/lib/util/keep_alive_wrapper.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class KeepAliveWrapper extends StatefulWidget {
4 | final Widget child;
5 |
6 | const KeepAliveWrapper(this.child, {Key? key}) : super(key: key);
7 |
8 | @override
9 | _KeepAliveWrapperState createState() => _KeepAliveWrapperState();
10 | }
11 |
12 | class _KeepAliveWrapperState extends State
13 | with AutomaticKeepAliveClientMixin {
14 | @override
15 | Widget build(BuildContext context) {
16 | super.build(context);
17 | return widget.child;
18 | }
19 |
20 | @override
21 | bool get wantKeepAlive => true;
22 | }
--------------------------------------------------------------------------------
/lib/util/limit_util.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | class LimitUtil {
4 | static const deFaultDurationTime = 300;
5 | static Timer? timer;
6 |
7 | // 防抖函数
8 | static debounce(Function doSomething, {durationTime = deFaultDurationTime}) {
9 | timer?.cancel();
10 | timer = Timer(Duration(milliseconds: durationTime), () {
11 | doSomething;
12 | });
13 | }
14 |
15 | // 节流函数
16 | static const String deFaultThrottleId = 'DeFaultThrottleId';
17 | static Map startTimeMap = {deFaultThrottleId: 0};
18 | static throttle(Function doSomething, {String throttleId = deFaultThrottleId, durationTime = deFaultDurationTime, Function? continueClick}) {
19 | int currentTime = DateTime.now().millisecondsSinceEpoch;
20 | if (currentTime - (startTimeMap[throttleId] ?? 0) > durationTime) {
21 | doSomething.call();
22 | startTimeMap[throttleId] = DateTime.now().millisecondsSinceEpoch;
23 | } else {
24 | continueClick?.call();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/util/list_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | // ignore: non_constant_identifier_names
4 | Widget ListItem(
5 | String left,
6 | Widget right,
7 | {Color? backgroundColor,
8 | Color? textColor})
9 | {
10 | return Container(
11 | height: 50,
12 | color: backgroundColor,
13 | child: Container(
14 | padding: const EdgeInsets.only(left: 15, right: 15, top: 12, bottom: 12),
15 | child: Row(
16 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
17 | children: [
18 | Text(left, style: TextStyle(color: textColor, fontSize: 16, height: 1), ),
19 | right
20 | ],
21 | ),
22 | ),
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/lib/util/no_shadow_scroll_behavior.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class NoShadowScrollBehavior extends ScrollBehavior {
4 | @override
5 | Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
6 | switch (getPlatform(context)) {
7 | case TargetPlatform.iOS:
8 | case TargetPlatform.macOS:
9 | return child;
10 | case TargetPlatform.android:
11 | case TargetPlatform.fuchsia:
12 | case TargetPlatform.linux:
13 | case TargetPlatform.windows:
14 | return GlowingOverscrollIndicator(
15 | child: child,
16 | //不显示头部水波纹
17 | showLeading: false,
18 | //不显示尾部水波纹
19 | showTrailing: false,
20 | axisDirection: axisDirection,
21 | color: Colors.transparent,
22 | );
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/lib/util/notify/counter_notify.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// 数值变化监听
4 | class CounterNotify extends ChangeNotifier {
5 | int _count = 0;
6 | int get count => _count;
7 |
8 |
9 | addCount([int number = 1]) {
10 | _count += number;
11 | notifyListeners();
12 | }
13 |
14 | setCount(int number) {
15 | _count = number;
16 | notifyListeners();
17 | }
18 |
19 | resetCount() {
20 | _count = 0;
21 | notifyListeners();
22 | }
23 | }
--------------------------------------------------------------------------------
/lib/util/notify/object_notify.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// 对象变化监听
4 | class ObjectNotify extends ChangeNotifier {
5 | late T _data;
6 | T get data => _data;
7 |
8 | ObjectNotify(T data) {
9 | _data = data;
10 | }
11 |
12 | update(T data) {
13 | _data = data;
14 | notifyListeners();
15 | }
16 | }
--------------------------------------------------------------------------------
/lib/util/parse_book.dart:
--------------------------------------------------------------------------------
1 | import 'package:book_app/log/log.dart';
2 | import 'package:book_app/mapper/book_db_provider.dart';
3 | import 'package:book_app/mapper/chapter_db_provider.dart';
4 | import 'package:book_app/model/book/book.dart';
5 | import 'package:book_app/module/book/home/book_home_controller.dart';
6 | import 'package:book_app/theme/color.dart';
7 | import 'package:book_app/util/dialog_build.dart';
8 | import 'package:book_app/util/toast.dart';
9 | import 'package:flutter/material.dart';
10 | import 'package:get/get.dart';
11 |
12 | import 'html_parse_util.dart';
13 |
14 | parseBookByShare(String bookName, String content) async{
15 | Get.dialog(
16 | DialogBuild(
17 | "分享小说",
18 | Text.rich(
19 | TextSpan(
20 | text: "是否解析来自其它APP分享的小说",
21 | children: [
22 | TextSpan(text: bookName, style: const TextStyle(color: Colors.lightBlueAccent))
23 | ],
24 | style: TextStyle(color: textColor(), fontSize: 14)
25 | )
26 | ),
27 | confirmFunction: () {
28 | Get.back();
29 | Future.delayed(const Duration(milliseconds: 500), () {
30 | BookHomeController bookHomeController = Get.find();
31 | bookHomeController.parseBookText(content.split("\n"), bookName);
32 | });
33 | },
34 | )
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/lib/util/parse_network_book.dart:
--------------------------------------------------------------------------------
1 | import 'dart:isolate';
2 |
3 | import 'package:book_app/log/log.dart';
4 | import 'package:book_app/model/book/book.dart';
5 | import 'package:book_app/model/message.dart';
6 | import 'package:book_app/util/html_parse_util.dart';
7 | import 'package:book_app/util/toast.dart';
8 |
9 | class ParseNetworkBook {
10 | String url;
11 | String? name;
12 | final SendPort sendPort;
13 | Isolate? _isolate;
14 |
15 | ParseNetworkBook(this.url, this.sendPort, {this.name});
16 |
17 | Future> parseInBackground() async{
18 | final p = ReceivePort();
19 | _isolate = await Isolate.spawn(_parse, p.sendPort,);
20 | return await p.first;
21 | }
22 |
23 | kill() {
24 | final _isolate = this._isolate;
25 | if (_isolate != null) {
26 | _isolate.kill();
27 | }
28 | }
29 |
30 | _parse(SendPort p) async{
31 | try {
32 | String? img;
33 | var results = (await HtmlParseUtil.parseChapter(url, img: (imgUrl) {
34 | img = imgUrl;
35 | },
36 | pageFunc: (page) {
37 | sendPort.send(Message(MessageType.parseNetworkBook, page));
38 | },
39 | name: (_bookName) {
40 | name = name ?? _bookName;
41 | }));
42 | url = results[0];
43 | var chapters = results[1];
44 | final Book book = Book(url: url, name: name, indexImg: img);
45 | var day = DateTime.now();
46 | book.updateTime = "${day.year}-${day.month}-${day.day}";
47 | return Isolate.exit(p, [book, chapters]);
48 | } catch (err) {
49 | Log.e(err);
50 | return Isolate.exit(p, []);
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/lib/util/path_util.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:path_provider/path_provider.dart';
4 |
5 | class PathUtil {
6 | static Future getSavePath(String path) async{
7 | Directory? dir;
8 | if (Platform.isAndroid) {
9 | dir = await getExternalStorageDirectory();
10 | } else {
11 | dir = await getApplicationDocumentsDirectory();
12 | }
13 | Directory _bookDir = Directory("${dir!.path}/$path");
14 | if (!_bookDir.existsSync()) {
15 | _bookDir.createSync();
16 | }
17 | return _bookDir.path;
18 | }
19 | }
--------------------------------------------------------------------------------
/lib/util/random_user_agent.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | List _userAgent = [
4 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
5 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB7.0)",
6 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
7 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)",
8 | "Mozilla/5.0 (Windows; U; Windows NT 6.1; ) AppleWebKit/534.12 (KHTML, like Gecko) Maxthon/3.0 Safari/534.12",
9 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)",
10 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)",
11 | "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.33 Safari/534.3 SE 2.X MetaSr 1.0",
12 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)",
13 | "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1 QQBrowser/6.9.11079.201",
14 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E) QQBrowser/6.9.11079.201",
15 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)",
16 | "Mozilla/5.0(Macintosh;U;IntelMacOSX10_6_8;en-us)AppleWebKit/534.50(KHTML,likeGecko)Version/5.1Safari/534.50",
17 | "Mozilla/5.0(Windows;U;WindowsNT6.1;en-us)AppleWebKit/534.50(KHTML,likeGecko)Version/5.1Safari/534.50",
18 | "Mozilla/5.0(compatible;MSIE9.0;WindowsNT6.1;Trident/5.0;",
19 | "Mozilla/4.0(compatible;MSIE8.0;WindowsNT6.0;Trident/4.0)",
20 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT6.0)",
21 | "Mozilla/4.0(compatible;MSIE6.0;WindowsNT5.1)",
22 | "Mozilla/5.0(Macintosh;IntelMacOSX10.6;rv:2.0.1)Gecko/20100101Firefox/4.0.1",
23 | "Mozilla/5.0(WindowsNT6.1;rv:2.0.1)Gecko/20100101Firefox/4.0.1",
24 | "Opera/9.80(Macintosh;IntelMacOSX10.6.8;U;en)Presto/2.8.131Version/11.11",
25 | "Opera/9.80(WindowsNT6.1;U;en)Presto/2.8.131Version/11.11",
26 | "Mozilla/5.0(Macintosh;IntelMacOSX10_7_0)AppleWebKit/535.11(KHTML,likeGecko)Chrome/17.0.963.56Safari/535.11",
27 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT5.1;Maxthon2.0)",
28 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT5.1;TencentTraveler4.0)",
29 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT5.1)",
30 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT5.1;TheWorld)",
31 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT5.1;Trident/4.0;SE2.XMetaSr1.0;SE2.XMetaSr1.0;.NETCLR2.0.50727;SE2.XMetaSr1.0)",
32 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT5.1;360SE)",
33 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT5.1;AvantBrowser)",
34 | "Mozilla/4.0(compatible;MSIE7.0;WindowsNT5.1)",
35 | "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/96.0.4664.110"
36 | ];
37 |
38 | String randomUserAgent() {
39 | return _userAgent[Random().nextInt(_userAgent.length)];
40 | }
41 |
--------------------------------------------------------------------------------
/lib/util/save_util.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:book_app/model/base.dart';
4 | import 'package:shared_preferences/shared_preferences.dart';
5 |
6 | class SaveUtil {
7 | static late SharedPreferences _sharedPreferences;
8 |
9 | static Future init() async{
10 | _sharedPreferences = await SharedPreferences.getInstance();
11 | }
12 |
13 | static void setModel(key, Base model) {
14 | _sharedPreferences.setString(key, model.toJson().toString());
15 | }
16 |
17 | static void setModelList(key, List list) {
18 | List data = [];
19 | for (var element in list) {
20 | data.add(json.encode(element.toJson()));
21 | }
22 | _sharedPreferences.setStringList(key, data);
23 | }
24 |
25 | static String? getModel(key) {
26 | return _sharedPreferences.getString(key);
27 | }
28 | static List? getModelList(key) {
29 | return _sharedPreferences.getStringList(key);
30 | }
31 |
32 | static void setTrue(key, {bool isTrue = true}) {
33 | _sharedPreferences.setBool(key, isTrue);
34 | }
35 | static bool? getTrue(key) {
36 | return _sharedPreferences.getBool(key);
37 | }
38 |
39 | static void setString(key, str) {
40 | _sharedPreferences.setString(key, str);
41 | }
42 | static String? getString(key) {
43 | return _sharedPreferences.getString(key);
44 | }
45 |
46 | static void remove(key) {
47 | _sharedPreferences.remove(key);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/util/system_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | getAppBarTop() {
3 |
4 | }
5 | /// 获取状态栏高度
6 | double getStatusBarHeight(BuildContext context) {
7 | return MediaQuery.of(context).padding.top;
8 | }
9 | late BuildContext globalContext;
10 |
--------------------------------------------------------------------------------
/lib/util/time_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class TimeUtil {
4 | static String getSystemTime() {
5 | var date = DateTime.now();
6 | return "${date.hour < 10 ? '0' + date.hour.toString() : date.hour}:${date.minute < 10 ? '0' + date.minute.toString() : date.minute}";
7 | }
8 |
9 | static String formatTime(int time) {
10 | String str = "";
11 | if (time ~/ 3600 > 0) {
12 | int hour = time ~/ 3600;
13 | str += "0$hour:";
14 | time = time % 3600;
15 | }
16 | if (time ~/ 60 > 0) {
17 | int minutes = time ~/ 60;
18 | str += minutes > 9 ? "$minutes:" : "0$minutes:";
19 | time = time % 60;
20 | } else {
21 | str += "00:";
22 | }
23 | str += time > 9 ? "$time" : "0$time";
24 | return str;
25 | }
26 |
27 | static String getMonthStr(int index) {
28 | return "${index + 1}月";
29 | }
30 |
31 | static String getYearStr(int diff) {
32 | return "${(DateTime.now().year - diff)}年";
33 | }
34 |
35 | static String getChineseDayDiff(DateTime selectedDay) {
36 | DateTime now = DateTime.now();
37 | DateTimeRange range = DateUtils.datesOnly(DateTimeRange(start: selectedDay, end: now));
38 | int dayDiff = range.duration.inDays;
39 | if (dayDiff == 0) {
40 | return "今天";
41 | }
42 | if (dayDiff > 0 && dayDiff <= 3) {
43 | switch(dayDiff) {
44 | case 1:
45 | return "昨天";
46 | case 2:
47 | return "前天";
48 | case 3:
49 | return "大前天";
50 | }
51 | }
52 | if (dayDiff < 0 && dayDiff >= -3) {
53 | switch(dayDiff) {
54 | case -1:
55 | return "明天";
56 | case -2:
57 | return "后天";
58 | case -3:
59 | return "大后天";
60 | }
61 | }
62 | return "${selectedDay.day}号";
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/util/toast.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter_easyloading/flutter_easyloading.dart';
4 |
5 | class Toast {
6 |
7 | /// 吐司
8 | static void toast({String toast = "加载中...", Duration? duration}) {
9 | EasyLoading.showToast(toast, duration: duration);
10 | }
11 |
12 | /// 长吐司
13 | static void toastL({String toast = "加载中..."}) {
14 | EasyLoading.show(status: toast, maskType: EasyLoadingMaskType.clear);
15 | }
16 |
17 | /// 长吐司
18 | static void toastLWithDismiss(Future Function() executor, {String toast = "加载中..."}) async{
19 | EasyLoading.show(status: toast, maskType: EasyLoadingMaskType.clear);
20 | executor().then((value) => cancel()).catchError((_) => cancel());
21 | }
22 |
23 | /// 取消吐司
24 | static void cancel() {
25 | EasyLoading.dismiss();
26 | }
27 | }
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: book_app
2 | description: 轻阅读
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.1.3+1
19 |
20 | environment:
21 | sdk: ">=2.12.0 <3.0.0"
22 |
23 | # Dependencies specify other packages that your package needs in order to work.
24 | # To automatically upgrade your package dependencies to the latest versions
25 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
26 | # dependencies can be manually updated by changing the version numbers below to
27 | # the latest version available on pub.dev. To see which dependencies have newer
28 | # versions available, run `flutter pub outdated`.
29 | dependencies:
30 | flutter:
31 | sdk: flutter
32 | flutter_localizations:
33 | sdk: flutter
34 |
35 |
36 | # The following adds the Cupertino Icons font to your application.
37 | # Use with the CupertinoIcons class for iOS style icons.
38 | cupertino_icons: ^1.0.2
39 | get: ^4.6.1
40 | flutter_easyloading: ^3.0.3
41 | shared_preferences: ^2.0.8
42 | sqflite: ^2.0.0+4
43 | json_annotation: ^4.1.0
44 | logger: ^1.1.0
45 | cached_network_image: ^3.1.0
46 | # 选色板
47 | flutter_colorpicker: ^1.0.3
48 | flutter_switch: ^0.3.2
49 | numberpicker: ^2.1.1
50 | file_picker: ^4.6.1
51 | permission_handler: ^8.3.0
52 | fast_gbk: ^1.0.0
53 | woshilll_flutter_plugin:
54 | git:
55 | url: git@github.com:woshilll/woshilll_flutter_plugin.git
56 | flutter_zoom_drawer: ^2.3.1+1
57 | draggable_scrollbar: ^0.1.0
58 | share_plus: ^4.0.1
59 | path_provider: ^2.0.9
60 | url_launcher: ^6.0.9
61 |
62 | dev_dependencies:
63 | flutter_test:
64 | sdk: flutter
65 |
66 | # The "flutter_lints" package below contains a set of recommended lints to
67 | # encourage good coding practices. The lint set provided by the package is
68 | # activated in the `analysis_options.yaml` file located at the root of your
69 | # package. See that file for information about deactivating specific lint
70 | # rules and activating additional ones.
71 | flutter_lints: ^1.0.0
72 | json_serializable: ^6.1.4
73 | build_runner: ^2.1.4
74 | dart_code_metrics: ^4.11.0
75 |
76 | # For information on the generic Dart part of this file, see the
77 | # following page: https://dart.dev/tools/pub/pubspec
78 |
79 | # The following section is specific to Flutter.
80 | flutter:
81 |
82 | # The following line ensures that the Material Icons font is
83 | # included with your application, so that you can use the icons in
84 | # the material Icons class.
85 | uses-material-design: true
86 | assets:
87 | - lib/resource/
88 | - lib/resource/image/screen_h.png
89 | # To add assets to your application, add an assets section, like this:
90 | # assets:
91 | # - images/a_dot_burr.jpeg
92 | # - images/a_dot_ham.jpeg
93 |
94 | # An image asset can refer to one or more resolution-specific "variants", see
95 | # https://flutter.dev/assets-and-images/#resolution-aware.
96 |
97 | # For details regarding adding assets from package dependencies, see
98 | # https://flutter.dev/assets-and-images/#from-packages
99 | # fonts:
100 | # - family: KaiTi
101 | # fonts:
102 | # - asset: lib/resource/font/KaiTi.ttf
103 | # To add custom fonts to your application, add a fonts section here,
104 | # in this "flutter" section. Each entry in this list should have a
105 | # "family" key with the font family name, and a "fonts" key with a
106 | # list giving the asset and other descriptors for the font. For
107 | # example:
108 | # fonts:
109 | # - family: Schyler
110 | # fonts:
111 | # - asset: fonts/Schyler-Regular.ttf
112 | # - asset: fonts/Schyler-Italic.ttf
113 | # style: italic
114 | # - family: Trajan Pro
115 | # fonts:
116 | # - asset: fonts/TrajanPro.ttf
117 | # - asset: fonts/TrajanPro_Bold.ttf
118 | # weight: 700
119 | #
120 | # For details regarding fonts from package dependencies,
121 | # see https://flutter.dev/custom-fonts/#from-packages
122 |
--------------------------------------------------------------------------------
/test/test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:book_app/log/log.dart';
4 | import 'package:book_app/util/html_parse_util.dart';
5 |
6 | void main() async{
7 | final RegExp nextPageReg = RegExp(r"下[1一]?页");
8 | Log.i("页".contains(nextPageReg));
9 | }
10 |
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woshilll/book_app/0076d8c22a01f5c3005b2fd510b3d085d6bea8b4/web/favicon.png
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woshilll/book_app/0076d8c22a01f5c3005b2fd510b3d085d6bea8b4/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woshilll/book_app/0076d8c22a01f5c3005b2fd510b3d085d6bea8b4/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woshilll/book_app/0076d8c22a01f5c3005b2fd510b3d085d6bea8b4/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/woshilll/book_app/0076d8c22a01f5c3005b2fd510b3d085d6bea8b4/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | book_app
30 |
31 |
32 |
33 |
36 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "book_app",
3 | "short_name": "book_app",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "小说阅读",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------