post_detail_view.dart 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
  3. import 'package:url_launcher/url_launcher.dart';
  4. import 'package:veloe_kemono_party_flutter/models/attachment.dart';
  5. import 'package:veloe_kemono_party_flutter/models/post.dart';
  6. import 'package:veloe_kemono_party_flutter/pages/widgets/smart_image_container.dart';
  7. class PostDetailView extends StatelessWidget {
  8. final Post post;
  9. const PostDetailView({super.key, required this.post});
  10. @override
  11. Widget build(BuildContext context) {
  12. return Scaffold(
  13. appBar: AppBar(
  14. title: Text(post.title),
  15. elevation: 0,
  16. ),
  17. body: LayoutBuilder(
  18. builder: (context, constraints) {
  19. return ConstrainedBox(
  20. constraints: BoxConstraints(
  21. minHeight: constraints.maxHeight,
  22. maxWidth: constraints.maxWidth,
  23. ),
  24. child: Column(
  25. mainAxisSize: MainAxisSize.min,
  26. crossAxisAlignment: CrossAxisAlignment.start,
  27. children: [
  28. // Header Section
  29. _buildTitle(context),
  30. // Content Area
  31. Flexible(
  32. child: Column(
  33. mainAxisSize: MainAxisSize.min,
  34. children: [
  35. if (post.attachments.isNotEmpty)
  36. _buildImageColumn(post.attachments),
  37. _buildHtmlContent(),
  38. ],
  39. ),
  40. ),
  41. // Footer Section
  42. _buildDateDisplay(context),
  43. ],
  44. ),
  45. );
  46. },
  47. ),
  48. );
  49. }
  50. Widget _buildTitle(BuildContext context) {
  51. return Padding(
  52. padding: const EdgeInsets.all(16.0),
  53. child: Text(
  54. post.title,
  55. style: Theme.of(context).textTheme.headlineMedium?.copyWith(
  56. fontWeight: FontWeight.w600,
  57. ),
  58. ),
  59. );
  60. }
  61. Widget _buildImageColumn(List<Attachment> attachments) {
  62. return Column(
  63. children: attachments.map((attachment) {
  64. return Padding(
  65. padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  66. child: SmartImageContainer(imageUrl: attachment.link),
  67. );
  68. }).toList(),
  69. );
  70. }
  71. Widget _buildHtmlContent() {
  72. return Padding(
  73. padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
  74. child: HtmlWidget(
  75. post.content,
  76. renderMode: RenderMode.column,
  77. onTapUrl: (url) => launchUrl(Uri.parse(url)),
  78. customWidgetBuilder: (element) {
  79. if (element.localName == 'img') {
  80. final src = element.attributes['src'];
  81. return src != null
  82. ? Padding(
  83. padding: const EdgeInsets.symmetric(vertical: 8),
  84. child: SmartImageContainer(imageUrl: src),
  85. )
  86. : const SizedBox.shrink();
  87. }
  88. return null;
  89. },
  90. ),
  91. );
  92. }
  93. Widget _buildDateDisplay(BuildContext context) {
  94. return Padding(
  95. padding: const EdgeInsets.all(16.0),
  96. child: Row(
  97. children: [
  98. Icon(Icons.calendar_today, size: 18, color: Colors.grey[700]),
  99. const SizedBox(width: 8),
  100. Text(post.published
  101. ,
  102. style: Theme.of(context).textTheme.bodyLarge?.copyWith(
  103. color: Colors.grey[700],
  104. ),
  105. ),
  106. ],
  107. ),
  108. );
  109. }
  110. }