import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/pages/video/detail/reply_new/reply_page.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:PiliPalaX/common/skeleton/video_reply.dart'; import 'package:PiliPalaX/common/widgets/http_error.dart'; import 'package:PiliPalaX/models/common/reply_type.dart'; import 'package:PiliPalaX/models/video/reply/item.dart'; import 'package:PiliPalaX/pages/video/detail/reply/widgets/reply_item.dart'; import 'package:get/get_navigation/src/dialog/dialog_route.dart'; import '../../../../utils/utils.dart'; import 'controller.dart'; class VideoReplyReplyPanel extends StatefulWidget { const VideoReplyReplyPanel({ this.rcount, this.oid, this.rpid, this.firstFloor, this.source, this.replyType, super.key, }); final dynamic rcount; final int? oid; final int? rpid; final ReplyItemModel? firstFloor; final String? source; final ReplyType? replyType; @override State createState() => _VideoReplyReplyPanelState(); } class _VideoReplyReplyPanelState extends State { late VideoReplyReplyController _videoReplyReplyController; late final _savedReplies = {}; @override void initState() { super.initState(); _videoReplyReplyController = Get.put( VideoReplyReplyController( widget.oid, widget.rpid.toString(), widget.replyType!), tag: widget.rpid.toString(), ); // 上拉加载更多 _videoReplyReplyController.scrollController.addListener( () { if (_videoReplyReplyController.scrollController.position.pixels >= _videoReplyReplyController .scrollController.position.maxScrollExtent - 300) { EasyThrottle.throttle('replylist', const Duration(milliseconds: 200), () { _videoReplyReplyController.onLoadMore(); }); } }, ); } @override void dispose() { _videoReplyReplyController.scrollController.removeListener(() {}); Get.delete(tag: widget.rpid.toString()); super.dispose(); } @override Widget build(BuildContext context) { return Container( height: widget.source == 'videoDetail' ? Utils.getSheetHeight(context) : null, color: Theme.of(context).colorScheme.surface, child: Column( children: [ if (widget.source == 'videoDetail') Container( height: 45, padding: const EdgeInsets.only(left: 12, right: 2), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('评论详情${widget.rcount > 0 ? '(${widget.rcount})' : ''}'), IconButton( tooltip: '关闭', icon: const Icon(Icons.close, size: 20), onPressed: Get.back, ), ], ), ), Divider( height: 1, color: Theme.of(context).dividerColor.withOpacity(0.1), ), Expanded( child: RefreshIndicator( onRefresh: () async { await _videoReplyReplyController.onRefresh(); }, child: CustomScrollView( controller: _videoReplyReplyController.scrollController, physics: const AlwaysScrollableScrollPhysics(), slivers: [ if (widget.firstFloor != null) ...[ // const SliverToBoxAdapter(child: SizedBox(height: 10)), SliverToBoxAdapter( child: ReplyItem( replyItem: widget.firstFloor, replyLevel: '2', showReplyRow: false, replyType: widget.replyType, needDivider: false, onReply: () { _onReply(widget.firstFloor); }, ), ), SliverToBoxAdapter( child: Divider( height: 20, color: Theme.of(context).dividerColor.withOpacity(0.1), thickness: 6, ), ), ], Obx(() => _buildBody( _videoReplyReplyController.loadingState.value)), ], ), ), ), ], ), ); } void _onReply(ReplyItemModel? item) { dynamic oid = item?.oid; dynamic root = item?.rpid; dynamic parent = item?.rpid; dynamic key = oid + root + parent; Navigator.of(context) .push( GetDialogRoute( pageBuilder: (buildContext, animation, secondaryAnimation) { return ReplyPage( oid: oid, root: root, parent: parent, replyType: ReplyType.video, replyItem: item, savedReply: _savedReplies[key], onSaveReply: (reply) { _savedReplies[key] = reply; }, ); }, transitionDuration: const Duration(milliseconds: 500), transitionBuilder: (context, animation, secondaryAnimation, child) { const begin = Offset(0.0, 1.0); const end = Offset.zero; const curve = Curves.linear; var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); return SlideTransition( position: animation.drive(tween), child: child, ); }, ), ) .then((value) { // 完成评论,数据添加 if (value != null && value['data'] != null) { _savedReplies[key] = null; List list = _videoReplyReplyController.loadingState.value is Success ? (_videoReplyReplyController.loadingState.value as Success) .response : []; list.add(value['data']); _videoReplyReplyController.loadingState.value = LoadingState.success(list); } }); } Widget _buildBody(LoadingState loadingState) { return loadingState is Success ? SliverMainAxisGroup( slivers: [ if (widget.firstFloor == null && _videoReplyReplyController.root != null) ...[ SliverToBoxAdapter( child: ReplyItem( replyItem: _videoReplyReplyController.root, replyLevel: '2', showReplyRow: false, replyType: widget.replyType, needDivider: false, onReply: () { _onReply(_videoReplyReplyController.root); }, ), ), SliverToBoxAdapter( child: Divider( height: 20, color: Theme.of(context).dividerColor.withOpacity(0.1), thickness: 6, ), ), ], SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { if (index == loadingState.response.length) { return Container( padding: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom), height: MediaQuery.of(context).padding.bottom + 100, child: Center( child: Obx( () => Text( _videoReplyReplyController.noMore.value, style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.outline, ), ), ), ), ); } else { return ReplyItem( replyItem: loadingState.response[index], replyLevel: '2', showReplyRow: false, replyType: widget.replyType, onReply: () { _onReply(loadingState.response[index]); }, onDelete: (rpid, frpid) { List list = (_videoReplyReplyController .loadingState.value as Success) .response; list = list.where((item) => item.rpid != rpid).toList(); _videoReplyReplyController.loadingState.value = LoadingState.success(list); }, ); } }, childCount: loadingState.response.length + 1, ), ), ], ) : loadingState is Error ? HttpError( errMsg: loadingState.errMsg, fn: _videoReplyReplyController.onReload, ) : SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return const VideoReplySkeleton(); }, childCount: 8, ), ); } }