lxm 1 year ago
parent
commit
d61343e271
  1. 1
      assets/lottie/sound_effect.json
  2. 23
      lib/modules/meeting/controllers/meeting_details_controller.dart
  3. 487
      lib/modules/meeting/views/generate_bottom_sheet.dart
  4. 69
      lib/modules/meeting/views/meeting_details_view.dart
  5. 137
      lib/modules/meeting/views/record_language_bottom_sheet.dart
  6. 16
      lib/modules/meeting/views/speaker_all_bottom_sheet.dart
  7. 69
      lib/modules/meeting/views/speech_tab.dart
  8. 17
      lib/modules/meeting/views/summary_tab.dart
  9. 407
      lib/modules/meeting/views/template_view.dart
  10. 2
      pubspec.yaml

1
assets/lottie/sound_effect.json

File diff suppressed because one or more lines are too long

23
lib/modules/meeting/controllers/meeting_details_controller.dart

@ -18,15 +18,18 @@ import '../model/meeting_details_model.dart';
import '../model/meeting_model.dart'; import '../model/meeting_model.dart';
class MeetingDetailsController extends GetxController class MeetingDetailsController extends GetxController
with GetSingleTickerProviderStateMixin { with GetTickerProviderStateMixin {
late TabController tabController; late TabController tabController;
final RxInt currentIndex = 0.obs; final RxInt currentIndex = 0.obs;
late AnimationController lottieController;
final PlayerController playerController = PlayerController(); final PlayerController playerController = PlayerController();
RxBool isTop = false.obs; RxBool isTop = false.obs;
RxInt durationMax = 0.obs; RxInt durationMax = 0.obs;
RxDouble dragOffset = 210.w.obs; RxDouble dragOffset = 210.w.obs;
RxBool isPlay = false.obs;
RxInt currentDuration = 0.obs; RxInt currentDuration = 0.obs;
RxInt speakerAllStarttime = (-1).obs; RxInt speakerAllStarttime = (-1).obs;
@ -65,6 +68,8 @@ class MeetingDetailsController extends GetxController
} }
currentIndex.value = tabController.index; currentIndex.value = tabController.index;
}); });
lottieController = AnimationController(vsync: this)
..duration = const Duration(seconds: 2);
final Map<String, dynamic> args = Get.arguments ?? {}; final Map<String, dynamic> args = Get.arguments ?? {};
id = args['id']; id = args['id'];
_headers = { _headers = {
@ -80,6 +85,7 @@ class MeetingDetailsController extends GetxController
tabController.dispose(); tabController.dispose();
playerController.dispose(); playerController.dispose();
_isDisposed = true; _isDisposed = true;
lottieController.dispose();
super.onClose(); super.onClose();
} }
@ -114,6 +120,15 @@ class MeetingDetailsController extends GetxController
} }
} }
}); });
playerController.onPlayerStateChanged.listen((PlayerState playerState) {
isPlay.value = playerState.isPlaying;
if (_isDisposed) return;
if (playerState.isPlaying) {
lottieController.repeat();
} else {
lottieController.stop();
}
});
if (meetingDetails.value.tasktype == 1) { if (meetingDetails.value.tasktype == 1) {
List speakerList = await SqfliteApi.getMeetingSpeaker(id); List speakerList = await SqfliteApi.getMeetingSpeaker(id);
textList.value = List.from(speakerList); textList.value = List.from(speakerList);
@ -161,12 +176,6 @@ class MeetingDetailsController extends GetxController
} }
} }
void speakerCreate() async {
EasyLoading.show();
await _audioSpeaker();
EasyLoading.dismiss();
}
void summaryCreate() async { void summaryCreate() async {
EasyLoading.show(); EasyLoading.show();
if (meetingDetails.value.tasktype != 1) { if (meetingDetails.value.tasktype != 1) {

487
lib/modules/meeting/views/generate_bottom_sheet.dart

@ -0,0 +1,487 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../controllers/meeting_details_controller.dart';
import 'record_language_bottom_sheet.dart';
import 'template_view.dart';
class GenerateBottomSheet extends StatefulWidget {
const GenerateBottomSheet({super.key});
@override
State<GenerateBottomSheet> createState() => _GenerateBottomSheetState();
}
class _GenerateBottomSheetState extends State<GenerateBottomSheet> {
final _controller = Get.find<MeetingDetailsController>();
final ScrollController _scrollController = ScrollController();
bool _isSpeaker = true;
String _languageText = '中文普通话(简体)';
String _language = 'zh-CN';
final List _originalTemplate = [
{
'id': 1,
'name': '会议纪要',
'icon': Icons.auto_stories,
'tag': ['主题', '代办事项'],
'desc': '',
},
{
'id': 2,
'name': 'Autopilot',
'icon': Icons.auto_graph,
'tag': ['自适应结构', '全场景适配'],
'desc': '',
},
{
'id': 3,
'name': '推理Autopilot',
'icon': Icons.add_to_drive_outlined,
'tag': ['推理模型', '自动结构化'],
'desc': '',
},
{
'id': 4,
'name': '通话',
'icon': Icons.perm_phone_msg,
'tag': ['主题', '代办事项'],
'desc': '',
},
];
int _templateId = 0;
int _lastTemplateId = 0;
List _templateList = [];
@override
void initState() {
super.initState();
if (_lastTemplateId != 0) {
_templateId = _lastTemplateId;
} else {
_templateId = _originalTemplate[0]['id'];
}
_templateList = List.from(_originalTemplate);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.w),
child: Row(
children: [
Icon(
Icons.audio_file_outlined,
color: Colors.grey,
size: 32.w,
),
5.horizontalSpace,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_controller.meetingData.value.title,
style: TextStyle(
fontSize: 16.sp,
color: Colors.black87,
),
),
Text(
'${_controller.meetingData.value.seconds}s',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey,
),
),
],
),
),
GestureDetector(
onTap: () => Get.back(),
child: Container(
width: 26.w,
height: 26.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(26.r),
),
child: Icon(
Icons.close,
size: 18.w,
color: Colors.grey[400],
),
),
)
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w),
margin: EdgeInsets.only(bottom: 10.w),
child: Divider(
height: 1,
color: Colors.grey[300],
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.w),
child: Row(
children: [
Expanded(
child: Text(
'选择总结模版',
style: TextStyle(
fontSize: 14.sp,
color: Colors.black87,
),
),
),
GestureDetector(
onTap: () async {
var result = await Get.to(TemplateView(
_templateId,
_lastTemplateId,
));
if (result != null) {
_scrollController.jumpTo(0);
List list = List.from(_originalTemplate);
int index = list
.indexWhere((element) => element['id'] == result['id']);
if (index != -1) {
Map itemToMove = list.removeAt(index);
list.insert(0, itemToMove);
} else {
list.insert(0, result);
}
setState(() {
_templateId = result['id'];
_templateList = list;
});
}
},
child: Row(
children: [
Text(
'查看全部',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey,
),
),
const Icon(
Icons.keyboard_arrow_right,
color: Colors.grey,
)
],
),
),
],
),
),
Container(
height: 130.w,
margin: EdgeInsets.only(bottom: 16.w),
child: ListView.builder(
controller: _scrollController,
itemCount: _templateList.length,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(horizontal: 4.w),
itemBuilder: (BuildContext context, int index) {
Map data = _templateList[index];
return _item(data);
},
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.r),
),
child: Column(
children: [
_row(
'区分说话人',
widget: Transform.scale(
scale: 0.8,
alignment: Alignment.centerRight,
child: Switch(
value: _isSpeaker,
onChanged: (bool value) {
setState(() {
_isSpeaker = value;
});
},
),
),
),
_row(
'录音语言',
value: _languageText,
isBorder: false,
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.grey[100],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(12.r),
),
),
builder: (BuildContext context) {
return RecordLanguageBottomSheet(_language);
},
).then((value) {
if (value != null) {
setState(() {
_languageText = value['name'];
_language = value['value'];
});
}
});
},
),
],
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 20.w),
child: GestureDetector(
onTap: () {
Get.back();
_controller.summaryCreate();
},
child: Container(
height: 48.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
'立即生成',
style: TextStyle(
fontSize: 16.sp,
color: Colors.white,
),
),
),
),
),
],
);
}
Widget _item(Map data) {
return Container(
width: 200.w,
margin: EdgeInsets.symmetric(horizontal: 8.w),
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.w),
),
child: GestureDetector(
onTap: () {
setState(() {
_templateId = data['id'];
});
},
child: Stack(
children: [
Container(
padding: EdgeInsets.all(5.w),
decoration: BoxDecoration(
color: Colors.white,
gradient: _templateId == data['id']
? const LinearGradient(
colors: [Colors.blue, Colors.pinkAccent],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
)
: null,
),
child: Container(
width: double.infinity,
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.w),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 5.w),
child: Icon(
data['icon'],
color: Colors.orange,
size: 30.w,
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 10.w),
child: Text(
data['name'],
style: TextStyle(
fontSize: 16.sp,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
),
data['tag'].isNotEmpty
? Wrap(
spacing: 5.w,
children: List.generate(
data['tag'].length,
(int idx) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 5.w, vertical: 2.w),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(4.r),
),
child: Text(
data['tag'][idx],
style: TextStyle(
fontSize: 10.sp,
color: Colors.grey[600],
),
),
);
},
),
)
: Text(
data['desc'],
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[600],
),
),
],
),
),
),
if (_lastTemplateId == data['id'])
Positioned(
top: 0,
left: 0,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 1.w),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12.w),
bottomRight: Radius.circular(4.w),
),
),
child: Text(
'上次使用',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.bold,
),
),
),
),
if (_templateId == data['id'])
Positioned(
right: -18.w,
bottom: -18.w,
child: Container(
width: 40.w,
height: 40.w,
padding: EdgeInsets.all(2.w),
alignment: Alignment.topLeft,
decoration: BoxDecoration(
color: Colors.pinkAccent,
borderRadius: BorderRadius.circular(40.r),
),
child: Icon(
Icons.check,
color: Colors.white,
size: 18.w,
),
),
),
],
),
),
);
}
Widget _row(
String title, {
String? value,
Widget? widget,
bool isBorder = true,
Function()? onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Padding(
padding: EdgeInsets.only(left: 15.w),
child: Container(
height: 46.w,
padding: EdgeInsets.only(right: 10.w),
decoration: BoxDecoration(
border: isBorder
? Border(
bottom: BorderSide(
color: Colors.grey[300]!,
),
)
: null,
),
child: Row(
children: [
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: 14.sp,
color: Colors.black87,
),
),
),
widget ??
Row(
children: [
Text(
value ?? '',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey,
),
),
const Icon(
Icons.keyboard_arrow_right,
color: Colors.grey,
)
],
),
],
),
),
),
);
}
}

