From 79f4e2e23b7341c25d19dc72209829984cebbd5d Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Thu, 17 Jul 2025 15:07:56 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting/views/meeting_details_view.dart | 476 +++++++++--------- 1 file changed, 242 insertions(+), 234 deletions(-) diff --git a/lib/modules/meeting/views/meeting_details_view.dart b/lib/modules/meeting/views/meeting_details_view.dart index dd73a4bba..46eca267f 100644 --- a/lib/modules/meeting/views/meeting_details_view.dart +++ b/lib/modules/meeting/views/meeting_details_view.dart @@ -67,98 +67,102 @@ class MeetingDetailsView extends GetView { ), onPressed: () => Get.back(), ), - title: Opacity( - opacity: controller.isTop.value ? 1.0 : 0.0, - child: Container( - width: 170.w, - padding: EdgeInsets.symmetric(horizontal: 2.w, vertical: 3.w), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: BorderRadius.circular(30.r), - ), - child: Row( - children: [ - GestureDetector( - onTap: () { - if (controller.meetingData.value.filepath.isEmpty) { - controller.downloadAudio(); - } else { - if (controller.isPlay.value) { - controller.playerController.pausePlayer(); + title: Obx( + () => Opacity( + opacity: (1 - (controller.dragOffset.value / 100.w)).clamp(0.0, 1.0), + child: Container( + width: 170.w, + padding: EdgeInsets.symmetric(horizontal: 2.w, vertical: 3.w), + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(30.r), + ), + child: Row( + children: [ + GestureDetector( + onTap: () { + if (controller.meetingData.value.filepath.isEmpty) { + controller.downloadAudio(); } else { - controller.playerController.startPlayer(); + if (controller.isPlay.value) { + controller.playerController.pausePlayer(); + } else { + controller.playerController.startPlayer(); + } } - } - }, - child: Obx( - () => !controller.isLoading.value && - controller.meetingData.value.filepath.isEmpty - ? Obx( - () => Stack( - alignment: Alignment.center, - children: [ - if (controller.isDownload.value) - SizedBox( - width: 26.w, - height: 26.w, - child: CircularProgressIndicator( - value: controller.downloadProgress.value, - valueColor: - const AlwaysStoppedAnimation( - Colors.red), - strokeWidth: 2.w, + }, + child: Obx( + () => !controller.isLoading.value && + controller.meetingData.value.filepath.isEmpty + ? Obx( + () => Stack( + alignment: Alignment.center, + children: [ + if (controller.isDownload.value) + SizedBox( + width: 26.w, + height: 26.w, + child: CircularProgressIndicator( + value: controller.downloadProgress.value, + valueColor: + const AlwaysStoppedAnimation( + Colors.red), + strokeWidth: 2.w, + ), ), + Icon( + Icons.download_for_offline, + color: Colors.black, + size: 30.w, ), - Icon( - Icons.download_for_offline, - color: Colors.black, - size: 30.w, - ), - ], + ], + ), + ) + : Icon( + controller.isPlay.value + ? Icons.pause_circle + : Icons.play_circle, + color: Colors.black, + size: 30.w, ), - ) - : Icon( - controller.isPlay.value - ? Icons.pause_circle - : Icons.play_circle, - color: Colors.black, - size: 30.w, - ), + ), ), - ), - 4.horizontalSpace, - GestureDetector( - onTap: () { - if (controller.isSpeakerText.value) return; - controller.dragOffset.value = 210.w; - controller.isTop.value = false; - }, - child: SizedBox( - width: 130.w, - child: FittedBox( - fit: BoxFit.scaleDown, - child: AudioFileWaveforms( - size: Size(350.w, 30.w), - playerController: controller.playerController, - waveformData: List.generate(88, (int index) { - return 0.01 + Random().nextDouble() * 0.09; - }), - waveformType: WaveformType.fitWidth, - enableSeekGesture: false, - playerWaveStyle: PlayerWaveStyle( - spacing: 4.w, - waveThickness: 2.w, - fixedWaveColor: Colors.grey[400]!, - liveWaveColor: Colors.red, - seekLineColor: Colors.red, - scaleFactor: 200, + 4.horizontalSpace, + GestureDetector( + onTap: () { + if (controller.isSpeakerText.value) return; + controller.dragOffset.value = 210.w; + controller.isTop.value = false; + }, + behavior: HitTestBehavior.translucent, + child: SizedBox( + width: 130.w, + height: 30.w, + child: FittedBox( + fit: BoxFit.scaleDown, + child: AudioFileWaveforms( + size: Size(350.w, 30.w), + playerController: controller.playerController, + waveformData: List.generate(88, (int index) { + return 0.01 + Random().nextDouble() * 0.09; + }), + waveformType: WaveformType.fitWidth, + enableSeekGesture: false, + playerWaveStyle: PlayerWaveStyle( + spacing: 4.w, + waveThickness: 2.w, + fixedWaveColor: Colors.grey[400]!, + liveWaveColor: Colors.red, + seekLineColor: Colors.red, + scaleFactor: 200, + ), ), ), ), ), - ), - ], + ], + ), ), ), ), @@ -373,176 +377,180 @@ class MeetingDetailsView extends GetView { Widget _soundWave(bool isDarkMode) { return Obx( - () => SizedBox( - height: controller.dragOffset.value < 0 - ? 0 - : controller.dragOffset.value > 210.w - ? 210.w - : controller.dragOffset.value, - child: Stack( - children: [ - Positioned( - bottom: 0, - child: Container( - width: 1.sw, - height: 200.w, - margin: EdgeInsets.only(bottom: 10.w), - decoration: BoxDecoration( - color: Colors.grey[200]!, - borderRadius: BorderRadius.vertical( - bottom: Radius.circular(20.r), + () => Opacity( + opacity: (controller.dragOffset.value / 210.w).clamp(0.0, 1.0), + child: SizedBox( + height: controller.dragOffset.value < 0 + ? 0 + : controller.dragOffset.value > 210.w + ? 210.w + : controller.dragOffset.value, + child: Stack( + children: [ + Positioned( + bottom: 0, + child: Container( + width: 1.sw, + height: 200.w, + margin: EdgeInsets.only(bottom: 10.w), + decoration: BoxDecoration( + color: Colors.grey[200]!, + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(20.r), + ), ), - ), - child: Column( - children: [ - Container( - margin: EdgeInsets.only(bottom: 5.w), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8.r), - ), - child: AudioFileWaveforms( - size: Size(350.w, 120.w), - playerController: controller.playerController, - waveformData: List.generate(88, (int index) { - return 0.01 + Random().nextDouble() * 0.09; - }), - waveformType: WaveformType.fitWidth, - playerWaveStyle: PlayerWaveStyle( - spacing: 4.w, - waveThickness: 2.w, - fixedWaveColor: Colors.grey[300]!, - liveWaveColor: Colors.red, - seekLineColor: Colors.red, - scaleFactor: 600, + child: Column( + children: [ + Container( + margin: EdgeInsets.only(bottom: 5.w), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + child: AudioFileWaveforms( + size: Size(350.w, 120.w), + playerController: controller.playerController, + waveformData: List.generate(88, (int index) { + return 0.01 + Random().nextDouble() * 0.09; + }), + waveformType: WaveformType.fitWidth, + playerWaveStyle: PlayerWaveStyle( + spacing: 4.w, + waveThickness: 2.w, + fixedWaveColor: Colors.grey[300]!, + liveWaveColor: Colors.red, + seekLineColor: Colors.red, + scaleFactor: 600, + ), ), ), - ), - SizedBox( - width: 350.w, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Obx( - () => Text( - DateUtil.formatDateMs( - controller.currentDuration.value, - format: 'HH:mm:ss', - isUtc: true, - ), - style: TextStyle( - color: isDarkMode - ? Colors.grey[400] - : Colors.grey[500], - fontSize: 12.sp, + SizedBox( + width: 350.w, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Obx( + () => Text( + DateUtil.formatDateMs( + controller.currentDuration.value, + format: 'HH:mm:ss', + isUtc: true, + ), + style: TextStyle( + color: isDarkMode + ? Colors.grey[400] + : Colors.grey[500], + fontSize: 12.sp, + ), ), ), - ), - Obx( - () => Text( - DateUtil.formatDateMs( - controller.durationMax.value, - format: 'HH:mm:ss', - isUtc: true, - ), - style: TextStyle( - color: isDarkMode - ? Colors.grey[400] - : Colors.grey[500], - fontSize: 12.sp, + Obx( + () => Text( + DateUtil.formatDateMs( + controller.durationMax.value, + format: 'HH:mm:ss', + isUtc: true, + ), + style: TextStyle( + color: isDarkMode + ? Colors.grey[400] + : Colors.grey[500], + fontSize: 12.sp, + ), ), ), - ), - ], + ], + ), ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - GestureDetector( - onTap: () { - int progress = - controller.currentDuration.value - 10000; - if (progress < 0) { - progress = 0; - } - controller.playerController.seekTo(progress); - }, - child: Icon( - Icons.replay_10, - color: Colors.black, - size: 30.w, + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: () { + int progress = + controller.currentDuration.value - 10000; + if (progress < 0) { + progress = 0; + } + controller.playerController.seekTo(progress); + }, + child: Icon( + Icons.replay_10, + color: Colors.black, + size: 30.w, + ), ), - ), - GestureDetector( - onTap: () { - if (controller.meetingData.value.filepath.isEmpty) { - controller.downloadAudio(); - } else { - if (controller.isPlay.value) { - controller.playerController.pausePlayer(); + GestureDetector( + onTap: () { + if (controller + .meetingData.value.filepath.isEmpty) { + controller.downloadAudio(); } else { - controller.playerController.startPlayer(); + if (controller.isPlay.value) { + controller.playerController.pausePlayer(); + } else { + controller.playerController.startPlayer(); + } } - } - }, - child: Obx( - () => !controller.isLoading.value && - controller - .meetingData.value.filepath.isEmpty - ? Obx( - () => Stack( - alignment: Alignment.center, - children: [ - if (controller.isDownload.value) - SizedBox( - width: 42.w, - height: 42.w, - child: CircularProgressIndicator( - value: controller - .downloadProgress.value, - valueColor: - const AlwaysStoppedAnimation< - Color>(Colors.red), - strokeWidth: 3.w, + }, + child: Obx( + () => !controller.isLoading.value && + controller + .meetingData.value.filepath.isEmpty + ? Obx( + () => Stack( + alignment: Alignment.center, + children: [ + if (controller.isDownload.value) + SizedBox( + width: 42.w, + height: 42.w, + child: CircularProgressIndicator( + value: controller + .downloadProgress.value, + valueColor: + const AlwaysStoppedAnimation< + Color>(Colors.red), + strokeWidth: 3.w, + ), ), + Icon( + Icons.download_for_offline, + color: Colors.black, + size: 48.w, ), - Icon( - Icons.download_for_offline, - color: Colors.black, - size: 48.w, - ), - ], + ], + ), + ) + : Icon( + controller.isPlay.value + ? Icons.pause_circle + : Icons.play_circle, + color: Colors.black, + size: 48.w, ), - ) - : Icon( - controller.isPlay.value - ? Icons.pause_circle - : Icons.play_circle, - color: Colors.black, - size: 48.w, - ), + ), ), - ), - GestureDetector( - onTap: () { - int progress = - controller.currentDuration.value + 10000; - controller.playerController.seekTo(progress); - }, - child: Icon( - Icons.forward_10, - color: Colors.black, - size: 30.w, + GestureDetector( + onTap: () { + int progress = + controller.currentDuration.value + 10000; + controller.playerController.seekTo(progress); + }, + child: Icon( + Icons.forward_10, + color: Colors.black, + size: 30.w, + ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), - ), - ], + ], + ), ), ), ); From 436caef664ac95dd1239d4d5814aa03006b623e3 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Thu, 17 Jul 2025 22:05:08 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Markdown=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting/views/meeting_details_view.dart | 1 + .../meeting/views/tabs/summary_tab.dart | 28 +++++++++++++++++++ pubspec.yaml | 6 ++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/modules/meeting/views/meeting_details_view.dart b/lib/modules/meeting/views/meeting_details_view.dart index 46eca267f..ab674cdaf 100644 --- a/lib/modules/meeting/views/meeting_details_view.dart +++ b/lib/modules/meeting/views/meeting_details_view.dart @@ -312,6 +312,7 @@ class MeetingDetailsView extends GetView { child: TabBarView( controller: controller.tabController, physics: const NeverScrollableScrollPhysics(), + clipBehavior: Clip.none, children: [ Obx( () => controller.isLoading.value diff --git a/lib/modules/meeting/views/tabs/summary_tab.dart b/lib/modules/meeting/views/tabs/summary_tab.dart index 2cba7aac5..3838769f0 100644 --- a/lib/modules/meeting/views/tabs/summary_tab.dart +++ b/lib/modules/meeting/views/tabs/summary_tab.dart @@ -1,8 +1,10 @@ import 'package:common_utils/common_utils.dart'; +import 'package:fleather/fleather.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:parchment/codecs.dart'; import '../../../../data/services/db/sqflite_api.dart'; import '../../controllers/meeting_details_controller.dart'; @@ -26,6 +28,10 @@ class _SummaryTabState extends State { final TextEditingController _addressController = TextEditingController(); final TextEditingController _personnelController = TextEditingController(); + final FocusNode _focusNode = FocusNode(); + final GlobalKey _editorKey = GlobalKey(); + FleatherController? _fleatherController; + @override void initState() { super.initState(); @@ -39,6 +45,7 @@ class _SummaryTabState extends State { : '[输入与会人员]'); _addressController.addListener(_handleAddressChanged); _personnelController.addListener(_handlePersonnelChanged); + _initController(); } void _handleAddressChanged() { @@ -101,6 +108,12 @@ class _SummaryTabState extends State { } } + Future _initController() async { + final markdown = const ParchmentMarkdownCodec() + .decode(_controller.meetingDetails.value.summary); + _fleatherController = FleatherController(document: markdown); + } + @override void dispose() { _addressController.dispose(); @@ -119,6 +132,7 @@ class _SummaryTabState extends State { ); return Stack( + clipBehavior: Clip.none, children: [ SingleChildScrollView( padding: EdgeInsets.only(bottom: 80.w), @@ -352,6 +366,20 @@ class _SummaryTabState extends State { ), ), ), + Positioned( + bottom: 0, + left: -12.w, + right: -12.w, + child: Container( + height: 40, + color: Colors.white, + alignment: Alignment.center, + child: FleatherToolbar.basic( + controller: _fleatherController!, + editorKey: _editorKey, + ), + ), + ), ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index 011abdb79..5cb80d6b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,10 +23,8 @@ dependencies: audio_session: ^0.1.18 flutter_markdown: ^0.7.7+1 markdown: ^7.1.1 - appflowy_editor: - git: - url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: main + fleather: ^1.24.0+1 + parchment: ^1.24.0 wifi_iot: ^0.3.19+2 permission_handler: ^10.4.5 web_socket_channel: ^2.4.0 From 10e84d7e0bf0912c1338446049f014f3527cc195 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Fri, 18 Jul 2025 11:19:06 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E8=B5=B0ble=E5=BD=95=E9=9F=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting/controllers/meeting_record_controller.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index 8cd767ee0..19ada35c7 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -138,7 +138,7 @@ class MeetingRecordController extends GetxController switch (audioType.value) { case 0: fileName.value = "现场录音"; - _audioSourceType = false; + _audioSourceType = true; break; case 1: fileName.value = "音、视频录音"; @@ -271,7 +271,7 @@ class MeetingRecordController extends GetxController void _startHardwareServices() { switch (audioType.value) { case 0: - // _bleManager.openEncoder(); + _bleManager.openEncoder(); break; case 1: _bleManager.openDecoder();