Browse Source

Merge commit 'a31fe4934b7137b603feffb301ce160991343902' into lxm

weicu
lxm 2 years ago
parent
commit
d1ff4002e6
  1. 6
      lib/data/services/ai_service.dart
  2. 6
      lib/data/services/db/database_helper.dart
  3. 12
      lib/data/services/db/sqflite_api.dart
  4. 8
      lib/data/services/deapsound_ai_service.dart
  5. 8
      lib/data/services/kouzi_ai_service.dart
  6. 1
      lib/data/services/volcano_ai_service.dart
  7. 12
      lib/main.dart
  8. 31
      lib/modules/meeting/controllers/meeting_edit_controller.dart
  9. 69
      lib/modules/meeting/controllers/meeting_record_controller.dart
  10. 5
      lib/modules/meeting/model/meeting_details_model.dart
  11. 9
      lib/modules/meeting/model/meeting_model.dart
  12. 4
      lib/modules/meeting/views/meeting_details_view.dart
  13. 12
      lib/modules/meeting/views/meeting_edit_view.dart
  14. 2
      lib/modules/meeting/views/meeting_record_view.dart
  15. 110
      lib/modules/meeting/views/meeting_view.dart
  16. 2
      pubspec.yaml

6
lib/data/services/ai_service.dart

@ -1,5 +1,11 @@
/// AI回复服务接口 /// AI回复服务接口
abstract class AiService { abstract class AiService {
/// 非流式输出方法
Future<String> sendMessage({
required List<Map<String, String>> messages,
required String systemPrompt,
});
/// 流式输出方法 /// 流式输出方法
Stream<String> sendMessageStream({ Stream<String> sendMessageStream({
required List<Map<String, String>> messages, required List<Map<String, String>> messages,

6
lib/data/services/db/database_helper.dart

@ -32,6 +32,9 @@ class DatabaseHelper {
title TEXT, title TEXT,
content TEXT, content TEXT,
seconds INTEGER, seconds INTEGER,
address TEXT,
longitude TEXT,
latitude TEXT,
creationtime INTEGER creationtime INTEGER
) )
'''); ''');
@ -41,7 +44,8 @@ class DatabaseHelper {
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
meetingid INTEGER, meetingid INTEGER,
audio TEXT, audio TEXT,
content TEXT content TEXT,
aisummary TEXT
) )
'''); ''');
await batch.commit(); await batch.commit();

12
lib/data/services/db/sqflite_api.dart

@ -41,12 +41,16 @@ class SqfliteApi {
'title': meeting['title'], 'title': meeting['title'],
'content': meeting['firstContent'], 'content': meeting['firstContent'],
'seconds': meeting['seconds'], 'seconds': meeting['seconds'],
'address': meeting['address'],
'longitude': meeting['longitude'],
'latitude': meeting['latitude'],
'creationtime': meeting['creationTime'], 'creationtime': meeting['creationTime'],
}); });
await txn.insert('meetingdetails', { await txn.insert('meetingdetails', {
'meetingid': id, 'meetingid': id,
'audio': meeting['audio'], 'audio': meeting['audio'],
'content': meeting['content'], 'content': meeting['content'],
'aisummary': meeting['aisummary'],
}); });
}); });
return id; return id;
@ -73,13 +77,19 @@ class SqfliteApi {
'title': meeting['title'], 'title': meeting['title'],
'content': meeting['firstContent'], 'content': meeting['firstContent'],
'seconds': meeting['seconds'], 'seconds': meeting['seconds'],
'address': meeting['address'],
'longitude': meeting['longitude'],
'latitude': meeting['latitude'],
}, },
where: 'id = ?', where: 'id = ?',
whereArgs: [id], whereArgs: [id],
); );
await txn.update( await txn.update(
'meetingdetails', 'meetingdetails',
{'content': meeting['content']}, {
'content': meeting['content'],
'aisummary': meeting['aisummary'],
},
where: 'meetingid = ?', where: 'meetingid = ?',
whereArgs: [id], whereArgs: [id],
); );

8
lib/data/services/deapsound_ai_service.dart

