From 66148cd1de90bf0cbf94c1cd2eca1f6d23419d5c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jul 2024 11:00:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E4=B8=BA=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 28 +++- .../detail/reply/widgets/reply_save.dart | 140 ++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 lib/pages/video/detail/reply/widgets/reply_save.dart diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 5f53b398..1330f482 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -16,11 +16,13 @@ import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/plugin/pl_gallery/index.dart'; +import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; +import 'reply_save.dart'; import 'zan.dart'; Box setting = GStrorage.setting; @@ -60,7 +62,10 @@ class ReplyItem extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel(item: replyItem); + return MorePanel( + item: replyItem, + mainFloor: true, + ); }, ); }, @@ -1008,7 +1013,12 @@ InlineSpan buildContent( class MorePanel extends StatelessWidget { final dynamic item; - const MorePanel({super.key, required this.item}); + final bool mainFloor; + const MorePanel({ + super.key, + required this.item, + this.mainFloor = false, + }); Future menuActionHandler(String type) async { String message = item.content.message ?? item.content; @@ -1030,6 +1040,13 @@ class MorePanel extends StatelessWidget { }, ); break; + case 'save': + Get.back(); + Navigator.push( + Get.context!, + PlPopupRoute(child: ReplySave(replyItem: item)), + ); + break; // case 'block': // SmartDialog.showToast('加入黑名单'); // break; @@ -1080,6 +1097,13 @@ class MorePanel extends StatelessWidget { leading: const Icon(Icons.copy_outlined, size: 19), title: Text('自由复制', style: textTheme.titleSmall), ), + if (mainFloor) + ListTile( + onTap: () async => await menuActionHandler('save'), + minLeadingWidth: 0, + leading: const Icon(Icons.save_alt_rounded, size: 19), + title: Text('本地保存', style: textTheme.titleSmall), + ), // ListTile( // onTap: () async => await menuActionHandler('block'), // minLeadingWidth: 0, diff --git a/lib/pages/video/detail/reply/widgets/reply_save.dart b/lib/pages/video/detail/reply/widgets/reply_save.dart new file mode 100644 index 00000000..c083806f --- /dev/null +++ b/lib/pages/video/detail/reply/widgets/reply_save.dart @@ -0,0 +1,140 @@ +import 'dart:math'; +import 'dart:typed_data'; +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/models/video/reply/item.dart'; +import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; +import 'package:saver_gallery/saver_gallery.dart'; + +class ReplySave extends StatefulWidget { + final ReplyItemModel? replyItem; + const ReplySave({required this.replyItem, super.key}); + + @override + State createState() => _ReplySaveState(); +} + +class _ReplySaveState extends State { + final _boundaryKey = GlobalKey(); + + void _generatePicWidget() async { + SmartDialog.showLoading(msg: '保存中'); + try { + RenderRepaintBoundary boundary = _boundaryKey.currentContext! + .findRenderObject() as RenderRepaintBoundary; + var image = await boundary.toImage(pixelRatio: 3); + ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); + Uint8List pngBytes = byteData!.buffer.asUint8List(); + String picName = + "plpl_reply_${DateTime.now().toString().replaceAll(RegExp(r'[- :]'), '').split('.').first}"; + final result = await SaverGallery.saveImage( + Uint8List.fromList(pngBytes), + name: '$picName.png', + androidRelativePath: "Pictures/PiliPala", + androidExistNotSave: false, + ); + if (result.isSuccess) { + SmartDialog.showToast('保存成功'); + } + } catch (err) { + print(err); + } finally { + SmartDialog.dismiss(); + } + } + + List _createWidgets(int count, Widget Function() builder) { + return List.generate(count, (_) => Expanded(child: builder())); + } + + List _createColumnWidgets() { + return _createWidgets(3, () => Row(children: _createRowWidgets())); + } + + List _createRowWidgets() { + return _createWidgets( + 4, + () => Center( + child: Transform.rotate( + angle: pi / 10, + child: const Text( + 'PiliPala', + style: TextStyle( + color: Color(0x08000000), + fontSize: 18, + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + ), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + width: Get.width, + height: Get.height, + margin: EdgeInsets.fromLTRB( + 0, + MediaQuery.of(context).padding.top + 4, + 0, + MediaQuery.of(context).padding.bottom + 4, + ), + color: Colors.transparent, + child: Column( + children: [ + Expanded( + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + ), + child: Center( + child: SingleChildScrollView( + child: RepaintBoundary( + key: _boundaryKey, + child: IntrinsicHeight( + child: Stack( + children: [ + ReplyItem( + replyItem: widget.replyItem, + showReplyRow: false, + ), + Positioned.fill( + child: Column( + children: _createColumnWidgets(), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FilledButton( + onPressed: () => Get.back(), + child: const Text('取消'), + ), + const SizedBox(width: 40), + FilledButton( + onPressed: _generatePicWidget, + child: const Text('保存'), + ), + ], + ), + ], + ), + ); + } +}