feat: 图文动态详情页&评论渲染

This commit is contained in:
guozhigq
2023-06-27 12:23:20 +08:00
parent 532ad52c17
commit bc8be693a8
20 changed files with 863 additions and 478 deletions

View File

@ -0,0 +1,248 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_reply.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/pages/dynamics/deatil/index.dart';
import 'package:pilipala/pages/dynamics/widgets/author_panel.dart';
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
import '../widgets/dynamic_panel.dart';
class DynamicDetailPage extends StatefulWidget {
// const DynamicDetailPage({super.key});
const DynamicDetailPage({Key? key}) : super(key: key);
@override
State<DynamicDetailPage> createState() => _DynamicDetailPageState();
}
class _DynamicDetailPageState extends State<DynamicDetailPage> {
late DynamicDetailController? _dynamicDetailController;
Future? _futureBuilderFuture;
late StreamController<bool> titleStreamC; // appBar title
final ScrollController scrollController = ScrollController();
bool _visibleTitle = false;
@override
void initState() {
super.initState();
int oid = 0;
int type = 0;
if (Get.arguments['floor'] == 1) {
oid = int.parse(Get.arguments['item'].basic!['comment_id_str']);
type = Get.arguments['item'].basic!['comment_type'];
} else {
oid = Get.arguments['item'].modules.moduleDynamic.major.draw.id;
type = 11;
}
_dynamicDetailController = Get.put(DynamicDetailController(oid, type));
_futureBuilderFuture = _dynamicDetailController!.queryReplyList();
titleStreamC = StreamController<bool>();
scrollController.addListener(_listen);
}
void _listen() async {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) {
if (!_dynamicDetailController!.isLoadingMore) {
_dynamicDetailController!.isLoadingMore = true;
await _dynamicDetailController!.queryReplyList(reqType: 'onLoad');
}
}
if (scrollController.offset > 55 && !_visibleTitle) {
_visibleTitle = true;
titleStreamC.add(true);
} else if (scrollController.offset <= 55 && _visibleTitle) {
_visibleTitle = false;
titleStreamC.add(false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
scrolledUnderElevation: 0,
centerTitle: false,
titleSpacing: 0,
title: StreamBuilder(
stream: titleStreamC.stream,
initialData: false,
builder: (context, AsyncSnapshot snapshot) {
return AnimatedOpacity(
opacity: snapshot.data ? 1 : 0,
duration: const Duration(milliseconds: 300),
child: author(_dynamicDetailController!.item, context),
);
},
),
// actions: _detailModel != null ? appBarAction() : [],
),
body: RefreshIndicator(
onRefresh: () async {
await _dynamicDetailController!.queryReplyList();
},
child: CustomScrollView(
controller: scrollController,
slivers: [
SliverToBoxAdapter(
child: DynamicPanel(
item: _dynamicDetailController!.item,
source: 'detail',
),
),
SliverPersistentHeader(
delegate: _MySliverPersistentHeaderDelegate(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border(
top: BorderSide(
width: 0.6,
color: Theme.of(context).dividerColor.withOpacity(0.05),
),
),
),
height: 45,
padding: const EdgeInsets.only(left: 15, right: 12),
child: Row(
children: [
Obx(
() => AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
transitionBuilder:
(Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation, child: child);
},
child: Text(
'${_dynamicDetailController!.acount.value}',
key: ValueKey<int>(
_dynamicDetailController!.acount.value),
),
),
),
const Text('条回复'),
const Spacer(),
// TextButton.icon(
// onPressed: () {},
// icon: const Icon(
// Icons.subject_rounded,
// size: 15,
// ),
// style: TextButton.styleFrom(
// padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
// foregroundColor:
// Theme.of(context).colorScheme.outline,
// ),
// label: Text(
// '按时间',
// style: TextStyle(
// fontSize: Theme.of(context)
// .textTheme
// .titleSmall!
// .fontSize,
// ),
// ),
// ),
],
),
),
),
pinned: true,
),
FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (snapshot.data['status']) {
// 请求成功
return Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index ==
_dynamicDetailController!.replyList.length) {
return Container(
padding: EdgeInsets.only(
bottom:
MediaQuery.of(context).padding.bottom),
height:
MediaQuery.of(context).padding.bottom + 100,
child: Center(
child: Obx(() => Text(
_dynamicDetailController!.noMore.value)),
),
);
} else {
return ReplyItem(
replyItem: _dynamicDetailController!
.replyList[index],
showReplyRow: true,
replyLevel: '1');
}
},
childCount:
_dynamicDetailController!.replyList.length + 1,
),
),
);
} else {
// 请求错误
return HttpError(
errMsg: data['msg'],
fn: () => setState(() {}),
);
}
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 5),
);
}
},
)
],
),
),
);
}
}
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
final double _minExtent = 45;
final double _maxExtent = 45;
final Widget child;
_MySliverPersistentHeaderDelegate({required this.child});
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
//创建child子组件
//shrinkOffsetchild偏移值minExtent~maxExtent
//overlapsContentSliverPersistentHeader覆盖其他子组件返回true否则返回false
return child;
}
//SliverPersistentHeader最大高度
@override
double get maxExtent => _maxExtent;
//SliverPersistentHeader最小高度
@override
double get minExtent => _minExtent;
@override
bool shouldRebuild(covariant _MySliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}