69
lib/modules/meeting/views/meeting_details_view.dart

@ -71,25 +71,23 @@ class MeetingDetailsView extends GetView<MeetingDetailsController> {
), ),
child: Row( child: Row(
children: [ children: [
StreamBuilder<PlayerState>( GestureDetector(
stream: controller.playerController.onPlayerStateChanged, onTap: () {
builder: (context, snapshot) { if (controller.isPlay.value) {
bool isPlay = snapshot.data?.isPlaying ?? false; controller.playerController.pausePlayer();
return GestureDetector( } else {
onTap: () { controller.playerController.startPlayer();
if (isPlay) { }
controller.playerController.pausePlayer();
} else {
controller.playerController.startPlayer();
}
},
child: Icon(
isPlay ? Icons.pause_circle : Icons.play_circle,
color: Colors.black,
size: 30.w,
),
);
}, },
child: Obx(
() => Icon(
controller.isPlay.value
? Icons.pause_circle
: Icons.play_circle,
color: Colors.black,
size: 30.w,
),
),
), ),
4.horizontalSpace, 4.horizontalSpace,
GestureDetector( GestureDetector(
@ -352,26 +350,23 @@ class MeetingDetailsView extends GetView<MeetingDetailsController> {
size: 30.w, size: 30.w,
), ),
), ),
StreamBuilder<PlayerState>( GestureDetector(
stream: onTap: () {
controller.playerController.onPlayerStateChanged, if (controller.isPlay.value) {
builder: (context, snapshot) { controller.playerController.pausePlayer();
bool isPlay = snapshot.data?.isPlaying ?? false; } else {
return GestureDetector( controller.playerController.startPlayer();
onTap: () { }
if (isPlay) {
controller.playerController.pausePlayer();
} else {
controller.playerController.startPlayer();
}
},
child: Icon(
isPlay ? Icons.pause_circle : Icons.play_circle,
color: Colors.black,
size: 48.w,
),
);
}, },
child: Obx(
() => Icon(
controller.isPlay.value
? Icons.pause_circle
: Icons.play_circle,
color: Colors.black,
size: 48.w,
),
),
), ),
GestureDetector( GestureDetector(
onTap: () { onTap: () {

137
lib/modules/meeting/views/record_language_bottom_sheet.dart

@ -0,0 +1,137 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
class RecordLanguageBottomSheet extends StatefulWidget {
final String language;
const RecordLanguageBottomSheet(this.language, {super.key});
@override
State<RecordLanguageBottomSheet> createState() =>
_RecordLanguageBottomSheetState();
}
class _RecordLanguageBottomSheetState extends State<RecordLanguageBottomSheet> {
final List _languages = [
{'name': '中文普通话(简体)', 'value': 'zh-CN'},
{'name': '粤语', 'value': 'cant'},
{'name': '四川话', 'value': 'sc'},
{'name': '上海话', 'value': 'zh_shanghai'},
{'name': '英文', 'value': 'en-US'},
{'name': '日语', 'value': 'ja-JP'},
{'name': '韩语', 'value': 'ko-KR'},
{'name': '法语', 'value': 'fr-FR'},
{'name': '西班牙语', 'value': 'es-MX'},
{'name': '葡萄牙语', 'value': 'pt-BR'},
{'name': '印尼语', 'value': 'id-ID'},
];
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'录音语言',
style: TextStyle(
fontSize: 16.sp,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
GestureDetector(
onTap: () => Get.back(),
child: Container(
width: 26.w,
height: 26.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(26.r),
),
child: Icon(
Icons.close,
size: 18.w,
color: Colors.grey[400],
),
),
)
],
),
),
Divider(
height: 1,
color: Colors.grey[300],
),
Container(
margin: EdgeInsets.symmetric(vertical: 20.w),
constraints: BoxConstraints(
maxHeight: 1.sh - 180.w,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.r),
),
child: ListView.builder(
itemCount: _languages.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
Map data = _languages[index];
return _item(index, data);
},
),
),
],
),
);
}
Widget _item(int index, Map data) {
return GestureDetector(
onTap: () {
Get.back(result: data);
},
child: Padding(
padding: EdgeInsets.only(left: 15.w),
child: Container(
height: 46.w,
padding: EdgeInsets.only(right: 10.w),
decoration: BoxDecoration(
border: index != 0
? Border(
top: BorderSide(
color: Colors.grey[300]!,
),
)
: null,
),
child: Row(
children: [
Expanded(
child: Text(
data['name'],
style: TextStyle(
fontSize: 14.sp,
color: Colors.black87,
),
),
),
if (widget.language == data['value'])
const Icon(
Icons.check,
color: Colors.black87,
),
],
),
),
),
);
}
}

