diff --git a/android/app/build.gradle b/android/app/build.gradle index 31d0601..09658ad 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -61,6 +61,7 @@ flutter { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation files('libs/AIKit.aar') // rxjava implementation 'io.reactivex.rxjava2:rxjava:2.2.9' diff --git a/android/app/libs/AIKit.aar b/android/app/libs/AIKit.aar new file mode 100644 index 0000000..ca6f61f Binary files /dev/null and b/android/app/libs/AIKit.aar differ diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a1eaa9c..86ad7e3 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + @@ -7,33 +8,35 @@ + + + android:icon="@mipmap/ic_launcher" + android:label="pj_supermarket_cloud" + android:requestRawExternalStorageAccess="true"> + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" /> - - + + @@ -51,8 +54,8 @@ - - + + diff --git a/android/app/src/main/kotlin/com/example/pj_supermarket_cloud/MainActivity.kt b/android/app/src/main/kotlin/com/example/pj_supermarket_cloud/MainActivity.kt index 3555471..ac7a6a9 100644 --- a/android/app/src/main/kotlin/com/example/pj_supermarket_cloud/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/pj_supermarket_cloud/MainActivity.kt @@ -10,8 +10,7 @@ class MainActivity : FlutterActivity() { // 人脸识别插件 val flutterFaceDiscernPlugin = FlutterFaceDiscernPlugin() flutterEngine.plugins.add(flutterFaceDiscernPlugin) - // tts插件 - + // 箭头全局键盘插件 } } diff --git a/lib/app.dart b/lib/app.dart index b70a1bd..1dc4d7d 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -31,10 +31,12 @@ class MainApp extends StatelessWidget { iconStyle: fTheme.style.iconStyle.copyWith( size: fTheme.typography.xl2.fontSize, ), + pagePadding: EdgeInsets.symmetric(vertical: 5.h, horizontal: 1.w), ), ); return GetMaterialApp( title: '收银机', + defaultTransition: Transition.noTransition, initialRoute: RouteNames.load, getPages: RoutePages.list, enableLog: true, diff --git a/lib/common/models/page/panel_page_models.dart b/lib/common/models/page/panel_page_models.dart index 9a4f993..2493435 100644 --- a/lib/common/models/page/panel_page_models.dart +++ b/lib/common/models/page/panel_page_models.dart @@ -20,6 +20,7 @@ enum PanelType implements BaseEnum { cardActive("60", "开卡管理"), printer("70", "打印测试"), logout("80", "退出登录"), + test("90", "测试"), ; @override diff --git a/lib/common/utils/app/control/app_control.dart b/lib/common/utils/app/control/app_control.dart index 05faaae..965cff5 100644 --- a/lib/common/utils/app/control/app_control.dart +++ b/lib/common/utils/app/control/app_control.dart @@ -39,6 +39,7 @@ class AppControl extends GetxController { serialCardManage.close(); srialSteelyardManage.close(); AppNetworkTool.closeListenNetwork(); + TtsManage.dispose(); } Future initAsync() async { @@ -52,6 +53,7 @@ class AppControl extends GetxController { await getDatabasesPath(), ); await AppFileTool.backupSqlFile(); + await TtsManage.initialize(); Get.offAllNamed(RouteNames.login); } } diff --git a/lib/common/utils/index.dart b/lib/common/utils/index.dart index fcbcd12..6c5b872 100644 --- a/lib/common/utils/index.dart +++ b/lib/common/utils/index.dart @@ -27,9 +27,9 @@ export './manage/print_manage.dart'; export './manage/serial_card_manage.dart'; export './manage/serial_steelyard_manage.dart'; export './manage/scan_code_manage.dart'; +export './manage/tts_manage.dart'; export './plugs/face_discern_plugs.dart'; -export './plugs/local_tts_plugs.dart'; export './router/router_observer_tool.dart'; diff --git a/lib/common/utils/manage/tts_manage.dart b/lib/common/utils/manage/tts_manage.dart new file mode 100644 index 0000000..b13140e --- /dev/null +++ b/lib/common/utils/manage/tts_manage.dart @@ -0,0 +1,48 @@ +import 'package:flutter_tts/flutter_tts.dart'; + +// 语音转文字 +class TtsManage { + static final FlutterTts _flutterTts = FlutterTts(); + static bool _isInitialized = false; + + // 初始化TTS + static Future initialize() async { + if (!_isInitialized) { + // 设置语言为中文 + await _flutterTts.setLanguage("zh-CN"); + // 设置音量 + await _flutterTts.setVolume(1.0); + // 设置语速 + await _flutterTts.setSpeechRate(0.7); + // 设置音调 + await _flutterTts.setPitch(1.0); + // 设置使用本地引擎 + await _flutterTts.setIosAudioCategory( + IosTextToSpeechAudioCategory.ambient, + [ + IosTextToSpeechAudioCategoryOptions.allowBluetooth, + IosTextToSpeechAudioCategoryOptions.allowBluetoothA2DP + ], + ); + + _isInitialized = true; + } + } + + // 播放文本 + static Future speak(String text) async { + await initialize(); + await _flutterTts.awaitSpeakCompletion(true); + await _flutterTts.speak(text, focus: true); + } + + // 停止播放 + static Future stop() async { + await _flutterTts.stop(); + } + + // 释放资源 + static Future dispose() async { + await _flutterTts.stop(); + } +} diff --git a/lib/common/utils/plugs/local_tts_plugs.dart b/lib/common/utils/plugs/local_tts_plugs.dart deleted file mode 100644 index 0df4765..0000000 --- a/lib/common/utils/plugs/local_tts_plugs.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/services.dart'; - -import 'package:pj_supermarket_cloud/common/index.dart'; - -abstract class LocalTtsPlugs { - static const String _tag = "本地tts插件"; - - static const MethodChannel _channel = MethodChannel( - Constants.localTTSPlugsMethodChannel, - ); - - static Future speeching({ - required String appId, - required String str, - String speed = "80", - }) async { - try { - final pa = { - "appId": appId, - "str": str, - "speed": speed, - }; - await _channel.invokeMethod('speeching', pa); - return true; - } catch (e) { - logger.w("|$_tag| jumpFaceDiscernPage 错误: $e"); - } - return false; - } -} diff --git a/lib/common/values/constants.dart b/lib/common/values/constants.dart index 398dddd..47fd9da 100644 --- a/lib/common/values/constants.dart +++ b/lib/common/values/constants.dart @@ -17,9 +17,6 @@ class Constants { /// 静态资源 url static const ossUrl = apiUrl; - /// ttl android 插件方法管道 - static const localTTSPlugsMethodChannel = 'local/tts'; - /// 人脸识别 插件方法管道 static const facePlugsMethodChannel = 'local/face_discern'; diff --git a/lib/common/widgets/app/app_img.dart b/lib/common/widgets/app/app_img.dart index 7f04444..f30b80d 100644 --- a/lib/common/widgets/app/app_img.dart +++ b/lib/common/widgets/app/app_img.dart @@ -1,10 +1,12 @@ import 'dart:io'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:common_utils/common_utils.dart'; import 'package:ducafe_ui_core/ducafe_ui_core.dart'; import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; import 'package:get/get.dart'; + import 'package:pj_supermarket_cloud/common/index.dart'; Widget appNetworkImg( @@ -14,34 +16,11 @@ Widget appNetworkImg( double imgHeight = 60, BoxFit fit = BoxFit.cover, }) { - return Image.network( - AppNetworkTool.ossImage(src), - width: imgWidth, - height: imgHeight, - fit: fit, - loadingBuilder: ( - BuildContext context, - Widget child, - ImageChunkEvent? loadingProgress, - ) { - if (loadingProgress == null) { - return child; - } else { - return Center( - child: SizedBox( - width: imgHeight, - height: imgHeight, - child: CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - (loadingProgress.expectedTotalBytes ?? 1) - : null, - ), - ), - ); - } - }, - errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { + return CachedNetworkImage( + imageUrl: AppNetworkTool.ossImage(src), + placeholder: (context, url) => + CircularProgressIndicator().width(50.w).height(50.w), + errorWidget: (context, url, error) { final theme = FTheme.of(context); return FIcon( FAssets.icons.globeLock, diff --git a/lib/common/widgets/order/mixin/order_mixin.dart b/lib/common/widgets/order/mixin/order_mixin.dart index ebaf2b2..ab9aab8 100644 --- a/lib/common/widgets/order/mixin/order_mixin.dart +++ b/lib/common/widgets/order/mixin/order_mixin.dart @@ -25,9 +25,6 @@ mixin OrderMixin { // 设备类型 SysType _sysType = SysType.canteen; - // 语音锁 - bool _speechingLock = false; - /// **************** end 需要重制 end **************** // 分类 @@ -316,18 +313,6 @@ mixin OrderMixin { } } - Future speeching(String str) async { - if (_speechingLock) return; - try { - _speechingLock = true; - await Future.delayed(const Duration(seconds: 1)); - await LocalTtsPlugs.speeching(appId: Constants.ttsId, str: str); - await Future.delayed(const Duration(seconds: 2)); - } finally { - _speechingLock = false; - } - } - // 获取商品渠道id String getOrderSourceTypeId() { String orderSourceTypeId = ""; @@ -408,11 +393,6 @@ mixin OrderMixin { /// **************** start 重制相关 start **************** - // 重置状态 - void closeStatus() { - _speechingLock = false; - } - /// **************** end 重制相关 end **************** /// **************** start 副屏幕相关 start **************** diff --git a/lib/common/widgets/order/order_common.dart b/lib/common/widgets/order/order_common.dart index bfc5342..e82b755 100644 --- a/lib/common/widgets/order/order_common.dart +++ b/lib/common/widgets/order/order_common.dart @@ -12,20 +12,21 @@ Widget orderProductCard( num price = 0.0, }) { final theme = FTheme.of(context); - return [ - appImg(image, width: double.infinity, height: 140.h), - Text(name, style: theme.typography.lg), - appPrice(context, price) - ] - .toColumn(crossAxisAlignment: CrossAxisAlignment.center) - .width(double.infinity) - .decorated( - border: Border.all( - color: theme.colorScheme.mutedForeground, - width: 1, - ), - borderRadius: theme.style.borderRadius / 3, - ); + return FCard( + style: theme.cardStyle.copyWith( + contentStyle: theme.cardStyle.contentStyle.copyWith( + padding: EdgeInsets.all(0), + ), + ), + image: appImg(image, width: double.infinity, height: 150.h), + subtitle: [ + Text(name, style: theme.typography.lg), + SizedBox(height: 5.h), + appPrice(context, price), + ] + .toColumn(crossAxisAlignment: CrossAxisAlignment.center) + .width(double.infinity), + ).width(double.infinity); } /// 菜品总价 diff --git a/lib/common/widgets/order/select_pay_type_dialog.dart b/lib/common/widgets/order/select_pay_type_dialog.dart index 844120b..b2817d1 100644 --- a/lib/common/widgets/order/select_pay_type_dialog.dart +++ b/lib/common/widgets/order/select_pay_type_dialog.dart @@ -58,9 +58,6 @@ class SelectPayTypeDialogController extends GetxController { // 二维码识别结果 ServiceQRCode? globalQrCode; - // 语音锁 - bool _speechingLock = false; - // 人脸识别回调锁 bool _faceCallbackLock = false; @@ -96,7 +93,6 @@ class SelectPayTypeDialogController extends GetxController { payUser.value = null; isRecognition.value = false; globalQrCode = null; - _speechingLock = false; _faceCallbackLock = false; } @@ -249,7 +245,7 @@ class SelectPayTypeDialogController extends GetxController { isRecognition.value = false; await app.scanCodeManage.close(); Toast.success(msg: "识别二维码成功"); - speeching("识别二维码成功"); + TtsManage.speak("识别二维码成功"); }); isRecognition.value = true; onDiscernUser?.call(payUser.value); @@ -275,7 +271,7 @@ class SelectPayTypeDialogController extends GetxController { switch (status) { case SerialCardResStatus.success: if (ObjectUtil.isEmpty(card)) return; - speeching("识别饭卡成功"); + TtsManage.speak("识别饭卡成功"); final isNetwork = AppNetworkTool.isAutoNetwork(); if (isNetwork) { final pa = {"card": card}; @@ -442,19 +438,6 @@ class SelectPayTypeDialogController extends GetxController { ]; return success.contains(type); } - - /// 语音 - Future speeching(String str) async { - if (_speechingLock) return; - try { - _speechingLock = true; - await Future.delayed(const Duration(seconds: 1)); - await LocalTtsPlugs.speeching(appId: Constants.ttsId, str: str); - await Future.delayed(const Duration(seconds: 2)); - } finally { - _speechingLock = false; - } - } } Future selectPayTypeDialog(BuildContext context) async { diff --git a/lib/pages/canteen_product/controller.dart b/lib/pages/canteen_product/controller.dart index 7e39c0e..febe47b 100644 --- a/lib/pages/canteen_product/controller.dart +++ b/lib/pages/canteen_product/controller.dart @@ -48,8 +48,6 @@ class CanteenProductController extends GetxController with OrderMixin { hourType.value = appSetting.hourType ?? ProductHourType.morning; hourTypeC.value = {hourType.value.code}; sysType = SysType.canteen; - // 重置状态 - closeStatus(); _listenPayTypeSelectCallback(); /// **************** end 组件初始化 end **************** @@ -103,8 +101,6 @@ class CanteenProductController extends GetxController with OrderMixin { app.scanCodeManage.close(); // 路由监听 routerBus?.cancel(); - // 重置状态 - closeStatus(); mqUpdateListen?.cancel(); hourTypeC.dispose(); } @@ -233,12 +229,12 @@ class CanteenProductController extends GetxController with OrderMixin { bool _validatePayUser() { bool flag = true; if (ObjectUtil.isEmpty(payUser.value?.walletBalance)) { - speeching("用户钱包不存在"); + TtsManage.speak("用户钱包不存在"); Toast.error(msg: "校验购物车失败,用户钱包不存在"); return false; } if (payUser.value!.walletBalance! < totalPrice.value) { - speeching("用户余额不足"); + TtsManage.speak("用户余额不足"); Toast.error(msg: "校验购物车失败,用户余额不足"); return false; } diff --git a/lib/pages/canteen_product/view.dart b/lib/pages/canteen_product/view.dart index b9183fd..28e3a55 100644 --- a/lib/pages/canteen_product/view.dart +++ b/lib/pages/canteen_product/view.dart @@ -81,7 +81,7 @@ class CanteenProductPage extends GetView { double maxHeight = constraints.maxHeight; double maxWidth = constraints.maxWidth; double itemWidth = (maxWidth - spacing * 5) / 6; - double itemHeight = (maxHeight - (runSpacing + 40) * 3) / 3; + double itemHeight = (maxHeight - (runSpacing + 30) * 3) / 3; return Obx( () => ListView.builder( padding: EdgeInsets.zero, diff --git a/lib/pages/card_active/controller.dart b/lib/pages/card_active/controller.dart index 377a526..012ec08 100644 --- a/lib/pages/card_active/controller.dart +++ b/lib/pages/card_active/controller.dart @@ -10,9 +10,6 @@ class CardActiveController extends GetxController { SerialCardManage get serialCardManage => appC.serialCardManage; - // 语音锁 - bool _speechingLock = false; - // table RxInt currentPage = 1.obs; @@ -37,7 +34,6 @@ class CardActiveController extends GetxController { @override void onInit() { super.onInit(); - _closeStatus(); getTableList(); } @@ -50,7 +46,6 @@ class CardActiveController extends GetxController { @override void onClose() { super.onClose(); - _closeStatus(); } Future doPage({int? page}) async { @@ -119,12 +114,12 @@ class CardActiveController extends GetxController { case SerialCardResStatus.warning: break; case SerialCardResStatus.error: - _speeching("请在合适区域放置饭卡"); + TtsManage.speak("请在合适区域放置饭卡"); Toast.error(msg: "请在合适区域放置饭卡"); break; } }; - _speeching("请在合适区域放置饭卡"); + TtsManage.speak("请在合适区域放置饭卡"); serialCardManage.sendWrite(v.cardNumber!); } catch (e, stack) { logger.d("写卡失败error:|$e|stack:|$stack|"); @@ -163,11 +158,11 @@ class CardActiveController extends GetxController { break; case SerialCardResStatus.error: Toast.error(msg: "请在合适区域放置饭卡"); - _speeching("请在合适区域放置饭卡"); + TtsManage.speak("请在合适区域放置饭卡"); break; } }; - _speeching("请在合适区域放置饭卡"); + TtsManage.speak("请在合适区域放置饭卡"); serialCardManage.sendRead(); } catch (e, stack) { _closeReadStatus(); @@ -183,7 +178,7 @@ class CardActiveController extends GetxController { final data = await WalletApi.openCardSuccess(pa); if (data == false) throw Error(); EasyLoading.dismiss(); - _speeching("激活成功"); + TtsManage.speak("激活成功"); Toast.success(msg: "激活成功"); } catch (e, stack) { logger.e("激活饭卡请求失败error:|$e|stack:|$stack|"); @@ -210,25 +205,8 @@ class CardActiveController extends GetxController { } } - Future _speeching(String str) async { - if (_speechingLock) return; - try { - _speechingLock = true; - await Future.delayed(const Duration(seconds: 1)); - await LocalTtsPlugs.speeching(appId: Constants.ttsId, str: str); - await Future.delayed(const Duration(seconds: 2)); - } finally { - _speechingLock = false; - } - } - void _closeReadStatus() { readLoading.value = false; readCardNumber.value = "未读取"; } - - // 重置状态 - void _closeStatus() { - _speechingLock = false; - } } diff --git a/lib/pages/panel/controller.dart b/lib/pages/panel/controller.dart index 70227aa..6641bdc 100644 --- a/lib/pages/panel/controller.dart +++ b/lib/pages/panel/controller.dart @@ -16,6 +16,7 @@ class PanelController extends GetxController { PanelItem(title: "开卡管理", visible: true, type: PanelType.cardActive), PanelItem(title: "设置", visible: true, type: PanelType.setting), PanelItem(title: "退出登录", visible: true, type: PanelType.logout), + PanelItem(title: "测试", visible: true, type: PanelType.test), ].obs; final canteenMenus = [ @@ -73,6 +74,10 @@ class PanelController extends GetxController { } Get.offAndToNamed(RouteNames.login); break; + case PanelType.test: + await TtsManage.initialize(); + await TtsManage.speak("购买成功1"); + break; default: break; } diff --git a/lib/pages/take_food/controller.dart b/lib/pages/take_food/controller.dart index 3896cc4..a239d4e 100644 --- a/lib/pages/take_food/controller.dart +++ b/lib/pages/take_food/controller.dart @@ -23,9 +23,6 @@ class TakeFoodController extends GetxController { ServiceQRCode? globalQrCode; - // 语音锁 - bool _speechingLock = false; - _initData() { update(["take_food"]); } @@ -42,7 +39,6 @@ class TakeFoodController extends GetxController { @override void onReady() { super.onReady(); - _closeStatus(); _initData(); } @@ -51,12 +47,6 @@ class TakeFoodController extends GetxController { super.onClose(); // 二维码读头 appC.serialCardManage.close(); - _closeStatus(); - } - - // 重置状态 - void _closeStatus() { - _speechingLock = false; } doCancelPay() { @@ -170,7 +160,7 @@ class TakeFoodController extends GetxController { if (flag == false) throw Error(); EasyLoading.dismiss(); Toast.error(msg: "核销成功"); - _speeching("核销成功"); + TtsManage.speak("核销成功"); doCancelPay(); } catch (e, stack) { logger.e("获取订单详情错误error:|$e|stack:|$stack|"); @@ -182,16 +172,4 @@ class TakeFoodController extends GetxController { void senSubScreen() { if (ObjectUtil.isEmpty(carList)) return; } - - Future _speeching(String str) async { - if (_speechingLock) return; - try { - _speechingLock = true; - await Future.delayed(const Duration(seconds: 1)); - await LocalTtsPlugs.speeching(appId: Constants.ttsId, str: str); - await Future.delayed(const Duration(seconds: 2)); - } finally { - _speechingLock = false; - } - } } diff --git a/pubspec.lock b/pubspec.lock index 9ffbb3f..7b423dc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -142,6 +142,30 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "8.9.4" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.3.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.0" characters: dependency: transitive description: @@ -411,6 +435,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: a77f77806a790eb9ba0118a5a3a936e81c4fea2b61533033b2b0c3d50bbde5ea + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.4.0" flutter_easyloading: dependency: "direct main" description: @@ -469,6 +501,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_tts: + dependency: "direct main" + description: + name: flutter_tts + sha256: baa3cb6b4990318460fe28bfa8c7869399e97223971532c02bd97c5e876aa3c5 + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.2.2" flutter_web_plugins: dependency: transitive description: flutter @@ -794,6 +834,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.6" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" package_config: dependency: transitive description: @@ -1010,6 +1058,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.2.3" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.28.0" shelf: dependency: transitive description: @@ -1063,6 +1119,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.0.0" sqflite: dependency: "direct main" description: @@ -1231,6 +1295,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.2.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 92ba5c9..a7a55b1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,12 +26,16 @@ dependencies: flutter_easyloading: ^3.0.5 # 图片处理 image: ^4.3.0 + # 图片缓存 + cached_network_image: 3.3.1 # 表格 data_table_2: ^2.5.18 # 分页 number_pagination: ^1.1.6 # 日期选择 syncfusion_flutter_datepicker: ^28.2.11 + # tts转语音 + flutter_tts: ^4.2.2 # 权限判断 permission_handler: ^11.4.0