@ -14,6 +14,14 @@ class DeapsoundAIService implements AiService {
apiKey = storage.read('logintoken'); apiKey = storage.read('logintoken');
} }
@override
Future<String> sendMessage({
required List<Map<String, String>> messages,
required String systemPrompt,
}) async {
return '';
}
@override @override
Stream<String> sendMessageStream({ Stream<String> sendMessageStream({
required List<Map<String, String>> messages, required List<Map<String, String>> messages,

8
lib/data/services/kouzi_ai_service.dart

@ -10,6 +10,14 @@ class KouziAIService implements AiService {
'pat_QGDh6UGm3PaeL9GRS95JxrJ9aoU92ehMLQQ2NCre1TeBC5YeTcjpDmlz3efsJ8lh'; 'pat_QGDh6UGm3PaeL9GRS95JxrJ9aoU92ehMLQQ2NCre1TeBC5YeTcjpDmlz3efsJ8lh';
final String modelId = '7481292724306657316'; final String modelId = '7481292724306657316';
@override
Future<String> sendMessage({
required List<Map<String, String>> messages,
required String systemPrompt,
}) async {
return '';
}
@override @override
Stream<String> sendMessageStream({ Stream<String> sendMessageStream({
required List<Map<String, String>> messages, required List<Map<String, String>> messages,

1
lib/data/services/volcano_ai_service.dart

@ -32,6 +32,7 @@ class VolcanoAIService implements AiService {
} }
// 非流式输出方法 // 非流式输出方法
@override
Future<String> sendMessage({ Future<String> sendMessage({
required List<Map<String, String>> messages, required List<Map<String, String>> messages,
required String systemPrompt, required String systemPrompt,

12
lib/main.dart

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
@ -82,6 +83,17 @@ class MainApp extends StatelessWidget {
locale: AppTranslations.deviceLocale(), locale: AppTranslations.deviceLocale(),
fallbackLocale: AppTranslations.fallbackLocale, fallbackLocale: AppTranslations.fallbackLocale,
supportedLocales: AppTranslations.supportedLocales, supportedLocales: AppTranslations.supportedLocales,
builder: EasyLoading.init(
builder: (BuildContext context, Widget? widget) {
EasyLoading.instance.userInteractions = false;
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.noScaling,
),
child: widget!,
);
},
),
); );
}, },
); );

31
lib/modules/meeting/controllers/meeting_edit_controller.dart

@ -1,13 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
import '../../../data/services/ai_service.dart';
import '../../../data/services/db/sqflite_api.dart'; import '../../../data/services/db/sqflite_api.dart';
import '../../../data/services/volcano_ai_service.dart';
import 'meeting_controller.dart'; import 'meeting_controller.dart';
import 'meeting_details_controller.dart'; import 'meeting_details_controller.dart';
class MeetingEditController extends GetxController class MeetingEditController extends GetxController
with GetSingleTickerProviderStateMixin { with GetSingleTickerProviderStateMixin {
late AiService _aiService;
final MeetingController meetingController = Get.find<MeetingController>(); final MeetingController meetingController = Get.find<MeetingController>();
final MeetingDetailsController meetingDetailsController = final MeetingDetailsController meetingDetailsController =
Get.find<MeetingDetailsController>(); Get.find<MeetingDetailsController>();
@ -21,6 +26,10 @@ class MeetingEditController extends GetxController
int creationTime = 0; int creationTime = 0;
RxString address = ''.obs;
String _longitude = '';
String _latitude = '';
int id = 0; int id = 0;
RxBool isPlay = false.obs; RxBool isPlay = false.obs;
@ -51,6 +60,9 @@ class MeetingEditController extends GetxController
text: meetingDetailsController.dataDetails.value.content, text: meetingDetailsController.dataDetails.value.content,
); );
creationTime = meetingDetailsController.meetingData.value.creationtime; creationTime = meetingDetailsController.meetingData.value.creationtime;
address.value = meetingDetailsController.meetingData.value.address;
_longitude = meetingDetailsController.meetingData.value.longitude;
_latitude = meetingDetailsController.meetingData.value.latitude;
audioPlayer.setAudioSource( audioPlayer.setAudioSource(
AudioSource.file(meetingDetailsController.dataDetails.value.audio)); AudioSource.file(meetingDetailsController.dataDetails.value.audio));
audioPlayer.playerStateStream.listen((state) async { audioPlayer.playerStateStream.listen((state) async {
@ -76,6 +88,15 @@ class MeetingEditController extends GetxController
} }
void confirm() async { void confirm() async {
EasyLoading.show();
_aiService = Get.find<VolcanoAIService>();
String aiSummary = await _aiService.sendMessage(
systemPrompt: '',
messages: [
{'role': 'user', 'content': contentController.text},
{'role': 'user', 'content': '帮我总结一下这次会议'},
],
);
String content = contentController.text; String content = contentController.text;
String firstContent = String firstContent =
content.length >= 30 ? content.substring(0, 30) : content; content.length >= 30 ? content.substring(0, 30) : content;
@ -87,6 +108,10 @@ class MeetingEditController extends GetxController
'firstContent': firstContent.replaceAll("\n", ""), 'firstContent': firstContent.replaceAll("\n", ""),
'seconds': meetingDetailsController.seconds, 'seconds': meetingDetailsController.seconds,
'content': contentController.text, 'content': contentController.text,
'aisummary': aiSummary,
'address': address.value,
'longitude': _longitude,
'latitude': _latitude,
}; };
final result = await SqfliteApi.editMeeting(id, data); final result = await SqfliteApi.editMeeting(id, data);
if (result != null) { if (result != null) {
@ -99,14 +124,20 @@ class MeetingEditController extends GetxController
dataMap['title'] = title; dataMap['title'] = title;
dataMap['content'] = firstContent.replaceAll("\n", ""); dataMap['content'] = firstContent.replaceAll("\n", "");
dataMap['seconds'] = meetingDetailsController.seconds; dataMap['seconds'] = meetingDetailsController.seconds;
dataMap['address'] = address.value;
meetingController.dataList[index] = dataMap; meetingController.dataList[index] = dataMap;
} }
meetingDetailsController.meetingData.value.title = title; meetingDetailsController.meetingData.value.title = title;
meetingDetailsController.meetingData.value.address = address.value;
meetingDetailsController.meetingData.value.longitude = _longitude;
meetingDetailsController.meetingData.value.latitude = _latitude;
meetingDetailsController.dataDetails.value.content = meetingDetailsController.dataDetails.value.content =
contentController.text; contentController.text;
meetingDetailsController.dataDetails.value.aisummary = aiSummary;
meetingDetailsController.meetingData.refresh(); meetingDetailsController.meetingData.refresh();
meetingDetailsController.dataDetails.refresh(); meetingDetailsController.dataDetails.refresh();
Get.back(); Get.back();
} }
EasyLoading.dismiss();
} }
} }

69
lib/modules/meeting/controllers/meeting_record_controller.dart

@ -3,18 +3,23 @@ import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
import '../../../data/services/ai_service.dart';
import '../../../data/services/asr_service.dart'; import '../../../data/services/asr_service.dart';
import '../../../data/services/db/sqflite_api.dart'; import '../../../data/services/db/sqflite_api.dart';
import '../../../data/services/speech_impl/xunfei_asr_service.dart'; import '../../../data/services/speech_impl/xunfei_asr_service.dart';
import '../../../data/services/volcano_ai_service.dart';
import 'meeting_controller.dart'; import 'meeting_controller.dart';
class MeetingRecordController extends GetxController class MeetingRecordController extends GetxController
with GetSingleTickerProviderStateMixin { with GetSingleTickerProviderStateMixin {
late AiService _aiService;
final MeetingController meetingController = Get.find<MeetingController>(); final MeetingController meetingController = Get.find<MeetingController>();
late AnimationController lottieController; late AnimationController lottieController;
@ -28,6 +33,10 @@ class MeetingRecordController extends GetxController
String title = ''; String title = '';
int creationTime = 0; int creationTime = 0;
RxString address = '深圳市高新技术产业园区W2-A座'.obs;
String _longitude = '';
String _latitude = '';
RxString intermediateContent = ''.obs; RxString intermediateContent = ''.obs;
RxString finalContent = ''.obs; RxString finalContent = ''.obs;
@ -75,7 +84,7 @@ class MeetingRecordController extends GetxController
} }
// 暂停 // 暂停
void _pause() async { Future<void> _pause() async {
await _stopRecorder(); await _stopRecorder();
lottieController.stop(); lottieController.stop();
isRecording.value = false; isRecording.value = false;
@ -150,7 +159,9 @@ class MeetingRecordController extends GetxController
// 完成 // 完成
void complete() async { void complete() async {
if (await Permission.storage.request().isGranted) { if (await Permission.storage.request().isGranted) {
_save(); EasyLoading.show();
await _pause();
_aiSummary();
} else { } else {
Get.snackbar( Get.snackbar(
'error'.tr, // 错误 'error'.tr, // 错误
@ -162,7 +173,19 @@ class MeetingRecordController extends GetxController
} }
} }
void _save() async { void _aiSummary() async {
_aiService = Get.find<VolcanoAIService>();
String aiSummary = await _aiService.sendMessage(
systemPrompt: '',
messages: [
{'role': 'user', 'content': finalContent.value},
{'role': 'user', 'content': '帮我总结一下这次会议'},
],
);
_save(aiSummary);
}
void _save(String aiSummary) async {
String firstContent = finalContent.value.length >= 30 String firstContent = finalContent.value.length >= 30
? finalContent.value.substring(0, 30) ? finalContent.value.substring(0, 30)
: finalContent.value; : finalContent.value;
@ -176,26 +199,32 @@ class MeetingRecordController extends GetxController
Uint8List wavData = Uint8List.fromList([...wavHeader, ...audio]); Uint8List wavData = Uint8List.fromList([...wavHeader, ...audio]);
String audiopPath = String audiopPath =
await _saveAudioToFile(wavData, 'meeting_$creationTime.wav'); await _saveAudioToFile(wavData, 'meeting_$creationTime.wav');
if (audiopPath.isEmpty) return; if (audiopPath.isNotEmpty) {
Map<String, Object?> data = { Map<String, Object?> data = {
'title': title,
'firstContent': firstContent.replaceAll("\n", ""),
'seconds': seconds.value,
'creationTime': creationTime,
'audio': audiopPath,
'content': finalContent.value,
};
final result = await SqfliteApi.insertMeeting(data);
if (result != null) {
meetingController.dataList.insert(0, {
'id': result,
'title': title, 'title': title,
'content': firstContent, 'firstContent': firstContent.replaceAll("\n", ""),
'seconds': seconds.value, 'seconds': seconds.value,
'creationtime': creationTime, 'creationTime': creationTime,
}); 'audio': audiopPath,
Get.back(); 'content': finalContent.value,
'aisummary': aiSummary,
'address': address.value,
'longitude': _longitude,
'latitude': _latitude,
};
final result = await SqfliteApi.insertMeeting(data);
if (result != null) {
meetingController.dataList.insert(0, {
'id': result,
'title': title,
'content': firstContent,
'seconds': seconds.value,
'creationtime': creationTime,
});
Get.back();
}
} }
EasyLoading.dismiss();
} }
Future<String> _saveAudioToFile(Uint8List audioData, String fileName) async { Future<String> _saveAudioToFile(Uint8List audioData, String fileName) async {

5
lib/modules/meeting/model/meeting_details_model.dart

@ -5,13 +5,15 @@ class MeetingDetailsModel {
late int meetingid; late int meetingid;
late String audio; late String audio;
late String content; late String content;
late String aisummary;
factory MeetingDetailsModel.fromJson(Map<String, dynamic> map) => factory MeetingDetailsModel.fromJson(Map<String, dynamic> map) =>
MeetingDetailsModel() MeetingDetailsModel()
..id = map["id"] ..id = map["id"]
..meetingid = map["meetingid"] ..meetingid = map["meetingid"]
..audio = map["audio"] ..audio = map["audio"]
..content = map["content"]; ..content = map["content"]
..aisummary = map["aisummary"];
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final data = <String, dynamic>{}; final data = <String, dynamic>{};
@ -19,6 +21,7 @@ class MeetingDetailsModel {
data['meetingid'] = meetingid; data['meetingid'] = meetingid;
data['audio'] = audio; data['audio'] = audio;
data['content'] = content; data['content'] = content;
data['aisummary'] = aisummary;
return data; return data;
} }
} }

9
lib/modules/meeting/model/meeting_model.dart

@ -5,6 +5,9 @@ class MeetingModel {
late String title; late String title;
late String content; late String content;
late int seconds; late int seconds;
late String address;
late String longitude;
late String latitude;
late int creationtime; late int creationtime;
factory MeetingModel.fromJson(Map<String, dynamic> map) => MeetingModel() factory MeetingModel.fromJson(Map<String, dynamic> map) => MeetingModel()
@ -12,6 +15,9 @@ class MeetingModel {
..title = map["title"] ..title = map["title"]
..content = map["content"] ..content = map["content"]
..seconds = map["seconds"] ..seconds = map["seconds"]
..address = map["address"]
..longitude = map["longitude"]
..latitude = map["latitude"]
..creationtime = map["creationtime"]; ..creationtime = map["creationtime"];
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -20,6 +26,9 @@ class MeetingModel {
data['title'] = title; data['title'] = title;
data['content'] = content; data['content'] = content;
data['seconds'] = seconds; data['seconds'] = seconds;
data['address'] = address;
data['longitude'] = longitude;
data['latitude'] = latitude;
data['creationtime'] = creationtime; data['creationtime'] = creationtime;
return data; return data;
} }

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

@ -90,7 +90,7 @@ class MeetingDetailsView extends GetView<MeetingDetailsController> {
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
), ),
Text( Text(
'深圳市高新技术产业园区W2-A座', controller.meetingData.value.address,
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 14.sp, fontSize: 14.sp,
@ -268,7 +268,7 @@ class MeetingDetailsView extends GetView<MeetingDetailsController> {
children: [ children: [
SingleChildScrollView( SingleChildScrollView(
child: Text( child: Text(
controller.dataDetails.value.content, controller.dataDetails.value.aisummary,
), ),
), ),
SingleChildScrollView( SingleChildScrollView(

12
lib/modules/meeting/views/meeting_edit_view.dart

@ -119,11 +119,13 @@ class MeetingEditView extends GetView<MeetingEditController> {
size: 16.w, size: 16.w,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
), ),
Text( Obx(
'深圳市高新技术产业园区W2-A座', () => Text(
style: TextStyle( controller.address.value,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], style: TextStyle(
fontSize: 14.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 14.sp,
),
), ),
), ),
], ],

2
lib/modules/meeting/views/meeting_record_view.dart

@ -131,7 +131,7 @@ class MeetingRecordView extends GetView<MeetingRecordController> {
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
), ),
Text( Text(
'深圳市高新技术产业园区W2-A座', controller.address.value,
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 14.sp, fontSize: 14.sp,

110
lib/modules/meeting/views/meeting_view.dart

@ -161,13 +161,12 @@ class MeetingView extends GetView<MeetingController> {
), ),
], ],
), ),
child: Row( child: Column(
children: [ children: [
Expanded( Row(
child: Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, Expanded(
children: [ child: Text(
Text(
data.title, data.title,
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode ? Colors.white : Colors.black87,
@ -175,44 +174,77 @@ class MeetingView extends GetView<MeetingController> {
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
Text( ),
data.content.isNotEmpty ? data.content : '无内容', 10.horizontalSpace,
style: TextStyle( Text(
color: isDarkMode ? Colors.white70 : Colors.black54, DateUtil.formatDateMs(
fontSize: 12.sp, data.creationtime,
), format: DateFormats.y_mo_d_h_m,
overflow: TextOverflow.ellipsis,
), ),
Text( style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54,
fontSize: 12.sp,
),
),
],
),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data.content.isNotEmpty ? data.content : '无内容',
style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54,
fontSize: 12.sp,
),
overflow: TextOverflow.ellipsis,
),
Row(
children: [
Icon(
Icons.location_on,
size: 14.w,
color: isDarkMode ? Colors.white70 : Colors.black54,
),
Expanded(
child: Text(
data.address,
style: TextStyle(
color: isDarkMode
? Colors.white70
: Colors.black54,
fontSize: 12.sp,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
],
),
),
Container(
width: 76.w,
height: 28.w,
margin: EdgeInsets.only(left: 10.w),
alignment: Alignment.center,
decoration: BoxDecoration(
color: isDarkMode ? AppColors.primary : Colors.blue,
borderRadius: BorderRadius.circular(28.w),
),
child: Text(
DateUtil.formatDateMs( DateUtil.formatDateMs(
data.creationtime, data.seconds * 1000,
format: DateFormats.y_mo_d_h_m, format: 'HH:mm:ss',
), isUtc: true,
style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54,
fontSize: 12.sp,
), ),
style: TextStyle(color: Colors.white, fontSize: 14.sp),
), ),
],
),
),
Container(
width: 76.w,
height: 28.w,
margin: EdgeInsets.only(left: 10.w),
alignment: Alignment.center,
decoration: BoxDecoration(
color: isDarkMode ? AppColors.primary : Colors.blue,
borderRadius: BorderRadius.circular(28.w),
),
child: Text(
DateUtil.formatDateMs(
data.seconds * 1000,
format: 'HH:mm:ss',
isUtc: true,
), ),
style: TextStyle(color: Colors.white, fontSize: 14.sp), ],
),
), ),
], ],
), ),

2
pubspec.yaml

@ -31,7 +31,7 @@ dependencies:
flutter_blue_plus: ^1.31.16 flutter_blue_plus: ^1.31.16
just_audio: ^0.9.36 just_audio: ^0.9.36
webview_flutter: ^4.10.0 webview_flutter: ^4.10.0
flutter_easyloading: ^3.0.5
window_size: window_size:
git: git:

Loading…
Cancel
Save