diff --git a/lib/common/skeleton/dynamic_card.dart b/lib/common/skeleton/dynamic_card.dart new file mode 100644 index 00000000..d816923f --- /dev/null +++ b/lib/common/skeleton/dynamic_card.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'skeleton.dart'; + +class DynamicCardSkeleton extends StatelessWidget { + const DynamicCardSkeleton({super.key}); + + @override + Widget build(BuildContext context) { + return Skeleton( + child: Container( + padding: const EdgeInsets.only(left: 12, right: 12, top: 12), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 8, + color: Theme.of(context).dividerColor.withOpacity(0.05), + ), + ), + ), + child: Column( + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.onInverseSurface, + borderRadius: BorderRadius.circular(20), + ), + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Theme.of(context).colorScheme.onInverseSurface, + width: 100, + height: 13, + margin: const EdgeInsets.only(bottom: 5), + ), + Container( + color: Theme.of(context).colorScheme.onInverseSurface, + width: 50, + height: 11, + ), + ], + ) + ], + ), + Container( + width: double.infinity, + margin: const EdgeInsets.only(top: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Theme.of(context).colorScheme.onInverseSurface, + width: double.infinity, + height: 13, + margin: const EdgeInsets.only(bottom: 7), + ), + Container( + color: Theme.of(context).colorScheme.onInverseSurface, + width: double.infinity, + height: 13, + margin: const EdgeInsets.only(bottom: 7), + ), + Container( + color: Theme.of(context).colorScheme.onInverseSurface, + width: 300, + height: 13, + margin: const EdgeInsets.only(bottom: 7), + ), + Container( + color: Theme.of(context).colorScheme.onInverseSurface, + width: 250, + height: 13, + margin: const EdgeInsets.only(bottom: 7), + ), + Container( + color: Theme.of(context).colorScheme.onInverseSurface, + width: 100, + height: 13, + margin: const EdgeInsets.only(bottom: 7), + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + for (var i = 0; i < 3; i++) + TextButton.icon( + onPressed: () {}, + icon: const Icon( + Icons.radio_button_unchecked_outlined, + size: 20, + ), + style: TextButton.styleFrom( + padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), + foregroundColor: Theme.of(context) + .colorScheme + .outline + .withOpacity(0.2), + ), + label: Text( + i == 0 + ? '转发' + : i == 1 + ? '评论' + : '点赞', + ), + ) + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index 0353f1a8..51e62c40 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -23,7 +23,7 @@ class DynamicsHttp { return { 'status': false, 'data': [], - 'msg': '请求错误 🙅', + 'msg': res.data['message'], }; } } diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index e4399e5b..c8903fe4 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -1,6 +1,10 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/dynamics.dart'; +import 'package:pilipala/http/search.dart'; import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/utils/utils.dart'; class DynamicsController extends GetxController { int page = 1; @@ -8,6 +12,7 @@ class DynamicsController extends GetxController { RxList? dynamicsList = [DynamicItemModel()].obs; RxString dynamicsType = 'all'.obs; RxString dynamicsTypeLabel = '全部'.obs; + final ScrollController scrollController = ScrollController(); Future queryFollowDynamic({type = 'init'}) async { var res = await DynamicsHttp.followDynamic( @@ -27,16 +32,22 @@ class DynamicsController extends GetxController { return res; } - onSelectType(value, label) { + onSelectType(value, label) async { dynamicsType.value = value; dynamicsTypeLabel.value = label; - queryFollowDynamic(); + await queryFollowDynamic(); + scrollController.animateTo(0, + duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); } - pushDetail(item, floor) { + pushDetail(item, floor, {action = 'all'}) async { + if (action == 'comment') { + Get.toNamed('/dynamicDetail', + arguments: {'item': item, 'floor': floor, 'action': action}); + return false; + } switch (item!.type) { case 'DYNAMIC_TYPE_FORWARD': - print('转发的动态'); Get.toNamed('/dynamicDetail', arguments: {'item': item, 'floor': floor}); break; @@ -45,10 +56,25 @@ class DynamicsController extends GetxController { arguments: {'item': item, 'floor': floor}); break; case 'DYNAMIC_TYPE_AV': - print('视频'); + String bvid = item.modules.moduleDynamic.major.archive.bvid; + int aid = item.modules.moduleDynamic.major.archive.aid; + String cover = item.modules.moduleDynamic.major.archive.cover; + String heroTag = Utils.makeHeroTag(aid); + try { + int cid = await SearchHttp.ab2c(bvid: bvid); + Get.toNamed('/video?bvid=$bvid&cid=$cid', + arguments: {'pic': cover, 'heroTag': heroTag}); + } catch (err) { + SmartDialog.showToast(err.toString()); + } break; case 'DYNAMIC_TYPE_ARTICLE': - print('文章/专栏'); + String title = item.modules.moduleDynamic.major.opus.title; + String url = item.modules.moduleDynamic.major.opus.jumpUrl; + Get.toNamed( + '/webview', + parameters: {'url': 'https:$url', 'type': 'note', 'pageTitle': title}, + ); break; case 'DYNAMIC_TYPE_PGC': print('番剧'); diff --git a/lib/pages/dynamics/deatil/view.dart b/lib/pages/dynamics/deatil/view.dart index 213c145d..17df4cec 100644 --- a/lib/pages/dynamics/deatil/view.dart +++ b/lib/pages/dynamics/deatil/view.dart @@ -24,6 +24,7 @@ class _DynamicDetailPageState extends State { late StreamController titleStreamC; // appBar title final ScrollController scrollController = ScrollController(); bool _visibleTitle = false; + String? action; @override void initState() { @@ -37,11 +38,16 @@ class _DynamicDetailPageState extends State { oid = Get.arguments['item'].modules.moduleDynamic.major.draw.id; type = 11; } - + action = + Get.arguments.containsKey('action') ? Get.arguments['action'] : null; _dynamicDetailController = Get.put(DynamicDetailController(oid, type)); _futureBuilderFuture = _dynamicDetailController!.queryReplyList(); titleStreamC = StreamController(); scrollController.addListener(_listen); + if (action == 'comment') { + _visibleTitle = true; + titleStreamC.add(true); + } } void _listen() async { @@ -90,12 +96,13 @@ class _DynamicDetailPageState extends State { child: CustomScrollView( controller: scrollController, slivers: [ - SliverToBoxAdapter( - child: DynamicPanel( - item: _dynamicDetailController!.item, - source: 'detail', + if (action != 'comment') + SliverToBoxAdapter( + child: DynamicPanel( + item: _dynamicDetailController!.item, + source: 'detail', + ), ), - ), SliverPersistentHeader( delegate: _MySliverPersistentHeaderDelegate( child: Container( diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index e7a0896e..fd540f04 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/dynamic_card.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/models/common/dynamics_type.dart'; import 'package:pilipala/models/dynamics/result.dart'; @@ -16,9 +17,9 @@ class DynamicsPage extends StatefulWidget { class _DynamicsPageState extends State with AutomaticKeepAliveClientMixin { - DynamicsController _dynamicsController = Get.put(DynamicsController()); + final DynamicsController _dynamicsController = Get.put(DynamicsController()); Future? _futureBuilderFuture; - final ScrollController scrollController = ScrollController(); + // final ScrollController scrollController = ScrollController(); bool _isLoadingMore = false; @override bool get wantKeepAlive => true; @@ -28,10 +29,11 @@ class _DynamicsPageState extends State super.initState(); _futureBuilderFuture = _dynamicsController.queryFollowDynamic(); - scrollController.addListener( + _dynamicsController.scrollController.addListener( () async { - if (scrollController.position.pixels >= - scrollController.position.maxScrollExtent - 200) { + if (_dynamicsController.scrollController.position.pixels >= + _dynamicsController.scrollController.position.maxScrollExtent - + 200) { if (!_isLoadingMore) { _isLoadingMore = true; await _dynamicsController.queryFollowDynamic(type: 'onLoad'); @@ -92,7 +94,7 @@ class _DynamicsPageState extends State List list = _dynamicsController.dynamicsList!; return Obx( () => ListView.builder( - controller: scrollController, + controller: _dynamicsController.scrollController, shrinkWrap: true, itemCount: list.length, itemBuilder: (BuildContext context, index) { @@ -105,19 +107,18 @@ class _DynamicsPageState extends State slivers: [ HttpError( errMsg: data['msg'], - fn: () => setState(() {}), + fn: () => _dynamicsController.queryFollowDynamic(), ) ], ); } } else { // 骨架屏 - // return SliverList( - // delegate: SliverChildBuilderDelegate((context, index) { - // return const VideoCardHSkeleton(); - // }, childCount: 10), - // ); - return Text('加载中'); + return ListView.builder( + physics: const NeverScrollableScrollPhysics(), + itemCount: 5, + itemBuilder: ((context, index) => const DynamicCardSkeleton()), + ); } }, ), diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 673d04a4..2c737f08 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -26,7 +26,8 @@ Widget action(item, context) { label: Text(stat.forward!.count ?? '转发'), ), TextButton.icon( - onPressed: () => _dynamicsController.pushDetail(item, 1), + onPressed: () => + _dynamicsController.pushDetail(item, 1, action: 'comment'), icon: const Icon( FontAwesomeIcons.comment, size: 16, diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart index 0bc2fd87..ef4691cb 100644 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ b/lib/pages/dynamics/widgets/pic_panel.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; Widget picWidget(item, context) { @@ -14,7 +15,6 @@ Widget picWidget(item, context) { } int len = pictures.length; List picList = []; - List list = []; for (var i = 0; i < len; i++) { picList.add(pictures[i].src ?? pictures[i].url); @@ -42,7 +42,8 @@ Widget picWidget(item, context) { return LayoutBuilder( builder: (context, BoxConstraints box) { double maxWidth = box.maxWidth; - double aspectRatio = 1.1; + double aspectRatio = 1.0; + double origAspectRatio = 0.0; double crossCount = len == 1 ? 1 : len < 3 @@ -51,20 +52,19 @@ Widget picWidget(item, context) { double height = 0.0; if (len == 1) { - aspectRatio = pictures.first.width / pictures.first.height; + origAspectRatio = + aspectRatio = pictures.first.width / pictures.first.height; + if (aspectRatio < 0.4) { + aspectRatio = 0.4; + } height = pictures.first.height * maxWidth / pictures.first.width; - if (pictures.first.width != 1920) { + if (origAspectRatio < 0.5 || pictures.first.width < 1920) { crossCount = 2; height = maxWidth / 2 / aspectRatio; } } else { aspectRatio = 1; - height = maxWidth / - crossCount * - (len % crossCount == 0 - ? len ~/ crossCount - : len ~/ crossCount + 1) + - 6; + height = maxWidth / crossCount * ((len / crossCount).ceil()) + 6; } return Container( padding: const EdgeInsets.only(top: 4), @@ -73,14 +73,20 @@ Widget picWidget(item, context) { ), clipBehavior: Clip.hardEdge, height: height, - child: GridView.count( - padding: EdgeInsets.zero, - physics: const NeverScrollableScrollPhysics(), - crossAxisCount: crossCount.toInt(), - mainAxisSpacing: 4.0, - crossAxisSpacing: 4.0, - childAspectRatio: aspectRatio, - children: list, + child: Stack( + children: [ + GridView.count( + padding: EdgeInsets.zero, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: crossCount.toInt(), + mainAxisSpacing: 4.0, + crossAxisSpacing: 4.0, + childAspectRatio: aspectRatio, + children: list, + ), + if (len == 1 && origAspectRatio < 0.4) + pBadge('长图', context, null, null, 6.0, 6.0, type: 'gray') + ], ), ); },