mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: 动态与专栏详情适配横屏双列模式
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:ffi';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -10,12 +12,14 @@ class HtmlRender extends StatelessWidget {
|
|||||||
this.htmlContent,
|
this.htmlContent,
|
||||||
this.imgCount,
|
this.imgCount,
|
||||||
this.imgList,
|
this.imgList,
|
||||||
|
required this.constrainedWidth,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? htmlContent;
|
final String? htmlContent;
|
||||||
final int? imgCount;
|
final int? imgCount;
|
||||||
final List<String>? imgList;
|
final List<String>? imgList;
|
||||||
|
final double constrainedWidth;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -57,7 +61,7 @@ class HtmlRender extends StatelessWidget {
|
|||||||
// height: isEmote ? 22 : null,
|
// height: isEmote ? 22 : null,
|
||||||
// );
|
// );
|
||||||
return NetworkImgLayer(
|
return NetworkImgLayer(
|
||||||
width: isEmote ? 22 : (Get.size.width - 23) / textScale,
|
width: isEmote ? 22 : (constrainedWidth - 23) / textScale,
|
||||||
height: isEmote ? 22 : 200,
|
height: isEmote ? 22 : 200,
|
||||||
src: imgUrl,
|
src: imgUrl,
|
||||||
ignoreHeight: !isEmote,
|
ignoreHeight: !isEmote,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -16,6 +17,7 @@ import 'package:PiliPalaX/pages/video/detail/reply_reply/index.dart';
|
|||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
import 'package:PiliPalaX/utils/id_utils.dart';
|
import 'package:PiliPalaX/utils/id_utils.dart';
|
||||||
|
|
||||||
|
import '../../../utils/grid.dart';
|
||||||
import '../widgets/dynamic_panel.dart';
|
import '../widgets/dynamic_panel.dart';
|
||||||
|
|
||||||
class DynamicDetailPage extends StatefulWidget {
|
class DynamicDetailPage extends StatefulWidget {
|
||||||
@@ -212,158 +214,67 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
CustomScrollView(
|
OrientationBuilder(
|
||||||
controller: scrollController,
|
builder: (context, orientation) {
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
double padding = max(context.width / 2 - Grid.maxRowWidth, 0);
|
||||||
slivers: [
|
if (orientation == Orientation.portrait) {
|
||||||
if (action != 'comment')
|
return CustomScrollView(
|
||||||
SliverToBoxAdapter(
|
controller: scrollController,
|
||||||
child: DynamicPanel(
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
item: _dynamicDetailController.item,
|
slivers: [
|
||||||
source: 'detail',
|
SliverToBoxAdapter(
|
||||||
),
|
child: DynamicPanel(
|
||||||
),
|
item: _dynamicDetailController.item,
|
||||||
SliverPersistentHeader(
|
source: 'detail',
|
||||||
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,
|
replyPersistentHeader(context),
|
||||||
padding: const EdgeInsets.only(left: 12, right: 6),
|
replyList(),
|
||||||
child: Row(
|
]
|
||||||
children: [
|
.map<Widget>((e) => SliverPadding(
|
||||||
Obx(
|
padding: EdgeInsets.symmetric(horizontal: padding),
|
||||||
() => AnimatedSwitcher(
|
sliver: e))
|
||||||
duration: const Duration(milliseconds: 400),
|
.toList(),
|
||||||
transitionBuilder:
|
);
|
||||||
(Widget child, Animation<double> animation) {
|
} else {
|
||||||
return ScaleTransition(
|
return Row(
|
||||||
scale: animation, child: child);
|
children: [
|
||||||
},
|
Expanded(
|
||||||
child: Text(
|
child: CustomScrollView(
|
||||||
'${_dynamicDetailController.acount.value}条回复',
|
controller: ScrollController(),
|
||||||
key: ValueKey<int>(
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
_dynamicDetailController.acount.value),
|
slivers: [
|
||||||
),
|
SliverPadding(
|
||||||
),
|
padding: EdgeInsets.only(left: padding / 2),
|
||||||
),
|
sliver: SliverToBoxAdapter(
|
||||||
const Spacer(),
|
child: DynamicPanel(
|
||||||
SizedBox(
|
item: _dynamicDetailController.item,
|
||||||
height: 35,
|
source: 'detail',
|
||||||
child: TextButton.icon(
|
),
|
||||||
onPressed: () =>
|
|
||||||
_dynamicDetailController.queryBySort(),
|
|
||||||
icon: const Icon(Icons.sort, size: 16),
|
|
||||||
label: Obx(() => Text(
|
|
||||||
_dynamicDetailController
|
|
||||||
.sortTypeLabel.value,
|
|
||||||
style: const TextStyle(fontSize: 13),
|
|
||||||
)),
|
)),
|
||||||
),
|
]),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
),
|
child: CustomScrollView(
|
||||||
pinned: true,
|
controller: scrollController,
|
||||||
),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
FutureBuilder(
|
slivers: [
|
||||||
future: _futureBuilderFuture,
|
SliverPadding(
|
||||||
builder: (context, snapshot) {
|
padding: EdgeInsets.only(right: padding / 2),
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
sliver: replyPersistentHeader(context)),
|
||||||
Map data = snapshot.data as Map;
|
SliverPadding(
|
||||||
if (snapshot.data['status']) {
|
padding: EdgeInsets.only(right: padding / 2),
|
||||||
// 请求成功
|
sliver: replyList()),
|
||||||
return Obx(
|
]
|
||||||
() => _dynamicDetailController.replyList.isEmpty &&
|
// .map<Widget>(
|
||||||
_dynamicDetailController.isLoadingMore
|
// (e) => SliverPadding(padding: padding, sliver: e))
|
||||||
? SliverList(
|
// .toList(),
|
||||||
delegate: SliverChildBuilderDelegate(
|
),
|
||||||
(context, index) {
|
),
|
||||||
return const VideoReplySkeleton();
|
],
|
||||||
}, childCount: 8),
|
);
|
||||||
)
|
}
|
||||||
: 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,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return ReplyItem(
|
|
||||||
replyItem: _dynamicDetailController
|
|
||||||
.replyList[index],
|
|
||||||
showReplyRow: true,
|
|
||||||
replyLevel: '1',
|
|
||||||
replyReply: (replyItem) =>
|
|
||||||
replyReply(replyItem),
|
|
||||||
replyType:
|
|
||||||
ReplyType.values[replyType],
|
|
||||||
addReply: (replyItem) {
|
|
||||||
_dynamicDetailController
|
|
||||||
.replyList[index].replies!
|
|
||||||
.add(replyItem);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
childCount: _dynamicDetailController
|
|
||||||
.replyList.length +
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// 请求错误
|
|
||||||
return HttpError(
|
|
||||||
errMsg: data['msg'],
|
|
||||||
fn: () => setState(() {}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 骨架屏
|
|
||||||
return SliverList(
|
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
|
||||||
return const VideoReplySkeleton();
|
|
||||||
}, childCount: 8),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 14,
|
bottom: MediaQuery.of(context).padding.bottom + 14,
|
||||||
@@ -415,6 +326,136 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SliverPersistentHeader replyPersistentHeader(BuildContext context) {
|
||||||
|
return 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: 12, right: 6),
|
||||||
|
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 Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 35,
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: () => _dynamicDetailController.queryBySort(),
|
||||||
|
icon: const Icon(Icons.sort, size: 16),
|
||||||
|
label: Obx(() => Text(
|
||||||
|
_dynamicDetailController.sortTypeLabel.value,
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pinned: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureBuilder<dynamic> replyList() {
|
||||||
|
return FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
Map data = snapshot.data as Map;
|
||||||
|
if (snapshot.data['status']) {
|
||||||
|
// 请求成功
|
||||||
|
return Obx(
|
||||||
|
() => _dynamicDetailController.replyList.isEmpty &&
|
||||||
|
_dynamicDetailController.isLoadingMore
|
||||||
|
? SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
|
return const VideoReplySkeleton();
|
||||||
|
}, childCount: 8),
|
||||||
|
)
|
||||||
|
: 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,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ReplyItem(
|
||||||
|
replyItem:
|
||||||
|
_dynamicDetailController.replyList[index],
|
||||||
|
showReplyRow: true,
|
||||||
|
replyLevel: '1',
|
||||||
|
replyReply: (replyItem) => replyReply(replyItem),
|
||||||
|
replyType: ReplyType.values[replyType],
|
||||||
|
addReply: (replyItem) {
|
||||||
|
_dynamicDetailController
|
||||||
|
.replyList[index].replies!
|
||||||
|
.add(replyItem);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
childCount:
|
||||||
|
_dynamicDetailController.replyList.length + 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 请求错误
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () => setState(() {}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
|
return const VideoReplySkeleton();
|
||||||
|
}, childCount: 8),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
|
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
@@ -13,6 +15,7 @@ import 'package:PiliPalaX/pages/video/detail/reply_new/index.dart';
|
|||||||
import 'package:PiliPalaX/pages/video/detail/reply_reply/index.dart';
|
import 'package:PiliPalaX/pages/video/detail/reply_reply/index.dart';
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
|
|
||||||
|
import '../../utils/grid.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
class HtmlRenderPage extends StatefulWidget {
|
class HtmlRenderPage extends StatefulWidget {
|
||||||
@@ -210,181 +213,123 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
),
|
),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
SingleChildScrollView(
|
OrientationBuilder(builder: (context, orientation) {
|
||||||
controller: scrollController,
|
double padding = max(context.width / 2 - Grid.maxRowWidth, 0);
|
||||||
child: FutureBuilder(
|
return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
future: _futureBuilderFuture,
|
Expanded(
|
||||||
builder: (context, snapshot) {
|
child: SingleChildScrollView(
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
controller: orientation == Orientation.portrait
|
||||||
snapshot.hasData) {
|
? scrollController
|
||||||
var data = snapshot.data;
|
: ScrollController(),
|
||||||
// fabAnimationCtr.forward();
|
child: Padding(
|
||||||
if (data != null && data['status']) {
|
padding: orientation == Orientation.portrait
|
||||||
return Column(
|
? EdgeInsets.symmetric(horizontal: padding)
|
||||||
children: [
|
: EdgeInsets.only(left: padding / 2),
|
||||||
Padding(
|
child: FutureBuilder(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
future: _futureBuilderFuture,
|
||||||
child: Row(
|
builder: (context, snapshot) {
|
||||||
children: [
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
NetworkImgLayer(
|
snapshot.hasData) {
|
||||||
width: 40,
|
var data = snapshot.data;
|
||||||
height: 40,
|
// fabAnimationCtr.forward();
|
||||||
type: 'avatar',
|
if (data != null && data['status']) {
|
||||||
src: _htmlRenderCtr.response['avatar']!,
|
return Column(
|
||||||
),
|
children: [
|
||||||
const SizedBox(width: 10),
|
Padding(
|
||||||
Column(
|
padding:
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
||||||
children: [
|
child: Row(
|
||||||
Text(_htmlRenderCtr.response['uname'],
|
children: [
|
||||||
style: TextStyle(
|
NetworkImgLayer(
|
||||||
fontSize: Theme.of(context)
|
width: 40,
|
||||||
.textTheme
|
height: 40,
|
||||||
.titleSmall!
|
type: 'avatar',
|
||||||
.fontSize,
|
src: _htmlRenderCtr.response['avatar']!,
|
||||||
)),
|
),
|
||||||
Text(
|
const SizedBox(width: 10),
|
||||||
_htmlRenderCtr.response['updateTime'],
|
Column(
|
||||||
style: TextStyle(
|
crossAxisAlignment:
|
||||||
color:
|
CrossAxisAlignment.start,
|
||||||
Theme.of(context).colorScheme.outline,
|
children: [
|
||||||
fontSize: Theme.of(context)
|
Text(_htmlRenderCtr.response['uname'],
|
||||||
.textTheme
|
|
||||||
.labelSmall!
|
|
||||||
.fontSize,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(12, 8, 12, 8),
|
|
||||||
child: HtmlRender(
|
|
||||||
htmlContent: _htmlRenderCtr.response['content'],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
width: 8,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.dividerColor
|
|
||||||
.withOpacity(0.05),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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: 12, right: 6),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Text('回复'),
|
|
||||||
const Spacer(),
|
|
||||||
SizedBox(
|
|
||||||
height: 35,
|
|
||||||
child: TextButton.icon(
|
|
||||||
onPressed: () => _htmlRenderCtr.queryBySort(),
|
|
||||||
icon: const Icon(Icons.sort, size: 16),
|
|
||||||
label: Obx(
|
|
||||||
() => Text(
|
|
||||||
_htmlRenderCtr.sortTypeLabel.value,
|
|
||||||
style: const TextStyle(fontSize: 13),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Obx(
|
|
||||||
() => _htmlRenderCtr.replyList.isEmpty &&
|
|
||||||
_htmlRenderCtr.isLoadingMore
|
|
||||||
? ListView.builder(
|
|
||||||
itemCount: 5,
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return const VideoReplySkeleton();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemCount:
|
|
||||||
_htmlRenderCtr.replyList.length + 1,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index ==
|
|
||||||
_htmlRenderCtr.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(
|
|
||||||
_htmlRenderCtr.noMore.value,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: Theme.of(context)
|
||||||
color: Theme.of(context)
|
.textTheme
|
||||||
.colorScheme
|
.titleSmall!
|
||||||
.outline,
|
.fontSize,
|
||||||
),
|
)),
|
||||||
|
Text(
|
||||||
|
_htmlRenderCtr
|
||||||
|
.response['updateTime'],
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.outline,
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelSmall!
|
||||||
|
.fontSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
} else {
|
const Spacer(),
|
||||||
return ReplyItem(
|
],
|
||||||
replyItem:
|
),
|
||||||
_htmlRenderCtr.replyList[index],
|
|
||||||
showReplyRow: true,
|
|
||||||
replyLevel: '1',
|
|
||||||
replyReply: (replyItem) =>
|
|
||||||
replyReply(replyItem),
|
|
||||||
replyType: ReplyType.values[type],
|
|
||||||
addReply: (replyItem) {
|
|
||||||
_htmlRenderCtr
|
|
||||||
.replyList[index].replies!
|
|
||||||
.add(replyItem);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
],
|
padding:
|
||||||
);
|
const EdgeInsets.fromLTRB(12, 8, 12, 8),
|
||||||
} else {
|
child: LayoutBuilder(
|
||||||
return const Text('error');
|
builder: (context, boxConstraints) {
|
||||||
}
|
return HtmlRender(
|
||||||
} else {
|
htmlContent:
|
||||||
// 骨架屏
|
_htmlRenderCtr.response['content'],
|
||||||
return const SizedBox();
|
constrainedWidth:
|
||||||
}
|
boxConstraints.maxWidth,
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
if (orientation == Orientation.portrait) ...[
|
||||||
|
Divider(
|
||||||
|
thickness: 8,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.dividerColor
|
||||||
|
.withOpacity(0.05)),
|
||||||
|
replyHeader(),
|
||||||
|
replyList(),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Text('error');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
if (orientation == Orientation.landscape) ...[
|
||||||
|
VerticalDivider(
|
||||||
|
thickness: 8,
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.05)),
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: scrollController,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(right: padding / 2),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
replyHeader(),
|
||||||
|
replyList(),
|
||||||
|
],
|
||||||
|
))))
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 14,
|
bottom: MediaQuery.of(context).padding.bottom + 14,
|
||||||
right: 14,
|
right: 14,
|
||||||
@@ -432,4 +377,80 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Obx replyList() {
|
||||||
|
return Obx(
|
||||||
|
() => _htmlRenderCtr.replyList.isEmpty && _htmlRenderCtr.isLoadingMore
|
||||||
|
? ListView.builder(
|
||||||
|
itemCount: 5,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return const VideoReplySkeleton();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: _htmlRenderCtr.replyList.length + 1,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == _htmlRenderCtr.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(
|
||||||
|
_htmlRenderCtr.noMore.value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ReplyItem(
|
||||||
|
replyItem: _htmlRenderCtr.replyList[index],
|
||||||
|
showReplyRow: true,
|
||||||
|
replyLevel: '1',
|
||||||
|
replyReply: (replyItem) => replyReply(replyItem),
|
||||||
|
replyType: ReplyType.values[type],
|
||||||
|
addReply: (replyItem) {
|
||||||
|
_htmlRenderCtr.replyList[index].replies!.add(replyItem);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Container replyHeader() {
|
||||||
|
return Container(
|
||||||
|
height: 45,
|
||||||
|
padding: const EdgeInsets.only(left: 12, right: 6),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Text('回复'),
|
||||||
|
const Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 35,
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: () => _htmlRenderCtr.queryBySort(),
|
||||||
|
icon: const Icon(Icons.sort, size: 16),
|
||||||
|
label: Obx(
|
||||||
|
() => Text(
|
||||||
|
_htmlRenderCtr.sortTypeLabel.value,
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user