diff --git a/lib/common/controller/archiver_download_controller.dart b/lib/common/controller/archiver_download_controller.dart index 0fae5b071..e18734314 100644 --- a/lib/common/controller/archiver_download_controller.dart +++ b/lib/common/controller/archiver_download_controller.dart @@ -7,7 +7,6 @@ import 'package:fehviewer/models/index.dart'; import 'package:fehviewer/pages/tab/controller/download_view_controller.dart'; import 'package:fehviewer/store/floor/dao/gallery_task_dao.dart'; import 'package:fehviewer/store/floor/dao/image_task_dao.dart'; -import 'package:fehviewer/store/floor/entity/gallery_task.dart'; import 'package:fehviewer/store/get_store.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:fehviewer/utils/toast.dart'; diff --git a/lib/common/controller/download_controller.dart b/lib/common/controller/download_controller.dart index c8f2c634b..e2cb8f57e 100644 --- a/lib/common/controller/download_controller.dart +++ b/lib/common/controller/download_controller.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:math' as math; import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; diff --git a/lib/common/exts.dart b/lib/common/exts.dart index 265f4e66f..27a26e505 100644 --- a/lib/common/exts.dart +++ b/lib/common/exts.dart @@ -5,10 +5,10 @@ import 'package:get/get.dart'; extension EhString on String { String toRealUrl() { - final DnsService dnsConfigController = Get.find(); - final bool enableDoH = dnsConfigController.enableDoH.value; - final bool enableCustomHosts = dnsConfigController.enableCustomHosts.value; - final List _dohDnsCacheList = dnsConfigController.dohCache; + final DnsService dnsService = Get.find(); + final bool enableDoH = dnsService.enableDoH; + final bool enableCustomHosts = dnsService.enableCustomHosts; + final List _dohDnsCacheList = dnsService.dohCache; final String host = Uri.parse(this).host; if (host.isEmpty) { return this; @@ -23,8 +23,8 @@ extension EhString on String { return this; } else if (enableDoH) { // logger.d(' enableDoH'); - Get.find().updateDoHCache(host); - final int _dohDnsCacheIndex = dnsConfigController.dohCache + Get.find().getDoHCache(host); + final int _dohDnsCacheIndex = dnsService.dohCache .indexWhere((DnsCache element) => element.host == host); final DnsCache? dohDnsCache = _dohDnsCacheIndex > -1 ? _dohDnsCacheList[_dohDnsCacheIndex] : null; @@ -36,6 +36,23 @@ extension EhString on String { return this; } + String get dfUrl { + final DnsService dnsService = Get.find(); + final df = dnsService.enableDomainFronting; + final String host = Uri.parse(this).host; + if (host.isEmpty) { + return this; + } + if (df) { + final realHost = dnsService.hostMapMerge[host] ?? host; + final String realUrl = replaceFirst(host, realHost); + logger.v('realUrl: $realUrl'); + return realUrl; + } + + return this; + } + String get gid { final RegExp urlRex = RegExp(r'/g/(\d+)/(\w+)/$'); final RegExpMatch? urlRult = urlRex.firstMatch(this); diff --git a/lib/common/global.dart b/lib/common/global.dart index a13f2b7e8..739e1bd05 100644 --- a/lib/common/global.dart +++ b/lib/common/global.dart @@ -1,9 +1,11 @@ import 'dart:io'; +// import 'package:cached_network_image/cached_network_image.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cookie_jar/cookie_jar.dart'; import 'package:device_info/device_info.dart'; import 'package:dio_cookie_manager/dio_cookie_manager.dart'; +import 'package:extended_image/extended_image.dart'; import 'package:fehviewer/const/const.dart'; import 'package:fehviewer/const/storages.dart'; import 'package:fehviewer/models/index.dart'; @@ -12,7 +14,6 @@ import 'package:fehviewer/network/gallery_request.dart'; import 'package:fehviewer/store/floor/database.dart'; import 'package:fehviewer/store/get_store.dart'; import 'package:fehviewer/store/hive/hive.dart'; -import 'package:fehviewer/utils/https_proxy.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:fehviewer/utils/storage.dart'; import 'package:fehviewer/utils/utility.dart'; @@ -170,7 +171,7 @@ class Global { static late PersistCookieJar cookieJar; - static HttpProxy httpProxy = HttpProxy('localhost', '$kProxyPort'); + // static HttpProxy httpProxy = HttpProxy('localhost', '$kProxyPort'); static String appSupportPath = ''; static String appDocPath = ''; @@ -224,9 +225,9 @@ class Global { } // 代理初始化 - if (Platform.isIOS || Platform.isAndroid) { - await CustomHttpsProxy.instance.init(); - } + // if (Platform.isIOS || Platform.isAndroid) { + // await CustomHttpsProxy.instance.init(); + // } logger.i('doc $appDocPath \napps $appSupportPath \ntemp $tempPath'); @@ -257,6 +258,8 @@ class Global { // 数据更新 // await dataUpdate(); + + _initImageHttpClient(); } static void creatDirs() { @@ -270,11 +273,11 @@ class Global { _initProfile(); - if ((profile.dnsConfig.enableCustomHosts ?? false) || - (profile.dnsConfig.enableDoH ?? false)) { - logger.v('${profile.dnsConfig.enableCustomHosts}'); - HttpOverrides.global = httpProxy; - } + // if ((profile.dnsConfig.enableCustomHosts ?? false) || + // (profile.dnsConfig.enableDoH ?? false)) { + // logger.v('${profile.dnsConfig.enableCustomHosts}'); + // HttpOverrides.global = httpProxy; + // } } static void _initProfile() { @@ -299,4 +302,13 @@ class Global { StorageUtil().setString(CLEAN_VER, '${EHConst.cleanDataVer}'); } } + + static void _initImageHttpClient() { + final HttpClient eClient = + ExtendedNetworkImageProvider.httpClient as HttpClient; + eClient.badCertificateCallback = + (X509Certificate cert, String host, int port) { + return true; + }; + } } diff --git a/lib/common/service/dns_service.dart b/lib/common/service/dns_service.dart index 2dff36c60..819d28b58 100644 --- a/lib/common/service/dns_service.dart +++ b/lib/common/service/dns_service.dart @@ -1,4 +1,5 @@ import 'package:fehviewer/common/global.dart'; +import 'package:fehviewer/const/const.dart'; import 'package:fehviewer/models/index.dart'; import 'package:fehviewer/utils/dns_util.dart'; import 'package:fehviewer/utils/logger.dart'; @@ -12,9 +13,21 @@ const String _regExpHost = r'^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$'; class DnsService extends ProfileService { - RxBool enableCustomHosts = false.obs; - RxBool enableDoH = false.obs; - RxBool enableDomainFronting = false.obs; + // RxBool enableCustomHosts = false.obs; + final _enableCustomHosts = false.obs; + bool get enableCustomHosts => _enableCustomHosts.value; + set enableCustomHosts(val) => _enableCustomHosts.value = val; + + // RxBool enableDoH = false.obs; + final _enableDoH = false.obs; + bool get enableDoH => _enableDoH.value; + set enableDoH(val) => _enableDoH.value = val; + + // RxBool enableDomainFronting = false.obs; + final _enableDomainFronting = false.obs; + bool get enableDomainFronting => _enableDomainFronting.value; + set enableDomainFronting(val) => _enableDomainFronting.value = val; + final RxList _hosts = [].obs; final RxList _dohCache = [].obs; @@ -22,6 +35,29 @@ class DnsService extends ProfileService { RxList get dohCache => _dohCache; + Map get hostMap { + final _map = {}; + for (final dc in hosts) { + if (dc.host != null && dc.addr != null) { + _map.putIfAbsent(dc.host!, () => dc.addr!); + } + } + return _map; + } + + Map get hostMapMerge { + final coutomHosts = hostMap; + + // 预置列表 + if (enableCustomHosts) { + for (final host in EHConst.internalHosts.entries) { + coutomHosts.putIfAbsent(host.key, () => host.value); + } + } + + return coutomHosts; + } + void removeCustomHostAt(int index) { _hosts.removeAt(index); } @@ -60,9 +96,9 @@ class DnsService extends ProfileService { } } - Future updateDoHCache(String host) async { + Future getDoHCache(String host) async { if (host == 'cloudflare-dns.com') { - return; + return null; } // 24小时 @@ -75,26 +111,29 @@ class DnsService extends ProfileService { if (dnsCache != null) { if (dnsCache.lastResolve != null && nowTime - (dnsCache.lastResolve ?? -1) > updateInterval) { - logger.wtf(' updateDoHCache $host'); + logger.d('updateDoHCache $host'); // get new and cache - final String _addr = await DnsUtil.doh(host); - // dnsCache - // ..lastResolve = nowTime - // ..addr = _addr; - dohCache[index] = dnsCache.copyWith(lastResolve: nowTime, addr: _addr); + final String? _addr = await DnsUtil.doh(host); + final _dc = dnsCache.copyWith(lastResolve: nowTime, addr: _addr); + dohCache[index] = _dc; + logger.d('rult ${_dc.toJson()}'); + return _dc; } } else { // get new - logger.wtf(' get new doh $host'); - final String _addr = await DnsUtil.doh(host); - // logger.d(' get new doh $host addr=$_addr'); - dohCache.add(DnsCache( + logger.d('get new doh $host'); + final String? _addr = await DnsUtil.doh(host); + logger.d(' get new doh $host addr=$_addr'); + final _dc = DnsCache( host: host, lastResolve: nowTime, addr: _addr, ttl: -1, addrs: [], - )); + ); + dohCache.add(_dc); + // logger.d(_dc.toJson()); + return _dc; } } @@ -102,39 +141,36 @@ class DnsService extends ProfileService { void onInit() { super.onInit(); final DnsConfig _dnsConfig = Global.profile.dnsConfig; - enableCustomHosts.value = _dnsConfig.enableCustomHosts ?? false; - _hosts(_dnsConfig.hosts); - _dohCache(_dnsConfig.dohCache); - ever(enableCustomHosts as RxInterface, (bool value) { - // _dnsConfig.enableCustomHosts = value; + enableCustomHosts = _dnsConfig.enableCustomHosts ?? false; + ever(_enableCustomHosts, (bool value) { Global.profile = Global.profile .copyWith(dnsConfig: _dnsConfig.copyWith(enableCustomHosts: value)); Global.saveProfile(); }); + + _hosts(_dnsConfig.hosts); ever>(_hosts, (List value) { - // _dnsConfig.hosts = value; Global.profile = Global.profile.copyWith(dnsConfig: _dnsConfig.copyWith(hosts: value)); Global.saveProfile(); }); - ever(enableDoH as RxInterface, (bool value) { - // _dnsConfig.enableDoH = value; + enableDoH = _dnsConfig.enableDoH ?? false; + ever(_enableDoH, (bool value) { Global.profile = Global.profile .copyWith(dnsConfig: _dnsConfig.copyWith(enableDoH: value)); Global.saveProfile(); }); - enableDoH.value = _dnsConfig.enableDoH ?? false; + + _dohCache(_dnsConfig.dohCache); everProfile>(_dohCache, (List value) { - // _dnsConfig.dohCache = value; Global.profile = Global.profile .copyWith(dnsConfig: _dnsConfig.copyWith(dohCache: value)); }); - enableDomainFronting.value = _dnsConfig.enableDomainFronting ?? false; - everProfile(enableDomainFronting as RxInterface, (bool value) { - // _dnsConfig.enableDomainFronting = value; + enableDomainFronting = _dnsConfig.enableDomainFronting ?? false; + everProfile(_enableDomainFronting, (bool value) { Global.profile = Global.profile.copyWith( dnsConfig: _dnsConfig.copyWith(enableDomainFronting: value)); }); diff --git a/lib/const/const.dart b/lib/const/const.dart index 66799aaaf..1dcae20ea 100644 --- a/lib/const/const.dart +++ b/lib/const/const.dart @@ -1,6 +1,10 @@ +import 'package:extended_image/extended_image.dart'; import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/common/service/ehconfig_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; enum ListModeEnum { @@ -63,6 +67,28 @@ class GetIds { static const String TAG_ADD_CLEAR_BTN = 'TAG_ADD_CLEAR_BTN'; } +LoadStateChanged defLoadStateChanged = (ExtendedImageState state) { + switch (state.extendedImageLoadState) { + case LoadState.loading: + return Container( + alignment: Alignment.center, + color: CupertinoDynamicColor.resolve( + CupertinoColors.systemGrey5, Get.context!), + child: const CupertinoActivityIndicator(), + ); + case LoadState.failed: + return Container( + alignment: Alignment.center, + child: const Icon( + Icons.error, + color: Colors.red, + ), + ); + default: + return null; + } +}; + // ignore: avoid_classes_with_only_static_members class EHConst { // 网页登陆页面 @@ -252,18 +278,18 @@ class EHConst { 'vietnamese': 'VI', }; - static List fontFamilyFallback = [ + static const List fontFamilyFallback = [ 'miui', // 'sans-serif', ]; - static List monoFontFamilyFallback = [ + static const List monoFontFamilyFallback = [ 'monaco', 'monospace', 'Menlo', 'Courier New', ]; - static List invList = [ + static const List invList = [ 500, 1000, 1500, @@ -285,4 +311,10 @@ class EHConst { 9500, 10000, ]; + + static const Map internalHosts = { + 'e-hentai.org': '104.20.134.21', + 'api.e-hentai.org': '37.48.89.16', + 'exhentai.org': '178.175.129.252', + }; } diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 9cee35ab4..dcfdf9be5 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -1,6 +1,7 @@ // GENERATED CODE - DO NOT MODIFY BY HAND import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; + import 'intl/messages_all.dart'; // ************************************************************************** diff --git a/lib/get_init.dart b/lib/get_init.dart index 9de730f0d..eae743f99 100644 --- a/lib/get_init.dart +++ b/lib/get_init.dart @@ -10,7 +10,6 @@ import 'common/controller/localfav_controller.dart'; import 'common/controller/quicksearch_controller.dart'; import 'common/controller/tag_trans_controller.dart'; import 'common/controller/user_controller.dart'; -import 'common/isolate_download/download_manager.dart'; import 'common/service/depth_service.dart'; import 'common/service/dns_service.dart'; import 'common/service/ehconfig_service.dart'; diff --git a/lib/utils/dio_retry/options.dart b/lib/network/dio_interceptor/dio_retry/options.dart similarity index 100% rename from lib/utils/dio_retry/options.dart rename to lib/network/dio_interceptor/dio_retry/options.dart diff --git a/lib/utils/dio_retry/retry_interceptor.dart b/lib/network/dio_interceptor/dio_retry/retry_interceptor.dart similarity index 98% rename from lib/utils/dio_retry/retry_interceptor.dart rename to lib/network/dio_interceptor/dio_retry/retry_interceptor.dart index 7fe20a8b8..e95fe5241 100644 --- a/lib/utils/dio_retry/retry_interceptor.dart +++ b/lib/network/dio_interceptor/dio_retry/retry_interceptor.dart @@ -1,6 +1,6 @@ import 'package:dio/dio.dart'; -import '../logger.dart'; +import '../../../utils/logger.dart'; import 'options.dart'; /// An interceptor that will try to send failed request again diff --git a/lib/network/dio_interceptor/domain_fronting/domain_fronting.dart b/lib/network/dio_interceptor/domain_fronting/domain_fronting.dart new file mode 100644 index 000000000..092653b85 --- /dev/null +++ b/lib/network/dio_interceptor/domain_fronting/domain_fronting.dart @@ -0,0 +1,145 @@ +import 'dart:async'; + +import 'package:dio/dio.dart'; + +typedef DomainFrontingDomainLookup = FutureOr Function( + String hostname); + +class DomainFronting { + DomainFronting({ + Map? hosts, + DomainFrontingDomainLookup? dnsLookup, + this.noIpSkip = true, + this.manual = false, + this.lookupCache = true, + }) { + _dnsLookup = dnsLookup; + // 复制字典防止缓存污染外部数据 + this.hosts = {...hosts ?? {}}; + } + + /// [hosts]: 设置固定域名解析 + /// + late Map hosts; + bool enable = true; + + /// [dnsLookup]: 设置后如果hosts中没有找到,将通过改方法解析ip + /// + late DomainFrontingDomainLookup? _dnsLookup; + + /// [lookupCache] 是否缓存[dnsLookup]返回的结果 + bool lookupCache; + + /// [noIpSkip]: 如果没有找到ip跳过DomainFronting功能,否则将抛出异常 + /// 设置为false所有请求默认启用,建议将[noIpSkip] 设置为 true + /// + bool noIpSkip; + + /// [manual] 手动模式,需要在请求的options传入[DomainFronting.auto] 或 [DomainFronting.ip] 才启用 + bool manual; + + // 用于手动模式 + static Options auto([Options? options]) { + if (options == null) { + return Options(extra: {'domainFronting': 'dns'}); + } + final extra = {...options.extra ?? {}, 'domainFronting': 'dns'}; + return options.copyWith(extra: extra); + } + + // 用于手动模式 + static Options ip(String ip, [Options? options]) { + if (options == null) { + return Options(extra: {'domainFronting': ip}); + } + final extra = {...options.extra ?? {}, 'domainFronting': ip}; + return options.copyWith(extra: extra); + } + + Future lookup(String hostname) async { + print('hostname: ${hostname}'); + if (hosts.containsKey(hostname)) { + return Future.value(hosts[hostname]); + } + if (_dnsLookup == null) return null; + final ip = await _dnsLookup!(hostname); + if (lookupCache && ip != null) hosts[hostname] = ip; + return ip; + } + + void bind(Interceptors interceptors) { + interceptors.add(DomainFrontingInterceptorRequest(this)); + interceptors.insert(0, DomainFrontingInterceptorResponse(this)); + } +} + +class DomainFrontingInterceptorResponse extends Interceptor { + DomainFrontingInterceptorResponse(this.df); + final DomainFronting df; + + @override + void onResponse( + Response e, + ResponseInterceptorHandler handler, + ) { + if ((!df.enable) || + (!e.requestOptions.extra.containsKey('domainFrontingRawOptions'))) { + handler.next(e); + return; + } + final rawOptions = e.requestOptions.extra['domainFrontingRawOptions']; + e.requestOptions = rawOptions; + handler.next(e); + } +} + +class DomainFrontingInterceptorRequest extends Interceptor { + DomainFrontingInterceptorRequest(this.df); + final DomainFronting df; + + @override + void onRequest( + RequestOptions options, RequestInterceptorHandler handler) async { + if ((!df.enable) || options.uri.scheme.toLowerCase() != 'https') { + handler.next(options); + return; + } + if (df.manual && (!options.extra.containsKey('domainFronting'))) { + handler.next(options); + return; + } + + final String domainFronting = options.extra['domainFronting'] ?? 'dns'; + final host = options.uri.host; + String? ip; + if (domainFronting != 'dns') { + ip = domainFronting; + } else { + try { + ip = await df.lookup(host); + } catch (error, stackTrace) { + final err = DioError(requestOptions: options, error: error); + err.stackTrace = stackTrace; + handler.reject(err, true); + return; + } + if (ip == null) { + if (df.noIpSkip) { + handler.next(options); + return; + } + final err = DioError( + requestOptions: options, + error: '[DomainFronting] Unable to get IP address'); + err.stackTrace = StackTrace.current; + handler.reject(err, true); + return; + } + } + final newUri = options.uri.replace(host: ip, queryParameters: {}); + final headers = {...options.headers, 'host': host}; + final extra = {...options.extra, 'domainFrontingRawOptions': options}; + handler.next(options.copyWith( + path: newUri.toString(), headers: headers, extra: extra)); + } +} diff --git a/lib/network/gallery_request.dart b/lib/network/gallery_request.dart index b79c806c4..3f2ae5a0e 100644 --- a/lib/network/gallery_request.dart +++ b/lib/network/gallery_request.dart @@ -2,7 +2,8 @@ import 'dart:convert'; import 'dart:io' as io; import 'dart:typed_data'; -import 'package:cached_network_image/cached_network_image.dart'; +import 'package:collection/collection.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; import 'package:cookie_jar/cookie_jar.dart'; import 'package:dio/dio.dart' as dio; import 'package:dio/dio.dart'; @@ -12,6 +13,7 @@ import 'package:extended_image/extended_image.dart'; import 'package:fehviewer/common/controller/advance_search_controller.dart'; import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/common/parser/eh_parser.dart'; +import 'package:fehviewer/common/service/dns_service.dart'; import 'package:fehviewer/common/service/ehconfig_service.dart'; import 'package:fehviewer/const/const.dart'; import 'package:fehviewer/generated/l10n.dart'; @@ -20,7 +22,6 @@ import 'package:fehviewer/pages/gallery/controller/archiver_controller.dart'; import 'package:fehviewer/pages/gallery/controller/torrent_controller.dart'; import 'package:fehviewer/pages/tab/controller/search_page_controller.dart'; import 'package:fehviewer/utils/dio_util.dart'; -import 'package:fehviewer/utils/https_proxy.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:fehviewer/utils/time.dart'; import 'package:fehviewer/utils/toast.dart'; @@ -36,7 +37,6 @@ import 'package:path/path.dart' as path; import 'package:permission_handler/permission_handler.dart'; import 'package:share/share.dart'; import 'package:tuple/tuple.dart'; -import 'package:collection/collection.dart'; import 'error.dart'; @@ -53,10 +53,10 @@ class Api { Api() { final String _baseUrl = EHConst.getBaseSite(Get.find().isSiteEx.value); - httpManager = HttpManager.getInstance(baseUrl: _baseUrl); + // httpManager = HttpManager.getInstance(baseUrl: _baseUrl); } - late HttpManager httpManager; + // late HttpManager httpManager; late String _baseUrl; //改为使用 PersistCookieJar,在文档中有介绍,PersistCookieJar将cookie保留在文件中, @@ -80,8 +80,13 @@ class Api { }) { final String _baseUrl = EHConst.getBaseSite(Get.find().isSiteEx.value); - return HttpManager(baseUrl ?? _baseUrl, - cache: cache, connectTimeout: connectTimeout); + final bool df = Get.find().enableDomainFronting; + return HttpManager( + baseUrl ?? _baseUrl, + cache: cache, + connectTimeout: connectTimeout, + domainFronting: df, + ); } static dio.Options getCacheOptions( @@ -130,7 +135,7 @@ class Api { _printCookie(); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final dio.Options _cacheOptions = getCacheOptions(forceRefresh: refresh); final String response = @@ -189,7 +194,7 @@ class Api { params.addAll(_searchController.advanceSearchMap); } - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final String response = await getHttpManager().get( _url, options: _cacheOptions, @@ -255,7 +260,7 @@ class Api { // logger.v(url); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final String response = await getHttpManager() .get(url, options: _cacheOptions, params: params) ?? ''; @@ -308,7 +313,7 @@ class Api { final dio.Options _cacheOptions = getCacheOptions(forceRefresh: refresh); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); String response = await getHttpManager() .get(url, options: _cacheOptions, params: params) ?? ''; @@ -381,7 +386,7 @@ class Api { // logger.d('获取画廊 $url'); time.showTime('获取画廊'); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); time.showTime('设置代理'); final String response = await getHttpManager() .get(url, options: getCacheOptions(forceRefresh: refresh)) ?? @@ -432,7 +437,7 @@ class Api { cookies.add(io.Cookie('nw', '1')); cookieJar.saveFromResponse(Uri.parse(Api.getBaseUrl()), cookies); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final String response = await getHttpManager().get( url, options: getCacheOptions(forceRefresh: refresh), @@ -451,7 +456,7 @@ class Api { // }) async { // final String url = href; // -// await CustomHttpsProxy.instance.init(); +// // await CustomHttpsProxy.instance.init(); // final String response = await getHttpManager() // .get(url, options: getCacheOptions(forceRefresh: refresh)) ?? // ''; @@ -576,7 +581,7 @@ class Api { // logger.d(reqJsonStr); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final rult = await getGalleryApi(reqJsonStr, refresh: refresh); // logger.d('$rult'); @@ -676,7 +681,7 @@ class Api { }; final String reqJsonStr = jsonEncode(reqMap); logger.d('$reqJsonStr'); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final rult = await getGalleryApi(reqJsonStr, refresh: true, cache: false); logger.d('$rult'); final Map rultMap = jsonDecode(rult.toString()); @@ -702,7 +707,7 @@ class Api { }; final String reqJsonStr = jsonEncode(reqMap); // logger.d('$reqJsonStr'); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final rult = await getGalleryApi(reqJsonStr, refresh: true, cache: false); // logger.d('$rult'); // final jsonObj = jsonDecode(rult.toString()); @@ -729,7 +734,7 @@ class Api { }; final String reqJsonStr = jsonEncode(reqMap); logger.d('$reqJsonStr'); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final rult = await getGalleryApi(reqJsonStr, refresh: true, cache: false); logger.d('$rult'); final Map rultMap = jsonDecode(rult.toString()); @@ -878,7 +883,7 @@ class Api { }) async { const String url = '/api.php'; - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final response = await getHttpManager( cache: cache, // baseUrl: EHConst.getBaseSite(), @@ -892,17 +897,17 @@ class Api { } /// 分享图片 - static Future shareImage(String imageUrl) async { - final CachedNetworkImage image = CachedNetworkImage(imageUrl: imageUrl); - final DefaultCacheManager manager = - image.cacheManager as DefaultCacheManager? ?? DefaultCacheManager(); - final Map? headers = image.httpHeaders; - final File file = await manager.getSingleFile( - image.imageUrl, - headers: headers, - ); - Share.shareFiles([file.path]); - } + // static Future shareImage(String imageUrl) async { + // final CachedNetworkImage image = CachedNetworkImage(imageUrl: imageUrl); + // final DefaultCacheManager manager = + // image.cacheManager as DefaultCacheManager? ?? DefaultCacheManager(); + // final Map? headers = image.httpHeaders; + // final File file = await manager.getSingleFile( + // image.imageUrl, + // headers: headers, + // ); + // Share.shareFiles([file.path]); + // } static Future shareImageExtended(String imageUrl) async { logger.d('imageUrl => $imageUrl'); @@ -1009,48 +1014,48 @@ class Api { return false; } - static Future _saveImage(String imageUrl, - {bool isAsset = false}) async { - try { - if (imageUrl == null) { - throw 'Save failed, picture does not exist!'; - } - - /// 保存的图片数据 - Uint8List imageBytes; - - if (isAsset == true) { - /// 保存资源图片 - final ByteData bytes = await rootBundle.load(imageUrl); - imageBytes = bytes.buffer.asUint8List(); - } else { - /// 保存网络图片 - logger.d('保存网络图片'); - final CachedNetworkImage image = CachedNetworkImage(imageUrl: imageUrl); - final DefaultCacheManager manager = - image.cacheManager as DefaultCacheManager? ?? DefaultCacheManager(); - final Map? headers = image.httpHeaders; - final File file = await manager.getSingleFile( - image.imageUrl, - headers: headers, - ); - imageBytes = await file.readAsBytes(); - } - - /// 保存图片 - final result = await ImageGallerySaver.saveImage(imageBytes); - - if (result == null || result == '') { - throw 'Save image fail'; - } - - logger.d('保存成功'); - return true; - } catch (e) { - logger.e(e.toString()); - rethrow; - } - } + // static Future _saveImage(String imageUrl, + // {bool isAsset = false}) async { + // try { + // if (imageUrl == null) { + // throw 'Save failed, picture does not exist!'; + // } + // + // /// 保存的图片数据 + // Uint8List imageBytes; + // + // if (isAsset == true) { + // /// 保存资源图片 + // final ByteData bytes = await rootBundle.load(imageUrl); + // imageBytes = bytes.buffer.asUint8List(); + // } else { + // /// 保存网络图片 + // logger.d('保存网络图片'); + // final CachedNetworkImage image = CachedNetworkImage(imageUrl: imageUrl); + // final DefaultCacheManager manager = + // image.cacheManager as DefaultCacheManager? ?? DefaultCacheManager(); + // final Map? headers = image.httpHeaders; + // final File file = await manager.getSingleFile( + // image.imageUrl, + // headers: headers, + // ); + // imageBytes = await file.readAsBytes(); + // } + // + // /// 保存图片 + // final result = await ImageGallerySaver.saveImage(imageBytes); + // + // if (result == null || result == '') { + // throw 'Save image fail'; + // } + // + // logger.d('保存成功'); + // return true; + // } catch (e) { + // logger.e(e.toString()); + // rethrow; + // } + // } static Future _saveImageExtended(String imageUrl, {bool isAsset = false}) async { @@ -1164,7 +1169,7 @@ class Api { // }) // .toOptions(); - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final dio.Response response = await Api.getHttpManager().postForm( url, options: _cacheOptinos, @@ -1211,7 +1216,7 @@ class Api { if (sourceId != null && sourceId.trim().isNotEmpty) 'nl': sourceId, }; - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); final String response = await Api.getHttpManager(connectTimeout: 5000).get( url, options: getCacheOptions( @@ -1236,7 +1241,7 @@ class Api { bool deleteOnError = true, VoidCallback? onDownloadComplete, }) async { - await CustomHttpsProxy.instance.init(); + // await CustomHttpsProxy.instance.init(); await Api.getHttpManager().downLoadFile( url, path, diff --git a/lib/pages/gallery/view/add_tags_page.dart b/lib/pages/gallery/view/add_tags_page.dart index f04c718f5..e96adecfc 100644 --- a/lib/pages/gallery/view/add_tags_page.dart +++ b/lib/pages/gallery/view/add_tags_page.dart @@ -1,14 +1,14 @@ import 'package:fehviewer/common/service/depth_service.dart'; import 'package:fehviewer/common/service/theme_service.dart'; import 'package:fehviewer/const/const.dart'; -import 'package:fehviewer/generated/l10n.dart'; import 'package:fehviewer/extension.dart'; +import 'package:fehviewer/generated/l10n.dart'; +import 'package:fehviewer/models/base/eh_models.dart'; import 'package:fehviewer/pages/gallery/controller/taginfo_controller.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:line_icons/line_icons.dart'; -import 'package:fehviewer/models/base/eh_models.dart'; const CupertinoDynamicColor _kClearButtonColor = CupertinoDynamicColor.withBrightness( diff --git a/lib/pages/gallery/view/comment_item.dart b/lib/pages/gallery/view/comment_item.dart index 5343b441f..8bfbef30e 100644 --- a/lib/pages/gallery/view/comment_item.dart +++ b/lib/pages/gallery/view/comment_item.dart @@ -1,4 +1,4 @@ -import 'package:cached_network_image/cached_network_image.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/common/service/depth_service.dart'; import 'package:fehviewer/common/service/ehconfig_service.dart'; @@ -13,6 +13,7 @@ import 'package:fehviewer/route/navigator_util.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:fehviewer/utils/vibrate.dart'; import 'package:fehviewer/widget/expandable_linkify.dart'; +import 'package:fehviewer/widget/network_extended_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart' hide SelectableText; import 'package:flutter_linkify/flutter_linkify.dart' as clif; @@ -69,10 +70,13 @@ class CommentItem extends StatelessWidget { child: Container( constraints: const BoxConstraints(maxWidth: 100, maxHeight: 140), - child: CachedNetworkImage( - httpHeaders: _httpHeaders, - imageUrl: e.imageUrl!, - placeholder: (_, __) => const CupertinoActivityIndicator(), + // child: CachedNetworkImage( + // httpHeaders: _httpHeaders, + // imageUrl: e.imageUrl!, + // placeholder: (_, __) => const CupertinoActivityIndicator(), + // ), + child: NetworkExtendedImage( + url: e.imageUrl ?? '', ), ), ), @@ -166,11 +170,14 @@ class CommentItem extends StatelessWidget { child: Container( constraints: const BoxConstraints(maxWidth: 100, maxHeight: 140), - child: CachedNetworkImage( - httpHeaders: _httpHeaders, - imageUrl: e.imageUrl!, - placeholder: (_, __) => - const CupertinoActivityIndicator(), + // child: CachedNetworkImage( + // httpHeaders: _httpHeaders, + // imageUrl: e.imageUrl!, + // placeholder: (_, __) => + // const CupertinoActivityIndicator(), + // ), + child: NetworkExtendedImage( + url: e.imageUrl ?? '', ), ), ); @@ -315,11 +322,14 @@ class CommentItem extends StatelessWidget { child: Container( constraints: const BoxConstraints(maxWidth: 100, maxHeight: 140), - child: CachedNetworkImage( - httpHeaders: _httpHeaders, - imageUrl: commentSpan.imageUrl ?? '', - placeholder: (_, __) => - const CupertinoActivityIndicator(), + // child: CachedNetworkImage( + // httpHeaders: _httpHeaders, + // imageUrl: commentSpan.imageUrl ?? '', + // placeholder: (_, __) => + // const CupertinoActivityIndicator(), + // ), + child: NetworkExtendedImage( + url: commentSpan.imageUrl ?? '', ), ), ); diff --git a/lib/pages/gallery/view/detail.dart b/lib/pages/gallery/view/detail.dart index e8e9f2ded..6c68657ce 100644 --- a/lib/pages/gallery/view/detail.dart +++ b/lib/pages/gallery/view/detail.dart @@ -1,5 +1,4 @@ import 'package:fehviewer/common/service/depth_service.dart'; -import 'package:fehviewer/common/service/ehconfig_service.dart'; import 'package:fehviewer/generated/l10n.dart'; import 'package:fehviewer/models/base/eh_models.dart'; import 'package:fehviewer/pages/gallery/controller/gallery_page_controller.dart'; diff --git a/lib/pages/gallery/view/gallery_widget.dart b/lib/pages/gallery/view/gallery_widget.dart index 6b762259e..6c9ed2247 100644 --- a/lib/pages/gallery/view/gallery_widget.dart +++ b/lib/pages/gallery/view/gallery_widget.dart @@ -1,4 +1,4 @@ -import 'package:cached_network_image/cached_network_image.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; import 'package:extended_text/extended_text.dart'; import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/common/service/depth_service.dart'; @@ -17,6 +17,7 @@ import 'package:fehviewer/route/navigator_util.dart'; import 'package:fehviewer/route/routes.dart'; // import 'package:fehviewer/utils/cust_lib/selectable_text.dart'; import 'package:fehviewer/utils/logger.dart'; +import 'package:fehviewer/widget/network_extended_image.dart'; import 'package:fehviewer/widget/rating_bar.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -42,16 +43,21 @@ class CoveTinyImage extends StatelessWidget { return Container( padding: const EdgeInsets.all(4), child: ClipRRect( - // 圆角 - borderRadius: BorderRadius.circular(4), - child: CachedNetworkImage( - httpHeaders: _httpHeaders, - width: 44, - height: 44, - fit: BoxFit.cover, - imageUrl: imgUrl, - ), - ), + // 圆角 + borderRadius: BorderRadius.circular(4), + // child: CachedNetworkImage( + // httpHeaders: _httpHeaders, + // width: 44, + // height: 44, + // fit: BoxFit.cover, + // imageUrl: imgUrl, + // ), + child: NetworkExtendedImage( + url: imgUrl, + height: 44, + width: 44, + fit: BoxFit.cover, + )), ); } } @@ -105,18 +111,22 @@ class CoverImage extends StatelessWidget { child: Container( height: imgHeight, width: imgWidth, - child: CachedNetworkImage( - placeholder: (_, __) { - return Container( - alignment: Alignment.center, - color: CupertinoDynamicColor.resolve( - CupertinoColors.systemGrey5, context), - child: const CupertinoActivityIndicator(), - ); - }, - imageUrl: imageUrl ?? '', + // child: CachedNetworkImage( + // placeholder: (_, __) { + // return Container( + // alignment: Alignment.center, + // color: CupertinoDynamicColor.resolve( + // CupertinoColors.systemGrey5, context), + // child: const CupertinoActivityIndicator(), + // ); + // }, + // imageUrl: imageUrl ?? '', + // fit: BoxFit.cover, + // httpHeaders: _httpHeaders, + // ), + child: NetworkExtendedImage( + url: imageUrl ?? '', fit: BoxFit.cover, - httpHeaders: _httpHeaders, ), ), ), @@ -526,12 +536,15 @@ class PreviewContainer extends StatelessWidget { Widget _buildImage() { if (galleryImage.isLarge ?? false) { // 缩略大图 - return CachedNetworkImage( - httpHeaders: _httpHeaders, - imageUrl: galleryImage.thumbUrl ?? '', - progressIndicatorBuilder: (_, __, ___) { - return const CupertinoActivityIndicator(); - }, + // return CachedNetworkImage( + // httpHeaders: _httpHeaders, + // imageUrl: galleryImage.thumbUrl ?? '', + // progressIndicatorBuilder: (_, __, ___) { + // return const CupertinoActivityIndicator(); + // }, + // ); + return NetworkExtendedImage( + url: galleryImage.thumbUrl ?? '', ); } else { return LayoutBuilder( diff --git a/lib/pages/gallery/view/preview_clipper.dart b/lib/pages/gallery/view/preview_clipper.dart index 6e321dabd..7e54ecc14 100644 --- a/lib/pages/gallery/view/preview_clipper.dart +++ b/lib/pages/gallery/view/preview_clipper.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'dart:ui' as ui; -import 'package:cached_network_image/cached_network_image.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; +import 'package:extended_image/extended_image.dart'; +import 'package:fehviewer/common/exts.dart'; import 'package:fehviewer/common/global.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -50,12 +52,21 @@ class PreviewImageClipper extends StatelessWidget { Future _loadPreviewImge(String imgUrl) async { final Map _httpHeaders = { 'Cookie': Global.profile.user.cookie ?? '', + 'host': Uri.parse(imgUrl).host, }; - final ImageStream imageStream = CachedNetworkImageProvider( - imgUrl, + + // final ImageStream imageStream = CachedNetworkImageProvider( + // imgUrl, + // scale: kScale, + // headers: _httpHeaders, + // ).resolve(const ImageConfiguration()); + + final ImageStream imageStream = ExtendedNetworkImageProvider( + imgUrl.dfUrl, scale: kScale, headers: _httpHeaders, ).resolve(const ImageConfiguration()); + // imageStream = // CachedNetworkImageProvider(imgUrl).resolve(ImageConfiguration()); final Completer completer = Completer(); diff --git a/lib/pages/gallery/view/taginfo_dialog.dart b/lib/pages/gallery/view/taginfo_dialog.dart index add1bafd7..dff63cf56 100644 --- a/lib/pages/gallery/view/taginfo_dialog.dart +++ b/lib/pages/gallery/view/taginfo_dialog.dart @@ -1,6 +1,5 @@ -import 'package:cached_network_image/cached_network_image.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; import 'package:fehviewer/common/controller/tag_trans_controller.dart'; -import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/common/service/depth_service.dart'; import 'package:fehviewer/const/const.dart'; import 'package:fehviewer/extension.dart'; @@ -10,6 +9,7 @@ import 'package:fehviewer/route/navigator_util.dart'; import 'package:fehviewer/store/floor/entity/tag_translat.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:fehviewer/utils/vibrate.dart'; +import 'package:fehviewer/widget/network_extended_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; @@ -212,17 +212,21 @@ class _TagDialogViewState extends State { EHConst.monoFontFamilyFallback), ), imageBuilder: (Uri uri, String? title, String? alt) { - return CachedNetworkImage( - imageUrl: uri.toString(), - httpHeaders: { - 'Cookie': Global.profile.user.cookie ?? '', - }, - placeholder: (_, __) { - return const Padding( - padding: EdgeInsets.all(8.0), - child: CupertinoActivityIndicator(), - ); - }); + // return CachedNetworkImage( + // imageUrl: uri.toString(), + // httpHeaders: { + // 'Cookie': Global.profile.user.cookie ?? '', + // }, + // placeholder: (_, __) { + // return const Padding( + // padding: EdgeInsets.all(8.0), + // child: CupertinoActivityIndicator(), + // ); + // }, + // ); + return NetworkExtendedImage( + url: uri.toString(), + ); }, ), const SizedBox(height: 12), diff --git a/lib/pages/image_view/controller/view_controller.dart b/lib/pages/image_view/controller/view_controller.dart index 8bd04ca15..2b008cba4 100644 --- a/lib/pages/image_view/controller/view_controller.dart +++ b/lib/pages/image_view/controller/view_controller.dart @@ -4,6 +4,7 @@ import 'package:fehviewer/common/controller/gallerycache_controller.dart'; import 'package:fehviewer/common/service/depth_service.dart'; import 'package:fehviewer/common/service/ehconfig_service.dart'; import 'package:fehviewer/const/const.dart'; +import 'package:fehviewer/generated/l10n.dart'; import 'package:fehviewer/models/base/eh_models.dart'; import 'package:fehviewer/pages/gallery/controller/gallery_page_controller.dart'; import 'package:fehviewer/pages/image_view/common.dart'; @@ -16,7 +17,6 @@ import 'package:flutter_statusbar_manager/flutter_statusbar_manager.dart'; import 'package:get/get.dart'; import 'package:orientation/orientation.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:fehviewer/generated/l10n.dart'; import 'view_state.dart'; diff --git a/lib/pages/image_view/controller/view_local_controller.dart b/lib/pages/image_view/controller/view_local_controller.dart index 4293571d8..aa0d0982b 100644 --- a/lib/pages/image_view/controller/view_local_controller.dart +++ b/lib/pages/image_view/controller/view_local_controller.dart @@ -1,4 +1,3 @@ import 'package:get/get.dart'; -import 'package:meta/meta.dart'; class ViewLocalController extends GetxController {} diff --git a/lib/pages/image_view/view/view_image.dart b/lib/pages/image_view/view/view_image.dart index 333404366..097a61007 100644 --- a/lib/pages/image_view/view/view_image.dart +++ b/lib/pages/image_view/view/view_image.dart @@ -1,4 +1,4 @@ -import 'package:cached_network_image/cached_network_image.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; import 'package:extended_image/extended_image.dart'; import 'package:fehviewer/common/global.dart'; @@ -103,7 +103,7 @@ class _ViewImageState extends State // 清除CachedNetworkImage的缓存 try { // CachedNetworkImage 清除指定缓存 - await CachedNetworkImage.evictFromCache(_currentImage?.imageUrl ?? ''); + // await CachedNetworkImage.evictFromCache(_currentImage?.imageUrl ?? ''); // extended_image 清除指定缓存 await clearDiskCachedImage(_currentImage?.imageUrl ?? ''); clearMemoryImageCache(); diff --git a/lib/pages/image_view/view/view_page.dart b/lib/pages/image_view/view/view_page.dart index e4648a732..fcf20bc1f 100644 --- a/lib/pages/image_view/view/view_page.dart +++ b/lib/pages/image_view/view/view_page.dart @@ -1,6 +1,7 @@ import 'package:extended_image/extended_image.dart'; import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/const/const.dart'; +import 'package:fehviewer/generated/l10n.dart'; import 'package:fehviewer/models/index.dart'; import 'package:fehviewer/pages/image_view/controller/view_state.dart'; import 'package:fehviewer/pages/image_view/view/view_image.dart'; @@ -17,7 +18,6 @@ import 'package:line_icons/line_icons.dart'; import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view_gallery.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:fehviewer/generated/l10n.dart'; import '../controller/view_controller.dart'; diff --git a/lib/pages/item/gallery_item.dart b/lib/pages/item/gallery_item.dart index de18453ca..d0c4fc16f 100644 --- a/lib/pages/item/gallery_item.dart +++ b/lib/pages/item/gallery_item.dart @@ -1,12 +1,16 @@ -import 'package:cached_network_image/cached_network_image.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; +import 'package:extended_image/extended_image.dart'; import 'package:fehviewer/common/global.dart'; +import 'package:fehviewer/common/exts.dart'; import 'package:fehviewer/common/service/ehconfig_service.dart'; +import 'package:fehviewer/const/const.dart'; import 'package:fehviewer/const/theme_colors.dart'; import 'package:fehviewer/extension.dart'; import 'package:fehviewer/models/index.dart'; import 'package:fehviewer/pages/item/controller/galleryitem_controller.dart'; import 'package:fehviewer/utils/utility.dart'; import 'package:fehviewer/widget/blur_image.dart'; +import 'package:fehviewer/widget/network_extended_image.dart'; import 'package:fehviewer/widget/rating_bar.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -447,24 +451,28 @@ class CoverImg extends StatelessWidget { final EhConfigService ehConfigService = Get.find(); final Map _httpHeaders = { 'Cookie': Global.profile.user.cookie ?? '', + 'host': Uri.parse(imgUrl).host, }; Widget image() { - if (imgUrl != null && imgUrl.isNotEmpty) { - return CachedNetworkImage( - placeholder: (_, __) { - return Container( - alignment: Alignment.center, - color: CupertinoDynamicColor.resolve( - CupertinoColors.systemGrey5, context), - child: const CupertinoActivityIndicator(), - ); - }, - // height: height, - width: width, - httpHeaders: _httpHeaders, - imageUrl: imgUrl, - fit: BoxFit.contain, + if (imgUrl.isNotEmpty) { + // return CachedNetworkImage( + // placeholder: (_, __) { + // return Container( + // alignment: Alignment.center, + // color: CupertinoDynamicColor.resolve( + // CupertinoColors.systemGrey5, context), + // child: const CupertinoActivityIndicator(), + // ); + // }, + // // height: height, + // width: width, + // httpHeaders: _httpHeaders, + // imageUrl: imgUrl.dfUrl, + // fit: BoxFit.contain, + // ); + return NetworkExtendedImage( + url: imgUrl, ); } else { return Container(); diff --git a/lib/pages/item/gallery_item_flow.dart b/lib/pages/item/gallery_item_flow.dart index c8433ae6b..f1caac1cb 100644 --- a/lib/pages/item/gallery_item_flow.dart +++ b/lib/pages/item/gallery_item_flow.dart @@ -7,7 +7,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; -import 'package:keframe/frame_separate_widget.dart'; import 'gallery_clipper.dart'; import 'gallery_item.dart'; diff --git a/lib/pages/item/gallery_item_simple.dart b/lib/pages/item/gallery_item_simple.dart index ae63f3fa6..3ec596a56 100644 --- a/lib/pages/item/gallery_item_simple.dart +++ b/lib/pages/item/gallery_item_simple.dart @@ -1,16 +1,16 @@ -import 'package:cached_network_image/cached_network_image.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/common/service/ehconfig_service.dart'; import 'package:fehviewer/const/theme_colors.dart'; import 'package:fehviewer/models/base/eh_models.dart'; import 'package:fehviewer/pages/item/controller/galleryitem_controller.dart'; import 'package:fehviewer/widget/blur_image.dart'; +import 'package:fehviewer/widget/network_extended_image.dart'; import 'package:fehviewer/widget/rating_bar.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; -import 'package:keframe/frame_separate_widget.dart'; const double kCoverImageWidth = 70.0; const double kItemWidth = 115.0; @@ -299,19 +299,25 @@ class CoverImg extends StatelessWidget { builder: (context, constraints) { return BlurImage( isBlur: _isBlur, - child: CachedNetworkImage( - placeholder: (_, __) { - return Container( - alignment: Alignment.center, - color: CupertinoDynamicColor.resolve( - CupertinoColors.systemGrey5, context), - child: const CupertinoActivityIndicator(), - ); - }, + // child: CachedNetworkImage( + // placeholder: (_, __) { + // return Container( + // alignment: Alignment.center, + // color: CupertinoDynamicColor.resolve( + // CupertinoColors.systemGrey5, context), + // child: const CupertinoActivityIndicator(), + // ); + // }, + // height: (height ?? 0) * constraints.maxWidth / (width ?? 0), + // width: constraints.maxWidth, + // httpHeaders: _httpHeaders, + // imageUrl: imgUrl, + // fit: BoxFit.fitWidth, + // ), + child: NetworkExtendedImage( + url: imgUrl, height: (height ?? 0) * constraints.maxWidth / (width ?? 0), width: constraints.maxWidth, - httpHeaders: _httpHeaders, - imageUrl: imgUrl, fit: BoxFit.fitWidth, ), ); diff --git a/lib/pages/item/user_item.dart b/lib/pages/item/user_item.dart index 7a6c8479b..50e8df78a 100644 --- a/lib/pages/item/user_item.dart +++ b/lib/pages/item/user_item.dart @@ -1,5 +1,7 @@ -import 'package:cached_network_image/cached_network_image.dart'; +// import 'package:cached_network_image/cached_network_image.dart'; +import 'package:extended_image/extended_image.dart'; import 'package:fehviewer/common/controller/user_controller.dart'; +import 'package:fehviewer/common/exts.dart'; import 'package:fehviewer/common/service/theme_service.dart'; import 'package:fehviewer/generated/l10n.dart'; import 'package:fehviewer/route/routes.dart'; @@ -68,13 +70,29 @@ class _UserItem extends State { final String _avatarUrl = _userController.user().avatarUrl ?? ''; if (_userController.isLogin && _avatarUrl.isNotEmpty) { return ClipOval( - child: CachedNetworkImage( - imageUrl: _avatarUrl, + // child: CachedNetworkImage( + // imageUrl: _avatarUrl, + // width: 55, + // height: 55, + // fit: BoxFit.cover, + // errorWidget: (_, __, ___) => _defAvatar, + // placeholder: (_, __) => _defAvatar, + // ), + child: ExtendedImage.network( + _avatarUrl.dfUrl, width: 55, height: 55, fit: BoxFit.cover, - errorWidget: (_, __, ___) => _defAvatar, - placeholder: (_, __) => _defAvatar, + loadStateChanged: (ExtendedImageState state) { + switch (state.extendedImageLoadState) { + case LoadState.loading: + return _defAvatar; + case LoadState.failed: + return _defAvatar; + default: + return null; + } + }, ), ); } else { @@ -141,13 +159,29 @@ class UserWidget extends GetView { final String _avatarUrl = controller.user().avatarUrl ?? ''; if (controller.isLogin && _avatarUrl.isNotEmpty) { return ClipOval( - child: CachedNetworkImage( - imageUrl: _avatarUrl, + // child: CachedNetworkImage( + // imageUrl: _avatarUrl, + // width: kAvatarSize, + // height: kAvatarSize, + // fit: BoxFit.cover, + // errorWidget: (_, __, ___) => _defAvatar, + // placeholder: (_, __) => _defAvatar, + // ), + child: ExtendedImage.network( + _avatarUrl.dfUrl, width: kAvatarSize, height: kAvatarSize, fit: BoxFit.cover, - errorWidget: (_, __, ___) => _defAvatar, - placeholder: (_, __) => _defAvatar, + loadStateChanged: (ExtendedImageState state) { + switch (state.extendedImageLoadState) { + case LoadState.loading: + return _defAvatar; + case LoadState.failed: + return _defAvatar; + default: + return null; + } + }, ), ); } else { diff --git a/lib/pages/login/login_cookie_page.dart b/lib/pages/login/login_cookie_page.dart index ef0f8005a..1149eac21 100644 --- a/lib/pages/login/login_cookie_page.dart +++ b/lib/pages/login/login_cookie_page.dart @@ -2,7 +2,6 @@ import 'package:fehviewer/common/controller/user_controller.dart'; import 'package:fehviewer/generated/l10n.dart'; import 'package:fehviewer/models/index.dart'; import 'package:fehviewer/network/eh_login.dart'; -import 'package:fehviewer/network/gallery_request.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:fehviewer/utils/toast.dart'; import 'package:flutter/cupertino.dart'; diff --git a/lib/pages/setting/advanced_setting_page.dart b/lib/pages/setting/advanced_setting_page.dart index bc11cdd47..0718459fb 100644 --- a/lib/pages/setting/advanced_setting_page.dart +++ b/lib/pages/setting/advanced_setting_page.dart @@ -1,7 +1,7 @@ import 'dart:io'; +import 'package:extended_image/extended_image.dart'; import 'package:fehviewer/common/controller/cache_controller.dart'; -import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/common/service/dns_service.dart'; import 'package:fehviewer/common/service/ehconfig_service.dart'; import 'package:fehviewer/common/service/locale_service.dart'; @@ -13,7 +13,6 @@ import 'package:fehviewer/pages/setting/log_page.dart'; import 'package:fehviewer/route/routes.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -43,7 +42,7 @@ class ListViewAdvancedSetting extends StatelessWidget { @override Widget build(BuildContext context) { final EhConfigService _ehConfigService = Get.find(); - final DnsService _dnsConfigController = Get.find(); + final DnsService _dnsService = Get.find(); final CacheController _cacheController = Get.find(); void _handlePureDarkChanged(bool newValue) { @@ -51,19 +50,26 @@ class ListViewAdvancedSetting extends StatelessWidget { } void _handleDoHChanged(bool newValue) { - if (!newValue && !_dnsConfigController.enableCustomHosts.value) { - /// 清除hosts 关闭代理 - logger.d(' 关闭代理'); - HttpOverrides.global = null; - } else if (newValue) { - /// 设置全局本地代理 - HttpOverrides.global = Global.httpProxy; - } - _dnsConfigController.enableDoH.value = newValue; + // if (!newValue && !_dnsService.enableCustomHosts) { + // /// 清除hosts 关闭代理 + // logger.d(' 关闭代理'); + // HttpOverrides.global = null; + // } else if (newValue) { + // /// 设置全局本地代理 + // HttpOverrides.global = Global.httpProxy; + // } + _dnsService.enableDoH = newValue; } void _handleEFChanged(bool newValue) { - _dnsConfigController.enableDomainFronting.value = newValue; + _dnsService.enableDomainFronting = newValue; + if (!newValue) return; + final HttpClient eClient = + ExtendedNetworkImageProvider.httpClient as HttpClient; + eClient.badCertificateCallback = + (X509Certificate cert, String host, int port) { + return true; + }; } final List _list = [ @@ -111,20 +117,19 @@ class ListViewAdvancedSetting extends StatelessWidget { Container(height: 38), Obx(() => SelectorSettingItem( title: S.of(context).custom_hosts, - selector: _dnsConfigController.enableCustomHosts.value + selector: _dnsService.enableCustomHosts ? S.of(context).on : S.of(context).off, onTap: () { Get.to(() => CustomHostsPage(), transition: Transition.cupertino); }, )), - if (kDebugMode) - TextSwitchItem( - S.of(context).domain_fronting, - intValue: _dnsConfigController.enableDomainFronting.value, - onChanged: _handleEFChanged, - desc: 'SNI', - ), + TextSwitchItem( + S.of(context).domain_fronting, + intValue: _dnsService.enableDomainFronting, + onChanged: _handleEFChanged, + desc: 'By pass SNI', + ), // TextSwitchItem( // 'DNS-over-HTTPS', // intValue: _dnsConfigController.enableDoH.value, diff --git a/lib/pages/setting/custom_hosts_page.dart b/lib/pages/setting/custom_hosts_page.dart index 2f93b43ed..44c1cf9f8 100644 --- a/lib/pages/setting/custom_hosts_page.dart +++ b/lib/pages/setting/custom_hosts_page.dart @@ -1,6 +1,3 @@ -import 'dart:io'; - -import 'package:fehviewer/common/global.dart'; import 'package:fehviewer/common/service/dns_service.dart'; import 'package:fehviewer/common/service/theme_service.dart'; import 'package:fehviewer/generated/l10n.dart'; @@ -19,14 +16,14 @@ class CustomHostsPage extends StatelessWidget { final DnsService dnsConfigController = Get.find(); void _handleEnableCustomHostDarkChanged(bool value) { - if (!value && !(dnsConfigController.enableDoH.value)) { - /// 关闭代理 - HttpOverrides.global = null; - } else if (value) { - /// 设置全局本地代理 - HttpOverrides.global = Global.httpProxy; - } - dnsConfigController.enableCustomHosts.value = value; + // if (!value && !(dnsConfigController.enableDoH)) { + // /// 关闭代理 + // HttpOverrides.global = null; + // } else if (value) { + // /// 设置全局本地代理 + // HttpOverrides.global = Global.httpProxy; + // } + dnsConfigController.enableCustomHosts = value; } return CupertinoPageScaffold( @@ -46,7 +43,7 @@ class CustomHostsPage extends StatelessWidget { children: [ Obx(() => TextSwitchItem( _title, - intValue: dnsConfigController.enableCustomHosts.value, + intValue: dnsConfigController.enableCustomHosts, onChanged: _handleEnableCustomHostDarkChanged, )), Container(height: 38), diff --git a/lib/pages/tab/bindings/tabhome_binding.dart b/lib/pages/tab/bindings/tabhome_binding.dart index af70c6521..2765e3ba4 100644 --- a/lib/pages/tab/bindings/tabhome_binding.dart +++ b/lib/pages/tab/bindings/tabhome_binding.dart @@ -1,11 +1,3 @@ -import 'package:fehviewer/pages/tab/controller/download_view_controller.dart'; -import 'package:fehviewer/pages/tab/controller/favorite_controller.dart'; -import 'package:fehviewer/pages/tab/controller/gallery_controller.dart'; -import 'package:fehviewer/pages/tab/controller/history_controller.dart'; -import 'package:fehviewer/pages/tab/controller/popular_controller.dart'; -import 'package:fehviewer/pages/tab/controller/setting_controller.dart'; -import 'package:fehviewer/pages/tab/controller/tabhome_controller.dart'; -import 'package:fehviewer/pages/tab/controller/watched_controller.dart'; import 'package:get/get.dart'; class TabHomeBinding extends Bindings { diff --git a/lib/pages/tab/controller/tabview_controller.dart b/lib/pages/tab/controller/tabview_controller.dart index 87feb4da2..8977274be 100644 --- a/lib/pages/tab/controller/tabview_controller.dart +++ b/lib/pages/tab/controller/tabview_controller.dart @@ -2,7 +2,6 @@ import 'package:dio/dio.dart'; import 'package:fehviewer/common/service/ehconfig_service.dart'; import 'package:fehviewer/common/service/theme_service.dart'; import 'package:fehviewer/models/index.dart'; -import 'package:fehviewer/network/gallery_request.dart'; import 'package:fehviewer/pages/tab/controller/search_page_controller.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:fehviewer/utils/toast.dart'; diff --git a/lib/pages/tab/view/search_page_new.dart b/lib/pages/tab/view/search_page_new.dart index 1ee962bcb..1d11c730e 100644 --- a/lib/pages/tab/view/search_page_new.dart +++ b/lib/pages/tab/view/search_page_new.dart @@ -17,7 +17,6 @@ import 'package:fehviewer/pages/tab/view/gallery_base.dart'; import 'package:fehviewer/pages/tab/view/tab_base.dart'; import 'package:fehviewer/utils/cust_lib/persistent_header_builder.dart'; import 'package:fehviewer/utils/cust_lib/sliver/sliver_persistent_header.dart'; -import 'package:fehviewer/utils/cust_lib/wrap/ext_wrap.dart'; import 'package:fehviewer/utils/logger.dart'; import 'package:fehviewer/utils/vibrate.dart'; import 'package:flutter/cupertino.dart'; diff --git a/lib/route/app_pages.dart b/lib/route/app_pages.dart index dc267cbae..9c1dc979a 100644 --- a/lib/route/app_pages.dart +++ b/lib/route/app_pages.dart @@ -19,7 +19,6 @@ import 'package:fehviewer/pages/setting/security_setting_page.dart'; import 'package:fehviewer/pages/setting/tab_setting.dart'; import 'package:fehviewer/pages/setting/view_setting_page.dart'; import 'package:fehviewer/pages/tab/bindings/splash_binding.dart'; -import 'package:fehviewer/pages/tab/bindings/tabhome_binding.dart'; import 'package:fehviewer/pages/tab/view/download_page.dart'; import 'package:fehviewer/pages/tab/view/favorite_page.dart'; import 'package:fehviewer/pages/tab/view/favorite_sel_page.dart'; diff --git a/lib/utils/dio_util.dart b/lib/utils/dio_util.dart index c5a11d933..db0c8a332 100644 --- a/lib/utils/dio_util.dart +++ b/lib/utils/dio_util.dart @@ -5,14 +5,16 @@ import 'package:dio/dio.dart'; import 'package:dio_firebase_performance/dio_firebase_performance.dart'; import 'package:dio_http_cache/dio_http_cache.dart'; import 'package:fehviewer/common/global.dart'; +import 'package:fehviewer/common/service/dns_service.dart'; import 'package:fehviewer/const/const.dart'; +import 'package:fehviewer/network/dio_interceptor/domain_fronting/domain_fronting.dart'; import 'package:fehviewer/utils/time.dart'; import 'package:fehviewer/utils/toast.dart'; -import 'package:flutter/foundation.dart'; +import 'package:get/get.dart' hide Response; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; -import 'dio_retry/options.dart'; -import 'dio_retry/retry_interceptor.dart'; +import '../network/dio_interceptor/dio_retry/options.dart'; +import '../network/dio_interceptor/dio_retry/retry_interceptor.dart'; import 'logger.dart'; const int kDefconnectTimeout = 10000; @@ -24,6 +26,7 @@ class HttpManager { String _baseUrl, { bool cache = true, bool retry = false, + bool domainFronting = false, int? connectTimeout = kDefconnectTimeout, }) { _options = BaseOptions( @@ -110,15 +113,37 @@ class HttpManager { ))); } - (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = - (HttpClient client) { - final HttpClient httpClient = HttpClient(); - httpClient.badCertificateCallback = - (X509Certificate cert, String host, int port) { - return true; + if (domainFronting) { + logger.d('domainFronting'); + final DnsService dnsServices = Get.find(); + final bool enableDoH = dnsServices.enableDoH; + + final coutomHosts = dnsServices.hostMapMerge; + + final domainFronting = DomainFronting( + hosts: coutomHosts, + dnsLookup: enableDoH + ? (String host) async { + final dc = await dnsServices.getDoHCache(host); + return dc?.addr ?? host; + } + : null, + ); + + // 允许证书错误的地址/ip + final hostWhiteList = coutomHosts.values.toSet(); + + (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = + (HttpClient client) { + client.badCertificateCallback = + (X509Certificate cert, String host, int port) { + return hostWhiteList.contains(host); + }; }; - return httpClient; - }; + + // 在其他插件添加完毕后再添加,以确保执行顺序正确 + domainFronting.bind(dio.interceptors); + } } //单例模式 @@ -130,11 +155,16 @@ class HttpManager { late BaseOptions _options; //单例模式,一个baseUrl只创建一次实例 - static HttpManager getInstance( - {String baseUrl = '', bool cache = true, bool retry = false}) { - final String _key = '${baseUrl}_$cache'; + static HttpManager getInstance({ + String baseUrl = '', + bool cache = true, + bool retry = false, + bool df = false, + }) { + final String _key = '${baseUrl}_${cache}_${retry}_${df}'; if (null == _instanceMap[_key]) { - _instanceMap[_key] = HttpManager(baseUrl, cache: cache, retry: retry); + _instanceMap[_key] = + HttpManager(baseUrl, cache: cache, retry: retry, domainFronting: df); } return _instanceMap[_key]!; } diff --git a/lib/utils/dns_util.dart b/lib/utils/dns_util.dart index 83f1dab9b..252aa568e 100644 --- a/lib/utils/dns_util.dart +++ b/lib/utils/dns_util.dart @@ -1,12 +1,12 @@ import 'package:basic_utils/basic_utils.dart'; class DnsUtil { - static Future doh(String host, + static Future doh(String host, {DnsApiProvider dhoResolve = DnsApiProvider.CLOUDFLARE}) async { final List? response = await DnsUtils.reverseDns(host, provider: dhoResolve); if (response == null) { - return host; + return null; } return (response..shuffle()).first.data; } diff --git a/lib/utils/eh_interceptor.dart b/lib/utils/eh_interceptor.dart index 51ea01800..a37418c10 100644 --- a/lib/utils/eh_interceptor.dart +++ b/lib/utils/eh_interceptor.dart @@ -50,10 +50,10 @@ class EHInterceptor extends Interceptor { Future _updateDoHCache(String host) async { final DnsService dnsConfigController = Get.find(); - final bool enableDoH = dnsConfigController.enableDoH.value; + final bool enableDoH = dnsConfigController.enableDoH; // 更新doh if (enableDoH) { - await dnsConfigController.updateDoHCache(host); + await dnsConfigController.getDoHCache(host); } } } diff --git a/lib/utils/https_proxy.dart b/lib/utils/https_proxy.dart index 33843998a..8cd4b2cee 100644 --- a/lib/utils/https_proxy.dart +++ b/lib/utils/https_proxy.dart @@ -81,7 +81,7 @@ class ClientConnectionHandler { Future dataHandler(dynamic data) async { // 自定义hosts final List _customHosts = dnsConfigController.hosts; - final bool enableDoH = dnsConfigController.enableDoH.value; + final bool enableDoH = dnsConfigController.enableDoH; if (server == null) { // 建立连接 @@ -94,7 +94,7 @@ class ClientConnectionHandler { // 更新doh if (enableDoH) { - await dnsConfigController.updateDoHCache(_oriHost); + await dnsConfigController.getDoHCache(_oriHost); // logger.d(' updateDoHCache end'); } @@ -116,7 +116,7 @@ class ClientConnectionHandler { final DnsCache? dohDnsCache = _dohDnsCacheIndex > -1 ? _dohDnsCacheList[_dohDnsCacheIndex] : null; - if (dnsConfigController.enableCustomHosts.value) { + if (dnsConfigController.enableCustomHosts) { realHost = customDnsCache?.addr ?? dohDnsCache?.addr ?? _oriHost; } else { realHost = dohDnsCache?.addr ?? _oriHost; diff --git a/lib/widget/network_extended_image.dart b/lib/widget/network_extended_image.dart new file mode 100644 index 000000000..e10ad1494 --- /dev/null +++ b/lib/widget/network_extended_image.dart @@ -0,0 +1,80 @@ +import 'package:extended_image/extended_image.dart'; +import 'package:fehviewer/common/exts.dart'; +import 'package:fehviewer/common/global.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class NetworkExtendedImage extends StatefulWidget { + const NetworkExtendedImage( + {Key? key, required this.url, this.height, this.width, this.fit}) + : super(key: key); + final String url; + final double? height; + final double? width; + final BoxFit? fit; + + @override + _NetworkExtendedImageState createState() => _NetworkExtendedImageState(); +} + +class _NetworkExtendedImageState extends State + with SingleTickerProviderStateMixin { + Map _httpHeaders = {}; + late AnimationController animationController; + + @override + void initState() { + super.initState(); + _httpHeaders = { + 'Cookie': Global.profile.user.cookie ?? '', + 'host': Uri.parse(widget.url).host, + }; + + animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 200), + ); + } + + @override + Widget build(BuildContext context) { + return ExtendedImage.network( + widget.url.dfUrl, + width: widget.width, + height: widget.height, + headers: _httpHeaders, + fit: widget.fit, + loadStateChanged: (ExtendedImageState state) { + switch (state.extendedImageLoadState) { + case LoadState.loading: + return Container( + alignment: Alignment.center, + color: CupertinoDynamicColor.resolve( + CupertinoColors.systemGrey5, context), + child: const CupertinoActivityIndicator(), + ); + case LoadState.completed: + animationController.forward(); + + return FadeTransition( + opacity: animationController, + child: ExtendedRawImage( + fit: BoxFit.contain, + image: state.extendedImageInfo?.image, + ), + ); + case LoadState.failed: + return Container( + alignment: Alignment.center, + child: const Icon( + Icons.error, + color: Colors.red, + ), + ); + default: + return null; + } + }, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index d2b26824c..b857fb844 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -120,13 +120,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "8.1.1" - cached_network_image: - dependency: "direct main" - description: - name: cached_network_image - url: "https://pub.flutter-io.cn" - source: hosted - version: "3.0.0" characters: dependency: transitive description: @@ -463,13 +456,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_blurhash: - dependency: transitive - description: - name: flutter_blurhash - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.6.0" flutter_cache_manager: dependency: "direct main" description: @@ -864,13 +850,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.0.0" - octo_image: - dependency: transitive - description: - name: octo_image - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.0+1" oktoast: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 71ed571e5..89601b648 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: fehviewer publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 1.1.19+235 +version: 1.1.19+236 environment: sdk: '>=2.13.0 <3.0.0' @@ -59,7 +59,7 @@ dependencies: get_storage: ^2.0.2 photo_view: ^0.11.1 # 图片相关 - cached_network_image: ^3.0.0 +# cached_network_image: ^3.0.0 extended_image: ^4.1.0 liquid_progress_indicator_ns: ^1.0.0 logger: ^1.0.0