16
lib/modules/meeting/views/speaker_all_bottom_sheet.dart

@ -4,6 +4,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:lottie/lottie.dart';
import '../../../data/services/db/sqflite_api.dart'; import '../../../data/services/db/sqflite_api.dart';
import '../controllers/meeting_details_controller.dart'; import '../controllers/meeting_details_controller.dart';
@ -361,6 +362,21 @@ class _SpeakerAllBottomSheetState extends State<SpeakerAllBottomSheet> {
fontSize: 12.sp, fontSize: 12.sp,
), ),
), ),
if (isActive)
Container(
width: 16.w,
height: 16.w,
margin: EdgeInsets.only(left: 5.w),
alignment: Alignment.center,
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
child: Transform.scale(
scale: 1.6,
child: Lottie.asset(
'assets/lottie/sound_effect.json',
),
),
),
], ],
), ),
Padding( Padding(

69
lib/modules/meeting/views/speech_tab.dart

@ -2,8 +2,10 @@ import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:lottie/lottie.dart';
import '../controllers/meeting_details_controller.dart'; import '../controllers/meeting_details_controller.dart';
import 'generate_bottom_sheet.dart';
import 'speaker_all_bottom_sheet.dart'; import 'speaker_all_bottom_sheet.dart';
import 'speaker_bottom_sheet.dart'; import 'speaker_bottom_sheet.dart';
@ -87,6 +89,43 @@ class SpeechTab extends GetView<MeetingDetailsController> {
fontSize: 13.sp, fontSize: 13.sp,
), ),
), ),
Obx(() {
bool isActive = false;
int starttime = item['starttime'];
int endtime = 0;
if (index == controller.textList.length - 1) {
endtime = controller.durationMax.value;
} else {
endtime =
controller.textList[index + 1]['starttime'];
}
if (controller.currentDuration.value != 0) {
if (controller.currentDuration.value >= starttime &&
controller.currentDuration.value < endtime) {
isActive = true;
}
}
return isActive
? Container(
width: 16.w,
height: 16.w,
margin: EdgeInsets.only(left: 5.w),
alignment: Alignment.center,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Colors.red[100],
borderRadius: BorderRadius.circular(4.r),
),
child: Transform.scale(
scale: 1.6,
child: Lottie.asset(
'assets/lottie/sound_effect.json',
controller: controller.lottieController,
),
),
)
: const SizedBox();
}),
], ],
), ),
), ),
@ -169,9 +208,17 @@ class SpeechTab extends GetView<MeetingDetailsController> {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: () { onTap: () {
controller.playerController if (isActive) {
.seekTo(item['starttime']); if (controller.isPlay.value) {
controller.playerController.startPlayer(); controller.playerController.pausePlayer();
} else {
controller.playerController.startPlayer();
}
} else {
controller.playerController
.seekTo(item['starttime']);
controller.playerController.startPlayer();
}
}, },
child: Container( child: Container(
width: double.infinity, width: double.infinity,
@ -244,7 +291,21 @@ class SpeechTab extends GetView<MeetingDetailsController> {
child: _button( child: _button(
Icons.auto_awesome, Icons.auto_awesome,
'生成', '生成',
controller.speakerCreate, () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.grey[100],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(12.r),
),
),
builder: (BuildContext context) {
return const GenerateBottomSheet();
},
);
},
), ),
), ),
], ],

