diff --git a/lib/common/widgets/html_render.dart b/lib/common/widgets/html_render.dart
index fed456a7..ee3f8b2f 100644
--- a/lib/common/widgets/html_render.dart
+++ b/lib/common/widgets/html_render.dart
@@ -1,3 +1,5 @@
+import 'dart:ffi';
+
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:get/get.dart';
@@ -10,12 +12,14 @@ class HtmlRender extends StatelessWidget {
this.htmlContent,
this.imgCount,
this.imgList,
+ required this.constrainedWidth,
super.key,
});
final String? htmlContent;
final int? imgCount;
final List? imgList;
+ final double constrainedWidth;
@override
Widget build(BuildContext context) {
@@ -57,7 +61,7 @@ class HtmlRender extends StatelessWidget {
// height: isEmote ? 22 : null,
// );
return NetworkImgLayer(
- width: isEmote ? 22 : (Get.size.width - 23) / textScale,
+ width: isEmote ? 22 : (constrainedWidth - 23) / textScale,
height: isEmote ? 22 : 200,
src: imgUrl,
ignoreHeight: !isEmote,
diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart
index c66082ac..9545a1be 100644
--- a/lib/pages/dynamics/detail/view.dart
+++ b/lib/pages/dynamics/detail/view.dart
@@ -1,4 +1,5 @@
import 'dart:async';
+import 'dart:math';
import 'package:easy_debounce/easy_throttle.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/id_utils.dart';
+import '../../../utils/grid.dart';
import '../widgets/dynamic_panel.dart';
class DynamicDetailPage extends StatefulWidget {
@@ -212,158 +214,67 @@ class _DynamicDetailPageState extends State
},
child: Stack(
children: [
- CustomScrollView(
- controller: scrollController,
- physics: const AlwaysScrollableScrollPhysics(),
- slivers: [
- if (action != 'comment')
- 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),
- ),
+ OrientationBuilder(
+ builder: (context, orientation) {
+ double padding = max(context.width / 2 - Grid.maxRowWidth, 0);
+ if (orientation == Orientation.portrait) {
+ return CustomScrollView(
+ controller: scrollController,
+ physics: const AlwaysScrollableScrollPhysics(),
+ slivers: [
+ SliverToBoxAdapter(
+ child: DynamicPanel(
+ item: _dynamicDetailController.item,
+ source: 'detail',
),
),
- height: 45,
- padding: const EdgeInsets.only(left: 12, right: 6),
- child: Row(
- children: [
- Obx(
- () => AnimatedSwitcher(
- duration: const Duration(milliseconds: 400),
- transitionBuilder:
- (Widget child, Animation animation) {
- return ScaleTransition(
- scale: animation, child: child);
- },
- child: Text(
- '${_dynamicDetailController.acount.value}条回复',
- key: ValueKey(
- _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),
+ replyPersistentHeader(context),
+ replyList(),
+ ]
+ .map((e) => SliverPadding(
+ padding: EdgeInsets.symmetric(horizontal: padding),
+ sliver: e))
+ .toList(),
+ );
+ } else {
+ return Row(
+ children: [
+ Expanded(
+ child: CustomScrollView(
+ controller: ScrollController(),
+ physics: const AlwaysScrollableScrollPhysics(),
+ slivers: [
+ SliverPadding(
+ padding: EdgeInsets.only(left: padding / 2),
+ sliver: SliverToBoxAdapter(
+ child: DynamicPanel(
+ item: _dynamicDetailController.item,
+ source: 'detail',
+ ),
)),
- ),
- )
- ],
+ ]),
),
- ),
- ),
- 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(
- () => _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),
- );
- }
- },
- )
- ],
+ Expanded(
+ child: CustomScrollView(
+ controller: scrollController,
+ physics: const AlwaysScrollableScrollPhysics(),
+ slivers: [
+ SliverPadding(
+ padding: EdgeInsets.only(right: padding / 2),
+ sliver: replyPersistentHeader(context)),
+ SliverPadding(
+ padding: EdgeInsets.only(right: padding / 2),
+ sliver: replyList()),
+ ]
+ // .map(
+ // (e) => SliverPadding(padding: padding, sliver: e))
+ // .toList(),
+ ),
+ ),
+ ],
+ );
+ }
+ },
),
Positioned(
bottom: MediaQuery.of(context).padding.bottom + 14,
@@ -415,6 +326,136 @@ class _DynamicDetailPageState extends State
),
);
}
+
+ 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 animation) {
+ return ScaleTransition(scale: animation, child: child);
+ },
+ child: Text(
+ '${_dynamicDetailController.acount.value}条回复',
+ key: ValueKey(_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 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 {
diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart
index d9b8fe20..d623c7d5 100644
--- a/lib/pages/html/view.dart
+++ b/lib/pages/html/view.dart
@@ -1,3 +1,5 @@
+import 'dart:math';
+
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.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/utils/feed_back.dart';
+import '../../utils/grid.dart';
import 'controller.dart';
class HtmlRenderPage extends StatefulWidget {
@@ -210,181 +213,123 @@ class _HtmlRenderPageState extends State
),
body: Stack(
children: [
- SingleChildScrollView(
- controller: scrollController,
- child: FutureBuilder(
- future: _futureBuilderFuture,
- builder: (context, snapshot) {
- if (snapshot.connectionState == ConnectionState.done &&
- snapshot.hasData) {
- var data = snapshot.data;
- // fabAnimationCtr.forward();
- if (data != null && data['status']) {
- return Column(
- children: [
- Padding(
- padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
- child: Row(
- children: [
- NetworkImgLayer(
- width: 40,
- height: 40,
- type: 'avatar',
- src: _htmlRenderCtr.response['avatar']!,
- ),
- const SizedBox(width: 10),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(_htmlRenderCtr.response['uname'],
- style: TextStyle(
- fontSize: Theme.of(context)
- .textTheme
- .titleSmall!
- .fontSize,
- )),
- Text(
- _htmlRenderCtr.response['updateTime'],
- style: TextStyle(
- color:
- Theme.of(context).colorScheme.outline,
- fontSize: Theme.of(context)
- .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,
+ OrientationBuilder(builder: (context, orientation) {
+ double padding = max(context.width / 2 - Grid.maxRowWidth, 0);
+ return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ Expanded(
+ child: SingleChildScrollView(
+ controller: orientation == Orientation.portrait
+ ? scrollController
+ : ScrollController(),
+ child: Padding(
+ padding: orientation == Orientation.portrait
+ ? EdgeInsets.symmetric(horizontal: padding)
+ : EdgeInsets.only(left: padding / 2),
+ child: FutureBuilder(
+ future: _futureBuilderFuture,
+ builder: (context, snapshot) {
+ if (snapshot.connectionState == ConnectionState.done &&
+ snapshot.hasData) {
+ var data = snapshot.data;
+ // fabAnimationCtr.forward();
+ if (data != null && data['status']) {
+ return Column(
+ children: [
+ Padding(
+ padding:
+ const EdgeInsets.fromLTRB(12, 12, 12, 8),
+ child: Row(
+ children: [
+ NetworkImgLayer(
+ width: 40,
+ height: 40,
+ type: 'avatar',
+ src: _htmlRenderCtr.response['avatar']!,
+ ),
+ const SizedBox(width: 10),
+ Column(
+ crossAxisAlignment:
+ CrossAxisAlignment.start,
+ children: [
+ Text(_htmlRenderCtr.response['uname'],
style: TextStyle(
- fontSize: 12,
- color: Theme.of(context)
- .colorScheme
- .outline,
- ),
+ fontSize: Theme.of(context)
+ .textTheme
+ .titleSmall!
+ .fontSize,
+ )),
+ Text(
+ _htmlRenderCtr
+ .response['updateTime'],
+ style: TextStyle(
+ color: Theme.of(context)
+ .colorScheme
+ .outline,
+ fontSize: Theme.of(context)
+ .textTheme
+ .labelSmall!
+ .fontSize,
),
),
- ),
- );
- } 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);
- },
- );
- }
- },
+ ],
+ ),
+ const Spacer(),
+ ],
+ ),
),
- ),
- ],
- );
- } else {
- return const Text('error');
- }
- } else {
- // 骨架屏
- return const SizedBox();
- }
- },
- ),
- ),
+ Padding(
+ padding:
+ const EdgeInsets.fromLTRB(12, 8, 12, 8),
+ child: LayoutBuilder(
+ builder: (context, boxConstraints) {
+ return HtmlRender(
+ htmlContent:
+ _htmlRenderCtr.response['content'],
+ 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(
bottom: MediaQuery.of(context).padding.bottom + 14,
right: 14,
@@ -432,4 +377,80 @@ class _HtmlRenderPageState extends State
),
);
}
+
+ 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),
+ ),
+ ),
+ ),
+ )
+ ],
+ ),
+ );
+ }
}