diff --git a/lib/common/constants.dart b/lib/common/constants.dart index 25168d23..d321b903 100644 --- a/lib/common/constants.dart +++ b/lib/common/constants.dart @@ -2,7 +2,8 @@ import 'package:flutter/material.dart'; class StyleString { static const double cardSpace = 8; - static BorderRadius mdRadius = BorderRadius.circular(6); - static const Radius imgRadius = Radius.circular(6); + static const double safeSpace = 12; + static BorderRadius mdRadius = BorderRadius.circular(10); + static const Radius imgRadius = Radius.circular(10); static const double aspectRatio = 16 / 10; } diff --git a/lib/common/skeleton/video_card_h.dart b/lib/common/skeleton/video_card_h.dart index 1d91cbb9..71a659ea 100644 --- a/lib/common/skeleton/video_card_h.dart +++ b/lib/common/skeleton/video_card_h.dart @@ -27,9 +27,6 @@ class VideoCardHSkeleton extends StatelessWidget { aspectRatio: StyleString.aspectRatio, child: LayoutBuilder( builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - double PR = MediaQuery.of(context).devicePixelRatio; return Container( decoration: BoxDecoration( color: Theme.of(context) diff --git a/lib/common/skeleton/video_card_v.dart b/lib/common/skeleton/video_card_v.dart index 859f95aa..956479ef 100644 --- a/lib/common/skeleton/video_card_v.dart +++ b/lib/common/skeleton/video_card_v.dart @@ -53,7 +53,7 @@ class VideoCardVSkeleton extends StatelessWidget { ), Container( width: 80, - height: 13, + height: 12, color: Theme.of(context).colorScheme.background, ), ], diff --git a/lib/common/widgets/appbar.dart b/lib/common/widgets/appbar.dart index ad2b0e3b..9e8fc8ca 100644 --- a/lib/common/widgets/appbar.dart +++ b/lib/common/widgets/appbar.dart @@ -13,7 +13,6 @@ class AppBarWidget extends StatelessWidget implements PreferredSizeWidget { final bool visible; @override - // TODO: implement preferredSize Size get preferredSize => child.preferredSize; @override diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index 607fffe0..3295721d 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; class HttpError extends StatelessWidget { - HttpError({required this.errMsg, required this.fn, super.key}); + const HttpError({required this.errMsg, required this.fn, super.key}); - String errMsg = ''; + final String? errMsg; final Function()? fn; @override @@ -16,7 +16,7 @@ class HttpError extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - errMsg, + errMsg ?? '请求异常', textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium, ), diff --git a/lib/common/widgets/live_card.dart b/lib/common/widgets/live_card.dart index 541dc80a..650440cf 100644 --- a/lib/common/widgets/live_card.dart +++ b/lib/common/widgets/live_card.dart @@ -6,9 +6,10 @@ import 'package:pilipala/pages/rcmd/controller.dart'; import 'package:pilipala/utils/utils.dart'; class LiveCard extends StatelessWidget { - var liveItem; + // ignore: prefer_typing_uninitialized_variables + final liveItem; - LiveCard({ + const LiveCard({ Key? key, required this.liveItem, }) : super(key: key); @@ -37,14 +38,11 @@ class LiveCard extends StatelessWidget { child: LayoutBuilder(builder: (context, boxConstraints) { double maxWidth = boxConstraints.maxWidth; double maxHeight = boxConstraints.maxHeight; - double PR = MediaQuery.of(context).devicePixelRatio; return Stack( children: [ Hero( tag: heroTag, child: NetworkImgLayer( - // 指定图片尺寸 - // src: videoItem.pic + '@${(maxWidth * 2).toInt()}w', src: liveItem.cover + '@.webp', type: 'emote', width: maxWidth, @@ -79,6 +77,7 @@ class LiveCard extends StatelessWidget { } class LiveContent extends StatelessWidget { + // ignore: prefer_typing_uninitialized_variables final liveItem; const LiveContent({Key? key, required this.liveItem}) : super(key: key); @override @@ -140,19 +139,19 @@ class LiveStat extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - // StatView( - // theme: 'white', - // view: view, - // ), - // const SizedBox(width: 8), - // StatDanMu( - // theme: 'white', - // danmu: danmaku, - // ), - ], - ), + // Row( + // children: [ + // StatView( + // theme: 'white', + // view: view, + // ), + // const SizedBox(width: 8), + // StatDanMu( + // theme: 'white', + // danmu: danmaku, + // ), + // ], + // ), Text( online.toString(), style: const TextStyle(fontSize: 11, color: Colors.white), diff --git a/lib/common/widgets/overlay_pop.dart b/lib/common/widgets/overlay_pop.dart index 91212d88..a3511402 100644 --- a/lib/common/widgets/overlay_pop.dart +++ b/lib/common/widgets/overlay_pop.dart @@ -3,8 +3,8 @@ import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; class OverlayPop extends StatelessWidget { - var videoItem; - OverlayPop({super.key, this.videoItem}); + final dynamic videoItem; + const OverlayPop({super.key, this.videoItem}); @override Widget build(BuildContext context) { diff --git a/lib/common/widgets/pull_to_refresh_header.dart b/lib/common/widgets/pull_to_refresh_header.dart index eaf67210..3333a0a6 100644 --- a/lib/common/widgets/pull_to_refresh_header.dart +++ b/lib/common/widgets/pull_to_refresh_header.dart @@ -1,3 +1,5 @@ +// ignore_for_file: depend_on_referenced_packages + import 'dart:math'; import 'dart:ui' as ui show Image; @@ -15,7 +17,8 @@ class PullToRefreshHeader extends StatelessWidget { this.info, this.lastRefreshTime, { this.color, - }); + Key? key, + }) : super(key: key); final PullToRefreshScrollNotificationInfo? info; final DateTime? lastRefreshTime; @@ -23,21 +26,21 @@ class PullToRefreshHeader extends StatelessWidget { @override Widget build(BuildContext context) { - final PullToRefreshScrollNotificationInfo? _info = info; - if (_info == null) { + final PullToRefreshScrollNotificationInfo? infos = info; + if (infos == null) { return Container(); } String text = ''; - if (_info.mode == PullToRefreshIndicatorMode.armed) { + if (infos.mode == PullToRefreshIndicatorMode.armed) { text = 'Release to refresh'; - } else if (_info.mode == PullToRefreshIndicatorMode.refresh || - _info.mode == PullToRefreshIndicatorMode.snap) { + } else if (infos.mode == PullToRefreshIndicatorMode.refresh || + infos.mode == PullToRefreshIndicatorMode.snap) { text = 'Loading...'; - } else if (_info.mode == PullToRefreshIndicatorMode.done) { + } else if (infos.mode == PullToRefreshIndicatorMode.done) { text = 'Refresh completed.'; - } else if (_info.mode == PullToRefreshIndicatorMode.drag) { + } else if (infos.mode == PullToRefreshIndicatorMode.drag) { text = 'Pull to refresh'; - } else if (_info.mode == PullToRefreshIndicatorMode.canceled) { + } else if (infos.mode == PullToRefreshIndicatorMode.canceled) { text = 'Cancel refresh'; } @@ -67,16 +70,15 @@ class PullToRefreshHeader extends StatelessWidget { Expanded( child: Container( alignment: Alignment.centerRight, - child: RefreshImage(top), margin: const EdgeInsets.only(right: 12.0), + child: RefreshImage(top, null), ), ), Column( children: [ Text(text, style: ts), Text( - 'Last updated:' + - DateFormat('yyyy-MM-dd hh:mm').format(time), + 'Last updated:${DateFormat('yyyy-MM-dd hh:mm').format(time)}', style: ts.copyWith(fontSize: 14), ) ], @@ -92,7 +94,7 @@ class PullToRefreshHeader extends StatelessWidget { } class RefreshImage extends StatelessWidget { - const RefreshImage(this.top); + const RefreshImage(this.top, Key? key) : super(key: key); final double top; diff --git a/lib/common/widgets/sliver_header.dart b/lib/common/widgets/sliver_header.dart new file mode 100644 index 00000000..13877a53 --- /dev/null +++ b/lib/common/widgets/sliver_header.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class SliverHeaderDelegate extends SliverPersistentHeaderDelegate { + SliverHeaderDelegate({required this.height, required this.child}); + + final double height; + final Widget child; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return child; + } + + @override + double get maxExtent => height; + + @override + double get minExtent => height; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => + true; +} diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index d8deb3e2..5ea9cb01 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/utils/utils.dart'; @@ -12,12 +11,16 @@ class StatDanMu extends StatelessWidget { @override Widget build(BuildContext context) { - Color color = - theme == 'white' ? Colors.white : Theme.of(context).colorScheme.outline; + Map colorObject = { + 'white': Colors.white, + 'gray': Theme.of(context).colorScheme.outline, + 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), + }; + Color color = colorObject[theme]!; return Row( children: [ Icon( - CupertinoIcons.ellipses_bubble, + Icons.subtitles_outlined, size: 14, color: color, ), diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index 302ceee6..4f2b3441 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/utils/utils.dart'; @@ -12,12 +11,16 @@ class StatView extends StatelessWidget { @override Widget build(BuildContext context) { - Color color = - theme == 'white' ? Colors.white : Theme.of(context).colorScheme.outline; + Map colorObject = { + 'white': Colors.white, + 'gray': Theme.of(context).colorScheme.outline, + 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), + }; + Color color = colorObject[theme]!; return Row( children: [ Icon( - CupertinoIcons.play_rectangle, + Icons.play_circle_outlined, size: 13, color: color, ), diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 435b4826..1f80da02 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; +import 'package:pilipala/common/widgets/stat/danmu.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/utils/utils.dart'; @@ -11,11 +12,11 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; // 视频卡片 - 水平布局 class VideoCardH extends StatelessWidget { // ignore: prefer_typing_uninitialized_variables - var videoItem; - Function()? longPress; - Function()? longPressEnd; + final videoItem; + final Function()? longPress; + final Function()? longPressEnd; - VideoCardH({ + const VideoCardH({ Key? key, required this.videoItem, this.longPress, @@ -53,11 +54,11 @@ class VideoCardH extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.fromLTRB( - StyleString.cardSpace, 7, StyleString.cardSpace, 7), + StyleString.safeSpace, 6, StyleString.safeSpace, 6), child: LayoutBuilder( builder: (context, boxConstraints) { double width = - (boxConstraints.maxWidth - StyleString.cardSpace * 6) / 2; + (boxConstraints.maxWidth - StyleString.cardSpace * 9) / 2; return SizedBox( height: width / StyleString.aspectRatio, child: Row( @@ -70,21 +71,16 @@ class VideoCardH extends StatelessWidget { builder: (context, boxConstraints) { double maxWidth = boxConstraints.maxWidth; double maxHeight = boxConstraints.maxHeight; - double PR = - MediaQuery.of(context).devicePixelRatio; return Stack( children: [ Hero( tag: heroTag, child: NetworkImgLayer( - // src: videoItem['pic'] + - // '@${(maxWidth * 2).toInt()}w', src: videoItem.pic + '@.webp', width: maxWidth, height: maxHeight, ), ), - // Image.network( videoItem['pic'], width: double.infinity, height: double.infinity,), pBadge(Utils.timeFormat(videoItem.duration!), context, null, 6.0, 6.0, null, type: 'gray'), @@ -104,12 +100,12 @@ class VideoCardH extends StatelessWidget { }, ), ), - Divider( - height: 1, - indent: 8, - endIndent: 12, - color: Theme.of(context).dividerColor.withOpacity(0.08), - ) + // Divider( + // height: 1, + // indent: 8, + // endIndent: 12, + // color: Theme.of(context).dividerColor.withOpacity(0.08), + // ) ], ), ), @@ -118,6 +114,7 @@ class VideoCardH extends StatelessWidget { } class VideoContent extends StatelessWidget { + // ignore: prefer_typing_uninitialized_variables final videoItem; const VideoContent({super.key, required this.videoItem}); @@ -133,9 +130,11 @@ class VideoContent extends StatelessWidget { Text( videoItem.title, textAlign: TextAlign.start, - style: TextStyle( - fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, - fontWeight: FontWeight.w500), + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), maxLines: 2, overflow: TextOverflow.ellipsis, ), @@ -150,6 +149,7 @@ class VideoContent extends StatelessWidget { style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, + letterSpacing: 0.3, color: i['type'] == 'em' ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface, @@ -183,12 +183,13 @@ class VideoContent extends StatelessWidget { Text( videoItem.owner.name, style: TextStyle( - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, color: Theme.of(context).colorScheme.outline, ), ), ], ), + const SizedBox(height: 3), Row( children: [ StatView( @@ -196,12 +197,16 @@ class VideoContent extends StatelessWidget { view: videoItem.stat.view, ), const SizedBox(width: 8), - Text( - Utils.dateFormat(videoItem.pubdate!), - style: TextStyle( - fontSize: 11, - color: Theme.of(context).colorScheme.outline), - ) + StatDanMu( + theme: 'gray', + danmu: videoItem.stat.danmaku, + ), + // Text( + // Utils.dateFormat(videoItem.pubdate!), + // style: TextStyle( + // fontSize: 11, + // color: Theme.of(context).colorScheme.outline), + // ) ], ), ], diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 412bac8c..1a8c5df6 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -6,16 +6,16 @@ import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/pages/rcmd/index.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/utils.dart'; -import 'package:pilipala/pages/home/controller.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; // 视频卡片 - 垂直布局 class VideoCardV extends StatelessWidget { - var videoItem; - Function()? longPress; - Function()? longPressEnd; + // ignore: prefer_typing_uninitialized_variables + final videoItem; + final Function()? longPress; + final Function()? longPressEnd; - VideoCardV({ + const VideoCardV({ Key? key, required this.videoItem, this.longPress, @@ -26,7 +26,7 @@ class VideoCardV extends StatelessWidget { Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(videoItem.id); return Card( - elevation: 0.8, + elevation: 0, clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder( borderRadius: StyleString.mdRadius, @@ -56,41 +56,40 @@ class VideoCardV extends StatelessWidget { borderRadius: const BorderRadius.only( topLeft: StyleString.imgRadius, topRight: StyleString.imgRadius, + bottomLeft: StyleString.imgRadius, + bottomRight: StyleString.imgRadius, ), child: AspectRatio( aspectRatio: StyleString.aspectRatio, child: LayoutBuilder(builder: (context, boxConstraints) { double maxWidth = boxConstraints.maxWidth; double maxHeight = boxConstraints.maxHeight; - double PR = MediaQuery.of(context).devicePixelRatio; return Stack( children: [ Hero( tag: heroTag, child: NetworkImgLayer( - // 指定图片尺寸 - // src: videoItem.pic + '@${(maxWidth * 2).toInt()}w', src: videoItem.pic + '@.webp', width: maxWidth, height: maxHeight, ), ), - if (videoItem.stat.view is int && - videoItem.stat.danmaku is int) - Positioned( - left: 0, - right: 0, - bottom: 0, - child: AnimatedOpacity( - opacity: 1, - duration: const Duration(milliseconds: 200), - child: VideoStat( - view: videoItem.stat.view, - danmaku: videoItem.stat.danmaku, - duration: videoItem.duration, - ), - ), - ), + // if (videoItem.stat.view is int && + // videoItem.stat.danmaku is int) + // Positioned( + // left: 0, + // right: 0, + // bottom: 0, + // child: AnimatedOpacity( + // opacity: 1, + // duration: const Duration(milliseconds: 200), + // child: VideoStat( + // view: videoItem.stat.view, + // danmaku: videoItem.stat.danmaku, + // duration: videoItem.duration, + // ), + // ), + // ), ], ); }), @@ -106,6 +105,7 @@ class VideoCardV extends StatelessWidget { } class VideoContent extends StatelessWidget { + // ignore: prefer_typing_uninitialized_variables final videoItem; const VideoContent({Key? key, required this.videoItem}) : super(key: key); @override @@ -113,7 +113,7 @@ class VideoContent extends StatelessWidget { return Expanded( child: Padding( // 多列 - padding: const EdgeInsets.fromLTRB(8, 8, 6, 7), + padding: const EdgeInsets.fromLTRB(4, 5, 6, 6), // 单列 // padding: const EdgeInsets.fromLTRB(14, 10, 4, 8), child: Column( @@ -124,78 +124,76 @@ class VideoContent extends StatelessWidget { videoItem.title, textAlign: TextAlign.start, style: const TextStyle( - // fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, fontSize: 13, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, ), maxLines: Get.find().crossAxisCount, overflow: TextOverflow.ellipsis, ), - SizedBox( - height: 18, - child: Row( - children: [ - if (videoItem.rcmdReason != null && - videoItem.rcmdReason.content != '') ...[ - Container( - padding: const EdgeInsets.fromLTRB(3, 1, 3, 1), + + Row( + children: [ + if (videoItem.rcmdReason != null && + videoItem.rcmdReason.content != '' || + videoItem.isFollowed == 1) ...[ + Container( + padding: const EdgeInsets.fromLTRB(3, 0, 3, 0), decoration: BoxDecoration( color: Theme.of(context) .colorScheme .primaryContainer .withOpacity(0.6), borderRadius: BorderRadius.circular(3)), - child: Text( - videoItem.rcmdReason.content, - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize, - color: Theme.of(context).colorScheme.primary, - ), - ), - ), - const SizedBox(width: 4) - ] else if (videoItem.isFollowed == 1) ...[ - Container( - padding: const EdgeInsets.fromLTRB(3, 1, 3, 1), - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.6), - borderRadius: BorderRadius.circular(3)), - child: Text( - '已关注', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize, - color: Theme.of(context).colorScheme.primary, - ), - ), - ), - const SizedBox(width: 4) - ], - Expanded( - child: LayoutBuilder(builder: - (BuildContext context, BoxConstraints constraints) { - return SizedBox( - width: constraints.maxWidth, + child: Center( child: Text( - videoItem.owner.name, - maxLines: 1, + videoItem.rcmdReason != null && + videoItem.rcmdReason.content != '' + ? videoItem.rcmdReason.content + : '已关注', style: TextStyle( fontSize: Theme.of(context) .textTheme - .labelMedium! + .labelSmall! .fontSize, - color: Theme.of(context).colorScheme.outline, + color: Theme.of(context).colorScheme.primary, ), ), - ); - }), - ), + )), + const SizedBox(width: 4) ], - ), + Expanded( + child: LayoutBuilder(builder: + (BuildContext context, BoxConstraints constraints) { + return SizedBox( + width: constraints.maxWidth, + child: Text( + videoItem.owner.name, + maxLines: 1, + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + ); + }), + ), + ], ), + // Row( + // children: [ + // StatView( + // theme: 'black', + // view: videoItem.stat.view, + // ), + // const SizedBox(width: 6), + // StatDanMu( + // theme: 'black', + // danmu: videoItem.stat.danmaku, + // ), + // ], + // ), ], ), ), @@ -219,7 +217,7 @@ class VideoStat extends StatelessWidget { Widget build(BuildContext context) { return Container( height: 45, - padding: const EdgeInsets.only(top: 22, left: 8, right: 8), + padding: const EdgeInsets.only(top: 22, left: 6, right: 6), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, @@ -240,7 +238,7 @@ class VideoStat extends StatelessWidget { theme: 'white', view: view, ), - const SizedBox(width: 8), + const SizedBox(width: 6), StatDanMu( theme: 'white', danmu: danmaku, diff --git a/lib/http/init.dart b/lib/http/init.dart index b8fb87de..08be1514 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -151,7 +151,7 @@ class Request { cancelToken: cancelToken, ); return response; - } on DioError catch (e) { + } on DioException catch (e) { print('get error: $e'); return Future.error(await ApiInterceptor.dioError(e)); } @@ -173,7 +173,7 @@ class Request { ); print('post success: ${response.data}'); return response; - } on DioError catch (e) { + } on DioException catch (e) { print('post error: $e'); return Future.error(await ApiInterceptor.dioError(e)); } @@ -193,7 +193,7 @@ class Request { print('downloadFile success: ${response.data}'); return response.data; - } on DioError catch (e) { + } on DioException catch (e) { print('downloadFile error: $e'); return Future.error(ApiInterceptor.dioError(e)); } diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index 8ee8c8a0..87b97810 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -1,12 +1,12 @@ import 'package:dio/dio.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart' hide Response; +// import 'package:get/get.dart' hide Response; class ApiInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { - print("请求之前"); + // print("请求之前"); // 在请求之前添加头部或认证信息 // options.headers['Authorization'] = 'Bearer token'; // options.headers['Content-Type'] = 'application/json'; @@ -19,30 +19,30 @@ class ApiInterceptor extends Interceptor { } @override - void onError(DioError err, ErrorInterceptorHandler handler) async { + void onError(DioException err, ErrorInterceptorHandler handler) async { // 处理网络请求错误 // handler.next(err); SmartDialog.showToast(await dioError(err)); super.onError(err, handler); } - static Future dioError(DioError error) async { + static Future dioError(DioException error) async { switch (error.type) { - case DioErrorType.badCertificate: + case DioExceptionType.badCertificate: return '证书有误!'; - case DioErrorType.badResponse: + case DioExceptionType.badResponse: return '服务器异常,请稍后重试!'; - case DioErrorType.cancel: + case DioExceptionType.cancel: return "请求已被取消,请重新请求"; - case DioErrorType.connectionError: + case DioExceptionType.connectionError: return '连接错误,请检查网络设置'; - case DioErrorType.connectionTimeout: + case DioExceptionType.connectionTimeout: return "网络连接超时,请检查网络设置"; - case DioErrorType.receiveTimeout: + case DioExceptionType.receiveTimeout: return "响应超时,请稍后重试!"; - case DioErrorType.sendTimeout: + case DioExceptionType.sendTimeout: return "发送请求超时,请检查网络设置"; - case DioErrorType.unknown: + case DioExceptionType.unknown: var res = await checkConect(); return res + " \n 网络异常,请稍后重试!"; default: diff --git a/lib/http/search.dart b/lib/http/search.dart index d7655e7a..4a1a4fdd 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:pilipala/http/index.dart'; import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/common/search_type.dart'; @@ -57,7 +55,7 @@ class SearchHttp { 'page': page }); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { - var data; + Object data; switch (searchType) { case SearchType.video: data = SearchVideoModel.fromJson(res.data['data']); diff --git a/lib/http/user.dart b/lib/http/user.dart index d4b126e5..3b734d79 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:pilipala/http/api.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/models/model_hot_video_item.dart'; diff --git a/lib/http/video.dart b/lib/http/video.dart index e0acdd72..1de728cb 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -125,7 +125,6 @@ class VideoHttp { return {'status': false, 'data': []}; } } catch (err) { - print('🐯:$err'); return {'status': false, 'data': [], 'msg': err}; } } @@ -200,7 +199,7 @@ class VideoHttp { if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; } else { - return {'status': true, 'data': [], 'msg': ''}; + return {'status': false, 'data': [], 'msg': res.data['message']}; } } @@ -338,7 +337,7 @@ class VideoHttp { // 视频播放进度 static Future heartBeat({bvid, cid, progress, realtime}) async { - var res = await Request().post(Api.heartBeat, queryParameters: { + await Request().post(Api.heartBeat, queryParameters: { // 'aid': aid, 'bvid': bvid, 'cid': cid, diff --git a/lib/main.dart b/lib/main.dart index 330f9245..ce0793c1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,6 +34,7 @@ class MyApp extends StatelessWidget { return GetMaterialApp( title: 'PiLiPaLa', theme: ThemeData( + fontFamily: 'HarmonyOS', colorScheme: lightDynamic ?? ColorScheme.fromSeed( seedColor: Colors.green, @@ -42,6 +43,7 @@ class MyApp extends StatelessWidget { useMaterial3: true, ), darkTheme: ThemeData( + fontFamily: 'HarmonyOS', colorScheme: darkDynamic ?? ColorScheme.fromSeed( seedColor: Colors.green, diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index 8aea087c..9579f2e4 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -193,7 +193,7 @@ class SearchUserItemModel { usign = json['usign']; fans = json['fans']; videos = json['videos']; - upic = 'https:' + json['upic']; + upic = 'https:${json['upic']}'; faceNft = json['face_nft']; faceNftType = json['face_nft_type']; verifyInfo = json['verify_info']; diff --git a/lib/models/search/suggest.dart b/lib/models/search/suggest.dart index ddc8c231..eff7cb50 100644 --- a/lib/models/search/suggest.dart +++ b/lib/models/search/suggest.dart @@ -31,7 +31,7 @@ class SearchSuggestItem { SearchSuggestItem.fromJson(Map json, String inputTerm) { value = json['value']; term = json['term']; - String reg = '$inputTerm'; + String reg = '$inputTerm'; try { if (json['name'].indexOf(inputTerm) != -1) { String str = json['name'].replaceAll(reg, '^'); diff --git a/lib/models/video/reply/member.dart b/lib/models/video/reply/member.dart index 5576cbd1..0801d110 100644 --- a/lib/models/video/reply/member.dart +++ b/lib/models/video/reply/member.dart @@ -1,5 +1,3 @@ -import 'package:get/get.dart'; - class ReplyMember { ReplyMember({ this.mid, diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index c5297c1d..1d0af54b 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_print + import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -7,7 +9,6 @@ import 'package:pilipala/models/common/dynamics_type.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/models/dynamics/up.dart'; import 'package:pilipala/models/live/item.dart'; -import 'package:pilipala/utils/utils.dart'; class DynamicsController extends GetxController { int page = 1; diff --git a/lib/pages/dynamics/deatil/controller.dart b/lib/pages/dynamics/deatil/controller.dart index 773bf1b2..2992756b 100644 --- a/lib/pages/dynamics/deatil/controller.dart +++ b/lib/pages/dynamics/deatil/controller.dart @@ -1,14 +1,13 @@ import 'package:get/get.dart'; import 'package:pilipala/http/reply.dart'; import 'package:pilipala/models/common/reply_sort_type.dart'; -import 'package:pilipala/models/video/reply/data.dart'; import 'package:pilipala/models/video/reply/item.dart'; class DynamicDetailController extends GetxController { DynamicDetailController(this.oid, this.type); int? oid; int? type; - var item; + dynamic item; int? floor; int currentPage = 0; bool isLoadingMore = false; diff --git a/lib/pages/dynamics/deatil/view.dart b/lib/pages/dynamics/deatil/view.dart index de40a43e..36c28680 100644 --- a/lib/pages/dynamics/deatil/view.dart +++ b/lib/pages/dynamics/deatil/view.dart @@ -77,8 +77,12 @@ class _DynamicDetailPageState extends State { Get.to( () => Scaffold( appBar: AppBar( - title: const Text('评论详情'), + titleSpacing: 0, centerTitle: false, + title: Text( + '评论详情', + style: Theme.of(context).textTheme.titleMedium, + ), ), body: VideoReplyReplyPanel( oid: oid, diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index b8165176..9330679d 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -1,13 +1,11 @@ import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/models/common/dynamics_type.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/pages/mine/index.dart'; import 'package:pilipala/utils/storage.dart'; @@ -55,6 +53,7 @@ class _DynamicsPageState extends State @override Widget build(BuildContext context) { + super.build(context); return Scaffold( appBar: AppBar( elevation: 0, diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index ebe5753a..d601c025 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -8,11 +8,12 @@ import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/pages/dynamics/index.dart'; class ActionPanel extends StatefulWidget { - ActionPanel({ + const ActionPanel({ super.key, this.item, }); - var item; + // ignore: prefer_typing_uninitialized_variables + final item; @override State createState() => _ActionPanelState(); diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart index d2015aee..b3d6e98e 100644 --- a/lib/pages/dynamics/widgets/additional_panel.dart +++ b/lib/pages/dynamics/widgets/additional_panel.dart @@ -153,12 +153,12 @@ Widget addWidget(item, context, type, {floor = 1}) { ), )); case 'ADDITIONAL_TYPE_MATCH': - return SizedBox(); + return const SizedBox(); case 'ADDITIONAL_TYPE_COMMON': - return SizedBox(); + return const SizedBox(); case 'ADDITIONAL_TYPE_VOTE': - return SizedBox(); + return const SizedBox(); default: - return Text('11'); + return const Text('11'); } } diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index 3dcf2315..444aa3c0 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -7,8 +7,8 @@ import 'content_panel.dart'; import 'forward_panel.dart'; class DynamicPanel extends StatelessWidget { - var item; - String? source; + final dynamic item; + final String? source; DynamicPanel({this.item, this.source, Key? key}) : super(key: key); final DynamicsController _dynamicsController = Get.put(DynamicsController()); diff --git a/lib/pages/dynamics/widgets/live_panel.dart b/lib/pages/dynamics/widgets/live_panel.dart index d72994a9..289efa33 100644 --- a/lib/pages/dynamics/widgets/live_panel.dart +++ b/lib/pages/dynamics/widgets/live_panel.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; diff --git a/lib/pages/dynamics/widgets/rich_node_panel.dart b/lib/pages/dynamics/widgets/rich_node_panel.dart index f13c6548..78d5aaba 100644 --- a/lib/pages/dynamics/widgets/rich_node_panel.dart +++ b/lib/pages/dynamics/widgets/rich_node_panel.dart @@ -90,7 +90,7 @@ InlineSpan richNode(item, context) { '/webview', parameters: { 'url': - 'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=${dynamicId}&isWeb=1', + 'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=$dynamicId&isWeb=1', 'type': 'vote', 'pageTitle': '投票' }, diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index c6350249..f6147de3 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -10,8 +9,8 @@ import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; class UpPanel extends StatefulWidget { - FollowUpModel? upData; - UpPanel(this.upData, {Key? key}) : super(key: key); + final FollowUpModel? upData; + const UpPanel(this.upData, {Key? key}) : super(key: key); @override State createState() => _UpPanelState(); @@ -168,7 +167,9 @@ class _UpPanelState extends State { smallSize: 8, label: data.type == 'live' ? const Text('Live') : null, textColor: Theme.of(context).colorScheme.onSecondaryContainer, - alignment: AlignmentDirectional.bottomCenter, + alignment: data.type == 'live' + ? AlignmentDirectional.topCenter + : AlignmentDirectional.topEnd, padding: const EdgeInsets.only(left: 6, right: 6), isLabelVisible: data.type == 'live' || (data.type == 'up' && (data.hasUpdate ?? false)), diff --git a/lib/pages/fan/view.dart b/lib/pages/fan/view.dart index 26fd0a85..08bbb3d4 100644 --- a/lib/pages/fan/view.dart +++ b/lib/pages/fan/view.dart @@ -44,7 +44,11 @@ class _FansPageState extends State { elevation: 0, scrolledUnderElevation: 0, centerTitle: false, - title: const Text('我的粉丝'), + titleSpacing: 0, + title: Text( + '我的粉丝', + style: Theme.of(context).textTheme.titleMedium, + ), ), body: RefreshIndicator( onRefresh: () async => await _fansController.queryFans('init'), @@ -57,7 +61,7 @@ class _FansPageState extends State { List list = _fansController.fansList; return Obx( () => list.length == 1 - ? SizedBox() + ? const SizedBox() : ListView.builder( controller: scrollController, itemCount: list.length, @@ -74,7 +78,7 @@ class _FansPageState extends State { } } else { // 骨架屏 - return SizedBox(); + return const SizedBox(); } }, ), diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 2a92eede..54dbf5f6 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -5,8 +5,9 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; class FavItem extends StatelessWidget { - var favFolderItem; - FavItem({super.key, required this.favFolderItem}); + // ignore: prefer_typing_uninitialized_variables + final favFolderItem; + const FavItem({super.key, required this.favFolderItem}); @override Widget build(BuildContext context) { @@ -38,7 +39,6 @@ class FavItem extends StatelessWidget { builder: (context, boxConstraints) { double maxWidth = boxConstraints.maxWidth; double maxHeight = boxConstraints.maxHeight; - double PR = MediaQuery.of(context).devicePixelRatio; return Hero( tag: heroTag, child: NetworkImgLayer( @@ -62,7 +62,7 @@ class FavItem extends StatelessWidget { } class VideoContent extends StatelessWidget { - final favFolderItem; + final dynamic favFolderItem; const VideoContent({super.key, required this.favFolderItem}); @override @@ -76,9 +76,11 @@ class VideoContent extends StatelessWidget { Text( favFolderItem.title, textAlign: TextAlign.start, - style: TextStyle( - fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, - fontWeight: FontWeight.w500), + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), ), Text( '${favFolderItem.mediaCount}个内容', diff --git a/lib/pages/favDetail/widget/fav_video_card.dart b/lib/pages/favDetail/widget/fav_video_card.dart index 8bf12f8a..2b4c2409 100644 --- a/lib/pages/favDetail/widget/fav_video_card.dart +++ b/lib/pages/favDetail/widget/fav_video_card.dart @@ -11,7 +11,7 @@ import '../controller.dart'; // 收藏视频卡片 - 水平布局 class FavVideoCardH extends StatelessWidget { - var videoItem; + final dynamic videoItem; final FavDetailController _favDetailController = Get.put(FavDetailController()); @@ -28,9 +28,9 @@ class FavVideoCardH extends StatelessWidget { decoration: BoxDecoration( color: Theme.of(context).colorScheme.errorContainer, ), - child: Row( + child: const Row( mainAxisAlignment: MainAxisAlignment.center, - children: const [ + children: [ Icon(Icons.clear_all_rounded), SizedBox(width: 6), Text('取消收藏') @@ -51,7 +51,8 @@ class FavVideoCardH extends StatelessWidget { child: Column( children: [ Padding( - padding: const EdgeInsets.fromLTRB(12, 5, 12, 5), + padding: const EdgeInsets.fromLTRB( + StyleString.safeSpace, 5, StyleString.safeSpace, 5), child: LayoutBuilder( builder: (context, boxConstraints) { double width = @@ -68,21 +69,16 @@ class FavVideoCardH extends StatelessWidget { builder: (context, boxConstraints) { double maxWidth = boxConstraints.maxWidth; double maxHeight = boxConstraints.maxHeight; - double PR = - MediaQuery.of(context).devicePixelRatio; return Stack( children: [ Hero( tag: heroTag, child: NetworkImgLayer( - // src: videoItem['pic'] + - // '@${(maxWidth * 2).toInt()}w', src: videoItem.pic + '@.webp', width: maxWidth, height: maxHeight, ), ), - // Image.network( videoItem['pic'], width: double.infinity, height: double.infinity,), Positioned( right: 4, bottom: 4, @@ -121,7 +117,7 @@ class FavVideoCardH extends StatelessWidget { } class VideoContent extends StatelessWidget { - final videoItem; + final dynamic videoItem; const VideoContent({super.key, required this.videoItem}); @override @@ -135,9 +131,11 @@ class VideoContent extends StatelessWidget { Text( videoItem.title, textAlign: TextAlign.start, - style: TextStyle( - fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, - fontWeight: FontWeight.w500), + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), maxLines: 2, overflow: TextOverflow.ellipsis, ), @@ -145,10 +143,11 @@ class VideoContent extends StatelessWidget { Text( videoItem.owner.name, style: TextStyle( - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, color: Theme.of(context).colorScheme.outline, ), ), + const SizedBox(height: 2), Row( children: [ StatView( diff --git a/lib/pages/follow/view.dart b/lib/pages/follow/view.dart index 714b9300..37cd466a 100644 --- a/lib/pages/follow/view.dart +++ b/lib/pages/follow/view.dart @@ -43,8 +43,12 @@ class _FollowPageState extends State { appBar: AppBar( elevation: 0, scrolledUnderElevation: 0, + titleSpacing: 0, centerTitle: false, - title: const Text('我的关注'), + title: Text( + '我的关注', + style: Theme.of(context).textTheme.titleMedium, + ), ), body: RefreshIndicator( onRefresh: () async => @@ -58,7 +62,7 @@ class _FollowPageState extends State { List list = _followController.followList; return Obx( () => list.length == 1 - ? SizedBox() + ? const SizedBox() : ListView.builder( controller: scrollController, itemCount: list.length, @@ -75,7 +79,7 @@ class _FollowPageState extends State { } } else { // 骨架屏 - return SizedBox(); + return const SizedBox(); } }, )), diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index c1ac61a2..301b3e84 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -11,13 +11,16 @@ Widget followItem({item}) { leading: Hero( tag: heroTag, child: NetworkImgLayer( - width: 38, - height: 38, + width: 45, + height: 45, type: 'avatar', src: item.face, ), ), - title: Text(item.uname), + title: Text( + item.uname, + style: const TextStyle(fontSize: 14), + ), subtitle: Text( item.sign, maxLines: 1, diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 2d8b2ea4..b5dc3ee3 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -10,8 +10,8 @@ import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/utils.dart'; class HistoryItem extends StatelessWidget { - var videoItem; - HistoryItem({super.key, required this.videoItem}); + final dynamic videoItem; + const HistoryItem({super.key, required this.videoItem}); @override Widget build(BuildContext context) { @@ -59,7 +59,7 @@ class HistoryItem extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.fromLTRB( - StyleString.cardSpace, 7, StyleString.cardSpace, 7), + StyleString.cardSpace, 5, StyleString.cardSpace, 5), child: LayoutBuilder( builder: (context, boxConstraints) { double width = @@ -76,14 +76,11 @@ class HistoryItem extends StatelessWidget { builder: (context, boxConstraints) { double maxWidth = boxConstraints.maxWidth; double maxHeight = boxConstraints.maxHeight; - double PR = MediaQuery.of(context).devicePixelRatio; return Stack( children: [ Hero( tag: heroTag, child: NetworkImgLayer( - // src: videoItem['pic'] + - // '@${(maxWidth * 2).toInt()}w', src: (videoItem.cover != '' ? videoItem.cover : videoItem.covers.first) + @@ -124,12 +121,6 @@ class HistoryItem extends StatelessWidget { }, ), ), - Divider( - height: 1, - indent: 8, - endIndent: 12, - color: Theme.of(context).dividerColor.withOpacity(0.08), - ) ], ), ); @@ -137,7 +128,7 @@ class HistoryItem extends StatelessWidget { } class VideoContent extends StatelessWidget { - final videoItem; + final dynamic videoItem; const VideoContent({super.key, required this.videoItem}); @override @@ -151,9 +142,11 @@ class VideoContent extends StatelessWidget { Text( videoItem.title, textAlign: TextAlign.start, - style: TextStyle( - fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, - fontWeight: FontWeight.w500), + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), maxLines: videoItem.videos > 1 ? 1 : 2, overflow: TextOverflow.ellipsis, ), @@ -162,7 +155,7 @@ class VideoContent extends StatelessWidget { videoItem.showTitle, textAlign: TextAlign.start, style: TextStyle( - fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, fontWeight: FontWeight.w400, color: Theme.of(context).colorScheme.outline), maxLines: 2, @@ -174,7 +167,7 @@ class VideoContent extends StatelessWidget { Text( videoItem.authorName, style: TextStyle( - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, color: Theme.of(context).colorScheme.outline, ), ), @@ -185,7 +178,8 @@ class VideoContent extends StatelessWidget { Text( Utils.dateFormat(videoItem.viewAt!), style: TextStyle( - fontSize: 11, + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, color: Theme.of(context).colorScheme.outline), ) ], diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 08d3038f..e154fc0d 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -7,9 +7,30 @@ import 'package:pilipala/pages/rcmd/index.dart'; class HomeController extends GetxController with GetTickerProviderStateMixin { bool flag = false; List tabs = [ - {'label': '直播', 'type': 'live'}, - {'label': '推荐', 'type': 'rcm'}, - {'label': '热门', 'type': 'hot'}, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '直播', + 'type': 'live' + }, + { + 'icon': const Icon( + Icons.thumb_up_off_alt_outlined, + size: 15, + ), + 'label': '推荐', + 'type': 'rcm' + }, + { + 'icon': const Icon( + Icons.whatshot_outlined, + size: 15, + ), + 'label': '热门', + 'type': 'hot' + }, ]; int initialIndex = 1; late TabController tabController; diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index d5aebe17..3a265c84 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -29,7 +29,7 @@ class _HomePageState extends State appBar: AppBar( titleSpacing: 0, title: Padding( - padding: const EdgeInsets.only(left: 4, right: 4), + padding: const EdgeInsets.only(left: 12, right: 12, bottom: 0), child: Stack( children: [ const Align( @@ -55,26 +55,45 @@ class _HomePageState extends State splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明 highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明 ), - child: TabBar( - controller: _homeController.tabController, - tabs: [ - for (var i in _homeController.tabs) Tab(text: i['label']), - ], - isScrollable: true, - indicatorWeight: 0, - indicatorPadding: - const EdgeInsets.symmetric(horizontal: 3, vertical: 8), - indicator: BoxDecoration( - color: Theme.of(context).colorScheme.secondaryContainer, - borderRadius: const BorderRadius.all(Radius.circular(20)), + child: Padding( + padding: const EdgeInsets.only(top: 4), + child: TabBar( + controller: _homeController.tabController, + tabs: [ + for (var i in _homeController.tabs) + // Tab(text: i['label']) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 0, vertical: 11), + child: Row( + children: [ + i['icon'], + const SizedBox(width: 4), + Text(i['label']) + ], + ), + ), + ], + isScrollable: true, + indicatorWeight: 0, + indicatorPadding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 5), + indicator: BoxDecoration( + color: Theme.of(context) + .colorScheme + .primaryContainer + .withOpacity(0.8), + borderRadius: + const BorderRadius.all(Radius.circular(20)), + ), + indicatorSize: TabBarIndicatorSize.tab, + labelColor: Theme.of(context).colorScheme.primary, + labelStyle: const TextStyle(fontSize: 13), + dividerColor: Colors.transparent, + unselectedLabelColor: + Theme.of(context).colorScheme.outline, + onTap: (value) => {_homeController.initialIndex = value}, ), - indicatorSize: TabBarIndicatorSize.tab, - labelColor: - Theme.of(context).colorScheme.onSecondaryContainer, - labelStyle: const TextStyle(fontSize: 13), - dividerColor: Colors.transparent, - unselectedLabelColor: Theme.of(context).colorScheme.outline, - onTap: (value) => {_homeController.initialIndex = value}, ), ), ), diff --git a/lib/pages/home/widgets/app_bar.dart b/lib/pages/home/widgets/app_bar.dart index 4991c655..198a9fa3 100644 --- a/lib/pages/home/widgets/app_bar.dart +++ b/lib/pages/home/widgets/app_bar.dart @@ -1,7 +1,5 @@ -import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index bf4d7bd2..826f4e6e 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -86,7 +86,7 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { return SliverList( delegate: SliverChildBuilderDelegate((context, index) { return const VideoCardHSkeleton(); - }, childCount: 5), + }, childCount: 10), ); } }, diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index dd435363..d02c9d0d 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -26,8 +26,12 @@ class _LaterPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('稍后再看'), + titleSpacing: 0, centerTitle: false, + title: Text( + '稍后再看', + style: Theme.of(context).textTheme.titleMedium, + ), ), body: CustomScrollView( controller: _laterController.scrollController, diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index 99904fe0..8aba4073 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -19,9 +19,6 @@ class LivePage extends StatefulWidget { class _LivePageState extends State { final LiveController _liveController = Get.put(LiveController()); - @override - bool get wantKeepAlive => true; - @override void initState() { super.initState(); @@ -50,7 +47,7 @@ class _LivePageState extends State { SliverPadding( // 单列布局 EdgeInsets.zero padding: const EdgeInsets.fromLTRB( - StyleString.cardSpace, 0, StyleString.cardSpace, 8), + StyleString.safeSpace, 0, StyleString.safeSpace, 0), sliver: FutureBuilder( future: _liveController.queryLiveList('init'), builder: (context, snapshot) { @@ -97,13 +94,13 @@ class _LivePageState extends State { return SliverGrid( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( // 行间距 - mainAxisSpacing: StyleString.cardSpace, + mainAxisSpacing: StyleString.cardSpace + 2, // 列间距 - crossAxisSpacing: StyleString.cardSpace, + crossAxisSpacing: StyleString.cardSpace + 3, // 列数 crossAxisCount: ctr.crossAxisCount, mainAxisExtent: - Get.size.width / ctr.crossAxisCount / StyleString.aspectRatio + 70, + Get.size.width / ctr.crossAxisCount / StyleString.aspectRatio + 60, ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { diff --git a/lib/pages/live/widgets/live_item.dart b/lib/pages/live/widgets/live_item.dart index af8f9f18..0e91c834 100644 --- a/lib/pages/live/widgets/live_item.dart +++ b/lib/pages/live/widgets/live_item.dart @@ -8,11 +8,11 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; // 视频卡片 - 垂直布局 class LiveCardV extends StatelessWidget { - LiveItemModel liveItem; - Function()? longPress; - Function()? longPressEnd; + final LiveItemModel liveItem; + final Function()? longPress; + final Function()? longPressEnd; - LiveCardV({ + const LiveCardV({ Key? key, required this.liveItem, this.longPress, @@ -23,7 +23,7 @@ class LiveCardV extends StatelessWidget { Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(liveItem.roomId); return Card( - elevation: 0.8, + elevation: 0, clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder( borderRadius: StyleString.mdRadius, @@ -52,6 +52,8 @@ class LiveCardV extends StatelessWidget { borderRadius: const BorderRadius.only( topLeft: StyleString.imgRadius, topRight: StyleString.imgRadius, + bottomLeft: StyleString.imgRadius, + bottomRight: StyleString.imgRadius, ), child: AspectRatio( aspectRatio: StyleString.aspectRatio, @@ -68,6 +70,18 @@ class LiveCardV extends StatelessWidget { height: maxHeight, ), ), + Positioned( + left: 0, + right: 0, + bottom: 0, + child: AnimatedOpacity( + opacity: 1, + duration: const Duration(milliseconds: 200), + child: VideoStat( + liveItem: liveItem, + ), + ), + ), ], ); }), @@ -83,36 +97,39 @@ class LiveCardV extends StatelessWidget { } class LiveContent extends StatelessWidget { - final liveItem; + final dynamic liveItem; const LiveContent({Key? key, required this.liveItem}) : super(key: key); @override Widget build(BuildContext context) { return Expanded( child: Padding( // 多列 - padding: const EdgeInsets.fromLTRB(8, 7, 6, 4), + padding: const EdgeInsets.fromLTRB(4, 5, 6, 6), child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( liveItem.title, textAlign: TextAlign.start, style: const TextStyle( fontSize: 13, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, ), - maxLines: 1, + maxLines: 2, overflow: TextOverflow.ellipsis, ), - const SizedBox(height: 4), Row( children: [ - UpTag(), + const UpTag(), Expanded( child: Text( liveItem.uname, textAlign: TextAlign.start, - style: const TextStyle( - fontSize: 13, + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -120,23 +137,50 @@ class LiveContent extends StatelessWidget { ) ], ), - const SizedBox(height: 2), - Row( - children: [ - Text( - '${'[' + liveItem.areaName}]', - style: const TextStyle(fontSize: 11), - ), - const Text(' • '), - Text( - liveItem.watchedShow['text_large'], - style: const TextStyle(fontSize: 11), - ), - ], - ), ], ), ), ); } } + +class VideoStat extends StatelessWidget { + final LiveItemModel? liveItem; + + const VideoStat({ + Key? key, + required this.liveItem, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 45, + padding: const EdgeInsets.only(top: 22, left: 10, right: 10), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black54, + ], + tileMode: TileMode.mirror, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + liveItem!.areaName!, + style: const TextStyle(fontSize: 11, color: Colors.white), + ), + Text( + liveItem!.watchedShow!['text_small'], + style: const TextStyle(fontSize: 11, color: Colors.white), + ), + ], + ), + ); + } +} diff --git a/lib/pages/liveRoom/controller.dart b/lib/pages/liveRoom/controller.dart index 42e28e10..ecd49f6f 100644 --- a/lib/pages/liveRoom/controller.dart +++ b/lib/pages/liveRoom/controller.dart @@ -8,7 +8,7 @@ import 'package:pilipala/models/live/room_info.dart'; class LiveRoomController extends GetxController { String cover = ''; late int roomId; - var liveItem; + dynamic liveItem; late String heroTag; double volume = 0.0; // 静音状态 @@ -75,6 +75,5 @@ class LiveRoomController extends GetxController { volumeOff.value = true; meeduPlayerController.setVolume(0); } - print('🌹:${volumeOff.value}'); } } diff --git a/lib/pages/liveRoom/view.dart b/lib/pages/liveRoom/view.dart index 9bfd079b..8e73b70c 100644 --- a/lib/pages/liveRoom/view.dart +++ b/lib/pages/liveRoom/view.dart @@ -1,9 +1,6 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:get/get.dart'; -import 'dart:ui'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'controller.dart'; @@ -18,7 +15,6 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); MeeduPlayerController? _meeduPlayerController; - StreamSubscription? _playerEventSubs; bool isShowCover = true; bool isPlay = true; @@ -27,7 +23,7 @@ class _LiveRoomPageState extends State { void initState() { super.initState(); _meeduPlayerController = _liveRoomController.meeduPlayerController; - _playerEventSubs = _meeduPlayerController!.onPlayerStatusChanged.listen( + _meeduPlayerController!.onPlayerStatusChanged.listen( (PlayerStatus status) { if (status == PlayerStatus.playing) { isShowCover = false; @@ -45,7 +41,6 @@ class _LiveRoomPageState extends State { @override Widget build(BuildContext context) { - final double statusBarHeight = MediaQuery.of(context).padding.top; final videoHeight = MediaQuery.of(context).size.width * 9 / 16; return Scaffold( @@ -97,8 +92,8 @@ class _LiveRoomPageState extends State { aspectRatio: 16 / 9, child: MeeduVideoPlayer( header: (BuildContext context, - MeeduPlayerController _meeduPlayerController, - Responsive) { + MeeduPlayerController meeduPlayerController, + Responsive responsive) { return AppBar( backgroundColor: Colors.transparent, primary: false, @@ -115,7 +110,7 @@ class _LiveRoomPageState extends State { height: 38, child: IconButton( onPressed: () => - _meeduPlayerController.enterPip(context), + meeduPlayerController.enterPip(context), icon: const Icon( Icons.branding_watermark_outlined, size: 19, diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index faaf449f..0c81d155 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -1,19 +1,12 @@ -import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; -import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/view.dart'; -import 'package:pilipala/pages/hot/view.dart'; import 'package:pilipala/pages/media/index.dart'; -import 'package:pilipala/pages/mine/index.dart'; -import 'package:pilipala/utils/storage.dart'; class MainController extends GetxController { List pages = [ const HomePage(), - // const HotPage(), const DynamicsPage(), const MediaPage(), ]; @@ -25,13 +18,6 @@ class MainController extends GetxController { ), 'label': "推荐", }, - // { - // 'icon': const Icon( - // Icons.eco, - // size: 20, - // ), - // 'label': "热门", - // }, { 'icon': const Icon( Icons.bolt, diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 43fa8ab0..4e3d6c96 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -155,7 +155,7 @@ class MediaPage extends StatelessWidget { } } else { // 骨架屏 - return SizedBox(); + return const SizedBox(); } }), ), @@ -165,9 +165,9 @@ class MediaPage extends StatelessWidget { } class FavFolderItem extends StatelessWidget { - FavFolderItem({super.key, this.item, this.index}); - FavFolderItemData? item; - int? index; + const FavFolderItem({super.key, this.item, this.index}); + final FavFolderItemData? item; + final int? index; @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(item!.fid); diff --git a/lib/pages/member/archive/view.dart b/lib/pages/member/archive/view.dart index 4ec77775..e66f157b 100644 --- a/lib/pages/member/archive/view.dart +++ b/lib/pages/member/archive/view.dart @@ -24,6 +24,7 @@ class _ArchivePanelState extends State @override Widget build(BuildContext context) { + super.build(context); return PullToRefreshNotification( onRefresh: () async { await Future.delayed(const Duration(seconds: 1)); diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 78f172bc..eb3e4f33 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -7,7 +7,6 @@ import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/member/archive.dart'; import 'package:pilipala/models/member/info.dart'; import 'package:pilipala/utils/storage.dart'; -import 'package:pilipala/utils/wbi_sign.dart'; class MemberController extends GetxController { late int mid; @@ -48,13 +47,13 @@ class MemberController extends GetxController { return res; } - Future getMemberCardInfo() async { - var res = await MemberHttp.memberCardInfo(mid: mid); - if (res['status']) { - print(userStat); - } - return res; - } + // Future getMemberCardInfo() async { + // var res = await MemberHttp.memberCardInfo(mid: mid); + // if (res['status']) { + // print(userStat); + // } + // return res; + // } // 关注/取关up Future actionRelationMod() async { diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 68d752e6..75d5516f 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -2,12 +2,8 @@ import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; -import 'package:loading_more_list/loading_more_list.dart'; -import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/pages/member/archive/view.dart'; import 'package:pilipala/pages/member/index.dart'; -import 'package:pilipala/utils/utils.dart'; import 'widgets/profile.dart'; @@ -231,7 +227,7 @@ class _MemberPageState extends State ), ); } else { - return SizedBox(); + return const SizedBox(); } } else { // 骨架屏 diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index cf765778..3cf560dc 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -1,3 +1,5 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -321,11 +323,11 @@ class MinePage extends StatelessWidget { } class ActionItem extends StatelessWidget { - Icon? icon; - Function? onTap; - String? text; + final Icon? icon; + final Function? onTap; + final String? text; - ActionItem({ + const ActionItem({ Key? key, this.icon, this.onTap, diff --git a/lib/pages/preview/controller.dart b/lib/pages/preview/controller.dart index 20d9e9a3..8bc938f8 100644 --- a/lib/pages/preview/controller.dart +++ b/lib/pages/preview/controller.dart @@ -7,7 +7,6 @@ import 'package:dio/dio.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; -import 'package:pilipala/utils/utils.dart'; import 'package:share_plus/share_plus.dart'; class PreviewController extends GetxController { @@ -36,10 +35,8 @@ class PreviewController extends GetxController { // Permission.photos ].request(); - final info = statuses[Permission.storage].toString(); + statuses[Permission.storage].toString(); // final photosInfo = statuses[Permission.photos].toString(); - - print('授权状态:$info'); } // 图片保存 @@ -52,6 +49,7 @@ class PreviewController extends GetxController { name: "pic_vvex${DateTime.now().toString().split('-').join()}"); if (result != null) { if (result['isSuccess']) { + // ignore: avoid_print print('已保存到相册'); } } diff --git a/lib/pages/preview/view.dart b/lib/pages/preview/view.dart index 3dbca113..3127c281 100644 --- a/lib/pages/preview/view.dart +++ b/lib/pages/preview/view.dart @@ -1,8 +1,9 @@ +// ignore_for_file: library_private_types_in_public_api + import 'package:dismissible_page/dismissible_page.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:extended_image/extended_image.dart'; -import 'package:pilipala/common/widgets/appbar.dart'; import 'controller.dart'; typedef DoubleClickAnimationListener = void Function(); @@ -145,6 +146,8 @@ class _ImagePreviewState extends State ], ), ); + } else { + return const SizedBox(); } }, initGestureConfigHandler: (ExtendedImageState state) { @@ -168,7 +171,8 @@ class _ImagePreviewState extends State bottom: 0, child: Container( // height: 45, - padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom, top: 20), + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom, top: 20), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, @@ -186,23 +190,29 @@ class _ImagePreviewState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Obx( - () => Text.rich( + () => Text.rich( TextSpan( - style: const TextStyle( - color: Colors.white, - fontSize: 18 - ), + style: const TextStyle( + color: Colors.white, fontSize: 18), children: [ - TextSpan(text: _previewController.currentPage.toString()), - const TextSpan(text: ' / '), - TextSpan(text: _previewController.imgList.length.toString()), - ]), + TextSpan( + text: + _previewController.currentPage.toString()), + const TextSpan(text: ' / '), + TextSpan( + text: _previewController.imgList.length + .toString()), + ]), ), ), const Spacer(), - ElevatedButton(onPressed: () => _previewController.onShareImg(), child: Text('分享')), + ElevatedButton( + onPressed: () => _previewController.onShareImg(), + child: const Text('分享')), const SizedBox(width: 10), - ElevatedButton(onPressed: () => _previewController.onSaveImg(), child: Text('保存')) + ElevatedButton( + onPressed: () => _previewController.onSaveImg(), + child: const Text('保存')) ], ), ), diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 448e5477..74814892 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -41,6 +41,7 @@ class _RcmdPageState extends State @override Widget build(BuildContext context) { + super.build(context); return RefreshIndicator( onRefresh: () async { return await _rcmdController.onRefresh(); @@ -53,7 +54,7 @@ class _RcmdPageState extends State padding: _rcmdController.crossAxisCount == 1 ? EdgeInsets.zero : const EdgeInsets.fromLTRB( - StyleString.cardSpace, 0, StyleString.cardSpace, 8), + StyleString.safeSpace, 0, StyleString.safeSpace, 0), sliver: FutureBuilder( future: _rcmdController.queryRcmdFeed('init'), builder: (context, snapshot) { @@ -99,13 +100,13 @@ class _RcmdPageState extends State return SliverGrid( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( // 行间距 - mainAxisSpacing: StyleString.cardSpace, + mainAxisSpacing: StyleString.cardSpace + 2, // 列间距 - crossAxisSpacing: StyleString.cardSpace, + crossAxisSpacing: StyleString.cardSpace + 3, // 列数 crossAxisCount: ctr.crossAxisCount, mainAxisExtent: - Get.size.width / ctr.crossAxisCount / StyleString.aspectRatio + 70, + Get.size.width / ctr.crossAxisCount / StyleString.aspectRatio + 60, ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart index 3355cc3e..300154a9 100644 --- a/lib/pages/search/view.dart +++ b/lib/pages/search/view.dart @@ -1,10 +1,9 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'controller.dart'; -import 'widgets/hotKeyword.dart'; +import 'widgets/hot_keyword.dart'; import 'widgets/search_text.dart'; class SearchPage extends StatefulWidget { diff --git a/lib/pages/search/widgets/hotKeyword.dart b/lib/pages/search/widgets/hot_keyword.dart similarity index 96% rename from lib/pages/search/widgets/hotKeyword.dart rename to lib/pages/search/widgets/hot_keyword.dart index f5afd3ac..17bb5661 100644 --- a/lib/pages/search/widgets/hotKeyword.dart +++ b/lib/pages/search/widgets/hot_keyword.dart @@ -1,7 +1,6 @@ // ignore: file_names import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:pilipala/common/widgets/network_img_layer.dart'; class HotKeyword extends StatelessWidget { final double? width; diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 4739d49c..9f5f84c3 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; class SearchText extends StatelessWidget { - String? searchText; - Function? onSelect; - int? searchTextIdx; - SearchText({super.key, this.searchText, this.onSelect, this.searchTextIdx}); + final String? searchText; + final Function? onSelect; + final int? searchTextIdx; + const SearchText( + {super.key, this.searchText, this.onSelect, this.searchTextIdx}); @override Widget build(BuildContext context) { diff --git a/lib/pages/searchPanel/view.dart b/lib/pages/searchPanel/view.dart index e37366a1..4d49f11c 100644 --- a/lib/pages/searchPanel/view.dart +++ b/lib/pages/searchPanel/view.dart @@ -11,10 +11,10 @@ import 'widgets/user_panel.dart'; import 'widgets/video_panel.dart'; class SearchPanel extends StatefulWidget { - String? keyword; - SearchType? searchType; - String? tag; - SearchPanel( + final String? keyword; + final SearchType? searchType; + final String? tag; + const SearchPanel( {required this.keyword, required this.searchType, this.tag, Key? key}) : super(key: key); @@ -57,6 +57,7 @@ class _SearchPanelState extends State @override Widget build(BuildContext context) { + super.build(context); return RefreshIndicator( onRefresh: () async { await _searchPanelController!.onRefresh(); diff --git a/lib/pages/searchPanel/widgets/live_panel.dart b/lib/pages/searchPanel/widgets/live_panel.dart index 8357958a..177085a8 100644 --- a/lib/pages/searchPanel/widgets/live_panel.dart +++ b/lib/pages/searchPanel/widgets/live_panel.dart @@ -12,8 +12,8 @@ Widget searchLivePanel(BuildContext context, ctr, list) { controller: ctr!.scrollController, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, - crossAxisSpacing: StyleString.cardSpace, - mainAxisSpacing: StyleString.cardSpace, + crossAxisSpacing: StyleString.cardSpace + 2, + mainAxisSpacing: StyleString.cardSpace + 3, mainAxisExtent: MediaQuery.of(context).size.width / 2 / StyleString.aspectRatio + 65, @@ -22,7 +22,7 @@ Widget searchLivePanel(BuildContext context, ctr, list) { itemBuilder: (context, index) { var i = list![index]; return Card( - elevation: 0.8, + elevation: 0, clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder( borderRadius: StyleString.mdRadius, @@ -32,41 +32,46 @@ Widget searchLivePanel(BuildContext context, ctr, list) { onTap: () {}, child: Column( children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder(builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - double PR = MediaQuery.of(context).devicePixelRatio; - return Stack( - children: [ - Hero( - tag: Utils.makeHeroTag(i.roomid), - child: NetworkImgLayer( - // 指定图片尺寸 - // src: videoItem.pic + '@${(maxWidth * 2).toInt()}w', - src: i.cover + '@.webp', - type: 'emote', - width: maxWidth, - height: maxHeight, - ), - ), - Positioned( - left: 0, - right: 0, - bottom: 0, - child: AnimatedOpacity( - opacity: 1, - duration: const Duration(milliseconds: 200), - child: LiveStat( - online: i.online, - cateName: i.cateName, + ClipRRect( + borderRadius: const BorderRadius.only( + topLeft: StyleString.imgRadius, + topRight: StyleString.imgRadius, + bottomLeft: StyleString.imgRadius, + bottomRight: StyleString.imgRadius, + ), + child: AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder(builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: Utils.makeHeroTag(i.roomid), + child: NetworkImgLayer( + src: i.cover + '@.webp', + type: 'emote', + width: maxWidth, + height: maxHeight, ), ), - ), - ], - ); - }), + Positioned( + left: 0, + right: 0, + bottom: 0, + child: AnimatedOpacity( + opacity: 1, + duration: const Duration(milliseconds: 200), + child: LiveStat( + online: i.online, + cateName: i.cateName, + ), + ), + ), + ], + ); + }), + ), ), LiveContent(liveItem: i) ], @@ -79,13 +84,13 @@ Widget searchLivePanel(BuildContext context, ctr, list) { } class LiveContent extends StatelessWidget { - final liveItem; + final dynamic liveItem; const LiveContent({Key? key, required this.liveItem}) : super(key: key); @override Widget build(BuildContext context) { return Expanded( child: Padding( - padding: const EdgeInsets.fromLTRB(8, 8, 6, 7), + padding: const EdgeInsets.fromLTRB(4, 5, 6, 6), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -99,6 +104,7 @@ class LiveContent extends StatelessWidget { style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, + letterSpacing: 0.3, color: i['type'] == 'em' ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface, diff --git a/lib/pages/searchPanel/widgets/user_panel.dart b/lib/pages/searchPanel/widgets/user_panel.dart index 471b8c98..918082bc 100644 --- a/lib/pages/searchPanel/widgets/user_panel.dart +++ b/lib/pages/searchPanel/widgets/user_panel.dart @@ -42,9 +42,8 @@ Widget searchUserPanel(BuildContext context, ctr, list) { children: [ Text( i!.uname, - style: TextStyle( - fontSize: - Theme.of(context).textTheme.titleMedium!.fontSize, + style: const TextStyle( + fontSize: 14, ), ), const SizedBox(width: 6), @@ -71,7 +70,6 @@ Widget searchUserPanel(BuildContext context, ctr, list) { ), ), ); - ; }, ); } diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index 994a7488..a36b5527 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -5,6 +5,7 @@ import 'package:pilipala/pages/setting/index.dart'; class SettingPage extends StatelessWidget { const SettingPage({super.key}); + @override Widget build(BuildContext context) { final SettingController settingController = Get.put(SettingController()); return Scaffold( diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 4c4c3085..1f7594b8 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -1,6 +1,4 @@ import 'dart:async'; -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:get/get.dart'; diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 6acd2e19..bb208b3b 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -105,7 +105,6 @@ class VideoIntroController extends GetxController { // 获取up主粉丝数 Future queryUserStat() async { var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!); - print('🌹:$result'); if (result['status']) { userStat = result['data']; } @@ -185,9 +184,11 @@ class VideoIntroController extends GetxController { if (!hasLike.value) { SmartDialog.showToast('点赞成功 👍'); hasLike.value = true; + videoDetail.value.stat!.like = videoDetail.value.stat!.like! + 1; } else if (hasLike.value) { SmartDialog.showToast('取消赞'); hasLike.value = false; + videoDetail.value.stat!.like = videoDetail.value.stat!.like! - 1; } hasLike.refresh(); } else { @@ -238,14 +239,15 @@ class VideoIntroController extends GetxController { onPressed: () async { var res = await VideoHttp.coinVideo( bvid: bvid, multiply: _tempThemeValue); - print(res); if (res['status']) { - SmartDialog.showToast('投币成功'); + SmartDialog.showToast('投币成功 👏'); + hasCoin.value = true; + videoDetail.value.stat!.coin = + videoDetail.value.stat!.coin! + _tempThemeValue; } else { SmartDialog.showToast(res['msg']); } Get.back(); - queryHasCoinVideo(); }, child: const Text('确定')) ], @@ -263,7 +265,10 @@ class VideoIntroController extends GetxController { delMediaIdsNew.add(i.id); } } - } catch (e) {} + } catch (e) { + // ignore: avoid_print + print(e); + } var result = await VideoHttp.favVideo( aid: IdUtils.bv2av(bvid), addIds: addMediaIdsNew.join(','), @@ -282,10 +287,8 @@ class VideoIntroController extends GetxController { // 分享视频 Future actionShareVideo() async { - var result = - await Share.share('${HttpString.baseUrl}/video/$bvid').whenComplete(() { - print("share completion block "); - }); + var result = await Share.share('${HttpString.baseUrl}/video/$bvid') + .whenComplete(() {}); return result; } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index e90e2c4a..8b1a6380 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -7,10 +6,7 @@ import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/pages/fav/index.dart'; -import 'package:pilipala/pages/favDetail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; -import 'package:pilipala/pages/video/detail/widgets/expandable_section.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/stat/danmu.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; @@ -19,7 +15,9 @@ import 'package:pilipala/pages/video/detail/introduction/controller.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; +import 'widgets/action_row_item.dart'; import 'widgets/fav_panel.dart'; +import 'widgets/intro_detail.dart'; import 'widgets/season.dart'; class VideoIntroPanel extends StatefulWidget { @@ -55,6 +53,7 @@ class _VideoIntroPanelState extends State @override Widget build(BuildContext context) { + super.build(context); return FutureBuilder( future: videoIntroController.queryVideoIntro(), builder: (context, snapshot) { @@ -79,10 +78,10 @@ class _VideoIntroPanelState extends State } class VideoInfo extends StatefulWidget { - bool loadingStatus = false; - VideoDetailData? videoDetail; + final bool loadingStatus; + final VideoDetailData? videoDetail; - VideoInfo({Key? key, required this.loadingStatus, this.videoDetail}) + const VideoInfo({Key? key, this.loadingStatus = false, this.videoDetail}) : super(key: key); @override @@ -95,14 +94,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Get.put(VideoIntroController(), tag: Get.arguments['heroTag']); bool isExpand = false; - /// 手动控制动画的控制器 - late AnimationController? _manualController; - - /// 手动控制 - late Animation? _manualAnimation; - - final FavController _favController = Get.put(FavController()); - late VideoDetailController? videoDetailCtr; Box localCache = GStrorage.localCache; late double sheetHeight; @@ -111,13 +102,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { void initState() { super.initState(); - /// 不设置重复,使用代码控制进度,动画时间1秒 - _manualController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 400), - ); - _manualAnimation = - Tween(begin: 0.5, end: 1.5).animate(_manualController!); videoDetailCtr = Get.find(tag: Get.arguments['heroTag']); sheetHeight = localCache.get('sheetHeight'); @@ -141,143 +125,123 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { return SliverPadding( - padding: const EdgeInsets.only(left: 12, right: 12, top: 10), + padding: const EdgeInsets.only( + left: StyleString.safeSpace, right: StyleString.safeSpace, top: 13), sliver: SliverToBoxAdapter( child: !widget.loadingStatus || videoItem.isNotEmpty ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SelectableRegion( - magnifierConfiguration: const TextMagnifierConfiguration(), - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Text( - !widget.loadingStatus - ? widget.videoDetail!.title - : videoItem['title'], - style: Theme.of(context).textTheme.titleMedium!.copyWith( - letterSpacing: 0.5, - ), - ), - ), - InkWell( - splashColor: Colors.transparent, - hoverColor: Colors.transparent, - highlightColor: Colors.transparent, + GestureDetector( onTap: () { - _manualController!.animateTo(isExpand ? 0 : 0.5); - setState(() { - isExpand = !isExpand; - }); + showBottomSheet( + context: context, + enableDrag: true, + builder: (BuildContext context) { + return IntroDetail(videoDetail: widget.videoDetail!); + }, + ); }, child: Row( children: [ - const SizedBox(width: 2), - StatView( - theme: 'gray', - view: !widget.loadingStatus - ? widget.videoDetail!.stat!.view - : videoItem['stat'].view, - size: 'medium', - ), - const SizedBox(width: 10), - StatDanMu( - theme: 'gray', - danmu: !widget.loadingStatus - ? widget.videoDetail!.stat!.danmaku - : videoItem['stat'].danmaku, - size: 'medium', - ), - const SizedBox(width: 10), - Text( - Utils.dateFormat( - !widget.loadingStatus - ? widget.videoDetail!.pubdate - : videoItem['pubdate'], - formatType: 'detail'), - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.outline), - ), - const Spacer(), - RotationTransition( - turns: _manualAnimation!, - child: SizedBox( - width: 35, - height: 35, - child: IconButton( - padding: const EdgeInsets.all(2.0), - onPressed: () { - /// 0.5代表 180弧度 - _manualController! - .animateTo(isExpand ? 0 : 0.5); - setState(() { - isExpand = !isExpand; - }); - }, - icon: Icon( - FontAwesomeIcons.angleUp, - size: 15, - color: Theme.of(context).colorScheme.outline, - ), + Expanded( + child: Text( + !widget.loadingStatus + ? widget.videoDetail!.title + : videoItem['title'], + style: const TextStyle( + fontSize: 18, + letterSpacing: 0.3, + fontWeight: FontWeight.w500, ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 20), + SizedBox( + width: 34, + height: 34, + child: IconButton( + style: ButtonStyle( + padding: + MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: + MaterialStateProperty.resolveWith((states) { + return Theme.of(context) + .highlightColor + .withOpacity(0.2); + }), + ), + onPressed: () { + showBottomSheet( + context: context, + enableDrag: true, + builder: (BuildContext context) { + return IntroDetail( + videoDetail: widget.videoDetail!); + }, + ); + }, + icon: const Icon(Icons.more_horiz), ), ), - const SizedBox(width: 10), ], ), ), - // 简介 默认收起 - if (!widget.loadingStatus) - ExpandedSection( - expand: isExpand, - begin: 0.0, - end: 1.0, - child: DefaultTextStyle( - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - height: 1.5, - fontSize: - Theme.of(context).textTheme.labelMedium?.fontSize, - ), - child: Padding( - padding: const EdgeInsets.only(bottom: 10), - child: SelectableRegion( - magnifierConfiguration: - const TextMagnifierConfiguration(), - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(widget.videoDetail!.bvid!), - Text.rich( - TextSpan( - children: [ - buildContent( - context, widget.videoDetail!), - ], - ), - ), - ], - ), - ), - ), + const SizedBox(height: 6), + Row( + children: [ + const SizedBox(width: 2), + StatView( + theme: 'black', + view: !widget.loadingStatus + ? widget.videoDetail!.stat!.view + : videoItem['stat'].view, + size: 'medium', + ), + const SizedBox(width: 10), + StatDanMu( + theme: 'black', + danmu: !widget.loadingStatus + ? widget.videoDetail!.stat!.danmaku + : videoItem['stat'].danmaku, + size: 'medium', + ), + const SizedBox(width: 10), + Text( + Utils.dateFormat( + !widget.loadingStatus + ? widget.videoDetail!.pubdate + : videoItem['pubdate'], + formatType: 'detail'), + style: const TextStyle(fontSize: 12), + ), + ], + ), + // 点赞收藏转发 + Padding( + padding: const EdgeInsets.only(top: 15), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: actionRow( + context, + videoIntroController, + videoDetailCtr, ), ), - const SizedBox(height: 8), - // 点赞收藏转发 - _actionGrid(context, videoIntroController, videoDetailCtr), + ), // 合集 if (!widget.loadingStatus && widget.videoDetail!.ugcSeason != null) ...[ seasonPanel(widget.videoDetail!.ugcSeason!, widget.videoDetail!.pages!.first.cid, sheetHeight) ], - Divider( - height: 26, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), + // Divider( + // height: 26, + // color: Theme.of(context).dividerColor.withOpacity(0.1), + // ), + const SizedBox(height: 20), GestureDetector( onTap: () { int mid = !widget.loadingStatus @@ -293,53 +257,84 @@ class _VideoInfoState extends State with TickerProviderStateMixin { }, child: Row( children: [ + const SizedBox(width: 5), NetworkImgLayer( type: 'avatar', src: !widget.loadingStatus ? widget.videoDetail!.owner!.face : videoItem['owner'].face, - width: 38, - height: 38, + width: 34, + height: 34, fadeInDuration: Duration.zero, fadeOutDuration: Duration.zero, ), - const SizedBox(width: 14), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(!widget.loadingStatus - ? widget.videoDetail!.owner!.name - : videoItem['owner'].name), - // const SizedBox(width: 10), - Text( - widget.loadingStatus - ? '- 粉丝' - : '${Utils.numFormat(videoIntroController.userStat['follower'])}粉丝', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelSmall! - .fontSize, - color: Theme.of(context).colorScheme.outline), - ), - ], + const SizedBox(width: 10), + Text( + !widget.loadingStatus + ? widget.videoDetail!.owner!.name + : videoItem['owner'].name, + style: const TextStyle(fontSize: 13), + ), + const SizedBox(width: 10), + Text( + widget.loadingStatus + ? '- 粉丝' + : Utils.numFormat( + videoIntroController.userStat['follower']), + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize, + color: Theme.of(context).colorScheme.outline), ), const Spacer(), AnimatedOpacity( opacity: widget.loadingStatus ? 0 : 1, duration: const Duration(milliseconds: 150), child: SizedBox( - height: 36, + height: 32, child: Obx( () => videoIntroController.followStatus.isNotEmpty - ? ElevatedButton( + ? TextButton( onPressed: () => videoIntroController .actionRelationMod(), - child: Text(videoIntroController - .followStatus['attribute'] == - 0 - ? '关注' - : '已关注'), + style: TextButton.styleFrom( + padding: const EdgeInsets.only( + left: 8, right: 8), + foregroundColor: + videoIntroController.followStatus[ + 'attribute'] != + 0 + ? Theme.of(context) + .colorScheme + .outline + : Theme.of(context) + .colorScheme + .onPrimary, + backgroundColor: + videoIntroController.followStatus[ + 'attribute'] != + 0 + ? Theme.of(context) + .colorScheme + .onInverseSurface + : Theme.of(context) + .colorScheme + .primary, // 设置按钮背景色 + ), + child: Text( + videoIntroController.followStatus[ + 'attribute'] != + 0 + ? '已关注' + : '关注', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .labelMedium! + .fontSize), + ), ) : ElevatedButton( onPressed: () => videoIntroController @@ -349,110 +344,87 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ), + const SizedBox(width: 4) ], ), ), - - Divider( - height: 26, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), - // const SizedBox(height: 10), + const SizedBox(height: 12), + // Divider( + // height: 12, + // color: Theme.of(context).dividerColor.withOpacity(0.1), + // ), ], ) - : const Center(child: CircularProgressIndicator()), + : const SizedBox( + height: 100, + child: Center( + child: CircularProgressIndicator(), + ), + ), ), ); } - // 喜欢 投币 分享 - Widget _actionGrid( - BuildContext context, videoIntroController, videoDetailCtr) { - return LayoutBuilder(builder: (context, constraints) { - return SizedBox( - height: constraints.maxWidth / 5 * 0.8, - child: Material( - child: GridView.count( - primary: false, - padding: const EdgeInsets.all(0), - crossAxisCount: 5, - childAspectRatio: 1.25, - children: [ - // InkWell( - // onTap: () => videoIntroController.actionOneThree(), - // borderRadius: StyleString.mdRadius, - // child: Padding( - // padding: const EdgeInsets.all(12), - // child: Image.asset( - // 'assets/images/logo/logo_big.png', - // width: 10, - // height: 10, - // ), - // ), - // ), - Obx( - () => ActionItem( - icon: const Icon(FontAwesomeIcons.thumbsUp), - selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), - onTap: () => videoIntroController.actionLikeVideo(), - selectStatus: videoIntroController.hasLike.value, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.videoDetail!.stat!.like!.toString() - : '-'), - ), - // ActionItem( - // icon: const Icon(FontAwesomeIcons.thumbsDown), - // selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), - // onTap: () => {}, - // selectStatus: false, - // loadingStatus: widget.loadingStatus, - // text: '不喜欢'), - Obx( - () => ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), - onTap: () => videoIntroController.actionCoinVideo(), - selectStatus: videoIntroController.hasCoin.value, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.videoDetail!.stat!.coin!.toString() - : '-'), - ), - Obx( - () => ActionItem( - icon: const Icon(FontAwesomeIcons.heart), - selectIcon: const Icon(FontAwesomeIcons.heartCircleCheck), - onTap: () => showFavBottomSheet(), - selectStatus: videoIntroController.hasFav.value, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.videoDetail!.stat!.favorite!.toString() - : '-'), - ), - ActionItem( - icon: const Icon(FontAwesomeIcons.shareFromSquare), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.videoDetail!.stat!.share!.toString() - : '-'), - ActionItem( - icon: const Icon(FontAwesomeIcons.comments), - onTap: () { - videoDetailCtr.tabCtr.animateTo(1); - }, - selectStatus: false, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.videoDetail!.stat!.reply!.toString() - : '-'), - ], - ), + Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) { + return Row(children: [ + Obx( + () => ActionRowItem( + icon: const Icon(FontAwesomeIcons.thumbsUp), + onTap: () => videoIntroController.actionLikeVideo(), + selectStatus: videoIntroController.hasLike.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.like!.toString() + : '-', ), - ); - }); + ), + const SizedBox(width: 8), + Obx( + () => ActionRowItem( + icon: const Icon(FontAwesomeIcons.b), + onTap: () => videoIntroController.actionCoinVideo(), + selectStatus: videoIntroController.hasCoin.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.coin!.toString() + : '-', + ), + ), + const SizedBox(width: 8), + Obx( + () => ActionRowItem( + icon: const Icon(FontAwesomeIcons.heart), + onTap: () => showFavBottomSheet(), + selectStatus: videoIntroController.hasFav.value, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.favorite!.toString() + : '-', + ), + ), + const SizedBox(width: 8), + ActionRowItem( + icon: const Icon(FontAwesomeIcons.comment), + onTap: () { + videoDetailCtr.tabCtr.animateTo(1); + }, + selectStatus: false, + loadingStatus: widget.loadingStatus, + text: !widget.loadingStatus + ? widget.videoDetail!.stat!.reply!.toString() + : '-', + ), + const SizedBox(width: 8), + ActionRowItem( + icon: const Icon(FontAwesomeIcons.share), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, + loadingStatus: widget.loadingStatus, + // text: !widget.loadingStatus + // ? widget.videoDetail!.stat!.share!.toString() + // : '-', + text: '转发'), + ]); } InlineSpan buildContent(BuildContext context, content) { @@ -491,54 +463,3 @@ class _VideoInfoState extends State with TickerProviderStateMixin { return TextSpan(children: spanChilds); } } - -class ActionItem extends StatelessWidget { - Icon? icon; - Icon? selectIcon; - Function? onTap; - bool? loadingStatus; - String? text; - bool selectStatus = false; - - ActionItem({ - Key? key, - this.icon, - this.selectIcon, - this.onTap, - this.loadingStatus, - this.text, - required this.selectStatus, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () => onTap!(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 4), - selectStatus - ? Icon(selectIcon!.icon!, - size: 21, color: Theme.of(context).primaryColor) - : Icon(icon!.icon!, - size: 21, color: Theme.of(context).colorScheme.outline), - const SizedBox(height: 4), - AnimatedOpacity( - opacity: loadingStatus! ? 0 : 1, - duration: const Duration(milliseconds: 200), - child: Text( - text ?? '', - style: TextStyle( - color: selectStatus - ? Theme.of(context).primaryColor - : Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall?.fontSize), - ), - ), - ], - ), - ); - } -} diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart new file mode 100644 index 00000000..789ceab6 --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:pilipala/common/constants.dart'; + +class ActionItem extends StatelessWidget { + final Icon? icon; + final Icon? selectIcon; + final Function? onTap; + final bool? loadingStatus; + final String? text; + final bool selectStatus; + + const ActionItem({ + Key? key, + this.icon, + this.selectIcon, + this.onTap, + this.loadingStatus, + this.text, + this.selectStatus = false, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () => onTap!(), + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + selectStatus + ? Icon(selectIcon!.icon!, + size: 21, color: Theme.of(context).primaryColor) + : Icon(icon!.icon!, + size: 21, color: Theme.of(context).colorScheme.outline), + const SizedBox(height: 4), + AnimatedOpacity( + opacity: loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: Text( + text ?? '', + style: TextStyle( + color: selectStatus + ? Theme.of(context).primaryColor + : Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context).textTheme.labelSmall?.fontSize), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/video/detail/introduction/widgets/action_row_item.dart b/lib/pages/video/detail/introduction/widgets/action_row_item.dart new file mode 100644 index 00000000..254d7bcd --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/action_row_item.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; + +class ActionRowItem extends StatelessWidget { + final Icon? icon; + final Icon? selectIcon; + final Function? onTap; + final bool? loadingStatus; + final String? text; + final bool selectStatus; + + const ActionRowItem({ + Key? key, + this.icon, + this.selectIcon, + this.onTap, + this.loadingStatus, + this.text, + this.selectStatus = false, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + color: selectStatus + ? Theme.of(context).colorScheme.primaryContainer.withOpacity(0.6) + : Theme.of(context).highlightColor.withOpacity(0.2), + borderRadius: const BorderRadius.all(Radius.circular(30)), + clipBehavior: Clip.hardEdge, + child: InkWell( + onTap: () => onTap!(), + child: Padding( + padding: const EdgeInsets.fromLTRB(13, 6.5, 15, 6.3), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (icon != null) ...[ + Icon(icon!.icon!, + size: 13, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSecondaryContainer), + const SizedBox(width: 6), + ], + AnimatedOpacity( + opacity: loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Text( + text ?? '', + key: ValueKey(text ?? ''), + style: TextStyle( + color: selectStatus + ? Theme.of(context).colorScheme.primary + : null, + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 625211b7..b640242d 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -5,8 +5,8 @@ import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/utils/storage.dart'; class FavPanel extends StatefulWidget { - var ctr; - FavPanel({this.ctr}); + final dynamic ctr; + const FavPanel({super.key, this.ctr}); @override State createState() => _FavPanelState(); @@ -111,7 +111,7 @@ class _FavPanelState extends State { } } else { // 骨架屏 - return Text('请求中'); + return const Text('请求中'); } }, ), diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart new file mode 100644 index 00000000..75bc9bd4 --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -0,0 +1,149 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/common/widgets/stat/danmu.dart'; +import 'package:pilipala/common/widgets/stat/view.dart'; +import 'package:pilipala/utils/storage.dart'; +import 'package:pilipala/utils/utils.dart'; + +Box localCache = GStrorage.localCache; +late double sheetHeight; + +class IntroDetail extends StatelessWidget { + final dynamic videoDetail; + + const IntroDetail({ + Key? key, + this.videoDetail, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + sheetHeight = localCache.get('sheetHeight'); + return Container( + color: Theme.of(context).colorScheme.background, + padding: const EdgeInsets.only(left: 14, right: 14), + height: sheetHeight, + child: Column( + children: [ + Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onSecondaryContainer + .withOpacity(0.5), + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoDetail!.title, + style: const TextStyle( + fontSize: 18, + letterSpacing: 0.5, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 10), + Row( + children: [ + const SizedBox(width: 2), + StatView( + theme: 'black', + view: videoDetail!.stat!.view, + size: 'medium', + ), + const SizedBox(width: 10), + StatDanMu( + theme: 'black', + danmu: videoDetail!.stat!.danmaku, + size: 'medium', + ), + const SizedBox(width: 10), + Text( + Utils.dateFormat(videoDetail!.pubdate, + formatType: 'detail'), + style: const TextStyle(fontSize: 12), + ), + ], + ), + const SizedBox(height: 20), + SizedBox( + width: double.infinity, + child: SelectableRegion( + magnifierConfiguration: + const TextMagnifierConfiguration(), + focusNode: FocusNode(), + selectionControls: MaterialTextSelectionControls(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(videoDetail!.bvid!), + const SizedBox(height: 4), + Text.rich( + TextSpan( + children: [ + buildContent(context, videoDetail!), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ) + ], + )); + } + + InlineSpan buildContent(BuildContext context, content) { + String desc = content.desc; + List descV2 = content.descV2; + // type + // 1 普通文本 + // 2 @用户 + List spanChilds = []; + if (descV2.isNotEmpty) { + for (var i = 0; i < descV2.length; i++) { + if (descV2[i].type == 1) { + spanChilds.add(TextSpan(text: descV2[i].rawText)); + } else if (descV2[i].type == 2) { + spanChilds.add( + TextSpan( + text: '@${descV2[i].rawText}', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + String heroTag = Utils.makeHeroTag(descV2[i].bizId); + Get.toNamed( + '/member?mid=${descV2[i].bizId}', + arguments: {'face': '', 'heroTag': heroTag}, + ); + }, + ), + ); + } + } + } else { + spanChilds.add(TextSpan(text: desc)); + } + return TextSpan(children: spanChilds); + } +} diff --git a/lib/pages/video/detail/introduction/widgets/menu_row.dart b/lib/pages/video/detail/introduction/widgets/menu_row.dart new file mode 100644 index 00000000..4a9aef35 --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/menu_row.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; + +class MenuRow extends StatelessWidget { + final bool? loadingStatus; + const MenuRow({ + Key? key, + this.loadingStatus, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + color: Theme.of(context).colorScheme.background, + padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row(children: [ + actionRowLineItem( + context, + () => {}, + loadingStatus, + '推荐', + selectStatus: true, + ), + const SizedBox(width: 8), + actionRowLineItem( + context, + () => {}, + loadingStatus, + '弹幕', + selectStatus: false, + ), + const SizedBox(width: 8), + actionRowLineItem( + context, + () => {}, + loadingStatus, + '评论列表', + selectStatus: false, + ), + const SizedBox(width: 8), + actionRowLineItem( + context, + () => {}, + loadingStatus, + '播放列表', + selectStatus: false, + ), + ]), + ), + ); + } + + Widget actionRowLineItem( + context, Function? onTap, bool? loadingStatus, String? text, + {bool selectStatus = false}) { + return Material( + color: selectStatus + ? Theme.of(context).highlightColor.withOpacity(0.2) + : Colors.transparent, + borderRadius: const BorderRadius.all(Radius.circular(30)), + clipBehavior: Clip.hardEdge, + child: InkWell( + onTap: () => onTap!(), + child: Container( + padding: const EdgeInsets.fromLTRB(13, 5.5, 13, 5.5), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(30)), + border: Border.all( + color: selectStatus + ? Colors.transparent + : Theme.of(context).highlightColor.withOpacity(0.2), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + AnimatedOpacity( + opacity: loadingStatus! ? 0 : 1, + duration: const Duration(milliseconds: 200), + child: Text( + text!, + style: const TextStyle(fontSize: 13), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/video/detail/introduction/widgets/season.dart b/lib/pages/video/detail/introduction/widgets/season.dart index a5f74af2..892fd7dc 100644 --- a/lib/pages/video/detail/introduction/widgets/season.dart +++ b/lib/pages/video/detail/introduction/widgets/season.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:pilipala/models/video_detail_res.dart'; Widget seasonPanel(UgcSeason ugcSeason, cid, sheetHeight) { @@ -75,21 +74,28 @@ Widget seasonPanel(UgcSeason ugcSeason, cid, sheetHeight) { ), ), child: Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), + padding: const EdgeInsets.fromLTRB(8, 12, 8, 12), child: Row( children: [ Expanded( child: Text( '合集:${ugcSeason.title!}', + style: Theme.of(context).textTheme.labelMedium, overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 15), + Image.asset( + 'assets/images/live.gif', + color: Theme.of(context).colorScheme.primary, + height: 11, + ), + const SizedBox(width: 4), Text( '${currentIndex + 1} / ${ugcSeason.epCount}', - style: Theme.of(context).textTheme.labelSmall, + style: Theme.of(context).textTheme.labelMedium, ), - const SizedBox(width: 2), + const SizedBox(width: 6), const Icon( Icons.arrow_forward_ios_outlined, size: 13, diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index b50e6589..b2521473 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -114,6 +114,7 @@ class _VideoReplyPanelState extends State @override Widget build(BuildContext context) { + super.build(context); return RefreshIndicator( onRefresh: () async { _videoReplyController.currentPage = 0; @@ -130,8 +131,18 @@ class _VideoReplyPanelState extends State floating: true, delegate: _MySliverPersistentHeaderDelegate( child: Container( - color: Theme.of(context).colorScheme.background, - padding: const EdgeInsets.fromLTRB(12, 6, 10, 6), + height: 45, + padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + border: Border( + bottom: BorderSide( + color: Theme.of(context) + .colorScheme + .outline + .withOpacity(0.1)), + ), + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -155,9 +166,11 @@ class _VideoReplyPanelState extends State child: TextButton.icon( onPressed: () => _videoReplyController.queryBySort(), - icon: const Icon(Icons.sort, size: 17), + icon: const Icon(Icons.sort, size: 15), label: Obx(() => Text( - _videoReplyController.sortTypeLabel.value)), + _videoReplyController.sortTypeLabel.value, + style: const TextStyle(fontSize: 12), + )), ), ) ], diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index f15a1eee..b0320d36 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -12,21 +12,21 @@ import 'package:pilipala/utils/utils.dart'; import 'zan.dart'; class ReplyItem extends StatelessWidget { - ReplyItem({ - super.key, + const ReplyItem({ this.replyItem, this.addReply, this.replyLevel, - this.showReplyRow, + this.showReplyRow = true, this.replyReply, this.replyType, - }); - ReplyItemModel? replyItem; - Function? addReply; - String? replyLevel; - bool? showReplyRow = true; - Function? replyReply; - ReplyType? replyType; + Key? key, + }) : super(key: key); + final ReplyItemModel? replyItem; + final Function? addReply; + final String? replyLevel; + final bool? showReplyRow; + final Function? replyReply; + final ReplyType? replyType; @override Widget build(BuildContext context) { @@ -108,8 +108,7 @@ class ReplyItem extends StatelessWidget { replyItem!.member!.vip!['vipStatus'] > 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, - fontSize: - Theme.of(context).textTheme.titleSmall!.fontSize, + fontSize: 13, ), ), const SizedBox(width: 6), @@ -118,7 +117,7 @@ class ReplyItem extends StatelessWidget { height: 11, ), const SizedBox(width: 6), - if (replyItem!.isUp!) UpTag(), + if (replyItem!.isUp!) const UpTag(), ], ), Positioned( @@ -186,7 +185,7 @@ class ReplyItem extends StatelessWidget { TextSpan( children: [ if (replyItem!.isTop!) - WidgetSpan(child: UpTag(tagText: 'TOP')), + const WidgetSpan(child: UpTag(tagText: 'TOP')), buildContent(context, replyItem!, replyReply, null), ], ), @@ -204,7 +203,7 @@ class ReplyItem extends StatelessWidget { child: ReplyItemRow( replies: replyItem!.replies, replyControl: replyItem!.replyControl, - f_rpid: replyItem!.rpid, + // f_rpid: replyItem!.rpid, replyItem: replyItem, replyReply: replyReply, ), @@ -216,7 +215,6 @@ class ReplyItem extends StatelessWidget { // 感谢、回复、复制 Widget bottonAction(context, replyControl) { - var color = Theme.of(context).colorScheme.outline; return Row( children: [ const SizedBox(width: 48), @@ -297,13 +295,13 @@ class ReplyItemRow extends StatelessWidget { super.key, this.replies, this.replyControl, - this.f_rpid, + // this.f_rpid, this.replyItem, this.replyReply, }); List? replies; ReplyControl? replyControl; - int? f_rpid; + // int? f_rpid; ReplyItemModel? replyItem; Function? replyReply; @@ -361,7 +359,7 @@ class ReplyItemRow extends StatelessWidget { }, ), if (replies![i].isUp) - WidgetSpan( + const WidgetSpan( child: UpTag(), ), buildContent( @@ -439,16 +437,14 @@ InlineSpan buildContent( color: Theme.of(context).colorScheme.primary, ), recognizer: TapGestureRecognizer() - ..onTap = () => { - Get.toNamed( - '/webview', - parameters: { - 'url': content.vote['url'], - 'type': 'vote', - 'pageTitle': content.vote['title'], - }, - ) - }, + ..onTap = () => Get.toNamed( + '/webview', + parameters: { + 'url': content.vote['url'], + 'type': 'vote', + 'pageTitle': content.vote['title'], + }, + ), ), ); return ''; @@ -554,11 +550,9 @@ InlineSpan buildContent( color: Theme.of(context).colorScheme.primary, ), recognizer: TapGestureRecognizer() - ..onTap = () => { - Get.toNamed('/searchResult', parameters: { - 'keyword': content.jumpUrl[matchStr]['title'] - }) - }, + ..onTap = () => Get.toNamed('/searchResult', parameters: { + 'keyword': content.jumpUrl[matchStr]['title'] + }), ), ); spanChilds.add( @@ -718,16 +712,14 @@ InlineSpan buildContent( color: Theme.of(context).colorScheme.primary, ), recognizer: TapGestureRecognizer() - ..onTap = () => { - Get.toNamed( - '/webview', - parameters: { - 'url': content.richText['note']['click_url'], - 'type': 'note', - 'pageTitle': '笔记预览' - }, - ) - }, + ..onTap = () => Get.toNamed( + '/webview', + parameters: { + 'url': content.richText['note']['click_url'], + 'type': 'note', + 'pageTitle': '笔记预览' + }, + ), ), ); } @@ -736,8 +728,8 @@ InlineSpan buildContent( } class UpTag extends StatelessWidget { - String? tagText; - UpTag({super.key, this.tagText = 'UP'}); + final String? tagText; + const UpTag({super.key, this.tagText = 'UP'}); @override Widget build(BuildContext context) { Color primary = Theme.of(context).colorScheme.primary; diff --git a/lib/pages/video/detail/reply/widgets/zan.dart b/lib/pages/video/detail/reply/widgets/zan.dart index ee125abc..8e630260 100644 --- a/lib/pages/video/detail/reply/widgets/zan.dart +++ b/lib/pages/video/detail/reply/widgets/zan.dart @@ -6,13 +6,13 @@ import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/item.dart'; class ZanButton extends StatefulWidget { - ZanButton({ + const ZanButton({ super.key, this.replyItem, this.replyType, }); - ReplyItemModel? replyItem; + final ReplyItemModel? replyItem; final ReplyType? replyType; @override diff --git a/lib/pages/video/detail/replyNew/view.dart b/lib/pages/video/detail/replyNew/view.dart index ff390eed..45131110 100644 --- a/lib/pages/video/detail/replyNew/view.dart +++ b/lib/pages/video/detail/replyNew/view.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -10,13 +9,14 @@ import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/utils/storage.dart'; class VideoReplyNewDialog extends StatefulWidget { - int? oid; - int? root; - int? parent; - ReplyType? replyType; - ReplyItemModel? replyItem; + final int? oid; + final int? root; + final int? parent; + final ReplyType? replyType; + final ReplyItemModel? replyItem; - VideoReplyNewDialog({ + const VideoReplyNewDialog({ + super.key, this.oid, this.root, this.parent, @@ -56,7 +56,9 @@ class _VideoReplyNewDialogState extends State _autoFocus() async { await Future.delayed(const Duration(milliseconds: 300)); - FocusScope.of(context).requestFocus(replyContentFocusNode); + if (context.mounted) { + FocusScope.of(context).requestFocus(replyContentFocusNode); + } } _printLatestValue() { @@ -91,9 +93,8 @@ class _VideoReplyNewDialogState extends State super.didChangeMetrics(); WidgetsBinding.instance.addPostFrameCallback((_) { // 键盘高度 - final viewInsets = EdgeInsets.fromWindowPadding( - WidgetsBinding.instance.window.viewInsets, - WidgetsBinding.instance.window.devicePixelRatio); + final viewInsets = EdgeInsets.fromViewPadding( + View.of(context).viewInsets, View.of(context).devicePixelRatio); _debouncer.run(() { if (mounted) { setState(() { @@ -197,7 +198,7 @@ class _VideoReplyNewDialogState extends State } } -typedef void DebounceCallback(); +typedef DebounceCallback = void Function(); class Debouncer { DebounceCallback? callback; diff --git a/lib/pages/video/detail/replyReply/view.dart b/lib/pages/video/detail/replyReply/view.dart index d0e9a60f..fc086f59 100644 --- a/lib/pages/video/detail/replyReply/view.dart +++ b/lib/pages/video/detail/replyReply/view.dart @@ -81,7 +81,7 @@ class _VideoReplyReplyPanelState extends State { if (widget.source == 'videoDetail') Container( height: 45, - padding: const EdgeInsets.only(left: 14, right: 14), + padding: const EdgeInsets.only(left: 12, right: 2), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 75e1b845..5fcaed58 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -5,6 +5,8 @@ import 'package:flutter_meedu_media_kit/meedu_player.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/common/widgets/sliver_header.dart'; +import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; @@ -171,8 +173,8 @@ class _VideoDetailPageState extends State controller: _meeduPlayerController!, header: (BuildContext context, MeeduPlayerController - _meeduPlayerController, - Responsive) { + meeduPlayerController, + Responsive responsive) { return AppBar( toolbarHeight: 40, backgroundColor: Colors.transparent, @@ -230,7 +232,7 @@ class _VideoDetailPageState extends State children: [ Container( width: double.infinity, - height: 45, + height: 0, decoration: BoxDecoration( border: Border( bottom: BorderSide( @@ -250,8 +252,8 @@ class _VideoDetailPageState extends State () => TabBar( controller: videoDetailController.tabCtr, dividerColor: Colors.transparent, - // indicatorColor: - // Theme.of(context).colorScheme.background, + indicatorColor: + Theme.of(context).colorScheme.background, tabs: videoDetailController.tabs .map((String name) => Tab(text: name)) .toList(), @@ -267,11 +269,20 @@ class _VideoDetailPageState extends State children: [ Builder( builder: (context) { - return const CustomScrollView( - key: PageStorageKey('简介'), + return CustomScrollView( + key: const PageStorageKey('简介'), slivers: [ - VideoIntroPanel(), - RelatedVideoPanel(), + const VideoIntroPanel(), + SliverPersistentHeader( + floating: true, + pinned: true, + delegate: SliverHeaderDelegate( + height: 50, + child: + const MenuRow(loadingStatus: false), + ), + ), + const RelatedVideoPanel(), ], ); }, @@ -296,6 +307,7 @@ class _VideoDetailPageState extends State snapshot.data!.toDouble(), continuePlay, playerStatus, + null, ); }), ) diff --git a/lib/pages/video/detail/widgets/app_bar.dart b/lib/pages/video/detail/widgets/app_bar.dart index 0f9a6cec..05730d2c 100644 --- a/lib/pages/video/detail/widgets/app_bar.dart +++ b/lib/pages/video/detail/widgets/app_bar.dart @@ -3,14 +3,15 @@ import 'package:flutter_meedu_media_kit/meedu_player.dart'; class ScrollAppBar extends StatelessWidget { final double scrollVal; - Function callback; + final Function callback; final PlayerStatus playerStatus; - ScrollAppBar( + const ScrollAppBar( this.scrollVal, this.callback, this.playerStatus, - ); + Key? key, + ) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/pages/video/detail/widgets/expandable_section.dart b/lib/pages/video/detail/widgets/expandable_section.dart index c3d49a80..afa68cc9 100644 --- a/lib/pages/video/detail/widgets/expandable_section.dart +++ b/lib/pages/video/detail/widgets/expandable_section.dart @@ -1,16 +1,20 @@ +// ignore_for_file: library_private_types_in_public_api + import 'package:flutter/material.dart'; class ExpandedSection extends StatefulWidget { - final Widget child; + final Widget? child; final bool expand; - double begin = 0.0; - double end = 1.0; + final double begin; + final double end; - ExpandedSection( - {this.expand = false, - required this.child, - required this.begin, - required this.end}); + const ExpandedSection({ + super.key, + this.expand = false, + this.child, + this.begin = 0.0, + this.end = 1.0, + }); @override _ExpandedSectionState createState() => _ExpandedSectionState(); diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index d9e555af..6411ce55 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_print + import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; @@ -5,7 +7,6 @@ import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/pages/dynamics/index.dart'; -import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/mine/index.dart'; import 'package:pilipala/pages/rcmd/controller.dart'; import 'package:pilipala/utils/cookie.dart'; diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 3d0f4fb9..d6de8ae4 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -67,6 +67,6 @@ class Routes { // 用户中心 GetPage(name: '/member', page: () => const MemberPage()), // 二级回复 - GetPage(name: '/replyReply', page: () => VideoReplyReplyPanel()), + GetPage(name: '/replyReply', page: () => const VideoReplyReplyPanel()), ]; } diff --git a/lib/utils/id_utils.dart b/lib/utils/id_utils.dart index cc9c4a03..227e58a7 100644 --- a/lib/utils/id_utils.dart +++ b/lib/utils/id_utils.dart @@ -1,3 +1,5 @@ +// ignore_for_file: constant_identifier_names + import 'dart:math'; import 'package:flutter/material.dart'; diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 4745e8c4..fdf5009e 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1,4 +1,6 @@ // 工具函数 +// ignore_for_file: non_constant_identifier_names + import 'dart:async'; import 'dart:io'; import 'dart:math'; @@ -52,7 +54,7 @@ class Utils { // 当前时间 int time = (DateTime.now().millisecondsSinceEpoch / 1000).round(); // 对比 - int _distance = (time - timeStamp).toInt(); + int distance = (time - timeStamp).toInt(); // 当前年日期 String currentYearStr = 'MM月DD日 hh:mm'; String lastYearStr = 'YY年MM月DD日 hh:mm'; @@ -65,12 +67,12 @@ class Utils { toInt: false, formatType: formatType); } - if (_distance <= 60) { + if (distance <= 60) { return '刚刚'; - } else if (_distance <= 3600) { - return '${(_distance / 60).floor()}分钟前'; - } else if (_distance <= 43200) { - return '${(_distance / 60 / 60).floor()}小时前'; + } else if (distance <= 3600) { + return '${(distance / 60).floor()}分钟前'; + } else if (distance <= 43200) { + return '${(distance / 60 / 60).floor()}小时前'; } else if (DateTime.fromMillisecondsSinceEpoch(time * 1000).year == DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000).year) { return CustomStamp_str(