17
lib/modules/meeting/views/summary_tab.dart

@ -6,6 +6,7 @@ import 'package:get/get.dart';
import '../../../data/services/db/sqflite_api.dart'; import '../../../data/services/db/sqflite_api.dart';
import '../controllers/meeting_details_controller.dart'; import '../controllers/meeting_details_controller.dart';
import 'generate_bottom_sheet.dart';
import 'speaker_all_bottom_sheet.dart'; import 'speaker_all_bottom_sheet.dart';
class SummaryTab extends StatefulWidget { class SummaryTab extends StatefulWidget {
@ -318,7 +319,21 @@ class _SummaryTabState extends State<SummaryTab> {
child: _button( child: _button(
Icons.auto_awesome, Icons.auto_awesome,
'生成', '生成',
_controller.summaryCreate, () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.grey[100],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(12.r),
),
),
builder: (BuildContext context) {
return const GenerateBottomSheet();
},
);
},
), ),
), ),
], ],

407
lib/modules/meeting/views/template_view.dart

@ -0,0 +1,407 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
class TemplateView extends StatefulWidget {
final int templateId;
final int lastTemplateId;
const TemplateView(this.templateId, this.lastTemplateId, {super.key});
@override
State<TemplateView> createState() => _TemplateViewState();
}
class _TemplateViewState extends State<TemplateView> {
final List _categoryList = [
{
'id': 1,
'name': '通用',
'template': [
{
'id': 1,
'name': '会议纪要',
'icon': Icons.auto_stories,
'tag': ['主题', '代办事项'],
'desc': '',
},
{
'id': 2,
'name': 'Autopilot',
'icon': Icons.auto_graph,
'tag': ['自适应结构', '全场景适配'],
'desc': '',
},
{
'id': 3,
'name': '推理Autopilot',
'icon': Icons.add_to_drive_outlined,
'tag': ['推理模型', '自动结构化'],
'desc': '',
},
{
'id': 4,
'name': '通话',
'icon': Icons.perm_phone_msg,
'tag': ['主题', '代办事项'],
'desc': '',
},
],
},
{
'id': 2,
'name': '会议',
'template': [
{
'id': 5,
'name': '咨询',
'icon': Icons.auto_stories,
'tag': ['主题', '代办事项'],
'desc': '',
},
{
'id': 6,
'name': '讨论',
'icon': Icons.auto_graph,
'tag': ['自适应结构', '全场景适配'],
'desc': '',
},
{
'id': 7,
'name': '汇报',
'icon': Icons.add_to_drive_outlined,
'tag': ['推理模型', '自动结构化'],
'desc': '',
},
{
'id': 8,
'name': '任务分配',
'icon': Icons.perm_phone_msg,
'tag': ['主题', '代办事项'],
'desc': '',
},
],
},
{
'id': 3,
'name': '演讲',
'template': [
{
'id': 9,
'name': '课堂笔记',
'icon': Icons.auto_stories,
'tag': ['主题', '代办事项'],
'desc': '',
},
{
'id': 10,
'name': '讲座',
'icon': Icons.auto_graph,
'tag': ['自适应结构', '全场景适配'],
'desc': '',
},
{
'id': 11,
'name': '培训',
'icon': Icons.add_to_drive_outlined,
'tag': ['推理模型', '自动结构化'],
'desc': '',
},
{
'id': 12,
'name': 'Sermon',
'icon': Icons.perm_phone_msg,
'tag': ['主题', '代办事项'],
'desc': '',
},
],
},
{
'id': 4,
'name': '自定义',
'template': [
{
'id': 13,
'name': '自定义',
'icon': Icons.auto_fix_high,
'tag': [],
'desc': '自定义模版',
},
],
},
];
Map _templateData = {};
@override
void initState() {
super.initState();
outerLoop:
for (var element in _categoryList) {
List template = element['template'];
for (var item in template) {
if (item['id'] == widget.templateId) {
_templateData = item;
break outerLoop;
}
}
}
}
@override
Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: _buildAppBar(isDarkMode),
body: _buildBody(context, isDarkMode),
);
}
// 构建应用栏
PreferredSizeWidget _buildAppBar(bool isDarkMode) {
return AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white,
elevation: 0.5,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: isDarkMode ? Colors.white : Colors.black,
size: 20.sp,
),
onPressed: () => Get.back(),
),
title: Text(
'选择总结模版',
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
centerTitle: true,
);
}
// 构建主体内容
Widget _buildBody(BuildContext context, bool isDarkMode) {
return Column(
children: [
Expanded(
child: ListView.builder(
itemCount: _categoryList.length,
itemBuilder: (BuildContext context, int index) {
Map data = _categoryList[index];
return _item(data);
},
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 20.w),
child: GestureDetector(
onTap: () {
Get.back(result: _templateData);
},
child: Container(
height: 48.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
'就用这个模版',
style: TextStyle(
fontSize: 16.sp,
color: Colors.white,
),
),
),
),
),
],
);
}
Widget _item(Map data) {
return Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.w),
child: Row(
children: [
Text(
data['name'],
style: TextStyle(
fontSize: 18.sp,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
const Icon(
Icons.keyboard_arrow_right,
color: Colors.grey,
)
],
),
),
Container(
height: 150.w,
margin: EdgeInsets.only(bottom: 15.w),
child: ListView.builder(
itemCount: data['template'].length,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(horizontal: 8.w),
itemBuilder: (BuildContext context, int index) {
Map template = data['template'][index];
return _template(template);
},
),
),
],
);
}
Widget _template(Map template) {
return Container(
width: 200.w,
margin: EdgeInsets.symmetric(horizontal: 4.w),
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.w),
),
child: GestureDetector(
onTap: () {
setState(() {
_templateData = template;
});
},
child: Stack(
children: [
Container(
padding: EdgeInsets.all(5.w),
decoration: BoxDecoration(
color: Colors.white,
gradient: _templateData['id'] == template['id']
? const LinearGradient(
colors: [Colors.blue, Colors.pinkAccent],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
)
: null,
),
child: Container(
width: double.infinity,
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.w),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 5.w),
child: Icon(
template['icon'],
color: Colors.orange,
size: 30.w,
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 10.w),
child: Text(
template['name'],
style: TextStyle(
fontSize: 16.sp,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
),
template['tag'].isNotEmpty
? Wrap(
spacing: 5.w,
children: List.generate(
template['tag'].length,
(int idx) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 5.w, vertical: 2.w),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(4.r),
),
child: Text(
template['tag'][idx],
style: TextStyle(
fontSize: 10.sp,
color: Colors.grey[600],
),
),
);
},
),
)
: Text(
template['desc'],
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[600],
),
),
],
),
),
),
if (widget.lastTemplateId == template['id'])
Positioned(
top: 0,
left: 0,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 1.w),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12.w),
bottomRight: Radius.circular(4.w),
),
),
child: Text(
'上次使用',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.bold,
),
),
),
),
if (_templateData['id'] == template['id'])
Positioned(
right: -18.w,
bottom: -18.w,
child: Container(
width: 40.w,
height: 40.w,
padding: EdgeInsets.all(2.w),
alignment: Alignment.topLeft,
decoration: BoxDecoration(
color: Colors.pinkAccent,
borderRadius: BorderRadius.circular(40.r),
),
child: Icon(
Icons.check,
color: Colors.white,
size: 18.w,
),
),
),
],
),
),
);
}
}

2
pubspec.yaml

@ -25,7 +25,7 @@ dependencies:
web_socket_channel: ^3.0.2 web_socket_channel: ^3.0.2
flutter_dotenv: ^5.1.0 flutter_dotenv: ^5.1.0
synchronized: ^3.1.0 synchronized: ^3.1.0
intl: ^0.19.0 intl: ^0.20.2
crypto: ^3.0.3 crypto: ^3.0.3
flutter_tts: ^4.2.2 flutter_tts: ^4.2.2
logger: ^2.0.2+1 logger: ^2.0.2+1

Loading…
Cancel
Save