From 422b41377866ad188f2649ad76afff01bd7d2db7 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Fri, 15 Aug 2025 09:52:25 +0800 Subject: [PATCH] opt ui opt req Signed-off-by: bggRGjQaUbCoE --- lib/common/widgets/appbar/appbar.dart | 2 +- lib/common/widgets/select_mask.dart | 8 +- lib/http/api.dart | 4 +- lib/http/danmaku.dart | 8 +- lib/http/danmaku_block.dart | 7 +- lib/http/fav.dart | 6 +- lib/http/live.dart | 4 +- lib/http/member.dart | 37 +- lib/http/msg.dart | 33 +- lib/http/reply.dart | 11 +- lib/http/user.dart | 24 +- lib/http/video.dart | 30 +- lib/pages/article/controller.dart | 17 - lib/pages/article/view.dart | 204 ++++++----- lib/pages/article/widgets/opus_content.dart | 221 ++++++------ lib/pages/common/multi_select/base.dart | 14 +- .../dynamics/widgets/live_panel_sub.dart | 2 +- .../dynamics/widgets/live_rcmd_panel.dart | 2 +- lib/pages/dynamics_tab/view.dart | 56 +-- lib/pages/dynamics_topic/view.dart | 21 +- lib/pages/episode_panel/view.dart | 82 ++--- lib/pages/fav/article/view.dart | 3 +- lib/pages/fav/note/child_view.dart | 185 +++++----- lib/pages/fav/note/controller.dart | 9 +- lib/pages/fav/note/widget/item.dart | 60 +--- lib/pages/fav/pgc/child_view.dart | 209 +++++------ lib/pages/fav/pgc/controller.dart | 11 +- lib/pages/fav/pgc/widget/item.dart | 71 +--- lib/pages/fav/video/view.dart | 3 +- lib/pages/fav_create/view.dart | 24 +- lib/pages/fav_detail/view.dart | 2 +- lib/pages/history/view.dart | 3 +- lib/pages/hot/view.dart | 3 +- lib/pages/member_coin_arc/view.dart | 2 +- lib/pages/member_dynamics/view.dart | 21 +- lib/pages/member_favorite/widget/item.dart | 49 ++- lib/pages/member_home/widgets/fav_item.dart | 15 +- lib/pages/member_like_arc/view.dart | 2 +- lib/pages/member_search/child/view.dart | 21 +- lib/pages/mine/widgets/item.dart | 18 +- lib/pages/rank/zone/view.dart | 3 +- lib/pages/rcmd/view.dart | 8 +- .../video/introduction/pgc/controller.dart | 11 +- lib/pages/video/introduction/pgc/view.dart | 2 +- .../video/introduction/ugc/controller.dart | 8 +- lib/pages/video/medialist/view.dart | 332 +++++++++--------- lib/pages/video/pay_coins/view.dart | 24 +- lib/pages/video/related/view.dart | 3 +- lib/pages/video/view_point/view.dart | 120 ++++--- lib/utils/request_utils.dart | 2 +- lib/utils/waterfall.dart | 49 ++- 51 files changed, 923 insertions(+), 1143 deletions(-) diff --git a/lib/common/widgets/appbar/appbar.dart b/lib/common/widgets/appbar/appbar.dart index f8c28dcec..c640647f7 100644 --- a/lib/common/widgets/appbar/appbar.dart +++ b/lib/common/widgets/appbar/appbar.dart @@ -33,7 +33,7 @@ class MultiSelectAppBarWidget extends StatelessWidget style: TextButton.styleFrom( visualDensity: VisualDensity.compact, ), - onPressed: () => ctr.handleSelect(true), + onPressed: () => ctr.handleSelect(checked: true), child: const Text('全选'), ), ...?children, diff --git a/lib/common/widgets/select_mask.dart b/lib/common/widgets/select_mask.dart index 4755b5db6..cf71ff032 100644 --- a/lib/common/widgets/select_mask.dart +++ b/lib/common/widgets/select_mask.dart @@ -1,14 +1,18 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:flutter/material.dart'; -Widget selectMask(ThemeData theme, bool checked) { +Widget selectMask( + ThemeData theme, + bool checked, { + BorderRadiusGeometry borderRadius = StyleString.mdRadius, +}) { return AnimatedOpacity( opacity: checked ? 1 : 0, duration: const Duration(milliseconds: 200), child: Container( alignment: Alignment.center, decoration: BoxDecoration( - borderRadius: StyleString.mdRadius, + borderRadius: borderRadius, color: Colors.black.withValues(alpha: 0.6), ), child: AnimatedScale( diff --git a/lib/http/api.dart b/lib/http/api.dart index 5aa3aeb65..6aafea006 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -134,9 +134,9 @@ class Api { // aid num 稿件avid 必要(可选) avid与bvid任选一个 // bvid str 稿件bvid 必要(可选) avid与bvid任选一个 // csrf str CSRF Token(位于cookie) 必要 - static const String oneThree = '/x/web-interface/archive/like/triple'; + static const String ugcTriple = '/x/web-interface/archive/like/triple'; - static const String triple = '/pgc/season/episode/like/triple'; + static const String pgcTriple = '/pgc/season/episode/like/triple'; // 获取指定用户创建的所有收藏夹信息 // 该接口也能查询目标内容id存在于那些收藏夹中 diff --git a/lib/http/danmaku.dart b/lib/http/danmaku.dart index 2681e9661..7c174d057 100644 --- a/lib/http/danmaku.dart +++ b/lib/http/danmaku.dart @@ -27,7 +27,7 @@ class DanmakuHttp { // assert(aid != null || bvid != null); // assert(csrf != null || access_key != null); // 构建参数对象 - var params = { + var data = { 'type': type, 'oid': oid, 'msg': msg, @@ -47,10 +47,8 @@ class DanmakuHttp { var response = await Request().post( Api.shootDanmaku, - data: params, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + data: data, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (response.statusCode != 200) { return { diff --git a/lib/http/danmaku_block.dart b/lib/http/danmaku_block.dart index 59fb71ce5..583368311 100644 --- a/lib/http/danmaku_block.dart +++ b/lib/http/danmaku_block.dart @@ -2,6 +2,7 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/models/user/danmaku_block.dart'; import 'package:PiliPlus/utils/accounts.dart'; +import 'package:dio/dio.dart'; class DanmakuFilterHttp { static Future danmakuFilter() async { @@ -22,10 +23,11 @@ class DanmakuFilterHttp { static Future danmakuFilterDel({required int ids}) async { var res = await Request().post( Api.danmakuFilterDel, - queryParameters: { + data: { 'ids': ids, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -43,11 +45,12 @@ class DanmakuFilterHttp { }) async { var res = await Request().post( Api.danmakuFilterAdd, - queryParameters: { + data: { 'type': type, 'filter': filter, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return { diff --git a/lib/http/fav.dart b/lib/http/fav.dart index ea00dc323..dd682b102 100644 --- a/lib/http/fav.dart +++ b/lib/http/fav.dart @@ -103,18 +103,20 @@ class FavHttp { var res = type == 11 ? await Request().post( Api.unfavFolder, - queryParameters: { + data: { 'media_id': id, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ) : await Request().post( Api.unfavSeason, - queryParameters: { + data: { 'platform': 'web', 'season_id': id, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; diff --git a/lib/http/live.dart b/lib/http/live.dart index 386143072..5164f56bb 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -420,9 +420,7 @@ class LiveHttp { var res = await Request().post( Api.setLiveFavTag, data: data, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { diff --git a/lib/http/member.dart b/lib/http/member.dart index 805ae4b66..f23efca1c 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -28,7 +28,6 @@ import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; class MemberHttp { static Future reportMember( @@ -38,16 +37,14 @@ class MemberHttp { }) async { var res = await Request().post( HttpString.spaceBaseUrl + Api.reportMember, - data: FormData.fromMap( - { - 'mid': mid, - 'reason': reason, - 'reason_v2': ?reasonV2, - 'csrf': Accounts.main.csrf, - }, - ), + data: { + 'mid': mid, + 'reason': reason, + 'reason_v2': ?reasonV2, + 'csrf': Accounts.main.csrf, + }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); - debugPrint('report: ${res.data}'); return { 'status': res.data['status'], 'msg': res.data['message'] ?? res.data['data'], @@ -510,9 +507,7 @@ class MemberHttp { 'fid': fid, 'csrf': Accounts.main.csrf, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -538,9 +533,7 @@ class MemberHttp { 'csrf': Accounts.main.csrf, // 'cross_domain': true }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true, 'msg': '操作成功'}; @@ -591,9 +584,7 @@ class MemberHttp { 'tag': tagName, 'csrf': Accounts.main.csrf, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -614,9 +605,7 @@ class MemberHttp { 'name': name, 'csrf': Accounts.main.csrf, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -636,9 +625,7 @@ class MemberHttp { 'tagid': tagid, 'csrf': Accounts.main.csrf, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; diff --git a/lib/http/msg.dart b/lib/http/msg.dart index e03825083..1ea4dbaab 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -187,16 +187,14 @@ class MsgHttp { String? biz, CancelToken? cancelToken, }) async { - final file = await MultipartFile.fromFile(path); - Map data = { - 'file_up': file, - 'category': ?category, - 'biz': ?biz, - 'csrf': Accounts.main.csrf, - }; var res = await Request().post( Api.uploadBfs, - data: FormData.fromMap(data), + data: FormData.fromMap({ + 'file_up': await MultipartFile.fromFile(path), + 'category': ?category, + 'biz': ?biz, + 'csrf': Accounts.main.csrf, + }), cancelToken: cancelToken, ); if (res.data['code'] == 0) { @@ -226,7 +224,8 @@ class MsgHttp { }); var res = await Request().post( HttpString.tUrl + Api.createTextDynamic, - data: FormData.fromMap(data), + data: data, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -272,7 +271,8 @@ class MsgHttp { }); var res = await Request().post( HttpString.tUrl + Api.removeMsg, - data: FormData.fromMap(data), + data: data, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -296,9 +296,7 @@ class MsgHttp { 'csrf_token': csrf, 'csrf': csrf, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -354,7 +352,8 @@ class MsgHttp { }); var res = await Request().post( HttpString.tUrl + Api.setTop, - data: FormData.fromMap(data), + data: data, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -407,7 +406,7 @@ class MsgHttp { }) async { String csrf = Accounts.main.csrf; final devId = getDevId(); - Map base = { + Map data = { 'msg': { 'sender_uid': senderUid, 'receiver_id': receiverId, @@ -425,7 +424,7 @@ class MsgHttp { 'csrf_token': csrf, 'csrf': csrf, }; - Map params = await WbiSign.makSign(base); + Map params = await WbiSign.makSign(data); var res = await Request().post( Api.sendMsg, queryParameters: { @@ -435,7 +434,7 @@ class MsgHttp { 'w_rid': params['w_rid'], 'wts': params['wts'], }, - data: base, + data: data, options: Options( contentType: Headers.formUrlEncodedContentType, ), diff --git a/lib/http/reply.dart b/lib/http/reply.dart index 2e4e5ffcb..9a6cbc129 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -196,9 +196,7 @@ class ReplyHttp { 'action': action, 'csrf': Accounts.main.csrf, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; @@ -216,13 +214,14 @@ class ReplyHttp { }) async { var res = await Request().post( Api.likeReply, - queryParameters: { + data: { 'type': type, 'oid': oid, 'rpid': rpid, 'action': action, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; @@ -263,9 +262,7 @@ class ReplyHttp { 'action': isUpTop ? 0 : 1, 'csrf': Accounts.main.csrf, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; diff --git a/lib/http/user.dart b/lib/http/user.dart index f8c3138b4..002375470 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -106,12 +106,15 @@ class UserHttp { account ??= Accounts.history; var res = await Request().post( Api.pauseHistory, - queryParameters: { + data: { 'switch': switchStatus, 'jsonp': 'jsonp', 'csrf': account.csrf, }, - options: Options(extra: {'account': account}), + options: Options( + extra: {'account': account}, + contentType: Headers.formUrlEncodedContentType, + ), ); return res; } @@ -134,11 +137,14 @@ class UserHttp { account ??= Accounts.history; var res = await Request().post( Api.clearHistory, - queryParameters: { + data: { 'jsonp': 'jsonp', 'csrf': account.csrf, }, - options: Options(extra: {'account': account}), + options: Options( + extra: {'account': account}, + contentType: Headers.formUrlEncodedContentType, + ), ); return res; } @@ -147,11 +153,12 @@ class UserHttp { static Future toViewLater({String? bvid, dynamic aid}) async { var res = await Request().post( Api.toViewLater, - queryParameters: { + data: { 'aid': ?aid, 'bvid': ?bvid, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true, 'msg': 'yeah!稍后再看'}; @@ -201,10 +208,11 @@ class UserHttp { static Future toViewClear([int? cleanType]) async { var res = await Request().post( Api.toViewClear, - queryParameters: { + data: { 'clean_type': ?cleanType, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true, 'msg': '操作完成'}; @@ -369,9 +377,7 @@ class UserHttp { "reason_type": reasonType, "reason_desc": reasonType == 0 ? reasonDesc : null, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + options: Options(contentType: Headers.formUrlEncodedContentType), ); return res.data as Map; } diff --git a/lib/http/video.dart b/lib/http/video.dart index b7a4c3157..d38c8ca59 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -360,9 +360,9 @@ class VideoHttp { } // 一键三连 pgc - static Future triple({dynamic epId, required dynamic seasonId}) async { + static Future pgcTriple({dynamic epId, required dynamic seasonId}) async { var res = await Request().post( - Api.triple, + Api.pgcTriple, data: { 'ep_id': epId, 'csrf': Accounts.main.csrf, @@ -384,9 +384,9 @@ class VideoHttp { } // 一键三连 - static Future oneThree({required String bvid}) async { + static Future ugcTriple({required String bvid}) async { var res = await Request().post( - Api.oneThree, + Api.ugcTriple, data: { 'aid': IdUtils.bv2av(bvid), 'eab_x': 2, @@ -394,6 +394,8 @@ class VideoHttp { 'source': 'web_normal', 'ga': 1, 'csrf': Accounts.main.csrf, + 'spmid': '333.788.0.0', + 'statistics': '{"appId":100,"platform":5}', }, options: Options( contentType: Headers.formUrlEncodedContentType, @@ -529,7 +531,7 @@ class VideoHttp { if (message == '') { return {'status': false, 'msg': '请输入评论内容'}; } - Map data = { + final data = { 'type': type, 'oid': oid, if (root != null && root != 0) 'root': root, @@ -560,12 +562,13 @@ class VideoHttp { }) async { var res = await Request().post( Api.replyDel, - queryParameters: { + data: { 'type': type, //type.index 'oid': oid, 'rpid': rpid, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return {'status': true}; @@ -637,11 +640,12 @@ class VideoHttp { }) async { await Request().post( Api.historyReport, - queryParameters: { + data: { 'aid': ?aid, 'type': ?type, 'csrf': Accounts.heartbeat.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); } @@ -659,7 +663,7 @@ class VideoHttp { final isPugv = videoType == VideoType.pugv; await Request().post( Api.heartBeat, - queryParameters: { + data: { if (isPugv) 'aid': ?aid else 'bvid': ?bvid, 'cid': cid, 'epid': ?epid, @@ -669,6 +673,7 @@ class VideoHttp { 'played_time': progress, 'csrf': Accounts.heartbeat.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); } @@ -679,12 +684,13 @@ class VideoHttp { }) async { await Request().post( Api.mediaListHistory, - queryParameters: { + data: { 'desc': desc, 'oid': oid, 'upper_mid': upperMid, 'csrf': Accounts.heartbeat.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); } @@ -692,10 +698,11 @@ class VideoHttp { static Future pgcAdd({int? seasonId}) async { var res = await Request().post( Api.pgcAdd, - queryParameters: { + data: { 'season_id': seasonId, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return { @@ -718,10 +725,11 @@ class VideoHttp { static Future pgcDel({int? seasonId}) async { var res = await Request().post( Api.pgcDel, - queryParameters: { + data: { 'season_id': seasonId, 'csrf': Accounts.main.csrf, }, + options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { return { diff --git a/lib/pages/article/controller.dart b/lib/pages/article/controller.dart index 6802795d5..4d9d8aff8 100644 --- a/lib/pages/article/controller.dart +++ b/lib/pages/article/controller.dart @@ -16,7 +16,6 @@ import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/url_utils.dart'; -import 'package:flutter/rendering.dart' show ScrollDirection; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -227,22 +226,6 @@ class ArticleController extends CommonDynController { SmartDialog.showToast(res['msg']); } } - - @override - void listener() { - showTitle.value = scrollController.positions.last.pixels >= 45; - final ScrollDirection direction1 = - scrollController.positions.first.userScrollDirection; - late final ScrollDirection direction2 = - scrollController.positions.last.userScrollDirection; - if (direction1 == ScrollDirection.forward || - direction2 == ScrollDirection.forward) { - showFab(); - } else if (direction1 == ScrollDirection.reverse || - direction2 == ScrollDirection.reverse) { - hideFab(); - } - } } class Summary { diff --git a/lib/pages/article/view.dart b/lib/pages/article/view.dart index d810e0b51..cddb12d7a 100644 --- a/lib/pages/article/view.dart +++ b/lib/pages/article/view.dart @@ -81,112 +81,7 @@ class _ArticlePageState extends CommonDynPageState { child: Stack( clipBehavior: Clip.none, children: [ - SafeArea( - top: false, - bottom: false, - child: Builder( - builder: (context) { - double padding = max( - context.width / 2 - Grid.smallCardWidth, - 0, - ); - if (isPortrait) { - return LayoutBuilder( - builder: (context, constraints) { - final maxWidth = - constraints.maxWidth - 2 * padding - 24; - return Padding( - padding: EdgeInsets.symmetric(horizontal: padding), - child: CustomScrollView( - controller: controller.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - _buildContent(theme, maxWidth), - SliverToBoxAdapter( - child: Divider( - thickness: 8, - color: theme.dividerColor.withValues( - alpha: 0.05, - ), - ), - ), - buildReplyHeader(theme), - Obx( - () => _buildReplyList( - theme, - controller.loadingState.value, - ), - ), - ], - ), - ); - }, - ); - } else { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: controller.ratio[0].toInt(), - child: LayoutBuilder( - builder: (context, constraints) { - final maxWidth = - constraints.maxWidth - padding / 4 - 24; - return CustomScrollView( - controller: controller.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - left: padding / 4, - bottom: - MediaQuery.paddingOf(context).bottom + - 80, - ), - sliver: _buildContent(theme, maxWidth), - ), - ], - ); - }, - ), - ), - VerticalDivider( - thickness: 8, - color: theme.dividerColor.withValues(alpha: 0.05), - ), - Expanded( - flex: controller.ratio[1].toInt(), - child: Scaffold( - key: scaffoldKey, - backgroundColor: Colors.transparent, - body: refreshIndicator( - onRefresh: controller.onRefresh, - child: Padding( - padding: EdgeInsets.only(right: padding / 4), - child: CustomScrollView( - controller: controller.scrollController, - physics: - const AlwaysScrollableScrollPhysics(), - slivers: [ - buildReplyHeader(theme), - Obx( - () => _buildReplyList( - theme, - controller.loadingState.value, - ), - ), - ], - ), - ), - ), - ), - ), - ], - ); - } - }, - ), - ), + _buildPage(theme, isPortrait), _buildBottom(theme), ], ), @@ -194,6 +89,103 @@ class _ArticlePageState extends CommonDynPageState { ); } + Widget _buildPage(ThemeData theme, bool isPortrait) { + return LayoutBuilder( + builder: (context, constraints) { + double padding = max( + context.width / 2 - Grid.smallCardWidth, + 0, + ); + + if (isPortrait) { + final maxWidth = constraints.maxWidth - 2 * padding - 24; + return Padding( + padding: EdgeInsets.symmetric(horizontal: padding), + child: CustomScrollView( + controller: controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + _buildContent(theme, maxWidth), + SliverToBoxAdapter( + child: Divider( + thickness: 8, + color: theme.dividerColor.withValues( + alpha: 0.05, + ), + ), + ), + buildReplyHeader(theme), + Obx( + () => _buildReplyList( + theme, + controller.loadingState.value, + ), + ), + ], + ), + ); + } + + padding = padding / 4; + final flex = controller.ratio[0].toInt(); + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: flex, + child: CustomScrollView( + controller: controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + left: padding, + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), + sliver: _buildContent( + theme, + constraints.maxWidth * flex - padding - 24, + ), + ), + ], + ), + ), + VerticalDivider( + thickness: 8, + color: theme.dividerColor.withValues(alpha: 0.05), + ), + Expanded( + flex: controller.ratio[1].toInt(), + child: Scaffold( + key: scaffoldKey, + backgroundColor: Colors.transparent, + body: refreshIndicator( + onRefresh: controller.onRefresh, + child: Padding( + padding: EdgeInsets.only(right: padding), + child: CustomScrollView( + controller: controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + buildReplyHeader(theme), + Obx( + () => _buildReplyList( + theme, + controller.loadingState.value, + ), + ), + ], + ), + ), + ), + ), + ), + ], + ); + }, + ); + } + Widget _buildContent(ThemeData theme, double maxWidth) => SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), sliver: Obx( diff --git a/lib/pages/article/widgets/opus_content.dart b/lib/pages/article/widgets/opus_content.dart index 72716f4aa..a884362ea 100644 --- a/lib/pages/article/widgets/opus_content.dart +++ b/lib/pages/article/widgets/opus_content.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/image_view.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; @@ -73,75 +72,71 @@ class OpusContent extends StatelessWidget { switch (element.paraType) { case 1 || 4: final isQuote = element.paraType == 4; - Widget widget = SelectionArea( - child: Text.rich( - textAlign: element.align == 1 ? TextAlign.center : null, - TextSpan( - children: element.text?.nodes?.map((item) { - switch (item.type) { - case 'TEXT_NODE_TYPE_RICH' when (item.rich != null): - Rich rich = item.rich!; - switch (rich.type) { - case 'RICH_TEXT_NODE_TYPE_EMOJI': - Emoji emoji = rich.emoji!; - final size = 20.0 * emoji.size; - return WidgetSpan( - child: NetworkImgLayer( - width: size, - height: size, - src: emoji.url, - type: ImageType.emote, - ), - ); - default: - return TextSpan( - text: - '${rich.type == 'RICH_TEXT_NODE_TYPE_WEB' ? '\u{1F517}' : ''}${item.rich!.text}', - style: _getStyle( - rich.style, - rich.type == 'RICH_TEXT_NODE_TYPE_TEXT' - ? null - : colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - switch (rich.type) { - case 'RICH_TEXT_NODE_TYPE_AT': - Get.toNamed('/member?mid=${rich.rid}'); - // case 'RICH_TEXT_NODE_TYPE_TOPIC': - default: - if (rich.jumpUrl != null) { - PiliScheme.routePushFromUrl( - rich.jumpUrl!, - ); - } - } - }, - ); - } - case 'TEXT_NODE_TYPE_FORMULA' - when (item.formula != null): - return WidgetSpan( - child: CachedNetworkSVGImage( - height: 65, - 'https://api.bilibili.com/x/web-frontend/mathjax/tex?formula=${Uri.encodeComponent(item.formula!.latexContent!)}', - colorFilter: ColorFilter.mode( - colorScheme.onSurfaceVariant, - BlendMode.srcIn, + Widget widget = SelectableText.rich( + textAlign: element.align == 1 ? TextAlign.center : null, + TextSpan( + children: element.text?.nodes?.map((item) { + switch (item.type) { + case 'TEXT_NODE_TYPE_RICH' when (item.rich != null): + Rich rich = item.rich!; + switch (rich.type) { + case 'RICH_TEXT_NODE_TYPE_EMOJI': + Emoji emoji = rich.emoji!; + final size = 20.0 * emoji.size; + return WidgetSpan( + child: NetworkImgLayer( + width: size, + height: size, + src: emoji.url, + type: ImageType.emote, ), - alignment: Alignment.centerLeft, - placeholderBuilder: (_) => - const SizedBox.shrink(), + ); + default: + return TextSpan( + text: + '${rich.type == 'RICH_TEXT_NODE_TYPE_WEB' ? '\u{1F517}' : ''}${item.rich!.text}', + style: _getStyle( + rich.style, + rich.type == 'RICH_TEXT_NODE_TYPE_TEXT' + ? null + : colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + switch (rich.type) { + case 'RICH_TEXT_NODE_TYPE_AT': + Get.toNamed('/member?mid=${rich.rid}'); + // case 'RICH_TEXT_NODE_TYPE_TOPIC': + default: + if (rich.jumpUrl != null) { + PiliScheme.routePushFromUrl( + rich.jumpUrl!, + ); + } + } + }, + ); + } + case 'TEXT_NODE_TYPE_FORMULA' when (item.formula != null): + return WidgetSpan( + child: CachedNetworkSVGImage( + height: 65, + 'https://api.bilibili.com/x/web-frontend/mathjax/tex?formula=${Uri.encodeComponent(item.formula!.latexContent!)}', + colorFilter: ColorFilter.mode( + colorScheme.onSurfaceVariant, + BlendMode.srcIn, ), - ); - default: - return _getSpan( - item.word, - isQuote ? colorScheme.onSurfaceVariant : null, - ); - } - }).toList(), - ), + alignment: Alignment.centerLeft, + placeholderBuilder: (_) => const SizedBox.shrink(), + ), + ); + default: + return _getSpan( + item.word, + isQuote ? colorScheme.onSurfaceVariant : null, + ); + } + }).toList(), ), ); if (isQuote) { @@ -223,25 +218,23 @@ class OpusContent extends StatelessWidget { imageUrl: ImageUtil.thumbnailUrl(element.line!.pic!.url!), ); case 5 when (element.list != null): - return SelectionArea( - child: Text.rich( - TextSpan( - children: element.list!.items?.indexed.map((entry) { - return TextSpan( - children: [ - const WidgetSpan( - child: Icon(MdiIcons.circleMedium), - alignment: PlaceholderAlignment.middle, - ), - ...entry.$2.nodes!.map((item) { - return _getSpan(item.word); - }), - if (entry.$1 < element.list!.items!.length - 1) - const TextSpan(text: '\n'), - ], - ); - }).toList(), - ), + return SelectableText.rich( + TextSpan( + children: element.list!.items?.indexed.map((entry) { + return TextSpan( + children: [ + const WidgetSpan( + child: Icon(MdiIcons.circleMedium), + alignment: PlaceholderAlignment.middle, + ), + ...entry.$2.nodes!.map((item) { + return _getSpan(item.word); + }), + if (entry.$1 < element.list!.items!.length - 1) + const TextSpan(text: '\n'), + ], + ); + }).toList(), ), ); case 6: @@ -292,7 +285,7 @@ class OpusContent extends StatelessWidget { children: [ NetworkImgLayer( radius: 6, - width: 65 * StyleString.aspectRatio, + width: 104, height: 65, src: element.linkCard!.card!.ugc!.cover, ), @@ -331,7 +324,7 @@ class OpusContent extends StatelessWidget { children: [ NetworkImgLayer( radius: 6, - width: 65 * StyleString.aspectRatio, + width: 104, height: 65, src: element.linkCard!.card!.common!.cover, ), @@ -368,7 +361,7 @@ class OpusContent extends StatelessWidget { children: [ NetworkImgLayer( radius: 6, - width: 65 * StyleString.aspectRatio, + width: 104, height: 65, src: element.linkCard!.card!.live!.cover, ), @@ -405,7 +398,7 @@ class OpusContent extends StatelessWidget { children: [ NetworkImgLayer( radius: 6, - width: 65 * StyleString.aspectRatio, + width: 104, height: 65, src: element.linkCard!.card!.opus!.cover, ), @@ -466,7 +459,7 @@ class OpusContent extends StatelessWidget { children: [ NetworkImgLayer( radius: 6, - width: 65 * StyleString.aspectRatio, + width: 104, height: 65, src: element.linkCard!.card!.music!.cover, ), @@ -504,7 +497,7 @@ class OpusContent extends StatelessWidget { children: [ NetworkImgLayer( radius: 6, - width: 65 * StyleString.aspectRatio, + width: 104, height: 65, src: e.cover, ), @@ -570,41 +563,35 @@ class OpusContent extends StatelessWidget { color: colorScheme.onInverseSurface, ), width: double.infinity, - child: SelectionArea(child: Text.rich(renderer.span!)), + child: SelectableText.rich(renderer.span!), ); default: if (kDebugMode) debugPrint('unknown type ${element.paraType}'); if (element.text?.nodes?.isNotEmpty == true) { - return SelectionArea( - child: Text.rich( - textAlign: element.align == 1 ? TextAlign.center : null, - TextSpan( - children: element.text!.nodes! - .map((item) => _getSpan(item.word)) - .toList(), - ), + return SelectableText.rich( + textAlign: element.align == 1 ? TextAlign.center : null, + TextSpan( + children: element.text!.nodes! + .map((item) => _getSpan(item.word)) + .toList(), ), ); } - return SelectionArea( - child: Text( - '不支持的类型 (${element.paraType})', - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.red, - ), + return SelectableText( + '不支持的类型 (${element.paraType})', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red, ), ); } } catch (e) { - return SelectionArea( - child: Text( - '错误的类型 $e', - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.red, - ), + return SelectableText( + '错误的类型 $e', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red, ), ); } @@ -676,7 +663,7 @@ Widget moduleBlockedItem( CachedNetworkImage( height: 16, color: Colors.white, - imageUrl: moduleBlocked.button!.icon!, + imageUrl: moduleBlocked.button!.icon!.http2https, ), Text(moduleBlocked.button!.text ?? ''), ], diff --git a/lib/pages/common/multi_select/base.dart b/lib/pages/common/multi_select/base.dart index e9ecca8fd..2d76b839f 100644 --- a/lib/pages/common/multi_select/base.dart +++ b/lib/pages/common/multi_select/base.dart @@ -12,8 +12,8 @@ abstract class MultiSelectBase { int get checkedCount; - void onSelect(T item, [bool disableSelect = true]); - void handleSelect([bool checked = false, bool disableSelect = true]); + void onSelect(T item); + void handleSelect({bool checked = false, bool disableSelect = true}); void onRemove(); } @@ -34,7 +34,7 @@ mixin CommonMultiSelectMixin loadingState.value.data!.where((v) => v.checked == true); @override - void onSelect(T item, [bool disableSelect = true]) { + void onSelect(T item) { List list = loadingState.value.data!; item.checked = !(item.checked ?? false); if (item.checked!) { @@ -43,17 +43,15 @@ mixin CommonMultiSelectMixin rxCount.value--; } loadingState.refresh(); - if (disableSelect) { - if (checkedCount == 0) { - enableMultiSelect.value = false; - } + if (checkedCount == 0) { + enableMultiSelect.value = false; } else { allSelected.value = checkedCount == list.length; } } @override - void handleSelect([bool checked = false, bool disableSelect = true]) { + void handleSelect({bool checked = false, bool disableSelect = true}) { if (loadingState.value.isSuccess) { final list = loadingState.value.data; if (list?.isNotEmpty == true) { diff --git a/lib/pages/dynamics/widgets/live_panel_sub.dart b/lib/pages/dynamics/widgets/live_panel_sub.dart index acc373677..d90ac844a 100644 --- a/lib/pages/dynamics/widgets/live_panel_sub.dart +++ b/lib/pages/dynamics/widgets/live_panel_sub.dart @@ -14,7 +14,7 @@ Widget livePanelSub( int floor = 1, required double maxWidth, }) { - maxWidth -= StyleString.safeSpace * 2; + maxWidth -= 24; SubscriptionNew? subItem = item.modules.moduleDynamic!.major?.subscriptionNew; LivePlayInfo? content = subItem?.liveRcmd?.content?.livePlayInfo; if (subItem == null || content == null) { diff --git a/lib/pages/dynamics/widgets/live_rcmd_panel.dart b/lib/pages/dynamics/widgets/live_rcmd_panel.dart index 8de0c3d3d..62e1db3e8 100644 --- a/lib/pages/dynamics/widgets/live_rcmd_panel.dart +++ b/lib/pages/dynamics/widgets/live_rcmd_panel.dart @@ -14,7 +14,7 @@ Widget liveRcmdPanel( int floor = 1, required double maxWidth, }) { - maxWidth -= StyleString.safeSpace * 2; + maxWidth -= 24; DynamicLiveModel? liveRcmd = item.modules.moduleDynamic?.major?.liveRcmd; if (liveRcmd == null) { return const SizedBox.shrink(); diff --git a/lib/pages/dynamics_tab/view.dart b/lib/pages/dynamics_tab/view.dart index 2915b01a6..baae7b24c 100644 --- a/lib/pages/dynamics_tab/view.dart +++ b/lib/pages/dynamics_tab/view.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'package:PiliPlus/common/constants.dart'; -import 'package:PiliPlus/common/skeleton/dynamic_card.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/loading_state.dart'; @@ -28,46 +26,11 @@ class DynamicsTabPage extends CommonPage { @override State createState() => _DynamicsTabPageState(); - - static Widget dynSkeleton(bool dynamicsWaterfallFlow) { - if (!dynamicsWaterfallFlow) { - return SliverCrossAxisGroup( - slivers: [ - const SliverFillRemaining(), - SliverConstrainedCrossAxis( - maxExtent: Grid.smallCardWidth * 2, - sliver: SliverList.builder( - itemBuilder: (context, index) { - return const DynamicCardSkeleton(); - }, - itemCount: 10, - ), - ), - const SliverFillRemaining(), - ], - ); - } - return SliverGrid( - gridDelegate: SliverGridDelegateWithExtentAndRatio( - crossAxisSpacing: StyleString.cardSpace / 2, - mainAxisSpacing: StyleString.cardSpace / 2, - maxCrossAxisExtent: Grid.smallCardWidth * 2, - childAspectRatio: StyleString.aspectRatio, - mainAxisExtent: 50, - ), - delegate: SliverChildBuilderDelegate( - (context, index) { - return const DynamicCardSkeleton(); - }, - childCount: 10, - ), - ); - } } class _DynamicsTabPageState extends CommonPageState - with AutomaticKeepAliveClientMixin { + with AutomaticKeepAliveClientMixin, DynMixin { StreamSubscription? _listener; late final MainController _mainController = Get.find(); @@ -135,23 +98,14 @@ class _DynamicsTabPageState ); } - late double _maxWidth; - Widget _buildBody(LoadingState?> loadingState) { return switch (loadingState) { - Loading() => DynamicsTabPage.dynSkeleton( - GlobalData().dynamicsWaterfallFlow, - ), + Loading() => dynSkeleton, Success(:var response) => response?.isNotEmpty == true ? GlobalData().dynamicsWaterfallFlow ? SliverWaterfallFlow( - gridDelegate: - SliverWaterfallFlowDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - crossAxisSpacing: StyleString.cardSpace / 2, - callback: (value) => _maxWidth = value, - ), + gridDelegate: gridDelegate, delegate: SliverChildBuilderDelegate( (_, index) { if (index == response.length - 1) { @@ -162,7 +116,7 @@ class _DynamicsTabPageState onRemove: (idStr) => controller.onRemove(index, idStr), onBlock: () => controller.onBlock(index), - maxWidth: _maxWidth, + maxWidth: maxWidth, ); }, childCount: response!.length, @@ -184,7 +138,7 @@ class _DynamicsTabPageState onRemove: (idStr) => controller.onRemove(index, idStr), onBlock: () => controller.onBlock(index), - maxWidth: _maxWidth, + maxWidth: maxWidth, ); }, itemCount: response!.length, diff --git a/lib/pages/dynamics_topic/view.dart b/lib/pages/dynamics_topic/view.dart index 829e6561e..321d6b6f8 100644 --- a/lib/pages/dynamics_topic/view.dart +++ b/lib/pages/dynamics_topic/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/custom_icon.dart'; import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart'; import 'package:PiliPlus/common/widgets/dynamic_sliver_appbar_medium.dart'; @@ -12,7 +11,6 @@ import 'package:PiliPlus/models_new/dynamic/dyn_topic_feed/item.dart'; import 'package:PiliPlus/models_new/dynamic/dyn_topic_top/top_details.dart'; import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; import 'package:PiliPlus/pages/dynamics_create/view.dart'; -import 'package:PiliPlus/pages/dynamics_tab/view.dart'; import 'package:PiliPlus/pages/dynamics_topic/controller.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/grid.dart'; @@ -35,7 +33,7 @@ class DynTopicPage extends StatefulWidget { State createState() => _DynTopicPageState(); } -class _DynTopicPageState extends State { +class _DynTopicPageState extends State with DynMixin { final DynTopicController _controller = Get.put( DynTopicController(), tag: Utils.generateRandomString(8), @@ -346,23 +344,14 @@ class _DynTopicPageState extends State { }; } - late double _maxWidth; - Widget _buildBody(LoadingState?> loadingState) { return switch (loadingState) { - Loading() => DynamicsTabPage.dynSkeleton( - GlobalData().dynamicsWaterfallFlow, - ), + Loading() => dynSkeleton, Success(:var response) => response?.isNotEmpty == true ? GlobalData().dynamicsWaterfallFlow ? SliverWaterfallFlow( - gridDelegate: - SliverWaterfallFlowDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - crossAxisSpacing: StyleString.cardSpace / 2, - callback: (value) => _maxWidth = value, - ), + gridDelegate: gridDelegate, delegate: SliverChildBuilderDelegate( (_, index) { if (index == response.length - 1) { @@ -373,7 +362,7 @@ class _DynTopicPageState extends State { if (item.dynamicCardItem != null) { return DynamicPanel( item: item.dynamicCardItem!, - maxWidth: _maxWidth, + maxWidth: maxWidth, ); } @@ -396,7 +385,7 @@ class _DynTopicPageState extends State { if (item.dynamicCardItem != null) { return DynamicPanel( item: item.dynamicCardItem!, - maxWidth: _maxWidth, + maxWidth: maxWidth, ); } else { return Text(item.topicType ?? 'err'); diff --git a/lib/pages/episode_panel/view.dart b/lib/pages/episode_panel/view.dart index 729a05e08..5de77d300 100644 --- a/lib/pages/episode_panel/view.dart +++ b/lib/pages/episode_panel/view.dart @@ -246,6 +246,7 @@ class _EpisodePanelState extends CommonCollapseSlidePageState { } Widget _buildBody(ThemeData theme, int tabIndex, episodes) { + final isCurrTab = tabIndex == widget.initialTabIndex; return KeepAliveWrapper( builder: (context) => ScrollablePositionedList.separated( padding: EdgeInsets.only( @@ -254,7 +255,7 @@ class _EpisodePanelState extends CommonCollapseSlidePageState { ), reverse: _isReversed[tabIndex], itemCount: episodes.length, - initialScrollIndex: _currentItemIndex, + initialScrollIndex: isCurrTab ? _currentItemIndex : 0, physics: const AlwaysScrollableScrollPhysics(), itemBuilder: (BuildContext context, int itemIndex) { final episode = episodes[itemIndex]; @@ -263,9 +264,7 @@ class _EpisodePanelState extends CommonCollapseSlidePageState { episode: episode, index: itemIndex, length: episodes.length, - isCurrentIndex: tabIndex == widget.initialTabIndex - ? itemIndex == _currentItemIndex - : false, + isCurrentIndex: isCurrTab ? itemIndex == _currentItemIndex : false, ); return widget.type == EpisodeType.season && widget.showTitle && @@ -394,47 +393,40 @@ class _EpisodePanelState extends CommonCollapseSlidePageState { spacing: 10, children: [ if (cover?.isNotEmpty == true) - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder( - builder: (context, boxConstraints) { - return Stack( - clipBehavior: Clip.none, - children: [ - NetworkImgLayer( - src: cover, - width: boxConstraints.maxWidth, - height: boxConstraints.maxHeight, - ), - if (duration != null && duration > 0) - PBadge( - text: DurationUtil.formatDuration(duration), - right: 6.0, - bottom: 6.0, - type: PBadgeType.gray, - ), - if (isCharging == true) - const PBadge( - text: '充电专属', - top: 6, - right: 6, - type: PBadgeType.error, - ) - else if (episode.badge != null) - PBadge( - text: episode.badge, - top: 6, - right: 6, - type: switch (episode.badge) { - '会员' => PBadgeType.primary, - '限免' => PBadgeType.free, - _ => PBadgeType.gray, - }, - ), - ], - ); - }, - ), + Stack( + clipBehavior: Clip.none, + children: [ + NetworkImgLayer( + src: cover, + width: 140.8, + height: 88, + ), + if (duration != null && duration > 0) + PBadge( + text: DurationUtil.formatDuration(duration), + right: 6.0, + bottom: 6.0, + type: PBadgeType.gray, + ), + if (isCharging == true) + const PBadge( + text: '充电专属', + top: 6, + right: 6, + type: PBadgeType.error, + ) + else if (episode.badge != null) + PBadge( + text: episode.badge, + top: 6, + right: 6, + type: switch (episode.badge) { + '会员' => PBadgeType.primary, + '限免' => PBadgeType.free, + _ => PBadgeType.gray, + }, + ), + ], ) else if (isCurrentIndex) Image.asset( diff --git a/lib/pages/fav/article/view.dart b/lib/pages/fav/article/view.dart index 39ed93f3a..c6f7a9269 100644 --- a/lib/pages/fav/article/view.dart +++ b/lib/pages/fav/article/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; @@ -38,7 +37,7 @@ class _FavArticlePageState extends State slivers: [ SliverPadding( padding: EdgeInsets.only( - top: StyleString.safeSpace - 5, + top: 7, bottom: MediaQuery.paddingOf(context).bottom + 80, ), sliver: Obx( diff --git a/lib/pages/fav/note/child_view.dart b/lib/pages/fav/note/child_view.dart index f9c68983e..14adf6713 100644 --- a/lib/pages/fav/note/child_view.dart +++ b/lib/pages/fav/note/child_view.dart @@ -32,108 +32,109 @@ class _FavNoteChildPageState extends State super.build(context); final theme = Theme.of(context); final padding = MediaQuery.paddingOf(context); - return LayoutBuilder( - builder: (context, constraints) => Stack( - clipBehavior: Clip.none, - children: [ - refreshIndicator( - onRefresh: _favNoteController.onRefresh, - child: CustomScrollView( - controller: _favNoteController.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only(bottom: padding.bottom + 80), - sliver: Obx( - () => _buildBody(_favNoteController.loadingState.value), - ), + final bottomH = 50 + padding.bottom; + return Stack( + clipBehavior: Clip.none, + children: [ + refreshIndicator( + onRefresh: _favNoteController.onRefresh, + child: CustomScrollView( + controller: _favNoteController.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.only(bottom: padding.bottom + 80), + sliver: Obx( + () => _buildBody(_favNoteController.loadingState.value), ), - ], - ), + ), + ], ), - Positioned( - top: constraints.maxHeight, - left: 0, - right: 0, - child: Obx( - () => AnimatedSlide( - offset: _favNoteController.enableMultiSelect.value - ? const Offset(0, -1) - : Offset.zero, - duration: const Duration(milliseconds: 150), - child: Container( - padding: padding, - decoration: BoxDecoration( - color: theme.colorScheme.onInverseSurface, - border: Border( - top: BorderSide( - width: 0.5, - color: theme.colorScheme.outline.withValues(alpha: 0.5), - ), + ), + Positioned( + left: 0, + right: 0, + bottom: -bottomH, + child: Obx( + () => AnimatedSlide( + offset: _favNoteController.enableMultiSelect.value + ? const Offset(0, -1) + : Offset.zero, + duration: const Duration(milliseconds: 150), + child: Container( + height: bottomH, + padding: padding, + decoration: BoxDecoration( + color: theme.colorScheme.onInverseSurface, + border: Border( + top: BorderSide( + width: 0.5, + color: theme.colorScheme.outline.withValues(alpha: 0.5), ), ), - width: double.infinity, - child: Row( - children: [ - const SizedBox(width: 16), - iconButton( - size: 32, - tooltip: '取消', - context: context, - icon: Icons.clear, - onPressed: _favNoteController.onDisable, - ), - const SizedBox(width: 12), - Obx( - () => Checkbox( - value: _favNoteController.allSelected.value, - onChanged: (value) { - _favNoteController.handleSelect( - !_favNoteController.allSelected.value, - ); - }, - ), - ), - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => _favNoteController.handleSelect( - !_favNoteController.allSelected.value, - ), - child: const Padding( - padding: EdgeInsets.only( - top: 14, - bottom: 14, - right: 12, - ), - child: Text('全选'), - ), - ), - const Spacer(), - FilledButton.tonal( - style: TextButton.styleFrom( - visualDensity: VisualDensity.compact, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - onPressed: () { - if (_favNoteController.checkedCount != 0) { - showConfirmDialog( - context: context, - title: '确定删除已选中的笔记吗?', - onConfirm: _favNoteController.onRemove, - ); - } + ), + width: double.infinity, + child: Row( + children: [ + const SizedBox(width: 16), + iconButton( + size: 32, + tooltip: '取消', + context: context, + icon: Icons.clear, + onPressed: _favNoteController.onDisable, + ), + const SizedBox(width: 12), + Obx( + () => Checkbox( + value: _favNoteController.allSelected.value, + onChanged: (value) { + _favNoteController.handleSelect( + checked: !_favNoteController.allSelected.value, + ); }, - child: const Text('删除'), ), - const SizedBox(width: 16), - ], - ), + ), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _favNoteController.handleSelect( + checked: !_favNoteController.allSelected.value, + disableSelect: false, + ), + child: const Padding( + padding: EdgeInsets.only( + top: 14, + bottom: 14, + right: 12, + ), + child: Text('全选'), + ), + ), + const Spacer(), + FilledButton.tonal( + style: TextButton.styleFrom( + visualDensity: VisualDensity.compact, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + onPressed: () { + if (_favNoteController.checkedCount != 0) { + showConfirmDialog( + context: context, + title: '确定删除已选中的笔记吗?', + onConfirm: _favNoteController.onRemove, + ); + } + }, + child: const Text('删除'), + ), + const SizedBox(width: 16), + ], ), ), ), ), - ], - ), + ), + ], ); } diff --git a/lib/pages/fav/note/controller.dart b/lib/pages/fav/note/controller.dart index bd4757d51..4e9102399 100644 --- a/lib/pages/fav/note/controller.dart +++ b/lib/pages/fav/note/controller.dart @@ -17,14 +17,9 @@ class FavNoteController } @override - void onSelect(FavNoteItemModel item, [bool disableSelect = true]) { - super.onSelect(item, false); - } - - @override - void handleSelect([bool checked = false, bool disableSelect = true]) { + void handleSelect({bool checked = false, bool disableSelect = true}) { allSelected.value = checked; - super.handleSelect(checked, false); + super.handleSelect(checked: checked, disableSelect: disableSelect); } @override diff --git a/lib/pages/fav/note/widget/item.dart b/lib/pages/fav/note/widget/item.dart index a2d86504e..5037fb158 100644 --- a/lib/pages/fav/note/widget/item.dart +++ b/lib/pages/fav/note/widget/item.dart @@ -1,5 +1,6 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/common/widgets/select_mask.dart'; import 'package:PiliPlus/models_new/fav/fav_note/list.dart'; import 'package:PiliPlus/pages/fav/note/controller.dart'; import 'package:PiliPlus/utils/page_utils.dart'; @@ -64,62 +65,9 @@ class FavNoteItem extends StatelessWidget { height: boxConstraints.maxHeight, ), Positioned.fill( - child: IgnorePointer( - child: LayoutBuilder( - builder: (context, constraints) => - AnimatedOpacity( - opacity: item.checked == true ? 1 : 0, - duration: const Duration( - milliseconds: 200, - ), - child: Container( - alignment: Alignment.center, - height: constraints.maxHeight, - width: - constraints.maxHeight * - StyleString.aspectRatio, - decoration: BoxDecoration( - borderRadius: StyleString.mdRadius, - color: Colors.black.withValues( - alpha: 0.6, - ), - ), - child: SizedBox( - width: 34, - height: 34, - child: AnimatedScale( - scale: item.checked == true ? 1 : 0, - duration: const Duration( - milliseconds: 250, - ), - curve: Curves.easeInOut, - child: IconButton( - tooltip: '取消选择', - style: ButtonStyle( - padding: - WidgetStateProperty.all( - EdgeInsets.zero, - ), - backgroundColor: - WidgetStatePropertyAll( - theme.colorScheme.surface - .withValues( - alpha: 0.8, - ), - ), - ), - onPressed: null, - icon: Icon( - Icons.done_all_outlined, - color: - theme.colorScheme.primary, - ), - ), - ), - ), - ), - ), - ), + child: selectMask( + theme, + item.checked == true, ), ), ], diff --git a/lib/pages/fav/pgc/child_view.dart b/lib/pages/fav/pgc/child_view.dart index afb9c1c36..d1f18b8a7 100644 --- a/lib/pages/fav/pgc/child_view.dart +++ b/lib/pages/fav/pgc/child_view.dart @@ -37,127 +37,128 @@ class _FavPgcChildPageState extends State super.build(context); final theme = Theme.of(context); final padding = MediaQuery.paddingOf(context); - return LayoutBuilder( - builder: (context, constraints) => Stack( - clipBehavior: Clip.none, - children: [ - refreshIndicator( - onRefresh: _favPgcController.onRefresh, - child: CustomScrollView( - controller: _favPgcController.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only(bottom: padding.bottom + 80), - sliver: Obx( - () => _buildBody(_favPgcController.loadingState.value), - ), + final bottomH = 50 + padding.bottom; + return Stack( + clipBehavior: Clip.none, + children: [ + refreshIndicator( + onRefresh: _favPgcController.onRefresh, + child: CustomScrollView( + controller: _favPgcController.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.only(bottom: padding.bottom + 80), + sliver: Obx( + () => _buildBody(_favPgcController.loadingState.value), ), - ], - ), + ), + ], ), - Positioned( - top: constraints.maxHeight, - left: 0, - right: 0, - child: Obx( - () => AnimatedSlide( - offset: _favPgcController.enableMultiSelect.value - ? const Offset(0, -1) - : Offset.zero, - duration: const Duration(milliseconds: 150), - child: Container( - padding: padding, - decoration: BoxDecoration( - color: theme.colorScheme.onInverseSurface, - border: Border( - top: BorderSide( - width: 0.5, - color: theme.colorScheme.outline.withValues(alpha: 0.5), - ), + ), + Positioned( + left: 0, + right: 0, + bottom: -bottomH, + child: Obx( + () => AnimatedSlide( + offset: _favPgcController.enableMultiSelect.value + ? const Offset(0, -1) + : Offset.zero, + duration: const Duration(milliseconds: 150), + child: Container( + height: bottomH, + padding: padding, + decoration: BoxDecoration( + color: theme.colorScheme.onInverseSurface, + border: Border( + top: BorderSide( + width: 0.5, + color: theme.colorScheme.outline.withValues(alpha: 0.5), ), ), - width: double.infinity, - child: Row( - children: [ - const SizedBox(width: 16), - iconButton( - size: 32, - tooltip: '取消', - context: context, - icon: Icons.clear, - onPressed: _favPgcController.onDisable, + ), + width: double.infinity, + child: Row( + children: [ + const SizedBox(width: 16), + iconButton( + size: 32, + tooltip: '取消', + context: context, + icon: Icons.clear, + onPressed: _favPgcController.onDisable, + ), + const SizedBox(width: 12), + Obx( + () => Checkbox( + value: _favPgcController.allSelected.value, + onChanged: (value) { + _favPgcController.handleSelect( + checked: !_favPgcController.allSelected.value, + ); + }, ), - const SizedBox(width: 12), - Obx( - () => Checkbox( - value: _favPgcController.allSelected.value, - onChanged: (value) { - _favPgcController.handleSelect( - !_favPgcController.allSelected.value, - ); - }, - ), + ), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _favPgcController.handleSelect( + checked: !_favPgcController.allSelected.value, + disableSelect: false, ), - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => _favPgcController.handleSelect( - !_favPgcController.allSelected.value, - ), - child: const Padding( - padding: EdgeInsets.only( - top: 14, - bottom: 14, - right: 12, - ), - child: Text('全选'), + child: const Padding( + padding: EdgeInsets.only( + top: 14, + bottom: 14, + right: 12, ), + child: Text('全选'), ), - const Spacer(), - ...const [ - (followStatus: 1, title: '想看'), - (followStatus: 2, title: '在看'), - (followStatus: 3, title: '看过'), - ] - .where( - (item) => item.followStatus != widget.followStatus, - ) - .map( - (item) => Padding( - padding: const EdgeInsets.only(left: 25), - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (_favPgcController.checkedCount != 0) { - _favPgcController.onUpdateList( - item.followStatus, - ); - } - }, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 14, - horizontal: 5, - ), - child: Text( - '标记为${item.title}', - style: TextStyle( - color: theme.colorScheme.onSurfaceVariant, - ), + ), + const Spacer(), + ...const [ + (followStatus: 1, title: '想看'), + (followStatus: 2, title: '在看'), + (followStatus: 3, title: '看过'), + ] + .where( + (item) => item.followStatus != widget.followStatus, + ) + .map( + (item) => Padding( + padding: const EdgeInsets.only(left: 25), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (_favPgcController.checkedCount != 0) { + _favPgcController.onUpdateList( + item.followStatus, + ); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 14, + horizontal: 5, + ), + child: Text( + '标记为${item.title}', + style: TextStyle( + color: theme.colorScheme.onSurfaceVariant, ), ), ), ), ), - const SizedBox(width: 20), - ], - ), + ), + const SizedBox(width: 20), + ], ), ), ), ), - ], - ), + ), + ], ); } diff --git a/lib/pages/fav/pgc/controller.dart b/lib/pages/fav/pgc/controller.dart index 070c124ab..845ec036a 100644 --- a/lib/pages/fav/pgc/controller.dart +++ b/lib/pages/fav/pgc/controller.dart @@ -24,14 +24,9 @@ class FavPgcController } @override - void onSelect(FavPgcItemModel item, [bool disableSelect = true]) { - super.onSelect(item, false); - } - - @override - void handleSelect([bool checked = false, bool disableSelect = true]) { + void handleSelect({bool checked = false, bool disableSelect = true}) { allSelected.value = checked; - super.handleSelect(checked, false); + super.handleSelect(checked: checked, disableSelect: disableSelect); } @override @@ -88,10 +83,10 @@ class FavPgcController ..refresh(); ctr.allSelected.value = false; } - afterDelete(removeList); } catch (e) { if (kDebugMode) debugPrint('fav pgc onUpdate: $e'); } + afterDelete(removeList); } SmartDialog.showToast(res['msg']); } diff --git a/lib/pages/fav/pgc/widget/item.dart b/lib/pages/fav/pgc/widget/item.dart index 6672abd8d..bbaa6b030 100644 --- a/lib/pages/fav/pgc/widget/item.dart +++ b/lib/pages/fav/pgc/widget/item.dart @@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/common/widgets/select_mask.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart'; import 'package:PiliPlus/pages/common/multi_select/base.dart'; @@ -81,71 +82,11 @@ class FavPgcItem extends StatelessWidget { ), ), Positioned.fill( - child: IgnorePointer( - child: LayoutBuilder( - builder: (context, constraints) => - AnimatedOpacity( - opacity: item.checked == true - ? 1 - : 0, - duration: const Duration( - milliseconds: 200, - ), - child: Container( - alignment: Alignment.center, - height: constraints.maxHeight, - width: - constraints.maxHeight * - StyleString.aspectRatio, - decoration: BoxDecoration( - borderRadius: - const BorderRadius.all( - Radius.circular(4), - ), - color: Colors.black.withValues( - alpha: 0.6, - ), - ), - child: SizedBox( - width: 34, - height: 34, - child: AnimatedScale( - scale: item.checked == true - ? 1 - : 0, - duration: const Duration( - milliseconds: 250, - ), - curve: Curves.easeInOut, - child: IconButton( - tooltip: '取消选择', - style: ButtonStyle( - padding: - WidgetStateProperty.all( - EdgeInsets.zero, - ), - backgroundColor: - WidgetStatePropertyAll( - theme - .colorScheme - .surface - .withValues( - alpha: 0.8, - ), - ), - ), - onPressed: null, - icon: Icon( - Icons.done_all_outlined, - color: theme - .colorScheme - .primary, - ), - ), - ), - ), - ), - ), + child: selectMask( + theme, + item.checked == true, + borderRadius: const BorderRadius.all( + Radius.circular(4), ), ), ), diff --git a/lib/pages/fav/video/view.dart b/lib/pages/fav/video/view.dart index 87282bf79..372706b9a 100644 --- a/lib/pages/fav/video/view.dart +++ b/lib/pages/fav/video/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; @@ -36,7 +35,7 @@ class _FavVideoPageState extends State slivers: [ SliverPadding( padding: EdgeInsets.only( - top: StyleString.safeSpace - 5, + top: 7, bottom: 80 + MediaQuery.paddingOf(context).bottom, ), sliver: Obx( diff --git a/lib/pages/fav_create/view.dart b/lib/pages/fav_create/view.dart index 34842b1fd..e19827945 100644 --- a/lib/pages/fav_create/view.dart +++ b/lib/pages/fav_create/view.dart @@ -246,20 +246,16 @@ class _CreateFavPageState extends State { if (_cover?.isNotEmpty == true) Padding( padding: const EdgeInsets.symmetric(vertical: 5), - child: LayoutBuilder( - builder: (context, constraints) { - return ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - child: CachedNetworkImage( - imageUrl: ImageUtil.thumbnailUrl(_cover!), - height: constraints.maxHeight, - width: constraints.maxHeight * 16 / 9, - fit: BoxFit.cover, - ), - ); - }, + child: ClipRRect( + borderRadius: const BorderRadius.all( + Radius.circular(6), + ), + child: CachedNetworkImage( + imageUrl: ImageUtil.thumbnailUrl(_cover!), + height: 55, + width: 88, + fit: BoxFit.cover, + ), ), ), Icon( diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index a4ad0270d..45d91c4f6 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -279,7 +279,7 @@ class _FavDetailPageState extends State { style: TextButton.styleFrom( visualDensity: VisualDensity.compact, ), - onPressed: () => _favDetailController.handleSelect(true), + onPressed: () => _favDetailController.handleSelect(checked: true), child: const Text('全选'), ), TextButton( diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart index f1a66dcee..32185dbc5 100644 --- a/lib/pages/history/view.dart +++ b/lib/pages/history/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/widgets/appbar/appbar.dart'; import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart'; @@ -60,7 +59,7 @@ class _HistoryPageState extends State slivers: [ SliverPadding( padding: EdgeInsets.only( - top: StyleString.safeSpace - 5, + top: 7, bottom: MediaQuery.paddingOf(context).bottom + 80, ), sliver: Obx( diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index 954e15936..1d8652f80 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; @@ -138,7 +137,7 @@ class _HotPageState extends CommonPageState ), SliverPadding( padding: EdgeInsets.only( - top: StyleString.safeSpace - 5, + top: 7, bottom: MediaQuery.paddingOf(context).bottom + 80, ), sliver: Obx( diff --git a/lib/pages/member_coin_arc/view.dart b/lib/pages/member_coin_arc/view.dart index 10568a73b..c8002d613 100644 --- a/lib/pages/member_coin_arc/view.dart +++ b/lib/pages/member_coin_arc/view.dart @@ -52,7 +52,7 @@ class _MemberCoinArcPageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - top: StyleString.safeSpace - 5, + top: 7, left: StyleString.safeSpace, right: StyleString.safeSpace, bottom: MediaQuery.paddingOf(context).bottom + 80, diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index 2f3dd50cd..2ac71e77f 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -1,10 +1,8 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; -import 'package:PiliPlus/pages/dynamics_tab/view.dart'; import 'package:PiliPlus/pages/member_dynamics/controller.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/grid.dart'; @@ -25,7 +23,7 @@ class MemberDynamicsPage extends StatefulWidget { } class _MemberDynamicsPageState extends State - with AutomaticKeepAliveClientMixin { + with AutomaticKeepAliveClientMixin, DynMixin { late MemberDynamicsController _memberDynamicController; late int mid; @@ -74,23 +72,14 @@ class _MemberDynamicsPageState extends State ), ); - late double _maxWidth; - Widget _buildContent(LoadingState?> loadingState) { return switch (loadingState) { - Loading() => DynamicsTabPage.dynSkeleton( - GlobalData().dynamicsWaterfallFlow, - ), + Loading() => dynSkeleton, Success(:var response) => response?.isNotEmpty == true ? GlobalData().dynamicsWaterfallFlow ? SliverWaterfallFlow( - gridDelegate: - SliverWaterfallFlowDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - crossAxisSpacing: StyleString.cardSpace / 2, - callback: (value) => _maxWidth = value, - ), + gridDelegate: gridDelegate, delegate: SliverChildBuilderDelegate( (_, index) { if (index == response.length - 1) { @@ -100,7 +89,7 @@ class _MemberDynamicsPageState extends State item: response[index], onRemove: _memberDynamicController.onRemove, onSetTop: _memberDynamicController.onSetTop, - maxWidth: _maxWidth, + maxWidth: maxWidth, ); }, childCount: response!.length, @@ -120,7 +109,7 @@ class _MemberDynamicsPageState extends State item: response[index], onRemove: _memberDynamicController.onRemove, onSetTop: _memberDynamicController.onSetTop, - maxWidth: _maxWidth, + maxWidth: maxWidth, ); }, itemCount: response!.length, diff --git a/lib/pages/member_favorite/widget/item.dart b/lib/pages/member_favorite/widget/item.dart index a2ee5f1dd..6bc55cd7a 100644 --- a/lib/pages/member_favorite/widget/item.dart +++ b/lib/pages/member_favorite/widget/item.dart @@ -56,34 +56,27 @@ class MemberFavItem extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder( - builder: (context, boxConstraints) { - return Stack( - clipBehavior: Clip.none, - children: [ - NetworkImgLayer( - src: item.cover, - width: boxConstraints.maxWidth, - height: boxConstraints.maxHeight, - ), - if (item.type == 21) - const PBadge( - right: 6, - top: 6, - text: '合集', - ) - else if (item.type == 11) - const PBadge( - right: 6, - top: 6, - text: '收藏夹', - ), - ], - ); - }, - ), + Stack( + clipBehavior: Clip.none, + children: [ + NetworkImgLayer( + src: item.cover, + width: 140.8, + height: 88, + ), + if (item.type == 21) + const PBadge( + right: 6, + top: 6, + text: '合集', + ) + else if (item.type == 11) + const PBadge( + right: 6, + top: 6, + text: '收藏夹', + ), + ], ), const SizedBox(width: 10), Expanded( diff --git a/lib/pages/member_home/widgets/fav_item.dart b/lib/pages/member_home/widgets/fav_item.dart index 4545a742b..836da834a 100644 --- a/lib/pages/member_home/widgets/fav_item.dart +++ b/lib/pages/member_home/widgets/fav_item.dart @@ -37,17 +37,10 @@ class MemberFavItem extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder( - builder: (context, boxConstraints) { - return NetworkImgLayer( - src: item.cover, - width: boxConstraints.maxWidth, - height: boxConstraints.maxHeight, - ); - }, - ), + NetworkImgLayer( + src: item.cover, + width: 140.8, + height: 88, ), const SizedBox(width: 10), Expanded( diff --git a/lib/pages/member_like_arc/view.dart b/lib/pages/member_like_arc/view.dart index 898611aea..d797f75b5 100644 --- a/lib/pages/member_like_arc/view.dart +++ b/lib/pages/member_like_arc/view.dart @@ -52,7 +52,7 @@ class _MemberLikeArcPageState extends State { slivers: [ SliverPadding( padding: EdgeInsets.only( - top: StyleString.safeSpace - 5, + top: 7, left: StyleString.safeSpace, right: StyleString.safeSpace, bottom: MediaQuery.paddingOf(context).bottom + 80, diff --git a/lib/pages/member_search/child/view.dart b/lib/pages/member_search/child/view.dart index f2685dc0a..1eeba4155 100644 --- a/lib/pages/member_search/child/view.dart +++ b/lib/pages/member_search/child/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; @@ -6,7 +5,6 @@ import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/member/search_type.dart'; import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; -import 'package:PiliPlus/pages/dynamics_tab/view.dart'; import 'package:PiliPlus/pages/member_search/child/controller.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/grid.dart'; @@ -31,7 +29,7 @@ class MemberSearchChildPage extends StatefulWidget { } class _MemberSearchChildPageState extends State - with AutomaticKeepAliveClientMixin { + with AutomaticKeepAliveClientMixin, DynMixin { MemberSearchChildController get _controller => widget.controller; @override @@ -66,14 +64,10 @@ class _MemberSearchChildPageState extends State childCount: 10, ), ), - MemberSearchType.dynamic => DynamicsTabPage.dynSkeleton( - GlobalData().dynamicsWaterfallFlow, - ), + MemberSearchType.dynamic => dynSkeleton, }; } - late double _maxWidth; - Widget _buildBody(LoadingState loadingState) { return switch (loadingState) { Loading() => _buildLoading, @@ -99,12 +93,7 @@ class _MemberSearchChildPageState extends State MemberSearchType.dynamic => GlobalData().dynamicsWaterfallFlow ? SliverWaterfallFlow( - gridDelegate: - SliverWaterfallFlowDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - crossAxisSpacing: StyleString.safeSpace, - callback: (value) => _maxWidth = value, - ), + gridDelegate: gridDelegate, delegate: SliverChildBuilderDelegate( (_, index) { if (index == response.length - 1) { @@ -112,7 +101,7 @@ class _MemberSearchChildPageState extends State } return DynamicPanel( item: response[index], - maxWidth: _maxWidth, + maxWidth: maxWidth, ); }, childCount: response!.length, @@ -130,7 +119,7 @@ class _MemberSearchChildPageState extends State } return DynamicPanel( item: response[index], - maxWidth: _maxWidth, + maxWidth: maxWidth, ); }, itemCount: response!.length, diff --git a/lib/pages/mine/widgets/item.dart b/lib/pages/mine/widgets/item.dart index 5436c23ba..490769538 100644 --- a/lib/pages/mine/widgets/item.dart +++ b/lib/pages/mine/widgets/item.dart @@ -54,17 +54,13 @@ class FavFolderItem extends StatelessWidget { ), ], ), - child: LayoutBuilder( - builder: (context, box) { - return Hero( - tag: heroTag, - child: NetworkImgLayer( - src: item.cover, - width: box.maxWidth, - height: box.maxHeight, - ), - ); - }, + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + src: item.cover, + width: 176, + height: 110, + ), ), ), Text( diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart index 1d8f9ed86..7e799a4e2 100644 --- a/lib/pages/rank/zone/view.dart +++ b/lib/pages/rank/zone/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; @@ -44,7 +43,7 @@ class _ZonePageState extends CommonPageState slivers: [ SliverPadding( padding: EdgeInsets.only( - top: StyleString.safeSpace - 5, + top: 7, bottom: MediaQuery.paddingOf(context).bottom + 80, ), sliver: Obx(() => _buildBody(controller.loadingState.value)), diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index b3f6dc9c4..3adf99ed2 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -115,11 +115,9 @@ class _RcmdPageState extends CommonPageState } else { return VideoCardV( videoItem: response[index], - onRemove: () { - controller.loadingState - ..value.data!.removeAt(index) - ..refresh(); - }, + onRemove: () => controller.loadingState + ..value.data!.removeAt(index) + ..refresh(), ); } }, diff --git a/lib/pages/video/introduction/pgc/controller.dart b/lib/pages/video/introduction/pgc/controller.dart index 1cb77944e..4aafa1c91 100644 --- a/lib/pages/video/introduction/pgc/controller.dart +++ b/lib/pages/video/introduction/pgc/controller.dart @@ -423,7 +423,7 @@ class PgcIntroController extends CommonIntroController { SmartDialog.showToast('已三连'); return; } - var result = await VideoHttp.triple(epId: epId, seasonId: seasonId); + var result = await VideoHttp.pgcTriple(epId: epId, seasonId: seasonId); if (result['status']) { PgcTriple data = result['data']; late final stat = pgcItem.stat!; @@ -431,7 +431,8 @@ class PgcIntroController extends CommonIntroController { stat.like++; hasLike.value = true; } - if ((data.coin == 1) != hasCoin) { + final hasCoin = data.coin == 1; + if (this.hasCoin != hasCoin) { stat.coin += 2; coinNum.value = 2; GlobalData().afterCoin(2); @@ -440,7 +441,11 @@ class PgcIntroController extends CommonIntroController { stat.favorite++; hasFav.value = true; } - SmartDialog.showToast('三连成功'); + if (!hasCoin) { + SmartDialog.showToast('投币失败'); + } else { + SmartDialog.showToast('三连成功'); + } } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/video/introduction/pgc/view.dart b/lib/pages/video/introduction/pgc/view.dart index 5d1cc43d1..90b2f8dad 100644 --- a/lib/pages/video/introduction/pgc/view.dart +++ b/lib/pages/video/introduction/pgc/view.dart @@ -113,7 +113,7 @@ class _PgcIntroPageState extends TripleState Widget? _buildBreif(PgcInfoModel item) { final img = item.brief?.img; if (img != null && img.isNotEmpty) { - final maxWidth = widget.maxWidth - 2 * StyleString.safeSpace; + final maxWidth = widget.maxWidth - 24; double padding = max(0, maxWidth - 400); final imgWidth = maxWidth - padding; padding = padding / 2; diff --git a/lib/pages/video/introduction/ugc/controller.dart b/lib/pages/video/introduction/ugc/controller.dart index 6b52ccbc1..9d8dbc7ab 100644 --- a/lib/pages/video/introduction/ugc/controller.dart +++ b/lib/pages/video/introduction/ugc/controller.dart @@ -193,7 +193,7 @@ class UgcIntroController extends CommonIntroController with ReloadMixin { SmartDialog.showToast('已三连'); return; } - var result = await VideoHttp.oneThree(bvid: bvid); + var result = await VideoHttp.ugcTriple(bvid: bvid); if (result['status']) { UgcTriple data = result['data']; late final stat = videoDetail.value.stat!; @@ -211,7 +211,11 @@ class UgcIntroController extends CommonIntroController with ReloadMixin { hasFav.value = true; } hasDislike.value = false; - SmartDialog.showToast('三连成功'); + if (data.coin != true) { + SmartDialog.showToast('投币失败'); + } else { + SmartDialog.showToast('三连成功'); + } } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/video/medialist/view.dart b/lib/pages/video/medialist/view.dart index d44935c92..dcaf5e324 100644 --- a/lib/pages/video/medialist/view.dart +++ b/lib/pages/video/medialist/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; @@ -51,7 +50,6 @@ class MediaListPanel extends CommonCollapseSlidePage { class _MediaListPanelState extends CommonCollapseSlidePageState { late final int _index; - late final RxBool desc = widget.desc.obs; @override void initState() { @@ -74,19 +72,14 @@ class _MediaListPanelState title: Text(widget.panelTitle ?? '稍后再看'), backgroundColor: Colors.transparent, actions: [ - Obx( - () { - final desc = this.desc.value; - return mediumButton( - tooltip: desc ? '顺序播放' : '倒序播放', - icon: desc - ? MdiIcons.sortAscending - : MdiIcons.sortDescending, - onPressed: () { - widget.onReverse(); - this.desc.value = !desc; - }, - ); + mediumButton( + tooltip: widget.desc ? '顺序播放' : '倒序播放', + icon: widget.desc + ? MdiIcons.sortAscending + : MdiIcons.sortDescending, + onPressed: () { + Get.back(); + widget.onReverse(); }, ), mediumButton( @@ -131,169 +124,170 @@ class _MediaListPanelState bottom: MediaQuery.paddingOf(context).bottom + 80, ), itemBuilder: ((context, index) { - var item = widget.mediaList[index]; if (index == widget.mediaList.length - 1 && (widget.count == null || widget.mediaList.length < widget.count!)) { widget.loadMoreMedia(); } + var item = widget.mediaList[index]; final isCurr = item.bvid == widget.getBvId(); - return SizedBox( - height: 98, - child: Material( - type: MaterialType.transparency, - child: InkWell( - onTap: () { - if (item.type != 2) { - SmartDialog.showToast('不支持播放该类型视频'); - return; - } - Get.back(); - widget.onChangeEpisode(item); - }, - onLongPress: () => imageSaveDialog( - title: item.title, - cover: item.cover, - aid: item.aid, - bvid: item.bvid, - ), - child: Stack( - clipBehavior: Clip.none, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 5, - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder( - builder: (context, boxConstraints) { - return Stack( - clipBehavior: Clip.none, - children: [ - NetworkImgLayer( - src: item.cover, - width: boxConstraints.maxWidth, - height: boxConstraints.maxHeight, - ), - if (item.badge?.isNotEmpty == true) - PBadge( - text: item.badge, - right: 6.0, - top: 6.0, - type: switch (item.badge) { - '充电专属' => PBadgeType.error, - _ => PBadgeType.primary, - }, - ), - PBadge( - text: DurationUtil.formatDuration( - item.duration, - ), - right: 6.0, - bottom: 6.0, - type: PBadgeType.gray, - ), - ], - ); - }, - ), - ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - item.title!, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: isCurr ? FontWeight.bold : null, - color: isCurr - ? theme.colorScheme.primary - : null, - ), - ), - if (item.type == 24 && - item.intro?.isNotEmpty == true) ...[ - const SizedBox(height: 3), - Text( - item.intro!, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 13, - color: theme.colorScheme.outline, - ), - ), - ], - const Spacer(), - Text( - item.upper!.name!, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12, - color: theme.colorScheme.outline, - ), - ), - if (item.type == 2) ...[ - const SizedBox(height: 3), - Row( - spacing: 8, - children: [ - StatWidget( - type: StatType.play, - value: item.cntInfo!.play, - ), - StatWidget( - type: StatType.danmaku, - value: item.cntInfo!.danmaku, - ), - ], - ), - ], - ], - ), - ), - ], - ), - ), - if (showDelBtn && !isCurr) - Positioned( - right: 12, - bottom: -6, - child: InkWell( - customBorder: const CircleBorder(), - onTap: () => showConfirmDialog( - context: context, - title: '确定移除该视频?', - onConfirm: () => widget.onDelete!(item, index), - ), - onLongPress: () => widget.onDelete!(item, index), - child: Padding( - padding: const EdgeInsets.all(9), - child: Icon( - Icons.clear, - size: 18, - color: theme.colorScheme.outline, - ), - ), - ), - ), - ], - ), - ), - ), - ); + return _buildItem(theme, index, item, isCurr, showDelBtn); }), separatorBuilder: (context, index) => const SizedBox(height: 2), ); }, ); + + Widget _buildItem( + ThemeData theme, + int index, + MediaListItemModel item, + bool isCurr, + bool showDelBtn, + ) { + return SizedBox( + height: 98, + child: Material( + type: MaterialType.transparency, + child: InkWell( + onTap: () { + if (item.type != 2) { + SmartDialog.showToast('不支持播放该类型视频'); + return; + } + Get.back(); + widget.onChangeEpisode(item); + }, + onLongPress: () => imageSaveDialog( + title: item.title, + cover: item.cover, + aid: item.aid, + bvid: item.bvid, + ), + child: Stack( + clipBehavior: Clip.none, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 5, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + clipBehavior: Clip.none, + children: [ + NetworkImgLayer( + src: item.cover, + width: 140.8, + height: 88, + ), + if (item.badge?.isNotEmpty == true) + PBadge( + text: item.badge, + right: 6.0, + top: 6.0, + type: switch (item.badge) { + '充电专属' => PBadgeType.error, + _ => PBadgeType.primary, + }, + ), + PBadge( + text: DurationUtil.formatDuration( + item.duration, + ), + right: 6.0, + bottom: 6.0, + type: PBadgeType.gray, + ), + ], + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.title!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: isCurr ? FontWeight.bold : null, + color: isCurr ? theme.colorScheme.primary : null, + ), + ), + if (item.type == 24 && + item.intro?.isNotEmpty == true) ...[ + const SizedBox(height: 3), + Text( + item.intro!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 13, + color: theme.colorScheme.outline, + ), + ), + ], + const Spacer(), + Text( + item.upper!.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, + color: theme.colorScheme.outline, + ), + ), + if (item.type == 2) ...[ + const SizedBox(height: 3), + Row( + spacing: 8, + children: [ + StatWidget( + type: StatType.play, + value: item.cntInfo!.play, + ), + StatWidget( + type: StatType.danmaku, + value: item.cntInfo!.danmaku, + ), + ], + ), + ], + ], + ), + ), + ], + ), + ), + if (showDelBtn && !isCurr) + Positioned( + right: 12, + bottom: -6, + child: InkWell( + customBorder: const CircleBorder(), + onTap: () => showConfirmDialog( + context: context, + title: '确定移除该视频?', + onConfirm: () => widget.onDelete!(item, index), + ), + onLongPress: () => widget.onDelete!(item, index), + child: Padding( + padding: const EdgeInsets.all(9), + child: Icon( + Icons.clear, + size: 18, + color: theme.colorScheme.outline, + ), + ), + ), + ), + ], + ), + ), + ), + ); + } } diff --git a/lib/pages/video/pay_coins/view.dart b/lib/pages/video/pay_coins/view.dart index c1c1fb5ff..ccaf40446 100644 --- a/lib/pages/video/pay_coins/view.dart +++ b/lib/pages/video/pay_coins/view.dart @@ -208,20 +208,16 @@ class _PayCoinsPageState extends State @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - bool isV = constraints.maxHeight > constraints.maxWidth; - return isV - ? _buildBody(isV) - : Row( - children: [ - const Spacer(), - Expanded(flex: 3, child: _buildBody(isV)), - const Spacer(), - ], - ); - }, - ); + bool isPortrait = context.isPortrait; + return isPortrait + ? _buildBody(isPortrait) + : Row( + children: [ + const Spacer(), + Expanded(flex: 3, child: _buildBody(isPortrait)), + const Spacer(), + ], + ); } Widget _buildBody(bool isV) => Stack( diff --git a/lib/pages/video/related/view.dart b/lib/pages/video/related/view.dart index 2e421f1db..e1e101d20 100644 --- a/lib/pages/video/related/view.dart +++ b/lib/pages/video/related/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart'; @@ -31,7 +30,7 @@ class _RelatedVideoPanelState extends State super.build(context); return SliverPadding( padding: const EdgeInsets.only( - top: StyleString.safeSpace - 5, + top: 7, bottom: 80, ), sliver: Obx(() => _buildBody(_relatedController.loadingState.value)), diff --git a/lib/pages/video/view_point/view.dart b/lib/pages/video/view_point/view.dart index 69ca744f6..3fae121ab 100644 --- a/lib/pages/video/view_point/view.dart +++ b/lib/pages/video/view_point/view.dart @@ -50,7 +50,7 @@ class _ViewPointsPageState toolbarHeight: 45, actions: [ const Text( - '分段进度条', + '分段进度条 ', style: TextStyle(fontSize: 16), ), Obx( @@ -116,63 +116,71 @@ class _ViewPointsPageState currentIndex = index; } } - return ListTile( - dense: true, - onTap: segment.from != null - ? () { - currentIndex = index; - plPlayerController?.danmakuController?.clear(); - plPlayerController?.videoPlayerController?.seek( - Duration(seconds: segment.from!), - ); - Get.back(); - } - : null, - leading: segment.url?.isNotEmpty == true - ? Container( - margin: const EdgeInsets.symmetric(vertical: 6), - decoration: currentIndex == index - ? BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(6), - ), - border: Border.all( - width: 1.8, - strokeAlign: BorderSide.strokeAlignOutside, - color: theme.colorScheme.primary, - ), - ) - : null, - child: LayoutBuilder( - builder: (context, constraints) => NetworkImgLayer( - radius: 6, - src: segment.url, - width: constraints.maxHeight * StyleString.aspectRatio, - height: constraints.maxHeight, - ), - ), - ) - : null, - title: Text( - segment.title ?? '', - style: TextStyle( - fontSize: 14, - fontWeight: currentIndex == index ? FontWeight.bold : null, - color: currentIndex == index ? theme.colorScheme.primary : null, - ), - ), - subtitle: Text( - '${segment.from != null ? DurationUtil.formatDuration(segment.from) : ''} - ${segment.to != null ? DurationUtil.formatDuration(segment.to) : ''}', - style: TextStyle( - fontSize: 13, - color: currentIndex == index - ? theme.colorScheme.primary - : theme.colorScheme.outline, - ), - ), - ); + final isCurr = currentIndex == index; + return _buildItem(theme, segment, isCurr); }, separatorBuilder: (context, index) => divider, ); } + + Widget _buildItem(ThemeData theme, Segment segment, bool isCurr) { + final theme = Theme.of(context); + return Material( + type: MaterialType.transparency, + child: InkWell( + onTap: segment.from != null + ? () { + Get.back(); + plPlayerController + ?..danmakuController?.clear() + ..videoPlayerController?.seek( + Duration(seconds: segment.from!), + ); + } + : null, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: StyleString.safeSpace, + vertical: 5, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + NetworkImgLayer( + src: segment.url, + width: 140.8, + height: 88, + ), + const SizedBox(width: 10), + Expanded( + child: Column( + spacing: 10, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + segment.title ?? '', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: isCurr + ? TextStyle( + fontWeight: FontWeight.bold, + color: theme.colorScheme.primary, + ) + : null, + ), + Text( + '${segment.from != null ? DurationUtil.formatDuration(segment.from) : ''} - ' + '${segment.to != null ? DurationUtil.formatDuration(segment.to) : ''}', + style: TextStyle(color: theme.colorScheme.outline), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } } diff --git a/lib/utils/request_utils.dart b/lib/utils/request_utils.dart index 6a833217b..1f92a3312 100644 --- a/lib/utils/request_utils.dart +++ b/lib/utils/request_utils.dart @@ -450,7 +450,7 @@ class RequestUtils { mid: isCopy ? mid : null, ).then((res) { if (res.isSuccess) { - ctr.handleSelect(false); + ctr.handleSelect(checked: false); if (!isCopy) { ctr.loadingState ..value.data!.removeWhere(removeList.contains) diff --git a/lib/utils/waterfall.dart b/lib/utils/waterfall.dart index 1d29adb0b..7aab380b6 100644 --- a/lib/utils/waterfall.dart +++ b/lib/utils/waterfall.dart @@ -1,8 +1,55 @@ -import 'package:flutter/material.dart' show ValueChanged; +import 'package:PiliPlus/common/constants.dart'; +import 'package:PiliPlus/common/skeleton/dynamic_card.dart'; +import 'package:PiliPlus/utils/global_data.dart'; +import 'package:PiliPlus/utils/grid.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart' show SliverConstraints; import 'package:waterfall_flow/waterfall_flow.dart' show SliverWaterfallFlowDelegate; +mixin DynMixin { + late double maxWidth; + + late final gridDelegate = SliverWaterfallFlowDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: Grid.smallCardWidth * 2, + crossAxisSpacing: 4, + callback: (value) => maxWidth = value, + ); + + late final skeDelegate = SliverGridDelegateWithExtentAndRatio( + crossAxisSpacing: 4, + mainAxisSpacing: 4, + maxCrossAxisExtent: Grid.smallCardWidth * 2, + childAspectRatio: StyleString.aspectRatio, + mainAxisExtent: 50, + ); + + Widget get dynSkeleton { + if (!GlobalData().dynamicsWaterfallFlow) { + return SliverCrossAxisGroup( + slivers: [ + const SliverFillRemaining(), + SliverConstrainedCrossAxis( + maxExtent: Grid.smallCardWidth * 2, + sliver: SliverList.builder( + itemBuilder: (_, _) => const DynamicCardSkeleton(), + itemCount: 10, + ), + ), + const SliverFillRemaining(), + ], + ); + } + return SliverGrid( + gridDelegate: skeDelegate, + delegate: SliverChildBuilderDelegate( + (_, _) => const DynamicCardSkeleton(), + childCount: 10, + ), + ); + } +} + class SliverWaterfallFlowDelegateWithMaxCrossAxisExtent extends SliverWaterfallFlowDelegate { /// Creates a delegate that makes masonry layouts with tiles that have a maximum