diff --git a/android/app/src/main/kotlin/cn/yooss/mood_diary/MainActivity.kt b/android/app/src/main/kotlin/cn/yooss/mood_diary/MainActivity.kt index 9024c34..74e3020 100644 --- a/android/app/src/main/kotlin/cn/yooss/mood_diary/MainActivity.kt +++ b/android/app/src/main/kotlin/cn/yooss/mood_diary/MainActivity.kt @@ -1,9 +1,7 @@ package cn.yooss.mood_diary -import android.os.Build +import android.os.Bundle import androidx.activity.enableEdgeToEdge -import androidx.annotation.RequiresApi -import androidx.core.view.WindowCompat import com.github.gzuliyujiang.oaid.DeviceID import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine @@ -11,20 +9,13 @@ import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterFragmentActivity() { - + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + } override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) - MethodChannel( - flutterEngine.dartExecutor.binaryMessenger, "view_channel" - ).setMethodCallHandler { call, result -> - if (call.method == "setSystemUIVisibility") { - setSystemUIVisibility() - result.success(null) - } else { - result.notImplemented() - } - } MethodChannel( flutterEngine.dartExecutor.binaryMessenger, "oaid_channel" ).setMethodCallHandler { call, result -> @@ -47,9 +38,4 @@ class MainActivity : FlutterFragmentActivity() { } } - private fun setSystemUIVisibility() { - WindowCompat.setDecorFitsSystemWindows(window, false) - enableEdgeToEdge() - } - } diff --git a/ios/Podfile b/ios/Podfile index fa34701..cb86c29 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '18.2' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index ec73d1c..72c3d46 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -85,6 +85,8 @@ PODS: - Flutter - media_kit_video (0.0.1): - Flutter + - network_info_plus (0.0.1): + - Flutter - ObjectBox (4.0.1) - objectbox_flutter_libs (0.0.1): - Flutter @@ -100,7 +102,6 @@ PODS: - Flutter - record_darwin (1.0.0): - Flutter - - FlutterMacOS - screen_brightness_ios (0.1.0): - Flutter - SDWebImage (5.19.7): @@ -166,6 +167,7 @@ DEPENDENCIES: - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) + - network_info_plus (from `.symlinks/plugins/network_info_plus/ios`) - objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) @@ -230,6 +232,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/media_kit_native_event_loop/ios" media_kit_video: :path: ".symlinks/plugins/media_kit_video/ios" + network_info_plus: + :path: ".symlinks/plugins/network_info_plus/ios" objectbox_flutter_libs: :path: ".symlinks/plugins/objectbox_flutter_libs/ios" package_info_plus: @@ -281,13 +285,14 @@ SPEC CHECKSUMS: media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e + network_info_plus: 6613d9d7cdeb0e6f366ed4dbe4b3c51c52d567a9 ObjectBox: 0bc4bb75eea85f6af06b369148b334c2056bbc29 objectbox_flutter_libs: 2ce0da386c780878687c736b528ceaf371573efb package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 quill_native_bridge_ios: 277bdf5bf0fa5d7a12999556b415a5c63dd76ec4 - record_darwin: df0a677188e5fed18472550298e675f19ddaffbe + record_darwin: 3b1a8e7d5c0cbf45ad6165b4d83a6ca643d929c3 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3 SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 @@ -303,6 +308,6 @@ SPEC CHECKSUMS: volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1 -PODFILE CHECKSUM: 83893f9a2d98085e99381d8f1dc69dcf7bd5b437 +PODFILE CHECKSUM: 9752b5340b4d3f9618318fcd530d907790e2a9f5 COCOAPODS: 1.15.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 96bbb9c..31edf9c 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 306627225710E691EF0E0A35 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A1A8341695BDC0741989253 /* Pods_Runner.framework */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; @@ -15,7 +16,6 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - B89FBBDC71186C5741E699DA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A1A8341695BDC0741989253 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -68,11 +68,11 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { + 523FF08A9B798D86BA8B9DD9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B89FBBDC71186C5741E699DA /* Pods_Runner.framework in Frameworks */, + 306627225710E691EF0E0A35 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -193,12 +193,12 @@ 3190980419AFCAD7AC11C68D /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, DDBAC86E6B3F372170C284A0 /* [CP] Embed Pods Frameworks */, B482CF705AC6985E445540F2 /* [CP] Copy Pods Resources */, + 523FF08A9B798D86BA8B9DD9 /* Frameworks */, ); buildRules = ( ); @@ -510,6 +510,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 3XA29H789G; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = cn.yooss.moodDiary.RunnerTests; @@ -528,6 +529,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 3XA29H789G; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = cn.yooss.moodDiary.RunnerTests; @@ -544,6 +546,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 3XA29H789G; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = cn.yooss.moodDiary.RunnerTests; diff --git a/lib/api/api.dart b/lib/api/api.dart index da9aa57..b3338ed 100644 --- a/lib/api/api.dart +++ b/lib/api/api.dart @@ -53,7 +53,8 @@ class Api { Future?> updatePosition() async { Position? position; - if (await Utils().permissionUtil.checkPermission(Permission.location)) { + if (await Utils().permissionUtil.checkPermission(Permission.location) && + await Geolocator.isLocationServiceEnabled()) { position = await Geolocator.getLastKnownPosition(forceAndroidLocationManager: true); position ??= await Geolocator.getCurrentPosition(locationSettings: AndroidSettings(forceLocationManager: true)); } diff --git a/lib/common/models/isar/diary.dart b/lib/common/models/isar/diary.dart index 1a7eca9..85bd41c 100644 --- a/lib/common/models/isar/diary.dart +++ b/lib/common/models/isar/diary.dart @@ -113,6 +113,48 @@ class Diary { } Diary(); + + // 将 Diary 对象转换为 JSON + Map toJson() { + return { + 'id': id, + 'categoryId': categoryId, + 'title': title, + 'content': content, + 'contentText': contentText, + 'time': time.toIso8601String(), + 'show': show, + 'mood': mood, + 'weather': weather, + 'imageName': imageName, + 'audioName': audioName, + 'videoName': videoName, + 'tags': tags, + 'position': position, + 'imageColor': imageColor, + 'aspect': aspect, + }; + } + + factory Diary.fromJson(Map json) { + return Diary() + ..id = json['id'] as String + ..categoryId = json['categoryId'] as String? + ..title = json['title'] as String + ..content = json['content'] as String + ..contentText = json['contentText'] as String + ..time = DateTime.parse(json['time'] as String) + ..show = json['show'] as bool + ..mood = (json['mood'] as num).toDouble() + ..weather = List.from(json['weather'] as List) + ..imageName = List.from(json['imageName'] as List) + ..audioName = List.from(json['audioName'] as List) + ..videoName = List.from(json['videoName'] as List) + ..tags = List.from(json['tags'] as List) + ..position = List.from(json['position'] as List) + ..imageColor = json['imageColor'] as int? + ..aspect = (json['aspect'] as num?)?.toDouble(); + } } int fastHash(String string) { diff --git a/lib/components/dashboard/dashboard_view.dart b/lib/components/dashboard/dashboard_view.dart index de8b39d..e611534 100644 --- a/lib/components/dashboard/dashboard_view.dart +++ b/lib/components/dashboard/dashboard_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:mood_diary/common/values/icons.dart'; import 'package:mood_diary/components/mood_icon/mood_icon_view.dart'; @@ -28,7 +29,7 @@ class DashboardComponent extends StatelessWidget { Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Icon( + FaIcon( icon, color: colorScheme.secondary, size: 32, diff --git a/lib/components/diary_tab_view/diary_tab_view_view.dart b/lib/components/diary_tab_view/diary_tab_view_view.dart index 0822e93..70fb8ba 100644 --- a/lib/components/diary_tab_view/diary_tab_view_view.dart +++ b/lib/components/diary_tab_view/diary_tab_view_view.dart @@ -17,15 +17,13 @@ class DiaryTabViewComponent extends StatelessWidget { @override Widget build(BuildContext context) { final logicTag = categoryId ?? 'default'; - final logic = - Get.put(DiaryTabViewLogic(categoryId: categoryId), tag: logicTag); + final logic = Get.put(DiaryTabViewLogic(categoryId: categoryId), tag: logicTag); final state = Bind.find(tag: logicTag).state; final i18n = AppLocalizations.of(context)!; Widget buildGrid() { return SliverWaterfallFlow( - gridDelegate: const SliverWaterfallFlowDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 250), + gridDelegate: const SliverWaterfallFlowDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 250), delegate: SliverChildBuilderDelegate( (context, index) { return LargeDiaryCardComponent( @@ -68,29 +66,28 @@ class DiaryTabViewComponent extends StatelessWidget { builder: (_) { return buildPlaceHolder(); }), - CustomScrollView( - cacheExtent: 2000, - slivers: [ - SliverOverlapInjector( - handle: - NestedScrollView.sliverOverlapAbsorberHandleFor(context)), - SliverPadding( - padding: const EdgeInsets.all(4.0), - sliver: GetBuilder( - id: 'TabView', - tag: logicTag, - builder: (_) { - return SliverAnimatedSwitcher( + GetBuilder( + id: 'TabView', + tag: logicTag, + builder: (_) { + return CustomScrollView( + primary: true, + cacheExtent: 2000, + slivers: [ + SliverOverlapInjector(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)), + SliverPadding( + padding: const EdgeInsets.all(4.0), + sliver: SliverAnimatedSwitcher( duration: const Duration(milliseconds: 400), child: switch (logic.diaryLogic.state.viewModeType) { ViewModeType.list => buildList(), ViewModeType.grid => buildGrid(), }, - ); - }), - ), - ], - ), + ), + ), + ], + ); + }), ], ); } diff --git a/lib/components/local_send/local_send_client/local_send_client_logic.dart b/lib/components/local_send/local_send_client/local_send_client_logic.dart new file mode 100644 index 0000000..4fe4402 --- /dev/null +++ b/lib/components/local_send/local_send_client/local_send_client_logic.dart @@ -0,0 +1,172 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:dio/dio.dart' as dio; +import 'package:get/get.dart'; +import 'package:mood_diary/common/models/isar/diary.dart'; +import 'package:mood_diary/utils/utils.dart'; + +import 'local_send_client_state.dart'; + +class LocalSendClientLogic extends GetxController { + final LocalSendClientState state = LocalSendClientState(); + + late RawDatagramSocket socket; + Timer? timer; + + @override + void onReady() async { + super.onReady(); + socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0); + socket.broadcastEnabled = true; + await startFindServer(); + } + + @override + void onClose() { + socket.close(); + timer?.cancel(); + super.onClose(); + } + + void _sendBroadcast() { + const message = 'Looking for server'; + socket.send(message.codeUnits, InternetAddress('255.255.255.255'), state.scanPort); + Utils().logUtil.printInfo('Broadcast sent'); + } + + // 尝试在 30 秒内找到服务器 + Future startFindServer() async { + state.isFindingServer = true; + update(); + + final found = await _findServer(timeout: const Duration(seconds: 30)); + + if (found) { + Utils().noticeUtil.showToast('找到服务器'); + } else { + state.isFindingServer = false; + update(); + } + return found; + } + + // 重新开始查找服务器 + Future restartFindServer() async { + // 确保之前的监听已停止 + timer?.cancel(); + socket.close(); + + // 重新初始化 socket 和监听 + socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0); + socket.broadcastEnabled = true; + + await startFindServer(); + } + + Future _findServer({required Duration timeout}) async { + final completer = Completer(); + + // 启动 30 秒超时定时器 + Future.delayed(timeout, () { + if (!completer.isCompleted) { + timer?.cancel(); + completer.complete(false); + } + }); + + // 轮询发送广播消息 + timer = Timer.periodic(state.broadcastInterval, (timer) { + _sendBroadcast(); + }); + + // 监听服务器响应 + socket.listen((RawSocketEvent event) async { + if (event == RawSocketEvent.read) { + final datagram = socket.receive(); + if (datagram != null) { + final serverResponse = String.fromCharCodes(datagram.data); + Utils().logUtil.printInfo('Found server: $serverResponse'); + + final serverInfo = serverResponse.split(':'); + state.serverIp = serverInfo[0]; + state.serverPort = int.parse(serverInfo[1]); + state.isFindingServer = false; + update(); + + timer?.cancel(); + socket.close(); + + if (!completer.isCompleted) { + completer.complete(true); + } + } + } + }); + + // 初次发送广播 + _sendBroadcast(); + + return completer.future; + } + + // 向服务器发送数据并监听进度 + Future sendData(Diary diary) async { + // 创建 FormData 并同步添加 JSON 和文件 + dio.FormData formData = dio.FormData(); + + // 添加 JSON 数据 + formData.fields.add(MapEntry('diary', jsonEncode(diary.toJson()))); + + // 同步添加图片文件 + for (var imageName in diary.imageName) { + final filePath = Utils().fileUtil.getRealPath('image', imageName); + formData.files.add(MapEntry( + 'image', + await dio.MultipartFile.fromFile(filePath, filename: imageName), + )); + } + + // 同步添加视频文件 + for (var videoName in diary.videoName) { + final filePath = Utils().fileUtil.getRealPath('video', videoName); + formData.files.add(MapEntry( + 'video', + await dio.MultipartFile.fromFile(filePath, filename: videoName), + )); + } + + // 同步添加缩略图文件 + for (var videoName in diary.videoName) { + final filePath = Utils().fileUtil.getRealPath('thumbnail', videoName); + formData.files.add(MapEntry( + 'thumbnail', + await dio.MultipartFile.fromFile(filePath, filename: 'thumbnail-${videoName.substring(6, 42)}.jpeg'), + )); + } + + // 同步添加音频文件 + for (var audioName in diary.audioName) { + final filePath = Utils().fileUtil.getRealPath('audio', audioName); + formData.files.add(MapEntry( + 'audio', + await dio.MultipartFile.fromFile(filePath, filename: audioName), + )); + } + + final startTime = DateTime.now(); + + // 发送请求并监听进度 + await Utils().httpUtil.dio.post( + 'http://${state.serverIp}:${state.serverPort}', + data: formData, + onSendProgress: (int sent, int total) { + final currentTime = DateTime.now(); + final timeElapsed = currentTime.difference(startTime).inMilliseconds / 1000; + state.speed.value = sent / timeElapsed; + state.progress.value = sent / total; + }, + ); + } +} diff --git a/lib/components/local_send/local_send_client/local_send_client_state.dart b/lib/components/local_send/local_send_client/local_send_client_state.dart new file mode 100644 index 0000000..71597e8 --- /dev/null +++ b/lib/components/local_send/local_send_client/local_send_client_state.dart @@ -0,0 +1,16 @@ +import 'package:get/get.dart'; + +class LocalSendClientState { + int? serverPort; + String? serverIp; + + int scanPort = 50001; + Duration broadcastInterval = const Duration(seconds: 3); + + RxDouble progress = .0.obs; + RxDouble speed = .0.obs; + + bool isFindingServer = false; + + LocalSendClientState(); +} diff --git a/lib/components/local_send/local_send_client/local_send_client_view.dart b/lib/components/local_send/local_send_client/local_send_client_view.dart new file mode 100644 index 0000000..8a92470 --- /dev/null +++ b/lib/components/local_send/local_send_client/local_send_client_view.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:get/get.dart'; +import 'package:mood_diary/common/models/isar/diary.dart'; +import 'package:mood_diary/utils/utils.dart'; + +import 'local_send_client_logic.dart'; +import 'local_send_client_state.dart'; + +class LocalSendClientComponent extends StatelessWidget { + const LocalSendClientComponent({super.key}); + + @override + Widget build(BuildContext context) { + final LocalSendClientLogic logic = Get.put(LocalSendClientLogic()); + final LocalSendClientState state = Bind.find().state; + + return GetBuilder( + assignId: true, + builder: (_) { + if (state.isFindingServer) { + return const ListTile( + title: Text('查找服务器'), + subtitle: LinearProgressIndicator(), + ); + } else if (state.serverIp == null) { + return ListTile( + title: const Text('未找到服务器'), + leading: const FaIcon(FontAwesomeIcons.triangleExclamation), + trailing: FilledButton( + onPressed: () { + logic.restartFindServer(); + }, + child: const FaIcon(FontAwesomeIcons.repeat)), + ); + } else { + return ListTile( + title: Text(state.serverIp!), + subtitle: Obx(() { + return Text( + '${Utils().fileUtil.bytesToUnits(state.speed.value.toInt())['size']}${Utils().fileUtil.bytesToUnits(state.speed.value.toInt())['unit']}/s ${state.progress.value * 100}%'); + }), + leading: const FaIcon(FontAwesomeIcons.server), + trailing: FilledButton( + onPressed: () async { + await logic + .sendData((await Utils().isarUtil.getDiaryByID(fastHash('01931742-bd6c-738a-a482-cc8a398b5c0c')))!); + }, + child: const FaIcon(FontAwesomeIcons.solidPaperPlane), + ), + ); + } + }, + ); + } +} diff --git a/lib/components/local_send/local_send_logic.dart b/lib/components/local_send/local_send_logic.dart new file mode 100644 index 0000000..e35f9bf --- /dev/null +++ b/lib/components/local_send/local_send_logic.dart @@ -0,0 +1,42 @@ +import 'package:get/get.dart'; +import 'package:network_info_plus/network_info_plus.dart'; + +import 'local_send_state.dart'; + +class LocalSendLogic extends GetxController { + final LocalSendState state = LocalSendState(); + final networkInfo = NetworkInfo(); + + @override + void onReady() async { + await getWifiInfo(); + super.onReady(); + } + + @override + void onClose() { + super.onClose(); + } + + Future getWifiInfo() async { + state.deviceIpAddress = (await networkInfo.getWifiIP()) ?? '无法获取'; + state.wifiSSID = (await networkInfo.getWifiName()) ?? '无法获取'; + update(['WifiInfo']); + } + + // // client + // Future findServer() async { + // state.findingServer.value = true; + // var serverInfo = await localSendClient.findServer(); + // if (serverInfo != null) { + // state.serverIp.value = serverInfo['ip']; + // state.serverPort.value = serverInfo['port']; + // state.findingServer.value = false; + // } + // } + + void changeType(String value) { + state.type = value; + update(['SegmentButton', 'Panel']); + } +} diff --git a/lib/components/local_send/local_send_server/local_send_server_logic.dart b/lib/components/local_send/local_send_server/local_send_server_logic.dart new file mode 100644 index 0000000..ac6f7c7 --- /dev/null +++ b/lib/components/local_send/local_send_server/local_send_server_logic.dart @@ -0,0 +1,108 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:get/get.dart'; +import 'package:mood_diary/common/models/isar/diary.dart'; +import 'package:mood_diary/utils/utils.dart'; +import 'package:network_info_plus/network_info_plus.dart'; +import 'package:shelf/shelf.dart' as shelf; +import 'package:shelf/shelf_io.dart'; +import 'package:shelf_multipart/shelf_multipart.dart'; + +import 'local_send_server_state.dart'; + +class LocalSendServerLogic extends GetxController { + final LocalSendServerState state = LocalSendServerState(); + late RawDatagramSocket socket; + HttpServer? httpServer; + + late final networkInfo = NetworkInfo(); + + @override + void onReady() async { + socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, state.scanPort); + state.serverIp = await networkInfo.getWifiIP(); + update(); + if (state.serverIp != null) { + await startBroadcastListener(); + await startServer(); + } + super.onReady(); + } + + @override + void onClose() { + socket.close(); + httpServer?.close(force: true); + super.onClose(); + } + + // 启动UDP广播监听 + Future startBroadcastListener() async { + Utils().logUtil.printInfo('Listening for broadcast on port ${state.scanPort}'); + socket.listen((RawSocketEvent event) { + if (event == RawSocketEvent.read) { + final datagram = socket.receive(); + if (datagram != null) { + final message = String.fromCharCodes(datagram.data); + Utils().logUtil.printInfo('Received broadcast: $message from ${datagram.address.address}'); + final response = '${state.serverIp}:${state.transferPort}'; + socket.send(response.codeUnits, datagram.address, datagram.port); + } + } + }); + } + + // 启动HTTP服务器 + Future startServer() async { + final handler = const shelf.Pipeline().addMiddleware(shelf.logRequests()).addHandler(_handleRequest); + httpServer = await serve(handler, state.serverIp!, state.transferPort); + Utils().logUtil.printInfo('Server started on http://${state.serverIp}:${state.transferPort}'); + } + + Future _handleRequest(shelf.Request request) async { + late Diary diary; + List images = []; + List videos = []; + List thumbnails = []; + List audios = []; + + // 处理表单数据 + if (request.formData() case var form?) { + await for (final formData in form.formData) { + final name = formData.name; + // 读取日记 JSON 数据 + if (name == 'diary') { + diary = Diary.fromJson(jsonDecode(await formData.part.readString())); + } else if (name == 'image' || name == 'video' || name == 'thumbnail' || name == 'audio') { + if (formData.filename != null) { + final tempFile = File(Utils().fileUtil.getCachePath(formData.filename!)); + final sink = tempFile.openWrite(); + await formData.part.pipe(sink); + await sink.close(); + + // 分类文件 + if (name == 'image') images.add(tempFile); + if (name == 'video') videos.add(tempFile); + if (name == 'thumbnail') thumbnails.add(tempFile); + if (name == 'audio') audios.add(tempFile); + } + } + } + } + return shelf.Response.ok('Data and files received successfully'); + } + +// 辅助函数,用于按块保存文件并调用进度更新回调 + Future _saveFileWithProgress(Stream> stream, String filename, String? mimeType, int contentLength, + void Function(int chunkLength) onProgress) async { + final file = File('/path/to/save/$filename'); + final sink = file.openWrite(); + await for (final chunk in stream) { + sink.add(chunk); + onProgress(chunk.length); // 调用进度更新回调 + } + await sink.close(); + return file; + } +} diff --git a/lib/components/local_send/local_send_server/local_send_server_state.dart b/lib/components/local_send/local_send_server/local_send_server_state.dart new file mode 100644 index 0000000..5e4139c --- /dev/null +++ b/lib/components/local_send/local_send_server/local_send_server_state.dart @@ -0,0 +1,14 @@ +import 'package:get/get.dart'; + +class LocalSendServerState { + String? serverIp; + + int scanPort = 50001; + int transferPort = 54321; + + RxDouble progress = .0.obs; + + RxDouble speed = .0.obs; + + LocalSendServerState(); +} diff --git a/lib/components/local_send/local_send_server/local_send_server_view.dart b/lib/components/local_send/local_send_server/local_send_server_view.dart new file mode 100644 index 0000000..6d212cf --- /dev/null +++ b/lib/components/local_send/local_send_server/local_send_server_view.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'local_send_server_logic.dart'; +import 'local_send_server_state.dart'; + +class LocalSendServerComponent extends StatelessWidget { + const LocalSendServerComponent({super.key}); + + @override + Widget build(BuildContext context) { + final LocalSendServerLogic logic = Get.put(LocalSendServerLogic()); + final LocalSendServerState state = Bind.find().state; + + return GetBuilder( + assignId: true, + builder: (_) { + return ListTile( + title: const Text('服务器已启动在'), + subtitle: Text('${state.serverIp}:${state.transferPort}'), + ); + }, + ); + } +} diff --git a/lib/components/local_send/local_send_state.dart b/lib/components/local_send/local_send_state.dart new file mode 100644 index 0000000..f0c6587 --- /dev/null +++ b/lib/components/local_send/local_send_state.dart @@ -0,0 +1,19 @@ +import 'package:get/get.dart'; + +class LocalSendState { + String wifiSSID = ''; + + String deviceIpAddress = ''; + + int transferPort = 54321; + int scanPort = 50001; + + RxBool findingServer = false.obs; + + RxString serverIp = ''.obs; + RxnInt serverPort = RxnInt(); + + String type = 'send'; + + LocalSendState(); +} diff --git a/lib/components/local_send/local_send_view.dart b/lib/components/local_send/local_send_view.dart new file mode 100644 index 0000000..8cda59e --- /dev/null +++ b/lib/components/local_send/local_send_view.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:mood_diary/components/local_send/local_send_client/local_send_client_view.dart'; +import 'package:mood_diary/components/local_send/local_send_server/local_send_server_view.dart'; + +import 'local_send_logic.dart'; +import 'local_send_state.dart'; + +class LocalSendComponent extends StatelessWidget { + const LocalSendComponent({super.key}); + + @override + Widget build(BuildContext context) { + final LocalSendLogic logic = Get.put(LocalSendLogic()); + final LocalSendState state = Bind.find().state; + + final textStyle = Theme.of(context).textTheme; + + Widget buildWifiInfo() { + return Column( + spacing: 8.0, + children: [ + Text( + '请检查以下信息,确保两台设备在同一局域网下', + style: textStyle.titleSmall, + ), + GetBuilder( + id: 'WifiInfo', + builder: (_) { + return Wrap( + spacing: 8.0, + children: [ + ActionChip( + onPressed: () { + logic.getWifiInfo(); + }, + label: Text('SSID : ${state.wifiSSID}'), + ), + ActionChip( + onPressed: () { + logic.getWifiInfo(); + }, + label: Text('IP : ${state.deviceIpAddress}'), + ), + ], + ); + }), + ], + ); + } + + Widget buildPortInfo() { + return Column( + spacing: 8.0, + children: [ + Text( + '如果您不知道这是什么,请不要修改', + style: textStyle.titleSmall, + ), + Wrap( + spacing: 8.0, + children: [ + ActionChip( + onPressed: () {}, + label: Text('扫描端口: ${state.scanPort}'), + ), + ActionChip( + onPressed: () {}, + label: Text('传输端口: ${state.transferPort}'), + ), + ], + ), + ], + ); + } + + return GetBuilder( + assignId: true, + builder: (_) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + spacing: 8.0, + children: [ + Wrap( + spacing: 8.0, + runSpacing: 8.0, + alignment: WrapAlignment.spaceEvenly, + children: [buildWifiInfo(), buildPortInfo()], + ), + GetBuilder( + id: 'SegmentButton', + builder: (_) { + return SegmentedButton( + segments: const [ + ButtonSegment( + value: 'send', + icon: Icon(Icons.send), + label: Text('发送'), + ), + ButtonSegment( + value: 'receive', + icon: Icon(Icons.move_to_inbox_rounded), + label: Text('接收'), + ), + ], + selected: {state.type}, + onSelectionChanged: (newSelection) { + logic.changeType(newSelection.first); + }, + ); + }), + GetBuilder( + id: 'Panel', + builder: (_) { + return state.type == 'send' ? const LocalSendClientComponent() : const LocalSendServerComponent(); + }), + ], + ), + ); + }, + ); + } +} diff --git a/lib/components/record_sheet/record_sheet_logic.dart b/lib/components/record_sheet/record_sheet_logic.dart index 2ead257..c39af43 100644 --- a/lib/components/record_sheet/record_sheet_logic.dart +++ b/lib/components/record_sheet/record_sheet_logic.dart @@ -47,8 +47,7 @@ class RecordSheetLogic extends GetxController with GetTickerProviderStateMixin { } Future startRecorder() async { - if (await Utils().permissionUtil.checkPermission(Permission.microphone) && - await Utils().permissionUtil.checkPermission(Permission.bluetoothConnect)) { + if (await Utils().permissionUtil.checkPermission(Permission.microphone)) { await animationController.forward(); state.isRecording.value = true; state.isStarted.value = true; diff --git a/lib/main.dart b/lib/main.dart index f77faaf..b0cb940 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,7 +12,6 @@ import 'package:intl/find_locale.dart'; import 'package:media_kit/media_kit.dart'; import 'package:mood_diary/router/app_pages.dart'; import 'package:mood_diary/router/app_routes.dart'; -import 'package:mood_diary/utils/channel.dart'; import 'package:mood_diary/utils/utils.dart'; Future initSystem() async { @@ -35,8 +34,6 @@ void platFormOption() { if (Platform.isAndroid) { //设置高刷 unawaited(FlutterDisplayMode.setHighRefreshRate()); - //设置状态栏沉浸 - unawaited(ViewChannel.setSystemUIVisibility()); } if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { doWhenWindowReady(() { diff --git a/lib/pages/backup_sync/backup_sync_logic.dart b/lib/pages/backup_sync/backup_sync_logic.dart new file mode 100644 index 0000000..0ba36c9 --- /dev/null +++ b/lib/pages/backup_sync/backup_sync_logic.dart @@ -0,0 +1,19 @@ +import 'package:get/get.dart'; + +import 'backup_sync_state.dart'; + +class BackupSyncLogic extends GetxController { + final BackupSyncState state = BackupSyncState(); + + @override + void onReady() { + // TODO: implement onReady + super.onReady(); + } + + @override + void onClose() { + // TODO: implement onClose + super.onClose(); + } +} diff --git a/lib/pages/backup_sync/backup_sync_state.dart b/lib/pages/backup_sync/backup_sync_state.dart new file mode 100644 index 0000000..a0401e0 --- /dev/null +++ b/lib/pages/backup_sync/backup_sync_state.dart @@ -0,0 +1,5 @@ +class BackupSyncState { + BackupSyncState() { + ///Initialize variables + } +} diff --git a/lib/pages/backup_sync/backup_sync_view.dart b/lib/pages/backup_sync/backup_sync_view.dart new file mode 100644 index 0000000..03ef5ff --- /dev/null +++ b/lib/pages/backup_sync/backup_sync_view.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:mood_diary/components/local_send/local_send_view.dart'; + +import 'backup_sync_logic.dart'; +import 'backup_sync_state.dart'; + +class BackupSyncPage extends StatelessWidget { + const BackupSyncPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final BackupSyncLogic logic = Get.put(BackupSyncLogic()); + final BackupSyncState state = Bind.find().state; + + return Scaffold( + appBar: AppBar( + title: const Text('备份与同步'), + ), + body: ListView( + children: const [ + ExpansionTile( + leading: Icon(Icons.wifi_tethering_rounded), + title: Text('局域网传输'), + children: [LocalSendComponent()], + ), + ExpansionTile( + leading: Icon(Icons.backup_rounded), + title: Text('WebDav'), + children: [], + ) + ], + ), + ); + } +} diff --git a/lib/pages/home/calendar/calendar_view.dart b/lib/pages/home/calendar/calendar_view.dart index 0e78c37..9103344 100644 --- a/lib/pages/home/calendar/calendar_view.dart +++ b/lib/pages/home/calendar/calendar_view.dart @@ -15,7 +15,7 @@ class CalendarPage extends StatelessWidget { @override Widget build(BuildContext context) { - final logic = Bind.find(); + final logic = Get.put(CalendarLogic()); final state = Bind.find().state; final colorScheme = Theme.of(context).colorScheme; final i18n = AppLocalizations.of(context)!; diff --git a/lib/pages/home/diary/diary_logic.dart b/lib/pages/home/diary/diary_logic.dart index 36fe507..dfc3b66 100644 --- a/lib/pages/home/diary/diary_logic.dart +++ b/lib/pages/home/diary/diary_logic.dart @@ -12,8 +12,7 @@ class DiaryLogic extends GetxController with GetTickerProviderStateMixin { final DiaryState state = DiaryState(); //初始化tab控制器,长度加一由于有一个默认分类 - late TabController tabController = - TabController(length: state.categoryList.length + 1, vsync: this); + late TabController tabController = TabController(length: state.categoryList.length + 1, vsync: this); late HomeLogic homeLogic = Bind.find(); @@ -43,10 +42,9 @@ class DiaryLogic extends GetxController with GetTickerProviderStateMixin { /// 在动态更新分类后要重新监听 void _tabBarListener() { if (tabController.indexIsChanging) return; - _checkPageChange(); + checkPageChange(); // 检查是否显示顶部内容 _checkShowTop(); - homeLogic.resetNavigatorBar(); } @@ -68,9 +66,7 @@ class DiaryLogic extends GetxController with GetTickerProviderStateMixin { if (tabController.index == 0) { await Bind.find(tag: 'default').paginationDiary(); } else { - await Bind.find( - tag: state.categoryList[tabController.index - 1].id) - .paginationDiary(); + await Bind.find(tag: state.categoryList[tabController.index - 1].id).paginationDiary(); } } } @@ -99,14 +95,10 @@ class DiaryLogic extends GetxController with GetTickerProviderStateMixin { /// 需要在以下情况调用 /// 1. tab bar 修改 /// 2. update ho - void _checkPageChange() { + void checkPageChange() { state.currentTabBarIndex = tabController.index; - // 获取当前分类ID,若为默认分类,设为 'default' - String categoryId = state.currentTabBarIndex == 0 - ? 'default' - : state.categoryList[state.currentTabBarIndex - 1].id; - + String categoryId = state.currentTabBarIndex == 0 ? 'default' : state.categoryList[state.currentTabBarIndex - 1].id; // 遍历 keyMap,更新每个分类的状态 state.keyMap.forEach((k, v) { v.currentState?.onPageChange(k == categoryId); @@ -129,15 +121,13 @@ class DiaryLogic extends GetxController with GetTickerProviderStateMixin { } } else { //查找分类对应的位置,加一是因为默认分类占了一个 - tabViewIndex = - state.categoryList.indexWhere((e) => e.id == categoryId) + 1; + tabViewIndex = state.categoryList.indexWhere((e) => e.id == categoryId) + 1; if (jump && tabController.index != 0) { tabController.animateTo(tabViewIndex); } } //如果控制器已经存在,重新获取,如果不存在,不需要任何操作 - if (tabViewIndex != 0 && - Bind.isRegistered(tag: categoryId)) { + if (tabViewIndex != 0 && Bind.isRegistered(tag: categoryId)) { await Bind.find(tag: categoryId).getDiary(); } await Bind.find(tag: 'default').getDiary(); @@ -154,9 +144,8 @@ class DiaryLogic extends GetxController with GetTickerProviderStateMixin { state.categoryList = await Utils().isarUtil.getAllCategoryAsync(); // 移除 Map 中不再存在的 Category id - state.keyMap.removeWhere((k, v) => - !state.categoryList.map((category) => category.id).contains(k) && - k != 'default'); + state.keyMap + .removeWhere((k, v) => !state.categoryList.map((category) => category.id).contains(k) && k != 'default'); // 为新的 Category 添加新的 GlobalKey for (var category in state.categoryList) { @@ -173,31 +162,22 @@ class DiaryLogic extends GetxController with GetTickerProviderStateMixin { //重新创建控制器 tabController.removeListener(_tabBarListener); - tabController = - TabController(length: state.categoryList.length + 1, vsync: this); + tabController = TabController(length: state.categoryList.length + 1, vsync: this); tabController.addListener(_tabBarListener); - update(); - _checkPageChange(); + update(['All']); + checkPageChange(); } //切换视图模式 Future changeViewMode(ViewModeType viewModeType) async { state.viewModeType = viewModeType; - if (state.currentTabBarIndex == 0) { - Bind.find(tag: 'default').update(['TabView']); - } else { - Bind.find( - tag: state.categoryList[state.currentTabBarIndex - 1].id) - .update(['TabView']); - } - homeLogic.state.isToTopShow = false; - homeLogic.update(['Fab']); + update(['TabBarView']); + _checkShowTop(); await Utils().prefUtil.setValue('homeViewMode', viewModeType.number); } // 回到顶部函数 Future toTop() async { - await state.innerController.animateTo(0.0, - duration: const Duration(milliseconds: 200), curve: Curves.linear); + await state.innerController.animateTo(0.0, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut); } } diff --git a/lib/pages/home/diary/diary_view.dart b/lib/pages/home/diary/diary_view.dart index b1457da..8109493 100644 --- a/lib/pages/home/diary/diary_view.dart +++ b/lib/pages/home/diary/diary_view.dart @@ -1,3 +1,4 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:get/get.dart'; @@ -14,7 +15,7 @@ class DiaryPage extends StatelessWidget { @override Widget build(BuildContext context) { - final logic = Bind.find(); + final logic = Get.put(DiaryLogic()); final state = Bind.find().state; final colorScheme = Theme.of(context).colorScheme; final i18n = AppLocalizations.of(context)!; @@ -37,6 +38,7 @@ class DiaryPage extends StatelessWidget { tabAlignment: TabAlignment.start, indicatorSize: TabBarIndicatorSize.label, splashFactory: NoSplash.splashFactory, + dragStartBehavior: DragStartBehavior.start, indicator: UnderlineTabIndicator( borderSide: BorderSide( color: colorScheme.primary, @@ -63,83 +65,96 @@ class DiaryPage extends StatelessWidget { Widget buildTabBarView() { List allViews = []; - // 添加全部日记页面 allViews.add(buildDiaryView(0, state.keyMap['default'], null)); - // 添加分类日记页面 allViews.addAll(List.generate(state.categoryList.length, (index) { - return buildDiaryView( - index + 1, - state.keyMap[state.categoryList[index].id], - state.categoryList[index].id); + return buildDiaryView(index + 1, state.keyMap[state.categoryList[index].id], state.categoryList[index].id); })); - return TabBarView( - controller: logic.tabController, - children: allViews, + return NotificationListener( + onNotification: (ScrollNotification notification) { + if (notification.metrics.axis == Axis.horizontal) { + logic.checkPageChange(); + } + return true; + }, + child: TabBarView( + controller: logic.tabController, + dragStartBehavior: DragStartBehavior.start, + children: allViews, + ), ); } - return NestedScrollView( - key: state.nestedScrollKey, - headerSliverBuilder: (context, _) { - return [ - SliverOverlapAbsorber( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - sliver: SliverAppBar( - title: Text( - state.customTitleName.isNotEmpty - ? state.customTitleName - : i18n.appName, - overflow: TextOverflow.ellipsis, - ), - actions: [ - IconButton( - onPressed: () { - showModalBottomSheet( - context: context, - showDragHandle: true, - useSafeArea: true, - builder: (context) { - return const SearchSheetComponent(); - }); - }, - icon: const Icon(Icons.search), - tooltip: i18n.diaryPageSearchButton, - ), - PopupMenuButton( - offset: const Offset(0, 46), - tooltip: i18n.diaryPageViewModeButton, - itemBuilder: (context) { - return >[ - CheckedPopupMenuItem( - checked: state.viewModeType == ViewModeType.list, - onTap: () async { - await logic.changeViewMode(ViewModeType.list); + return GetBuilder( + assignId: true, + builder: (_) { + return NestedScrollView( + key: state.nestedScrollKey, + headerSliverBuilder: (context, _) { + return [ + SliverOverlapAbsorber( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + sliver: SliverAppBar( + title: GetBuilder( + id: 'Title', + builder: (_) { + return Text( + state.customTitleName.isNotEmpty ? state.customTitleName : i18n.appName, + overflow: TextOverflow.ellipsis, + ); + }), + pinned: true, + actions: [ + IconButton( + onPressed: () { + showModalBottomSheet( + context: context, + showDragHandle: true, + useSafeArea: true, + builder: (context) { + return const SearchSheetComponent(); + }); }, - child: Text(i18n.diaryViewModeList), + icon: const Icon(Icons.search), + tooltip: i18n.diaryPageSearchButton, ), - const PopupMenuDivider(), - CheckedPopupMenuItem( - checked: state.viewModeType == ViewModeType.grid, - onTap: () async { - await logic.changeViewMode(ViewModeType.grid); + PopupMenuButton( + offset: const Offset(0, 46), + tooltip: i18n.diaryPageViewModeButton, + itemBuilder: (context) { + return >[ + CheckedPopupMenuItem( + checked: state.viewModeType == ViewModeType.list, + onTap: () async { + await logic.changeViewMode(ViewModeType.list); + }, + child: Text(i18n.diaryViewModeList), + ), + const PopupMenuDivider(), + CheckedPopupMenuItem( + checked: state.viewModeType == ViewModeType.grid, + onTap: () async { + await logic.changeViewMode(ViewModeType.grid); + }, + child: Text(i18n.diaryViewModeGrid), + ), + ]; }, - child: Text(i18n.diaryViewModeGrid), ), - ]; - }, + ], + bottom: PreferredSize(preferredSize: const Size.fromHeight(46.0), child: buildTabBar()), + ), ), - ], - bottom: PreferredSize( - preferredSize: const Size.fromHeight(46.0), - child: buildTabBar()), - ), - ), - ]; - }, - body: buildTabBarView(), - ); + ]; + }, + body: GetBuilder( + id: 'TabBarView', + builder: (_) { + return buildTabBarView(); + }), + ); + }); } } diff --git a/lib/pages/home/media/media_view.dart b/lib/pages/home/media/media_view.dart index d878f25..ce49f57 100644 --- a/lib/pages/home/media/media_view.dart +++ b/lib/pages/home/media/media_view.dart @@ -17,7 +17,7 @@ class MediaPage extends StatelessWidget { @override Widget build(BuildContext context) { - final logic = Bind.find(); + final logic = Get.put(MediaLogic()); final state = Bind.find().state; final i18n = AppLocalizations.of(context)!; diff --git a/lib/pages/home/setting/setting_logic.dart b/lib/pages/home/setting/setting_logic.dart index 628e793..3d5f0b1 100644 --- a/lib/pages/home/setting/setting_logic.dart +++ b/lib/pages/home/setting/setting_logic.dart @@ -15,8 +15,7 @@ class SettingLogic extends GetxController { final SettingState state = SettingState(); late final homeLogic = Bind.find(); - late TextEditingController textEditingController = - TextEditingController(text: state.customTitle); + late TextEditingController textEditingController = TextEditingController(text: state.customTitle); @override void onInit() { @@ -88,6 +87,10 @@ class SettingLogic extends GetxController { Get.toNamed(AppRoutes.diarySettingPage); } + void toBackupAndSyncPage() { + Get.toNamed(AppRoutes.backupSyncPage); + } + void cancelCustomTitle() { textEditingController.clear(); Get.backLegacy(); @@ -97,9 +100,7 @@ class SettingLogic extends GetxController { if (textEditingController.text.isNotEmpty) { state.customTitle = textEditingController.text; update(['CustomTitle']); - await Utils() - .prefUtil - .setValue('customTitleName', textEditingController.text); + await Utils().prefUtil.setValue('customTitleName', textEditingController.text); Get.backLegacy(); textEditingController.clear(); Utils().noticeUtil.showToast('重启应用后生效'); @@ -129,8 +130,7 @@ class SettingLogic extends GetxController { //导入 Future import() async { Get.backLegacy(); - FilePickerResult? result = await FilePicker.platform - .pickFiles(allowedExtensions: ['zip'], type: FileType.custom); + FilePickerResult? result = await FilePicker.platform.pickFiles(allowedExtensions: ['zip'], type: FileType.custom); if (result != null) { Utils().noticeUtil.showToast('数据导入中,请不要离开页面'); await Utils().fileUtil.extractFile(result.files.single.path!); diff --git a/lib/pages/home/setting/setting_view.dart b/lib/pages/home/setting/setting_view.dart index 44c5d18..9bfcfe4 100644 --- a/lib/pages/home/setting/setting_view.dart +++ b/lib/pages/home/setting/setting_view.dart @@ -15,24 +15,14 @@ class SettingPage extends StatelessWidget { @override Widget build(BuildContext context) { - final logic = Bind.find(); + final logic = Get.put(SettingLogic()); final state = Bind.find().state; final textStyle = Theme.of(context).textTheme; final colorScheme = Theme.of(context).colorScheme; final i18n = AppLocalizations.of(context)!; Widget buildManager() { - return Column( - children: [ - ListTile( - title: Text( - '管理', - style: textStyle.titleLarge!.copyWith(color: colorScheme.primary, fontWeight: FontWeight.bold), - ), - ), - const DashboardComponent(), - ], - ); + return const DashboardComponent(); } Widget buildData() { @@ -56,6 +46,14 @@ class SettingPage extends StatelessWidget { }, leading: const Icon(Icons.delete_outline), ), + ListTile( + title: const Text('备份与同步'), + trailing: const Icon(Icons.chevron_right), + onTap: () { + logic.toBackupAndSyncPage(); + }, + leading: const Icon(Icons.sync), + ), ListTile( title: Text(i18n.settingExport), onTap: () { diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 41c1ee8..230014c 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -4,6 +4,8 @@ import 'package:mood_diary/pages/about/about_view.dart'; import 'package:mood_diary/pages/agreement/agreement_view.dart'; import 'package:mood_diary/pages/analyse/analyse_logic.dart'; import 'package:mood_diary/pages/analyse/analyse_view.dart'; +import 'package:mood_diary/pages/backup_sync/backup_sync_logic.dart'; +import 'package:mood_diary/pages/backup_sync/backup_sync_view.dart'; import 'package:mood_diary/pages/category_manager/category_manager_logic.dart'; import 'package:mood_diary/pages/category_manager/category_manager_view.dart'; import 'package:mood_diary/pages/diary_details/diary_details_view.dart'; @@ -15,12 +17,8 @@ import 'package:mood_diary/pages/edit/edit_logic.dart'; import 'package:mood_diary/pages/edit/edit_view.dart'; import 'package:mood_diary/pages/font/font_logic.dart'; import 'package:mood_diary/pages/font/font_view.dart'; -import 'package:mood_diary/pages/home/calendar/calendar_logic.dart'; -import 'package:mood_diary/pages/home/diary/diary_logic.dart'; import 'package:mood_diary/pages/home/home_logic.dart'; import 'package:mood_diary/pages/home/home_view.dart'; -import 'package:mood_diary/pages/home/media/media_logic.dart'; -import 'package:mood_diary/pages/home/setting/setting_logic.dart'; import 'package:mood_diary/pages/image/image_logic.dart'; import 'package:mood_diary/pages/image/image_view.dart'; import 'package:mood_diary/pages/laboratory/laboratory_logic.dart'; @@ -60,11 +58,6 @@ class AppPages { page: () => const HomePage(), binds: [ Bind.lazyPut(() => HomeLogic()), - Bind.lazyPut(() => DiaryLogic()), - Bind.lazyPut(() => CalendarLogic()), - Bind.lazyPut(() => MediaLogic()), - //Bind.lazyPut(() => AssistantLogic()), - Bind.lazyPut(() => SettingLogic()), ], ), //分析 @@ -175,5 +168,10 @@ class AppPages { page: () => const DiarySettingPage(), binds: [Bind.lazyPut(() => DiarySettingLogic())], ), + GetPage( + name: AppRoutes.backupSyncPage, + page: () => const BackupSyncPage(), + binds: [Bind.lazyPut(() => BackupSyncLogic())], + ), ]; } diff --git a/lib/router/app_routes.dart b/lib/router/app_routes.dart index 7f35b12..07f975d 100644 --- a/lib/router/app_routes.dart +++ b/lib/router/app_routes.dart @@ -69,4 +69,7 @@ abstract class AppRoutes { //日记个性化 static const diarySettingPage = '/diarySetting'; + + //备份与同步页 + static const backupSyncPage = '/backupSync'; } diff --git a/lib/utils/aes_util.dart b/lib/utils/aes_util.dart new file mode 100644 index 0000000..8d8d5d4 --- /dev/null +++ b/lib/utils/aes_util.dart @@ -0,0 +1,27 @@ +import 'dart:convert'; +import 'package:encrypt/encrypt.dart' as encrypt; + +class AESUtil { + final encrypt.Key _key; + final encrypt.IV _iv; + + // 初始化时设置密钥和初始化向量(IV) + AESUtil(String key, String iv) + : _key = encrypt.Key.fromUtf8(key.padRight(32, ' ')), + // 32字节密钥 + _iv = encrypt.IV.fromUtf8(iv.padRight(16, ' ')); // 16字节IV + + // 加密数据 + String encryptData(String data) { + final encrypter = encrypt.Encrypter(encrypt.AES(_key)); + final encrypted = encrypter.encrypt(data, iv: _iv); + return encrypted.base64; // 返回加密后的数据 + } + + // 解密数据 + String decryptData(String encryptedData) { + final encrypter = encrypt.Encrypter(encrypt.AES(_key)); + final decrypted = encrypter.decrypt64(encryptedData, iv: _iv); + return decrypted; // 返回解密后的数据 + } +} diff --git a/lib/utils/channel.dart b/lib/utils/channel.dart index 0b12b4c..23e0aae 100644 --- a/lib/utils/channel.dart +++ b/lib/utils/channel.dart @@ -1,13 +1,5 @@ import 'package:flutter/services.dart'; -class ViewChannel { - static const MethodChannel _channel = MethodChannel('view_channel'); - - static setSystemUIVisibility() async { - await _channel.invokeMethod('setSystemUIVisibility'); - } -} - class FontChannel { static const MethodChannel _channel = MethodChannel('font_channel'); diff --git a/lib/utils/data/isar.dart b/lib/utils/data/isar.dart index 03993bc..157a0f7 100644 --- a/lib/utils/data/isar.dart +++ b/lib/utils/data/isar.dart @@ -1,6 +1,8 @@ import 'package:isar/isar.dart'; +import 'package:latlong2/latlong.dart'; import 'package:mood_diary/common/models/isar/category.dart'; import 'package:mood_diary/common/models/isar/diary.dart'; +import 'package:mood_diary/common/models/map.dart'; import 'package:mood_diary/utils/utils.dart'; import 'package:path/path.dart'; import 'package:uuid/uuid.dart'; @@ -237,6 +239,24 @@ class IsarUtil { isar.close(); } + // 获取用于地图显示的对象 + Future> getAllMapItem() async { + List res = []; + + /// 所有的日记 + /// 要满足以下条件 + /// 1. 有定位坐标 + /// 2. 有照片 + /// 3. show + var diaries = + await _isar.diarys.where().showEqualTo(true).positionIsNotEmpty().imageNameIsNotEmpty().findAllAsync(); + for (var diary in diaries) { + res.add(DiaryMapItem(LatLng(double.parse(diary.position[0]), double.parse(diary.position[1])), diary.isarId, + diary.imageName.first)); + } + return res; + } + //构建搜索 void buildSearch(String dir) async { var isar = Isar.open( diff --git a/lib/utils/localsend/client.dart b/lib/utils/localsend/client.dart deleted file mode 100644 index cf6b0cb..0000000 --- a/lib/utils/localsend/client.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:mood_diary/utils/utils.dart'; - -class LocalLanClient { - final dio = Utils().httpUtil.dio; - final int port; - RawDatagramSocket? socket; - - LocalLanClient({this.port = 4040}); - - /// 初始化UDP套接字 - Future initSocket() async { - socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, port); - socket!.broadcastEnabled = true; - print("UDP socket initialized on port $port"); - } - - /// 广播设备信息 - void broadcastPresence() { - final message = utf8.encode("LocalLanClient: device here"); - socket!.send(message, InternetAddress("224.0.0.1"), port); - print("Presence broadcasted"); - } - - /// 监听其他设备的广播消息 - void listenForDevices() { - socket!.listen((RawSocketEvent event) { - if (event == RawSocketEvent.read) { - final packet = socket!.receive(); - if (packet != null) { - final message = utf8.decode(packet.data); - print("Discovered device at ${packet.address.address}: $message"); - } - } - }); - } - - /// 发送文件到指定设备IP - Future sendFile(String filePath, String deviceIp) async { - final file = File(filePath); - final formData = FormData.fromMap({ - "file": await MultipartFile.fromFile(file.path, filename: file.path.split('/').last), - }); - - try { - final response = await dio.post( - 'http://$deviceIp:$port/upload', - data: formData, - ); - print("File sent successfully: ${response.data}"); - } catch (e) { - print("Error sending file: $e"); - } - } - - /// 关闭UDP套接字 - void closeSocket() { - socket?.close(); - print("Socket closed"); - } -} diff --git a/lib/utils/localsend/server.dart b/lib/utils/localsend/server.dart deleted file mode 100644 index cd6d289..0000000 --- a/lib/utils/localsend/server.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:io'; - -import 'package:shelf/shelf.dart'; -import 'package:shelf/shelf_io.dart' as shelf_io; -import 'package:shelf_multipart/shelf_multipart.dart'; - -class LocalLanServer { - final int port; - HttpServer? _server; - - LocalLanServer({this.port = 4040}); - - /// 启动文件服务器 - Future start() async { - // 配置路由和中间件 - final handler = const Pipeline() - .addMiddleware(logRequests()) // 日志中间件 - .addHandler(_router); // 路由处理器 - - // 启动 Shelf 服务器 - _server = await shelf_io.serve(handler, InternetAddress.anyIPv4, port); - print("Shelf server started on port $port"); - } - - /// 路由处理器 - Future _router(Request request) async { - if (request.url.path == 'upload' && request.method == 'POST') { - return _handleFileUpload(request); - } else { - return Response.notFound('Not Found'); - } - } - - /// 处理文件上传请求 - Future _handleFileUpload(Request request) async { - try { - final multipart = request.multipart(); - await for (final part in multipart!.parts) { - final fileName = 'received_${DateTime.now().millisecondsSinceEpoch}.file'; - final file = File('/path/to/save/$fileName'); // 替换为实际保存路径 - final sink = file.openWrite(); - await part.pipe(sink); // 将文件写入本地 - await sink.close(); - print("File received: ${file.path}"); - } - return Response.ok('File received successfully'); - } catch (e) { - print("Error receiving file: $e"); - return Response.internalServerError(body: 'File upload failed'); - } - } - - /// 停止服务器 - Future stop() async { - await _server?.close(); - print("Shelf server stopped"); - } -} diff --git a/lib/utils/media_util.dart b/lib/utils/media_util.dart index 7f7821a..f3912f5 100644 --- a/lib/utils/media_util.dart +++ b/lib/utils/media_util.dart @@ -18,9 +18,9 @@ class MediaUtil { List nameList = []; for (var imageFile in imageFileList) { //生成新的名字 - var imageName = 'image-${const Uuid().v7()}.webp'; + var imageName = 'image-${const Uuid().v7()}.png'; nameList.add(imageName); - await _compressAndSaveImage(imageFile, Utils().fileUtil.getRealPath('image', imageName), CompressFormat.webp); + await _compressAndSaveImage(imageFile, Utils().fileUtil.getRealPath('image', imageName), CompressFormat.png); } return nameList; } diff --git a/lib/utils/permission_util.dart b/lib/utils/permission_util.dart index 708d2cc..7cc7ddc 100644 --- a/lib/utils/permission_util.dart +++ b/lib/utils/permission_util.dart @@ -1,9 +1,14 @@ +import 'dart:io'; + import 'package:mood_diary/utils/utils.dart'; import 'package:permission_handler/permission_handler.dart'; class PermissionUtil { //权限申请 Future checkPermission(Permission permission) async { + if (Platform.isMacOS) { + return true; + } //检查当前权限 final status = await permission.status; //如果还没有授权或者拒绝过 diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index fdb81c5..da39ace 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -6,8 +6,6 @@ import 'package:mood_diary/utils/data/supabase.dart'; import 'package:mood_diary/utils/file_util.dart'; import 'package:mood_diary/utils/http_util.dart'; import 'package:mood_diary/utils/layout_util.dart'; -import 'package:mood_diary/utils/localsend/client.dart'; -import 'package:mood_diary/utils/localsend/server.dart'; import 'package:mood_diary/utils/log_util.dart'; import 'package:mood_diary/utils/media_util.dart'; import 'package:mood_diary/utils/notice_util.dart'; @@ -59,8 +57,4 @@ class Utils { late final PrefUtil prefUtil = PrefUtil(); late final SupabaseUtil supabaseUtil = SupabaseUtil(); - - late final LocalLanClient localLanClient = LocalLanClient(); - - late final LocalLanServer localLanServer = LocalLanServer(); } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 031aae0..9f2359f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -18,6 +18,7 @@ import isar_flutter_libs import local_auth_darwin import media_kit_libs_macos_video import media_kit_video +import network_info_plus import objectbox_flutter_libs import package_info_plus import path_provider_foundation @@ -44,6 +45,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin")) MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) + NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 6be0626..764db97 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -30,6 +30,8 @@ PODS: - FlutterMacOS - media_kit_video (0.0.1): - FlutterMacOS + - network_info_plus (0.0.1): + - FlutterMacOS - ObjectBox (4.0.1) - objectbox_flutter_libs (0.0.1): - FlutterMacOS @@ -76,6 +78,7 @@ DEPENDENCIES: - media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`) - media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`) - media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`) + - network_info_plus (from `Flutter/ephemeral/.symlinks/plugins/network_info_plus/macos`) - objectbox_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/objectbox_flutter_libs/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) @@ -124,6 +127,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos media_kit_video: :path: Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos + network_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/network_info_plus/macos objectbox_flutter_libs: :path: Flutter/ephemeral/.symlinks/plugins/objectbox_flutter_libs/macos package_info_plus: @@ -153,7 +158,7 @@ SPEC CHECKSUMS: app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 - device_info_plus: 74e614483d05c89290d30a4c8feae15d555f7427 + device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215 dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f fc_native_video_thumbnail: 927d4dcfd4c7e9f2cc1a20bb52dfee83de3792c2 file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d @@ -165,14 +170,15 @@ SPEC CHECKSUMS: media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82 media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5 media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5 + network_info_plus: 2cb02d8435635eae13b3b79279681985121cf30c ObjectBox: 0bc4bb75eea85f6af06b369148b334c2056bbc29 objectbox_flutter_libs: 769e6f44f7381c8a8e46a2ed5c71c6068bb476f7 - package_info_plus: f5790acc797bf17c3e959e9d6cf162cc68ff7523 + package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 quill_native_bridge_macos: f90985c5269ac7ba84d933605b463d96e5f544fe record_darwin: a0d515a0ef78c440c123ea3ac76184c9927a94d6 screen_brightness_macos: 2d6d3af2165592d9a55ffcd95b7550970e41ebda - share_plus: fd717ef89a2801d3491e737630112b80c310640e + share_plus: 1fa619de8392a4398bfaf176d441853922614e89 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite_darwin: a553b1fd6fe66f53bbb0fe5b4f5bab93f08d7a13 tflite_flutter: b186ea3c62c076a90652f47c01005fe36c75171e diff --git a/pubspec.lock b/pubspec.lock index 0597852..a1ed715 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -78,6 +78,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.0" + asn1lib: + dependency: transitive + description: + name: asn1lib + sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5" + url: "https://pub.dev" + source: hosted + version: "1.5.8" async: dependency: transitive description: @@ -426,10 +434,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: c4af09051b4f0508f6c1dc0a5c085bf014d5c9a4a0678ce1799c2b4d716387a0 + sha256: f545ffbadee826f26f2e1a0f0cbd667ae9a6011cc0f77c0f8f00a969655e6e95 url: "https://pub.dev" source: hosted - version: "11.1.0" + version: "11.1.1" device_info_plus_platform_interface: dependency: transitive description: @@ -478,6 +486,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.7.0" + encrypt: + dependency: "direct main" + description: + name: encrypt + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" + url: "https://pub.dev" + source: hosted + version: "5.0.3" equatable: dependency: transitive description: @@ -724,10 +740,10 @@ packages: dependency: transitive description: name: flutter_keyboard_visibility_temp_fork - sha256: e342172aaa6173a661e822c85a005f8c5d0a04a1d263e00cb9f9155adab9cb7c + sha256: "2d94acecfc170d244157821cc67e784f60972677aac94a6672626a5d6b2dc537" url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.1.3" flutter_keyboard_visibility_windows: dependency: transitive description: @@ -1385,8 +1401,32 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - objectbox: + network_info_plus: + dependency: "direct main" + description: + name: network_info_plus + sha256: bf9e39e523e9951d741868dc33ac386b0bc24301e9b7c8a7d60dbc34879150a8 + url: "https://pub.dev" + source: hosted + version: "6.1.1" + network_info_plus_platform_interface: dependency: transitive + description: + name: network_info_plus_platform_interface + sha256: b7f35f4a7baef511159e524499f3c15464a49faa5ec10e92ee0bce265e664906 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + objectbox: + dependency: "direct main" description: name: objectbox sha256: ea823f4bf1d0a636e7aa50b43daabb64dd0fbd80b85a033016ccc1bc4f76f432 @@ -1421,10 +1461,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: df3eb3e0aed5c1107bb0fdb80a8e82e778114958b1c5ac5644fb1ac9cae8a998 + sha256: da8d9ac8c4b1df253d1a328b7bf01ae77ef132833479ab40763334db13b91cce url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.1.1" package_info_plus_platform_interface: dependency: transitive description: @@ -1569,6 +1609,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" polylabel: dependency: transitive description: @@ -1685,10 +1733,10 @@ packages: dependency: transitive description: name: quill_native_bridge_web - sha256: "59d673b946ecb8dbcdd387a4957de12f0263690bdbe159832355855ac7a45de9" + sha256: bb3ab017fdb9b60a29cac0bce3acfd48396d13c1bd0499c97af112c84937b4d1 url: "https://pub.dev" source: hosted - version: "0.0.1-dev.4" + version: "0.0.1-dev.5" quill_native_bridge_windows: dependency: transitive description: @@ -1853,10 +1901,10 @@ packages: dependency: "direct main" description: name: share_plus - sha256: "3af2cda1752e5c24f2fc04b6083b40f013ffe84fb90472f30c6499a9213d5442" + sha256: "9c9bafd4060728d7cdb2464c341743adbd79d327cb067ec7afb64583540b47c8" url: "https://pub.dev" source: hosted - version: "10.1.1" + version: "10.1.2" share_plus_platform_interface: dependency: transitive description: @@ -1869,10 +1917,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" + sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.3" shared_preferences_android: dependency: transitive description: @@ -2002,10 +2050,10 @@ packages: dependency: transitive description: name: sqflite - sha256: "79a297dc3cc137e758c6a4baf83342b039e5a6d2436fcdf3f96a00adaaf2ad62" + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" sqflite_android: dependency: transitive description: @@ -2026,10 +2074,10 @@ packages: dependency: transitive description: name: sqflite_darwin - sha256: "769733dddf94622d5541c73e4ddc6aa7b252d865285914b6fcd54a63c4b4f027" + sha256: "96a698e2bc82bd770a4d6aab00b42396a7c63d9e33513a56945cbccb594c2474" url: "https://pub.dev" source: hosted - version: "2.4.1-1" + version: "2.4.1" sqflite_platform_interface: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fbd8088..c5a74f9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: path_provider: 2.1.5 calendar_date_picker2: 1.1.7 logger: 2.4.0 - flutter_drawing_board: ^0.9.5 + flutter_drawing_board: 0.9.5 flutter_displaymode: 0.6.0 fl_chart: 0.69.0 file_picker: 8.1.3 @@ -44,20 +44,20 @@ dependencies: local_auth_android: 1.0.46 permission_handler: 11.3.1 image_picker: 1.1.2 - device_info_plus: 11.1.0 + device_info_plus: 11.1.1 flutter_image_compress: 2.3.0 photo_view: 0.15.0 - package_info_plus: 8.1.0 + package_info_plus: 8.1.1 uuid: 4.5.1 flutter_quill: 10.8.5 - share_plus: 10.1.1 + share_plus: 10.1.2 url_launcher: 6.3.1 archive: 3.6.1 crypto: 3.0.6 markdown_widget: 2.3.2+6 flutter_colorpicker: 1.1.0 geolocator: 13.0.1 - shared_preferences: 2.3.2 + shared_preferences: 2.3.3 isar: 4.0.0-dev.14 isar_flutter_libs: 4.0.0-dev.14 fluttertoast: 8.2.8 @@ -91,6 +91,9 @@ dependencies: smooth_page_indicator: 1.2.0+3 table_calendar: 3.1.2 unicons: 3.0.0 + network_info_plus: 6.1.1 + encrypt: 5.0.3 + objectbox: 4.0.3 flutter_localizations: @@ -129,13 +132,4 @@ flutter: uses-material-design: true -msix_config: - display_name: 心绪日记 - app_installer: - publish_folder_path: \win - publisher_display_name: ZhuJHua - # certificate_path: D:\msix.pfx - # certificate_password: liuzhuming - logo_path: assets/icon/icon.png - trim_logo: false - identity_name: cn.yooss.moodiary +