From b9d594bc8bc33d421e0cef0fd353b55783681728 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Mon, 29 Dec 2025 21:02:05 +0800 Subject: [PATCH] tweaks Signed-off-by: bggRGjQaUbCoE --- .../widgets/image/network_img_layer.dart | 5 +- .../interactiveviewer_gallery.dart | 1 + .../widgets/loading_widget/http_error.dart | 8 +- lib/common/widgets/pendant_avatar.dart | 33 +-- lib/pages/article/view.dart | 4 +- lib/pages/article/widgets/article_ops.dart | 82 +++---- lib/pages/article/widgets/html_render.dart | 3 + lib/pages/article/widgets/opus_content.dart | 23 +- lib/pages/dynamics/widgets/author_panel.dart | 11 +- lib/pages/dynamics/widgets/blocked_item.dart | 5 +- lib/pages/dynamics/widgets/dyn_content.dart | 2 +- lib/pages/dynamics/widgets/module_panel.dart | 14 +- lib/pages/fav_create/view.dart | 19 +- lib/pages/hot/view.dart | 8 +- .../live_room/contribution_rank/view.dart | 1 + lib/pages/live_room/view.dart | 7 +- lib/pages/live_search/child/view.dart | 1 + lib/pages/login_devices/view.dart | 1 + lib/pages/main_reply/view.dart | 1 + lib/pages/member/widget/user_info_card.dart | 200 +++++++++--------- lib/pages/member_profile/view.dart | 14 +- lib/pages/search/widgets/hot_keyword.dart | 5 +- lib/pages/search_trending/view.dart | 5 +- .../video/reply/widgets/reply_item_grpc.dart | 37 ++-- .../whisper_detail/widget/chat_item.dart | 9 +- lib/utils/image_utils.dart | 11 + 26 files changed, 280 insertions(+), 230 deletions(-) diff --git a/lib/common/widgets/image/network_img_layer.dart b/lib/common/widgets/image/network_img_layer.dart index ebb276876..2088461c0 100644 --- a/lib/common/widgets/image/network_img_layer.dart +++ b/lib/common/widgets/image/network_img_layer.dart @@ -19,7 +19,6 @@ class NetworkImgLayer extends StatelessWidget { this.quality, this.semanticsLabel, this.radius, - this.imageBuilder, this.isLongPic = false, this.forceUseCacheWidth = false, this.getPlaceHolder, @@ -35,7 +34,6 @@ class NetworkImgLayer extends StatelessWidget { final int? quality; final String? semanticsLabel; final double? radius; - final ImageWidgetBuilder? imageBuilder; final bool isLongPic; final bool forceUseCacheWidth; final Widget Function()? getPlaceHolder; @@ -93,9 +91,8 @@ class NetworkImgLayer extends StatelessWidget { fadeOutDuration: fadeOutDuration ?? const Duration(milliseconds: 120), fadeInDuration: fadeInDuration ?? const Duration(milliseconds: 120), filterQuality: FilterQuality.low, - placeholder: (BuildContext context, String url) => + placeholder: (context, url) => getPlaceHolder?.call() ?? _placeholder(context, noRadius), - imageBuilder: imageBuilder, errorWidget: (context, url, error) => _placeholder(context, noRadius), colorBlendMode: reduce ? BlendMode.modulate : null, color: reduce ? reduceLuxColor : null, diff --git a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart index 0ca8661a0..d60ed3faa 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart @@ -340,6 +340,7 @@ class _InteractiveviewerGalleryState extends State fadeInDuration: Duration.zero, fadeOutDuration: Duration.zero, imageUrl: ImageUtils.thumbnailUrl(item.url, widget.quality), + placeholder: (_, _) => const SizedBox.expand(), ); }, ), diff --git a/lib/common/widgets/loading_widget/http_error.dart b/lib/common/widgets/loading_widget/http_error.dart index ddf48fb71..f657033d3 100644 --- a/lib/common/widgets/loading_widget/http_error.dart +++ b/lib/common/widgets/loading_widget/http_error.dart @@ -47,10 +47,10 @@ class HttpError extends StatelessWidget { if (onReload != null) FilledButton.tonal( onPressed: onReload, - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.colorScheme.primary.withAlpha(20), - ), + style: FilledButton.styleFrom( + tapTargetSize: .padded, + backgroundColor: theme.colorScheme.primary.withAlpha(20), + shadowColor: Colors.transparent, ), child: Text( btnText ?? '点击重试', diff --git a/lib/common/widgets/pendant_avatar.dart b/lib/common/widgets/pendant_avatar.dart index 93101c27f..14bd91fc8 100644 --- a/lib/common/widgets/pendant_avatar.dart +++ b/lib/common/widgets/pendant_avatar.dart @@ -2,10 +2,8 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/common/avatar_badge_type.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/utils/extension/string_ext.dart'; -import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; class PendantAvatar extends StatelessWidget { @@ -44,6 +42,23 @@ class PendantAvatar extends StatelessWidget { Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isMemberAvatar = size == 80; + Widget? pendant; + if (showDynDecorate && !garbPendantImage.isNullOrEmpty) { + final pendantSize = size * 1.75; + pendant = Positioned( + // -(size * 1.75 - size) / 2 + top: -0.375 * size + (size == 80 ? 2 : 0), + child: IgnorePointer( + child: NetworkImgLayer( + radius: 0, + width: pendantSize, + height: pendantSize, + src: garbPendantImage, + getPlaceHolder: () => const SizedBox.shrink(), + ), + ), + ); + } return Stack( alignment: Alignment.bottomCenter, clipBehavior: Clip.none, @@ -55,19 +70,7 @@ class PendantAvatar extends StatelessWidget { onTap: onTap, child: _buildAvatar(colorScheme, isMemberAvatar), ), - if (showDynDecorate && !garbPendantImage.isNullOrEmpty) - Positioned( - top: - -0.375 * - (size == 80 ? size - 4 : size), // -(size * 1.75 - size) / 2 - child: IgnorePointer( - child: CachedNetworkImage( - width: size * 1.75, - height: size * 1.75, - imageUrl: ImageUtils.thumbnailUrl(garbPendantImage), - ), - ), - ), + ?pendant, if (roomId != null) Positioned( bottom: 0, diff --git a/lib/pages/article/view.dart b/lib/pages/article/view.dart index 1095fbfa2..376fef444 100644 --- a/lib/pages/article/view.dart +++ b/lib/pages/article/view.dart @@ -175,7 +175,7 @@ class _ArticlePageState extends CommonDynPageState { // if (kDebugMode) debugPrint('moduleBlocked'); final moduleBlocked = controller.opusData!.modules.moduleBlocked!; content = SliverToBoxAdapter( - child: moduleBlockedItem(theme, moduleBlocked, maxWidth), + child: moduleBlockedItem(context, theme, moduleBlocked, maxWidth), ); } else if (controller.articleData?.content != null) { if (controller.articleData?.type == 3) { @@ -292,6 +292,8 @@ class _ArticlePageState extends CommonDynPageState { fadeOutDuration: const Duration( milliseconds: 120, ), + placeholder: (_, _) => + const SizedBox.shrink(), ), ), if (pic.isLongPic == true) diff --git a/lib/pages/article/widgets/article_ops.dart b/lib/pages/article/widgets/article_ops.dart index ee2b8b305..35a361a73 100644 --- a/lib/pages/article/widgets/article_ops.dart +++ b/lib/pages/article/widgets/article_ops.dart @@ -14,7 +14,7 @@ class ArticleOpus extends StatelessWidget { @override Widget build(BuildContext context) { - if ((_ops == null || _ops.isEmpty)) { + if (_ops == null || _ops.isEmpty) { return const SliverToBoxAdapter(); } @@ -23,47 +23,47 @@ class ArticleOpus extends StatelessWidget { itemBuilder: (context, index) { try { final item = _ops[index]; - if (item.insert is String) { - return SelectableText(item.insert); + switch (item.insert) { + case String e: + return SelectableText(e); + case Insert(:final card): + if (card != null) { + if (card.url?.isNotEmpty == true) { + return GestureDetector( + onTap: () { + switch (item.attributes?.clazz) { + case 'article-card card': + if (card.id != null) { + Get.toNamed( + '/articlePage', + parameters: { + 'id': card.id!.substring(2), + 'type': 'read', + }, + ); + } + case 'video-card card': + if (card.id != null) { + PiliScheme.videoPush(null, card.id); + } + case 'vote-card card': + if (card.id != null) { + showVoteDialog(context, int.parse(card.id!)); + } + } + }, + child: ClipRRect( + borderRadius: StyleString.mdRadius, + child: CachedNetworkImage( + imageUrl: ImageUtils.thumbnailUrl(card.url, 60), + placeholder: (_, _) => const SizedBox.shrink(), + ), + ), + ); + } + } } - - if (item.insert is Insert) { - InsertCard card = item.insert.card; - if (card.url?.isNotEmpty == true) { - return GestureDetector( - onTap: () { - switch (item.attributes?.clazz) { - case 'article-card card': - if (card.id != null) { - Get.toNamed( - '/articlePage', - parameters: { - 'id': card.id!.substring(2), - 'type': 'read', - }, - ); - } - case 'video-card card': - if (card.id != null) { - PiliScheme.videoPush(null, card.id); - } - case 'vote-card card': - if (card.id != null) { - showVoteDialog(context, int.parse(card.id!)); - } - } - }, - child: ClipRRect( - borderRadius: StyleString.mdRadius, - child: CachedNetworkImage( - imageUrl: ImageUtils.thumbnailUrl(card.url, 60), - ), - ), - ); - } - } - - return Text('${item.attributes}'); + return Text(item.attributes.toString()); } catch (e) { return Text(e.toString()); } diff --git a/lib/pages/article/widgets/html_render.dart b/lib/pages/article/widgets/html_render.dart index 859bd9c25..6b31d102d 100644 --- a/lib/pages/article/widgets/html_render.dart +++ b/lib/pages/article/widgets/html_render.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/models/common/image_preview_type.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -43,6 +44,7 @@ Widget htmlRender({ height: height != null ? double.parse(height) : null, imageUrl: ImageUtils.thumbnailUrl(imgUrl), fit: BoxFit.contain, + placeholder: (_, _) => const SizedBox.shrink(), ); } final size = isEmote ? 22.0 : null; @@ -56,6 +58,7 @@ Widget htmlRender({ child: CachedNetworkImage( width: size, height: size, + memCacheWidth: size?.cacheSize(context), imageUrl: ImageUtils.thumbnailUrl(imgUrl, 60), fadeInDuration: const Duration(milliseconds: 120), fadeOutDuration: const Duration(milliseconds: 120), diff --git a/lib/pages/article/widgets/opus_content.dart b/lib/pages/article/widgets/opus_content.dart index 958a43ae0..ce52c14e7 100644 --- a/lib/pages/article/widgets/opus_content.dart +++ b/lib/pages/article/widgets/opus_content.dart @@ -11,6 +11,7 @@ import 'package:PiliPlus/models/dynamics/article_content_model.dart' import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; import 'package:PiliPlus/utils/image_utils.dart'; @@ -216,7 +217,7 @@ class OpusContent extends StatelessWidget { final pic = element.pic!.pics!.first; final width = pic.width == null ? null - : math.min(maxWidth.toDouble(), pic.width!); + : math.min(maxWidth, pic.width!); final height = width == null || pic.height == null ? null : width * pic.height! / pic.width!; @@ -231,10 +232,11 @@ class OpusContent extends StatelessWidget { child: CachedNetworkImage( width: width, height: height, + memCacheWidth: width?.cacheSize(context), imageUrl: ImageUtils.thumbnailUrl(pic.url!, 60), fadeInDuration: const Duration(milliseconds: 120), fadeOutDuration: const Duration(milliseconds: 120), - placeholder: (context, url) => + placeholder: (_, _) => Image.asset('assets/images/loading.png'), ), ), @@ -255,11 +257,14 @@ class OpusContent extends StatelessWidget { ); } case 3 when (element.line != null): + final height = element.line!.pic!.height?.toDouble(); return CachedNetworkImage( width: maxWidth, fit: BoxFit.contain, - height: element.line!.pic!.height?.toDouble(), + height: height, + memCacheHeight: height?.cacheSize(context), imageUrl: ImageUtils.thumbnailUrl(element.line!.pic!.url!), + placeholder: (_, _) => const SizedBox.shrink(), ); case 5 when (element.list != null): return SelectableText.rich( @@ -688,6 +693,7 @@ class OpusContent extends StatelessWidget { } Widget moduleBlockedItem( + BuildContext context, ThemeData theme, ModuleBlocked moduleBlocked, double maxWidth, @@ -714,14 +720,17 @@ Widget moduleBlockedItem( Widget icon(double width) { return CachedNetworkImage( width: width, + memCacheWidth: width.cacheSize(context), fit: BoxFit.contain, imageUrl: ImageUtils.thumbnailUrl( isDarkMode ? moduleBlocked.icon!.imgDark : moduleBlocked.icon!.imgDay, ), + placeholder: (_, _) => const SizedBox.shrink(), ); } - Widget btn({ + Widget btn( + BuildContext context, { OutlinedBorder? shape, VisualDensity? visualDensity, EdgeInsetsGeometry? padding, @@ -749,7 +758,9 @@ Widget moduleBlockedItem( CachedNetworkImage( height: 16, color: Colors.white, - imageUrl: moduleBlocked.button!.icon!.http2https, + memCacheHeight: 16.cacheSize(context), + placeholder: (_, _) => const SizedBox.shrink(), + imageUrl: ImageUtils.safeThumbnailUrl(moduleBlocked.button!.icon), ), Text(moduleBlocked.button!.text ?? ''), ], @@ -781,6 +792,7 @@ Widget moduleBlockedItem( if (moduleBlocked.button != null) ...[ const SizedBox(height: 8), btn( + context, visualDensity: const VisualDensity(vertical: -2.5), ), ], @@ -817,6 +829,7 @@ Widget moduleBlockedItem( ), if (moduleBlocked.button != null) btn( + context, visualDensity: const VisualDensity(vertical: -3, horizontal: -4), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(6)), diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 34279011d..4dbc6f15b 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -13,9 +13,10 @@ import 'package:PiliPlus/pages/save_panel/view.dart'; import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/date_utils.dart'; import 'package:PiliPlus/utils/extension/context_ext.dart'; -import 'package:PiliPlus/utils/extension/string_ext.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; import 'package:PiliPlus/utils/feed_back.dart'; +import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -167,9 +168,13 @@ class AuthorPanel extends StatelessWidget { children: [ CachedNetworkImage( height: 32, - imageUrl: moduleAuthor.decorate!.cardUrl.http2https, + memCacheHeight: 32.cacheSize(context), + imageUrl: ImageUtils.safeThumbnailUrl( + moduleAuthor.decorate!.cardUrl, + ), + placeholder: (_, _) => const SizedBox.shrink(), ), - if (moduleAuthor.decorate?.fan?.numStr?.isNotEmpty == + if (moduleAuthor.decorate!.fan?.numStr?.isNotEmpty == true) Padding( padding: const EdgeInsets.only(right: 32), diff --git a/lib/pages/dynamics/widgets/blocked_item.dart b/lib/pages/dynamics/widgets/blocked_item.dart index 179a0b771..c777f44ca 100644 --- a/lib/pages/dynamics/widgets/blocked_item.dart +++ b/lib/pages/dynamics/widgets/blocked_item.dart @@ -3,13 +3,14 @@ import 'package:PiliPlus/pages/article/widgets/opus_content.dart' show moduleBlockedItem; import 'package:flutter/material.dart'; -Widget blockedItem({ +Widget blockedItem( + BuildContext context, { required ThemeData theme, required ModuleBlocked blocked, required double maxWidth, }) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 1), - child: moduleBlockedItem(theme, blocked, maxWidth - 26), + child: moduleBlockedItem(context, theme, blocked, maxWidth - 26), ); } diff --git a/lib/pages/dynamics/widgets/dyn_content.dart b/lib/pages/dynamics/widgets/dyn_content.dart index cafa375be..66bced1fe 100644 --- a/lib/pages/dynamics/widgets/dyn_content.dart +++ b/lib/pages/dynamics/widgets/dyn_content.dart @@ -44,6 +44,6 @@ List dynContent( floor: floor, ), if (moduleDynamic?.major?.blocked case final blocked?) - blockedItem(theme: theme, blocked: blocked, maxWidth: maxWidth), + blockedItem(context, theme: theme, blocked: blocked, maxWidth: maxWidth), ]; } diff --git a/lib/pages/dynamics/widgets/module_panel.dart b/lib/pages/dynamics/widgets/module_panel.dart index 0e88fe37b..260ccf0ae 100644 --- a/lib/pages/dynamics/widgets/module_panel.dart +++ b/lib/pages/dynamics/widgets/module_panel.dart @@ -11,7 +11,8 @@ import 'package:PiliPlus/pages/dynamics/widgets/live_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/live_panel_sub.dart'; import 'package:PiliPlus/pages/dynamics/widgets/live_rcmd_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/video_panel.dart'; -import 'package:PiliPlus/utils/extension/string_ext.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; +import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart' hide InkWell; @@ -154,13 +155,10 @@ Widget module( width: 45, height: 45, fit: BoxFit.cover, - imageUrl: item - .modules - .moduleDynamic! - .major! - .common! - .cover! - .http2https, + memCacheWidth: 45.cacheSize(context), + imageUrl: ImageUtils.safeThumbnailUrl( + item.modules.moduleDynamic!.major!.common!.cover, + ), ), ), Expanded( diff --git a/lib/pages/fav_create/view.dart b/lib/pages/fav_create/view.dart index 62b0b4c34..4ddfd6c57 100644 --- a/lib/pages/fav_create/view.dart +++ b/lib/pages/fav_create/view.dart @@ -1,5 +1,6 @@ import 'dart:io' show File; +import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart'; import 'package:PiliPlus/http/fav.dart'; import 'package:PiliPlus/http/loading_state.dart'; @@ -7,9 +8,7 @@ import 'package:PiliPlus/http/msg.dart'; import 'package:PiliPlus/utils/extension/file_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; import 'package:PiliPlus/utils/fav_utils.dart'; -import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; @@ -181,6 +180,7 @@ class _CreateFavPageState extends State { Builder( builder: (context) { return ListTile( + visualDensity: .standard, tileColor: theme.colorScheme.onInverseSurface, onTap: () { EasyThrottle.throttle( @@ -242,16 +242,11 @@ class _CreateFavPageState extends State { if (_cover?.isNotEmpty == true) Padding( padding: const EdgeInsets.symmetric(vertical: 5), - child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - child: CachedNetworkImage( - imageUrl: ImageUtils.thumbnailUrl(_cover!), - height: 55, - width: 88, - fit: BoxFit.cover, - ), + child: NetworkImgLayer( + src: _cover, + height: 55, + width: 88, + radius: 6, ), ), Icon( diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index d37512501..8fcf73d9b 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart'; +import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart'; import 'package:PiliPlus/common/widgets/view_safe_area.dart'; @@ -10,8 +11,6 @@ import 'package:PiliPlus/pages/home/controller.dart'; import 'package:PiliPlus/pages/hot/controller.dart'; import 'package:PiliPlus/pages/rank/view.dart'; import 'package:PiliPlus/utils/grid.dart'; -import 'package:PiliPlus/utils/image_utils.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -41,10 +40,11 @@ class _HotPageState extends CommonPageState child: Column( mainAxisSize: MainAxisSize.min, children: [ - CachedNetworkImage( + NetworkImgLayer( width: 35, height: 35, - imageUrl: ImageUtils.thumbnailUrl(iconUrl), + radius: 0, + src: iconUrl, ), const SizedBox(height: 4), Text( diff --git a/lib/pages/live_room/contribution_rank/view.dart b/lib/pages/live_room/contribution_rank/view.dart index 289d5f36c..4a7282f81 100644 --- a/lib/pages/live_room/contribution_rank/view.dart +++ b/lib/pages/live_room/contribution_rank/view.dart @@ -126,6 +126,7 @@ class _ContributionRankTypeState extends State<_ContributionRankType> onRefresh: _controller.onRefresh, child: CustomScrollView( controller: _controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), slivers: [ ViewSliverSafeArea( sliver: Obx( diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 36df36b84..7072f7e6c 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -26,9 +26,10 @@ import 'package:PiliPlus/plugin/pl_player/utils/danmaku_options.dart'; import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; import 'package:PiliPlus/plugin/pl_player/view.dart'; import 'package:PiliPlus/services/service_locator.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/size_ext.dart'; -import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; +import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -392,7 +393,9 @@ class _LiveRoomPageState extends State fit: BoxFit.cover, width: maxWidth, height: maxHeight, - imageUrl: appBackground.http2https, + memCacheWidth: maxWidth.cacheSize(context), + imageUrl: ImageUtils.safeThumbnailUrl(appBackground), + placeholder: (_, _) => const SizedBox.shrink(), ); } else { child = Image.asset( diff --git a/lib/pages/live_search/child/view.dart b/lib/pages/live_search/child/view.dart index 2d26b8eb9..232a9ed8c 100644 --- a/lib/pages/live_search/child/view.dart +++ b/lib/pages/live_search/child/view.dart @@ -39,6 +39,7 @@ class _LiveSearchChildPageState extends State onRefresh: _controller.onRefresh, child: CustomScrollView( controller: _controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverPadding( padding: EdgeInsets.only( diff --git a/lib/pages/login_devices/view.dart b/lib/pages/login_devices/view.dart index b982c1d8e..1c130f119 100644 --- a/lib/pages/login_devices/view.dart +++ b/lib/pages/login_devices/view.dart @@ -28,6 +28,7 @@ class LoginDevicesPageState extends State { body: refreshIndicator( onRefresh: _controller.onRefresh, child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), slivers: [ ViewSliverSafeArea( sliver: Obx( diff --git a/lib/pages/main_reply/view.dart b/lib/pages/main_reply/view.dart index f8399b795..7504e555a 100644 --- a/lib/pages/main_reply/view.dart +++ b/lib/pages/main_reply/view.dart @@ -76,6 +76,7 @@ class _MainReplyPageState extends State { right: padding.right, ), child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), slivers: [ buildReplyHeader(colorScheme), Obx( diff --git a/lib/pages/member/widget/user_info_card.dart b/lib/pages/member/widget/user_info_card.dart index 98a6ca8e4..2b2b53713 100644 --- a/lib/pages/member/widget/user_info_card.dart +++ b/lib/pages/member/widget/user_info_card.dart @@ -15,6 +15,7 @@ import 'package:PiliPlus/pages/follow_type/followed/view.dart'; import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/extension/context_ext.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; import 'package:PiliPlus/utils/image_utils.dart'; @@ -49,12 +50,13 @@ class UserInfoCard extends StatelessWidget { Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isLight = colorScheme.isLight; - final isPortrait = context.width < 600; + final width = context.width; + final isPortrait = width < 600; return ViewSafeArea( top: !isPortrait, child: isPortrait - ? _buildV(context, colorScheme, isLight) - : _buildH(colorScheme, isLight), + ? _buildV(context, colorScheme, isLight, width) + : _buildH(context, colorScheme, isLight), ); } @@ -107,7 +109,12 @@ class UserInfoCard extends StatelessWidget { ); } - Widget _buildHeader(ColorScheme colorScheme, bool isLight) { + Widget _buildHeader( + BuildContext context, + ColorScheme colorScheme, + bool isLight, + double width, + ) { String imgUrl = (isLight ? images.imgUrl @@ -120,21 +127,14 @@ class UserInfoCard extends StatelessWidget { child: Hero( tag: imgUrl, child: CachedNetworkImage( - imageUrl: ImageUtils.thumbnailUrl(imgUrl), - width: double.infinity, + fit: .cover, height: 135, - imageBuilder: (context, imageProvider) => DecoratedBox( - decoration: BoxDecoration( - image: DecorationImage( - image: imageProvider, - fit: BoxFit.cover, - colorFilter: ColorFilter.mode( - isLight ? const Color(0x5DFFFFFF) : const Color(0x8D000000), - isLight ? BlendMode.lighten : BlendMode.darken, - ), - ), - ), - ), + width: width, + memCacheWidth: width.cacheSize(context), + imageUrl: ImageUtils.thumbnailUrl(imgUrl), + placeholder: (_, _) => const SizedBox.shrink(), + color: isLight ? const Color(0x5DFFFFFF) : const Color(0x8D000000), + colorBlendMode: isLight ? BlendMode.lighten : BlendMode.darken, ), ), ); @@ -461,47 +461,52 @@ class UserInfoCard extends StatelessWidget { ), ); - Column _buildV(BuildContext context, ColorScheme colorScheme, bool isLight) => - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + Column _buildV( + BuildContext context, + ColorScheme colorScheme, + bool isLight, + double width, + ) => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + clipBehavior: Clip.none, children: [ - Stack( - clipBehavior: Clip.none, + Column( + crossAxisAlignment: .stretch, + mainAxisSize: MainAxisSize.min, children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildHeader(colorScheme, isLight), - SizedBox( - width: double.infinity, - height: MediaQuery.textScalerOf(context).scale(30) + 60, - ), - ], - ), - Positioned( - top: 110, - left: 20, - child: _buildAvatar, - ), - Positioned( - left: 160, - top: 140, - right: 15, - bottom: 0, - child: _buildRight(colorScheme), + _buildHeader(context, colorScheme, isLight, width), + SizedBox( + height: MediaQuery.textScalerOf(context).scale(30) + 60, ), ], ), - const SizedBox(height: 5), - ..._buildLeft(colorScheme, isLight), - if (card.prInfo?.content?.isNotEmpty == true) - buildPrInfo(colorScheme, isLight, card.prInfo!), - const SizedBox(height: 5), + Positioned( + top: 110, + left: 20, + child: _buildAvatar, + ), + Positioned( + left: 160, + top: 140, + right: 15, + bottom: 0, + child: _buildRight(colorScheme), + ), ], - ); + ), + const SizedBox(height: 5), + ..._buildLeft(colorScheme, isLight), + if (card.prInfo?.content?.isNotEmpty == true) + buildPrInfo(context, colorScheme, isLight, card.prInfo!), + const SizedBox(height: 5), + ], + ); Widget buildPrInfo( + BuildContext context, ColorScheme colorScheme, bool isLight, SpacePrInfo prInfo, @@ -509,22 +514,24 @@ class UserInfoCard extends StatelessWidget { final textColor = Utils.parseColor( isLight ? prInfo.textColor : prInfo.textColorNight, ); + String? icon = !isLight && prInfo.iconNight?.isNotEmpty == true + ? prInfo.iconNight + : prInfo.icon?.isNotEmpty == true + ? prInfo.icon + : null; + Widget child = Container( margin: const EdgeInsets.only(top: 8), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), color: Utils.parseColor(isLight ? prInfo.bgColor : prInfo.bgColorNight), child: Row( children: [ - if (!isLight && prInfo.iconNight?.isNotEmpty == true) ...[ + if (icon != null) ...[ CachedNetworkImage( - imageUrl: ImageUtils.thumbnailUrl(card.prInfo!.iconNight!), - height: 20, - ), - const SizedBox(width: 16), - ] else if (prInfo.icon?.isNotEmpty == true) ...[ - CachedNetworkImage( - imageUrl: ImageUtils.thumbnailUrl(card.prInfo!.icon!), height: 20, + memCacheHeight: 20.cacheSize(context), + imageUrl: ImageUtils.thumbnailUrl(icon), + placeholder: (_, _) => const SizedBox.shrink(), ), const SizedBox(width: 16), ], @@ -553,46 +560,47 @@ class UserInfoCard extends StatelessWidget { return child; } - Column _buildH(ColorScheme colorScheme, bool isLight) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // _buildHeader(context), - const SizedBox(height: 56), - Row( + Column _buildH(BuildContext context, ColorScheme colorScheme, bool isLight) => + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(width: 20), - Padding( - padding: EdgeInsets.only( - top: 10, - bottom: card.prInfo?.content?.isNotEmpty == true ? 0 : 10, - ), - child: _buildAvatar, + // _buildHeader(context), + const SizedBox(height: 56), + Row( + children: [ + const SizedBox(width: 20), + Padding( + padding: EdgeInsets.only( + top: 10, + bottom: card.prInfo?.content?.isNotEmpty == true ? 0 : 10, + ), + child: _buildAvatar, + ), + const SizedBox(width: 10), + Expanded( + flex: 5, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + ..._buildLeft(colorScheme, isLight), + const SizedBox(height: 5), + ], + ), + ), + Expanded( + flex: 3, + child: _buildRight(colorScheme), + ), + const SizedBox(width: 20), + ], ), - const SizedBox(width: 10), - Expanded( - flex: 5, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 10), - ..._buildLeft(colorScheme, isLight), - const SizedBox(height: 5), - ], - ), - ), - Expanded( - flex: 3, - child: _buildRight(colorScheme), - ), - const SizedBox(width: 20), + if (card.prInfo?.content?.isNotEmpty == true) + buildPrInfo(context, colorScheme, isLight, card.prInfo!), ], - ), - if (card.prInfo?.content?.isNotEmpty == true) - buildPrInfo(colorScheme, isLight, card.prInfo!), - ], - ); + ); Widget _buildFollowedUp( ColorScheme colorScheme, diff --git a/lib/pages/member_profile/view.dart b/lib/pages/member_profile/view.dart index db7b84b27..7a3f7a146 100644 --- a/lib/pages/member_profile/view.dart +++ b/lib/pages/member_profile/view.dart @@ -1,6 +1,7 @@ import 'dart:io' show File; import 'package:PiliPlus/common/constants.dart'; +import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/init.dart'; @@ -16,13 +17,11 @@ import 'package:PiliPlus/utils/date_utils.dart'; import 'package:PiliPlus/utils/extension/file_ext.dart'; import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; -import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; @@ -141,12 +140,11 @@ class _EditProfilePageState extends State { title: '头像', widget: Padding( padding: const EdgeInsets.symmetric(vertical: 5), - child: ClipOval( - child: CachedNetworkImage( - width: 55, - height: 55, - imageUrl: ImageUtils.thumbnailUrl(response.face), - ), + child: NetworkImgLayer( + width: 55, + height: 55, + type: .avatar, + src: response.face, ), ), onTap: () => EasyThrottle.throttle( diff --git a/lib/pages/search/widgets/hot_keyword.dart b/lib/pages/search/widgets/hot_keyword.dart index 3d1c3f59a..b00c9c74a 100644 --- a/lib/pages/search/widgets/hot_keyword.dart +++ b/lib/pages/search/widgets/hot_keyword.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/models_new/search/search_trending/list.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/image_utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -55,8 +56,10 @@ class HotKeyword extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 4), child: CachedNetworkImage( - imageUrl: ImageUtils.thumbnailUrl(i.icon!), height: 15, + memCacheHeight: 15.cacheSize(context), + imageUrl: ImageUtils.thumbnailUrl(i.icon!), + placeholder: (_, _) => const SizedBox.shrink(), ), ) else if (i.showLiveIcon == true) diff --git a/lib/pages/search_trending/view.dart b/lib/pages/search_trending/view.dart index 62897b399..c9c707065 100644 --- a/lib/pages/search_trending/view.dart +++ b/lib/pages/search_trending/view.dart @@ -9,6 +9,7 @@ import 'package:PiliPlus/models_new/search/search_trending/list.dart'; import 'package:PiliPlus/pages/search_trending/controller.dart'; import 'package:PiliPlus/utils/extension/context_ext.dart'; import 'package:PiliPlus/utils/extension/get_ext.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/size_ext.dart'; import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -200,8 +201,10 @@ class _SearchTrendingPageState extends State { if (item.icon?.isNotEmpty == true) ...[ const SizedBox(width: 4), CachedNetworkImage( - imageUrl: ImageUtils.thumbnailUrl(item.icon!), height: 16, + memCacheHeight: 16.cacheSize(context), + imageUrl: ImageUtils.thumbnailUrl(item.icon!), + placeholder: (_, _) => const SizedBox.shrink(), ), ] else if (item.showLiveIcon == true) ...[ const SizedBox(width: 4), diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index 5b211b798..5587f61e5 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -21,7 +21,7 @@ import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/date_utils.dart'; import 'package:PiliPlus/utils/duration_utils.dart'; import 'package:PiliPlus/utils/extension/context_ext.dart'; -import 'package:PiliPlus/utils/extension/string_ext.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/image_utils.dart'; @@ -136,23 +136,25 @@ class ReplyItemGrpc extends StatelessWidget { children: [ CachedNetworkImage( height: 38, - imageUrl: replyItem.member.garbCardImage.http2https, + memCacheHeight: 38.cacheSize(context), + imageUrl: ImageUtils.safeThumbnailUrl( + replyItem.member.garbCardImage, + ), + placeholder: (_, _) => const SizedBox.shrink(), ), if (replyItem.member.hasGarbCardNumber()) Text( 'NO.\n${replyItem.member.garbCardNumber}', - style: replyItem.member.garbCardFanColor.startsWith('#') - ? TextStyle( - fontSize: 8, - fontFamily: 'digital_id_num', - color: Color( - int.parse( - replyItem.member.garbCardFanColor - .replaceFirst('#', '0xFF'), - ), - ), - ) - : null, + style: TextStyle( + fontSize: 8, + fontFamily: 'digital_id_num', + color: + replyItem.member.garbCardFanColor.startsWith('#') + ? Utils.parseColor( + replyItem.member.garbCardFanColor, + ) + : null, + ), ), ], ), @@ -607,12 +609,11 @@ class ReplyItemGrpc extends StatelessWidget { if (!isCv && url.hasPrefixIcon()) WidgetSpan( child: CachedNetworkImage( - imageUrl: ImageUtils.thumbnailUrl(url.prefixIcon), height: 19, + memCacheHeight: 19.cacheSize(context), color: theme.colorScheme.primary, - placeholder: (context, url) { - return const SizedBox.shrink(); - }, + imageUrl: ImageUtils.thumbnailUrl(url.prefixIcon), + placeholder: (_, _) => const SizedBox.shrink(), ), ), TextSpan( diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index c2cbc28b1..d1ff6b4c0 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -757,12 +757,13 @@ class ChatItem extends StatelessWidget { Widget msgTypePictureCard_13(dynamic content) { final url = content['jump_url']; - return ClipRRect( - borderRadius: StyleString.mdRadius, - child: GestureDetector( - onTap: url == null ? null : () => PiliScheme.routePushFromUrl(url), + return GestureDetector( + onTap: url == null ? null : () => PiliScheme.routePushFromUrl(url), + child: ClipRRect( + borderRadius: StyleString.mdRadius, child: CachedNetworkImage( imageUrl: ImageUtils.thumbnailUrl(content['pic_url']), + placeholder: (_, _) => const SizedBox.shrink(), ), ), ).constraintWidth(constraints: const BoxConstraints(maxWidth: 400.0)); diff --git a/lib/utils/image_utils.dart b/lib/utils/image_utils.dart index 2c4e513b3..8539583da 100644 --- a/lib/utils/image_utils.dart +++ b/lib/utils/image_utils.dart @@ -264,6 +264,17 @@ abstract final class ImageUtils { } } + static final _suffixRegex = RegExp( + r'\.(jpg|jpeg|png|webp|gif|avif)$', + caseSensitive: false, + ); + static String safeThumbnailUrl(String? src) { + if (src != null && _suffixRegex.hasMatch(src)) { + return thumbnailUrl(src); + } + return src.http2https; + } + static final _thumbRegex = RegExp( r'(@(\d+[a-z]_?)*)(\..*)?$', caseSensitive: false,