Эх сурвалжийг харах

fix bug 0009184 - review by Jimmy

melon.yin 2 жил өмнө
parent
commit
f6392c47a9

+ 199 - 0
lib/view/player/buffer_waiter.dart

@@ -0,0 +1,199 @@
+import 'dart:async';
+import 'dart:math' as math;
+import 'package:fis_common/logger/logger.dart';
+import 'package:fis_vid/data_channel/channel.dart';
+
+/// Vid缓冲等待
+class VidBufferWaiter {
+  VidBufferWaiter(
+    this.channel, {
+    this.timeout = 10 * 1000,
+  });
+
+  // ignore: constant_identifier_names
+  static const _FIRST_FRAME_LOAD_TIME_LIMIT = 60 * 1000; // 一分钟
+
+  /// 数据通道
+  final VidDataChannel channel;
+
+  /// 等待超时时间,默认10秒
+  final int timeout;
+
+  double _frameRate = 0;
+  int _frameCount = 0;
+  int _framePerSize = 0;
+
+  int _vidHeadSize = 0;
+
+  int _maxDuration = 0;
+
+  Timer? _timer;
+
+  /// vid头信息尺寸(含扩展)
+  int get vidHeaderSize => _vidHeadSize;
+
+  /// 初始化
+  void init() {
+    _frameRate = channel.probe.frameRate;
+    _frameCount = channel.imageCount;
+    _vidHeadSize = _calcHeaderSize();
+    _framePerSize = _calcFramePerSize();
+  }
+
+  /// 等待首帧
+  Future<void> waitSingleVid() async {
+    logger.i("VidPlayer - Wait single vid start.");
+    final completer = Completer();
+
+    const int waitInterval = 100;
+    const int waitCountLimit = _FIRST_FRAME_LOAD_TIME_LIMIT ~/ waitInterval;
+    int waitCount = 0;
+
+    _timer = Timer.periodic(
+      const Duration(milliseconds: waitInterval),
+      (timer) {
+        waitCount++;
+
+        if (channel.isBufferedDone) {
+          // 全部缓冲完成
+          timer.cancel();
+          completer.complete();
+          logger.i("VidPlayer - Wait single vid end.");
+          return;
+        }
+
+        if (waitCount >= waitCountLimit) {
+          completer.completeError(Exception("First frame load timeout"));
+          logger.i("VidPlayer - Wait single vid: timeout.");
+        }
+      },
+    );
+    return completer.future;
+  }
+
+  /// 等待缓冲
+  Future<void> waitBuffer(int frameIndex) {
+    logger.i("VidPlayer - Wait buffer start.");
+    final completer = Completer();
+
+    final bufferredSize = channel.getBufferSize();
+    final needBufferSize = _calcNeedWaitSize(frameIndex);
+
+    const int waitInterval = 100;
+    final int waitCountLimit = timeout ~/ waitInterval;
+    int waitCount = 0;
+    final startTime = DateTime.now();
+
+    _timer = Timer.periodic(
+      const Duration(milliseconds: waitInterval),
+      (timer) {
+        // 函数 - 完成等待
+        void finishFn() {
+          timer.cancel();
+          completer.complete();
+          final endTime = DateTime.now();
+          final spendTime = endTime.difference(startTime).inMilliseconds;
+          logger.i("VidPlayer - Wait buffer end, spend time: $spendTime ms.");
+        }
+
+        waitCount++;
+        final bufferedSize = channel.getBufferSize();
+        if (bufferedSize >= needBufferSize) {
+          // 缓存足够
+          return finishFn();
+        }
+
+        if (waitCount >= waitCountLimit) {
+          // 等待超时
+          final bufferredSizeNow = channel.getBufferSize();
+          if (bufferredSizeNow - bufferredSize < 1024) {
+            // 缓冲小于1k,报超时
+            completer.completeError(Exception("Wait buffer timeout"));
+            logger.i("VidPlayer - Wait buffer timeout.");
+          } else {
+            // 放行
+            finishFn();
+          }
+        }
+      },
+    );
+    return completer.future;
+  }
+
+  /// 记录单帧耗时
+  void recordFrameSpendTime(int millseconds) {
+    _maxDuration = math.max(_maxDuration, millseconds);
+  }
+
+  /// 取消等待
+  void cancel() {
+    _timer?.cancel();
+  }
+
+  int _calcNeedWaitSize(int frameIndex) {
+    int needWaitSize = 0;
+
+    final int bufferedSize = channel.getBufferSize();
+    final int totalSize = channel.getFileSize();
+
+    // 已缓冲帧数
+    final int bufferredFrameCount = bufferedSize ~/ _framePerSize;
+    // 未缓冲帧数
+    final int unbufferredFrameCount = _frameCount - bufferredFrameCount;
+
+    // 未缓冲数据字节数
+    final int unbufferredSize = totalSize - bufferedSize;
+    // 未缓冲数据字节数 所需耗时
+    final int needBufferTime = unbufferredFrameCount * _maxDuration;
+
+    // 未缓冲播放时长
+    final int unbufferredDuration =
+        ((1000 / _frameRate) * unbufferredFrameCount).toInt();
+
+    // 每秒需要字节数
+    final int perSecondNeedSize = (_framePerSize * _frameRate).toInt();
+
+    // 缓存和播放同时结束所需耗时
+    int needWaitTime = needBufferTime - unbufferredDuration;
+    if (needWaitTime < 0) {
+      // 预计缓冲比播放快(可能是网络波动大导致出现加载)
+      // 缓存1秒
+      needWaitSize = perSecondNeedSize;
+    } else {
+      needWaitTime += 1; //多缓存一秒
+      needWaitSize = needWaitTime * perSecondNeedSize;
+
+      if (frameIndex > bufferredFrameCount) {
+        // 手动定位到了后面指定帧,需要额外缓存相差的帧数
+        final needWaitExtraCount = frameIndex - bufferredFrameCount;
+        final needWaitExtraSize = _framePerSize * needWaitExtraCount;
+        needWaitSize += needWaitExtraSize;
+      }
+    }
+
+    return math.max(unbufferredSize, needWaitSize);
+  }
+
+  int _calcFramePerSize() {
+    final size = _calcFramesBytesSize();
+    final perSize = size ~/ channel.imageCount;
+    return perSize;
+  }
+
+  int _calcFramesBytesSize() {
+    int size = channel.getFileSize();
+    size -= _vidHeadSize;
+    return size;
+  }
+
+  int _calcHeaderSize() {
+    int size = 0;
+    size += channel.probe.toBytes().length;
+    size += channel.extendedData.length;
+    size += channel.imageCount * 8;
+    size += 16; // Header Size
+    size += 4; // Version Size
+    size += 4; // ImageFormat
+    return size;
+  }
+}

+ 27 - 6
lib/view/player/control_board/streaming_progress_bar.dart

@@ -1,4 +1,5 @@
 import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
+import 'package:fis_common/logger/logger.dart';
 import 'package:fis_measure/interfaces/process/player/play_controller.dart';
 import 'package:fis_measure/view/player/controller.dart';
 import 'package:fis_measure/view/player/events.dart';
@@ -22,7 +23,13 @@ class _StreamingProgressBarState extends State<StreamingProgressBar> {
   double bufferedProgress = 0;
 
   int get bufferedCount {
-    return (bufferedProgress * totalCount).toInt();
+    int count = (bufferedProgress * totalCount).toInt();
+    final currentPlayedIndex = controller.currentFrame?.index ?? 0;
+    if (count < currentPlayedIndex) {
+      // 缓冲进度至少需要追平播放(可播放)进度
+      count = currentPlayedIndex;
+    }
+    return count;
   }
 
   @override
@@ -97,11 +104,13 @@ class _StreamingProgressBarState extends State<StreamingProgressBar> {
   }
 
   void _onDownloadProgressChanged(Object sender, VidDownloadProgressInfo e) {
-    if (bufferedProgress != e.progress) {
-      bufferedProgress = e.progress;
-      if (mounted) {
-        setState(() {});
-      }
+    if (e.progress == 1) {
+      bufferedProgress = 1;
+    } else {
+      bufferedProgress = _calcRealFramesBufferPercent();
+    }
+    if (mounted) {
+      setState(() {});
     }
   }
 
@@ -112,4 +121,16 @@ class _StreamingProgressBarState extends State<StreamingProgressBar> {
       }
     }
   }
+
+  double _calcRealFramesBufferPercent() {
+    final channel = controller.dataChannel;
+    final int fileSize = channel.getFileSize();
+    final int bufferredSize = channel.getBufferSize();
+
+    final int framesTotalSize = fileSize - controller.vidHeaderSize;
+    final int framesBufferredSize = bufferredSize - controller.vidHeaderSize;
+    double percent = framesBufferredSize / framesTotalSize;
+
+    return percent;
+  }
 }

+ 29 - 82
lib/view/player/controller.dart

@@ -5,7 +5,7 @@ import 'dart:async';
 import 'package:fis_common/event/event_type.dart';
 import 'package:fis_i18n/i18n.dart';
 import 'package:fis_measure/interfaces/process/player/play_controller.dart';
-import 'package:fis_ui/index.dart';
+import 'package:fis_measure/view/player/buffer_waiter.dart';
 import 'package:fis_vid/data_channel/channel.dart';
 import 'package:flutter/foundation.dart';
 import 'package:vid/us/vid_us_image.dart';
@@ -22,6 +22,7 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
   VidPlayerController(String url) {
     _url = url;
     _dataChannel = VidDataChannel.create(url);
+    _bufferWaiter = VidBufferWaiter(_dataChannel);
     _isFirstFrame = true;
     eventHandler = FEventHandler<VidPlayerEvent>();
     frameUpdated = FEventHandler<VidUsImage>();
@@ -37,8 +38,6 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
 
   static const _HAS_VIEW_STATUS_ARR = [VidPlayStatus.play, VidPlayStatus.pause];
 
-  static const _FRAME_LOAD_RETRY_LIMIT = 120; // 500ms一次,等一分钟
-
   @override
   late final FEventHandler<VidPlayerEvent> eventHandler;
 
@@ -56,17 +55,15 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
 
   late final String _url;
   late final VidDataChannel _dataChannel;
+  late final VidBufferWaiter _bufferWaiter;
 
   _PlayAssistant? _playAssistant;
-  double _speed = 1.0;
   VidPlayStatus _status = VidPlayStatus.init;
   int _frameIndex = -1;
   VidUsImage? _frame;
   bool _disposed = false;
   bool _isFirstFrame = false;
   bool _loading = false;
-  int _frameLoadRetryCount = 0;
-  Timer? _bufferWaitingTimer;
 
   @override
   String get url => _url;
@@ -77,9 +74,6 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
   @override
   bool get disposed => _disposed;
 
-  /// Current play speed
-  double get currentSpeed => _speed;
-
   @override
   VidPlayStatus get status => _status;
 
@@ -115,20 +109,14 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
   double get contrast => _contrast;
   double _contrast = 1.0;
 
-  /// Set play speed [0.5~2]
-  ///
-  /// [speed] speed value [0.5~2]
-  void setSpeed(double speed) {
-    if (speed < 0.5 || speed > 2) return;
-
-    _speed = speed;
-    eventHandler.emit(this, VidPlayerSpeedChangeEvent(currentSpeed));
-  }
+  /// vid头信息尺寸(含扩展)
+  int get vidHeaderSize => _bufferWaiter.vidHeaderSize;
 
   @override
   Future<bool> load() async {
     final loaded = await _dataChannel.load(10 * 1000);
     if (loaded) {
+      _bufferWaiter.init();
       _setStatus(VidPlayStatus.ready);
     } else {
       _setStatus(VidPlayStatus.loadFail);
@@ -194,9 +182,16 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
     _frameIndex = index;
     _loading = true;
     _updateFrameLoadState(true);
+
+    final start = DateTime.now();
     final result = await _waitUpdateFrame();
+    final end = DateTime.now();
+
     if (result) {
       _updateFrameLoadState(false);
+
+      final spendTime = end.difference(start).inMilliseconds;
+      _bufferWaiter.recordFrameSpendTime(spendTime);
     }
     return result;
   }
@@ -269,20 +264,28 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
   }
 
   /// 等待更新帧
-  Future<bool> _waitUpdateFrame([int? timeout]) async {
+  Future<bool> _waitUpdateFrame() async {
     if (_disposed) return false;
 
-    try {
-      final t = timeout ?? _playAssistant?._playIntervalMillSeconds ?? 500;
-      _frame = await _dataChannel.getImage(currentFrameIndex, t);
+    Future<bool> _fetchOnce() async {
+      int timeout = 500;
+      if (!isSingleFrame) {
+        // 视频:一帧刷新时长,buffer 10ms 处理渲染
+        timeout = _playAssistant!._playIntervalMillSeconds - 10;
+      }
+      _frame = await _dataChannel.getImage(currentFrameIndex, timeout);
       emitFrameUpdate();
-      _frameLoadRetryCount = 0;
       return true;
+    }
+
+    try {
+      return await _fetchOnce();
     } catch (e) {
       if (e is ReadTimeoutException) {
         try {
+          // 等待一次缓存
           await _waitFrameBufferFluently();
-          return await _waitUpdateFrame(500);
+          return await _fetchOnce();
         } catch (e) {
           _emitErrorOccured(i18nBook.measure.frameLoadTimeout.t);
         }
@@ -294,66 +297,11 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
   }
 
   Future<void> _waitFrameBufferFluently() async {
-    const duration = Duration(milliseconds: 500);
     if (isSingleFrame) {
-      await _waitSingleFrameBuffer(duration);
+      await _bufferWaiter.waitSingleVid();
     } else {
-      await _waitVideoBuffer(duration);
+      await _bufferWaiter.waitBuffer(_frameIndex);
     }
-    _frameLoadRetryCount = 0;
-  }
-
-  Future<void> _waitSingleFrameBuffer(Duration duration) async {
-    final completer = Completer();
-    Timer.periodic(
-      duration,
-      (timer) {
-        if (_dataChannel.isBufferedDone) {
-          timer.cancel();
-          completer.complete();
-          return;
-        }
-
-        _frameLoadRetryCount++;
-        if (_frameLoadRetryCount >= _FRAME_LOAD_RETRY_LIMIT) {
-          completer.completeError(Exception());
-        }
-      },
-    );
-    return completer.future;
-  }
-
-  Future<void> _waitVideoBuffer(Duration duration) async {
-    final singleSize = currentFrame?.imageData.length ?? 2048;
-    int fluntFrameCount = (_playAssistant!._frameRate * 1).toInt(); //1s缓冲
-    final leftCount = totalFramesCount - (currentFrameIndex + 1);
-    if (fluntFrameCount > leftCount) {
-      fluntFrameCount = leftCount;
-    }
-    final bufferedSize = _dataChannel.getBufferSize();
-    int fluentSize = bufferedSize + fluntFrameCount * singleSize;
-    final totalSize = _dataChannel.getFileSize();
-    if (fluentSize > totalSize) {
-      fluentSize = totalSize;
-    }
-    final completer = Completer();
-    Timer.periodic(
-      duration,
-      (timer) {
-        final bufferedSize = _dataChannel.getBufferSize();
-        if (bufferedSize >= fluentSize) {
-          timer.cancel();
-          completer.complete();
-          return;
-        }
-
-        _frameLoadRetryCount++;
-        if (_frameLoadRetryCount >= _FRAME_LOAD_RETRY_LIMIT) {
-          completer.completeError(Exception());
-        }
-      },
-    );
-    return completer.future;
   }
 
   /// [Carotid] ✅用于设置颈动脉单帧展示
@@ -405,7 +353,6 @@ class VidPlayerController extends ChangeNotifier implements IPlayerController {
   @override
   void dispose() {
     _disposed = true;
-    _bufferWaitingTimer?.cancel();
     _stop(needNotify: false);
     eventHandler.dispose();
     _dataChannel.close();

+ 100 - 10
pubspec.lock

@@ -5,6 +5,7 @@ packages:
     dependency: transitive
     description:
       name: archive
+      sha256: "76d919e063a09d19d61f99463a20dba1c1e8d0bc3a5eba91bac2853fac095373"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.3.4"
@@ -12,6 +13,7 @@ packages:
     dependency: transitive
     description:
       name: args
+      sha256: b003c3098049a51720352d219b0bb5f219b60fbfb68e7a4748139a06a5676515
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.3.1"
@@ -19,6 +21,7 @@ packages:
     dependency: transitive
     description:
       name: asn1lib
+      sha256: "17b53aa5f2983420a99534c067b99c03715fff8a0ec7b55627dd3397a765c2cf"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.3.0"
@@ -26,6 +29,7 @@ packages:
     dependency: transitive
     description:
       name: async
+      sha256: "271b8899fc99f9df4f4ed419fa14e2fff392c7b2c162fbb87b222e2e963ddc73"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.9.0"
@@ -33,6 +37,7 @@ packages:
     dependency: "direct main"
     description:
       name: audio_video_progress_bar
+      sha256: "7be62917604e19e3873e12c8025bbc6dbee5876e1fa4cd7772ea1d0eb0e88d99"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.11.0"
@@ -40,6 +45,7 @@ packages:
     dependency: transitive
     description:
       name: boolean_selector
+      sha256: "5bbf32bc9e518d41ec49718e2931cd4527292c9b0c6d2dffcf7fe6b9a8a8cf72"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.0"
@@ -47,6 +53,7 @@ packages:
     dependency: transitive
     description:
       name: characters
+      sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.2.1"
@@ -54,6 +61,7 @@ packages:
     dependency: transitive
     description:
       name: charcode
+      sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.3.1"
@@ -61,6 +69,7 @@ packages:
     dependency: transitive
     description:
       name: clock
+      sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.1"
@@ -68,13 +77,15 @@ packages:
     dependency: transitive
     description:
       name: collection
+      sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.16.0"
+    version: "1.17.0"
   convert:
     dependency: transitive
     description:
       name: convert
+      sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.1.1"
@@ -82,6 +93,7 @@ packages:
     dependency: transitive
     description:
       name: crypto
+      sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.0.2"
@@ -89,6 +101,7 @@ packages:
     dependency: transitive
     description:
       name: csslib
+      sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.17.2"
@@ -96,6 +109,7 @@ packages:
     dependency: transitive
     description:
       name: custom_pop_up_menu
+      sha256: "4fc58444afa5b2007e7df36fd4f2696440ffed821831b8865bb6c67168aa9c8b"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.2.2"
@@ -103,6 +117,7 @@ packages:
     dependency: "direct main"
     description:
       name: dio
+      sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "4.0.6"
@@ -110,6 +125,7 @@ packages:
     dependency: transitive
     description:
       name: encrypt
+      sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "5.0.1"
@@ -117,6 +133,7 @@ packages:
     dependency: transitive
     description:
       name: fake_async
+      sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.3.1"
@@ -124,6 +141,7 @@ packages:
     dependency: transitive
     description:
       name: ffi
+      sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.1"
@@ -131,6 +149,7 @@ packages:
     dependency: transitive
     description:
       name: file
+      sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "6.1.4"
@@ -147,8 +166,8 @@ packages:
     dependency: "direct main"
     description:
       path: "."
-      ref: d0627bc
-      resolved-ref: d0627bc7261aa14bac9e0fbe4ac30608a15e62c4
+      ref: e2016d4
+      resolved-ref: e2016d4cf6514df16d0029cb5805b29719bbb11e
       url: "http://git.ius.plus:88/Project-Wing/fis_lib_i18n.git"
     source: git
     version: "0.0.1"
@@ -156,8 +175,8 @@ packages:
     dependency: "direct main"
     description:
       path: "."
-      ref: c8c5122
-      resolved-ref: c8c5122aad7e44e9cefeb8a105aae37ca72e90cb
+      ref: c241544354
+      resolved-ref: c2415443542df53c78b271e74aff9f51abb8bc80
       url: "http://git.ius.plus:88/Project-Wing/fis_lib_jsonrpc.git"
     source: git
     version: "0.0.1"
@@ -165,8 +184,8 @@ packages:
     dependency: "direct overridden"
     description:
       path: "."
-      ref: "5897e06"
-      resolved-ref: "5897e066290ccac81b0edd89c368e69994584b10"
+      ref: "33166353fb"
+      resolved-ref: "33166353fb2c4a50346d416ca2488150a0d61fba"
       url: "http://git.ius.plus/bakamaka.guan/fis_lib_business_components.git"
     source: git
     version: "0.0.1"
@@ -192,8 +211,8 @@ packages:
     dependency: "direct main"
     description:
       path: "."
-      ref: b9eb4eb
-      resolved-ref: b9eb4eb8b0aebfd35f2163210f8eb46061e8f1a7
+      ref: "168a553"
+      resolved-ref: "168a5535d304bd33646bc08da0bd1cceb36ffb34"
       url: "http://git.ius.plus/Project-Wing/fis_lib_ui.git"
     source: git
     version: "0.0.1"
@@ -224,6 +243,7 @@ packages:
     dependency: "direct main"
     description:
       name: flutter_easyloading
+      sha256: "9b43cf38ef9fddcd0fd1b7821ea9d80b494484e3522383b76e7bf6da67ebf8b9"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.0.3"
@@ -231,6 +251,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_keyboard_visibility
+      sha256: "86b71bbaffa38e885f5c21b1182408b9be6951fd125432cf6652c636254cef2d"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "5.4.0"
@@ -238,6 +259,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_keyboard_visibility_linux
+      sha256: "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.0"
@@ -245,6 +267,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_keyboard_visibility_macos
+      sha256: c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.0"
@@ -252,6 +275,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_keyboard_visibility_platform_interface
+      sha256: e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.0"
@@ -259,6 +283,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_keyboard_visibility_web
+      sha256: d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.0"
@@ -266,6 +291,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_keyboard_visibility_windows
+      sha256: fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.0"
@@ -273,6 +299,7 @@ packages:
     dependency: "direct dev"
     description:
       name: flutter_lints
+      sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.4"
@@ -280,6 +307,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_slidable
+      sha256: ab07e4c793f8d0c9c9e2062d264bd9e61cf50e3ecbbef496d4f4a4f1e705cd38
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.2.0"
@@ -287,6 +315,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_spinkit
+      sha256: "77a2117c0517ff909221f3160b8eb20052ab5216107581168af574ac1f05dff8"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "5.1.0"
@@ -299,6 +328,7 @@ packages:
     dependency: transitive
     description:
       name: flutter_treeview
+      sha256: ce7a66452e02877700890cb674773ea0af28d914192acfb5bf55a50ce35b5819
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.7+1"
@@ -306,6 +336,7 @@ packages:
     dependency: "direct overridden"
     description:
       name: flutter_typeahead
+      sha256: "0ec56e1deac7556f3616f3cd53c9a25bf225dc8b72e9f44b5a7717e42bb467b5"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "4.1.1"
@@ -318,6 +349,7 @@ packages:
     dependency: "direct main"
     description:
       name: get
+      sha256: "34cd0cfbbb76e3aa3f9d582b467a5d326235dd7a57276b6d8b5ead51cf51a256"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "4.6.1"
@@ -325,6 +357,7 @@ packages:
     dependency: transitive
     description:
       name: getwidget
+      sha256: f98a1a96d946e640e4b5e3bd1fd692a50b53f3d1afc8c128f098139a0cf5607e
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.1"
@@ -332,6 +365,7 @@ packages:
     dependency: transitive
     description:
       name: html
+      sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.15.1"
@@ -339,6 +373,7 @@ packages:
     dependency: transitive
     description:
       name: http
+      sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.13.5"
@@ -346,6 +381,7 @@ packages:
     dependency: transitive
     description:
       name: http_parser
+      sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "4.0.2"
@@ -353,6 +389,7 @@ packages:
     dependency: "direct main"
     description:
       name: image
+      sha256: "02bafd3b4f399bfeb10034deba9753d93b55ce41cd0c4d3d8b355626f80e5b32"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.1.3"
@@ -360,6 +397,7 @@ packages:
     dependency: transitive
     description:
       name: intl
+      sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.17.0"
@@ -367,6 +405,7 @@ packages:
     dependency: transitive
     description:
       name: js
+      sha256: a5e201311cb08bf3912ebbe9a2be096e182d703f881136ec1e81a2338a9e120d
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.6.4"
@@ -374,6 +413,7 @@ packages:
     dependency: transitive
     description:
       name: lints
+      sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.1"
@@ -381,6 +421,7 @@ packages:
     dependency: transitive
     description:
       name: matcher
+      sha256: "80c2989398773fa06e2457e9ff08580f24e9858b28462a722241cb53e5613478"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.12.12"
@@ -388,6 +429,7 @@ packages:
     dependency: transitive
     description:
       name: material_color_utilities
+      sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.0"
@@ -395,6 +437,7 @@ packages:
     dependency: transitive
     description:
       name: meta
+      sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.8.0"
@@ -402,6 +445,7 @@ packages:
     dependency: transitive
     description:
       name: path
+      sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.8.2"
@@ -409,6 +453,7 @@ packages:
     dependency: "direct main"
     description:
       name: path_drawing
+      sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.1"
@@ -416,6 +461,7 @@ packages:
     dependency: transitive
     description:
       name: path_parsing
+      sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.1"
@@ -423,6 +469,7 @@ packages:
     dependency: "direct main"
     description:
       name: path_provider
+      sha256: "050e8e85e4b7fecdf2bb3682c1c64c4887a183720c802d323de8a5fd76d372dd"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.11"
@@ -430,6 +477,7 @@ packages:
     dependency: transitive
     description:
       name: path_provider_android
+      sha256: "1dab723dd8feeb80afb39c7be894f09df1457243d930010f6f328fb8c660c5e1"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.21"
@@ -437,6 +485,7 @@ packages:
     dependency: transitive
     description:
       name: path_provider_ios
+      sha256: "03d639406f5343478352433f00d3c4394d52dac8df3d847869c5e2333e0bbce8"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.11"
@@ -444,6 +493,7 @@ packages:
     dependency: transitive
     description:
       name: path_provider_linux
+      sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.7"
@@ -451,6 +501,7 @@ packages:
     dependency: transitive
     description:
       name: path_provider_macos
+      sha256: "2a97e7fbb7ae9dcd0dfc1220a78e9ec3e71da691912e617e8715ff2a13086ae8"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.6"
@@ -458,6 +509,7 @@ packages:
     dependency: transitive
     description:
       name: path_provider_platform_interface
+      sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.5"
@@ -465,6 +517,7 @@ packages:
     dependency: transitive
     description:
       name: path_provider_windows
+      sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.3"
@@ -472,6 +525,7 @@ packages:
     dependency: transitive
     description:
       name: petitparser
+      sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "5.1.0"
@@ -479,6 +533,7 @@ packages:
     dependency: transitive
     description:
       name: platform
+      sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.1.0"
@@ -486,6 +541,7 @@ packages:
     dependency: transitive
     description:
       name: plugin_platform_interface
+      sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.3"
@@ -493,6 +549,7 @@ packages:
     dependency: transitive
     description:
       name: pointer_interceptor
+      sha256: fee6ba42b910637465bc0d367ba27066c6eccfbc3bc0ceb14831915acc600db0
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.9.3+3"
@@ -500,6 +557,7 @@ packages:
     dependency: transitive
     description:
       name: pointycastle
+      sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.6.2"
@@ -507,6 +565,7 @@ packages:
     dependency: transitive
     description:
       name: process
+      sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "4.2.4"
@@ -514,6 +573,7 @@ packages:
     dependency: transitive
     description:
       name: qr
+      sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.0.1"
@@ -521,6 +581,7 @@ packages:
     dependency: "direct main"
     description:
       name: quiver
+      sha256: "93982981971e812c94d4a6fa3a57b89f9ec12b38b6380cd3c1370c3b01e4580e"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.1.0"
@@ -533,6 +594,7 @@ packages:
     dependency: transitive
     description:
       name: source_span
+      sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.9.1"
@@ -540,6 +602,7 @@ packages:
     dependency: transitive
     description:
       name: stack_trace
+      sha256: f8d9f247e2f9f90e32d1495ff32dac7e4ae34ffa7194c5ff8fcc0fd0e52df774
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.10.0"
@@ -547,6 +610,7 @@ packages:
     dependency: transitive
     description:
       name: stream_channel
+      sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.1"
@@ -554,6 +618,7 @@ packages:
     dependency: transitive
     description:
       name: string_scanner
+      sha256: "862015c5db1f3f3c4ea3b94dc2490363a84262994b88902315ed74be1155612f"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.1"
@@ -561,6 +626,7 @@ packages:
     dependency: transitive
     description:
       name: synchronized
+      sha256: "7b530acd9cb7c71b0019a1e7fa22c4105e675557a4400b6a401c71c5e0ade1ac"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.0.0+3"
@@ -568,6 +634,7 @@ packages:
     dependency: transitive
     description:
       name: term_glyph
+      sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.2.1"
@@ -575,6 +642,7 @@ packages:
     dependency: transitive
     description:
       name: test_api
+      sha256: ceeddf59d613e862e77f4b506cfc2945ac9637ce0b4c00f4f4c1ac639f3e9731
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.4.14"
@@ -582,6 +650,7 @@ packages:
     dependency: transitive
     description:
       name: typed_data
+      sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.3.1"
@@ -589,6 +658,7 @@ packages:
     dependency: "direct main"
     description:
       name: universal_html
+      sha256: "5ff50b7c14d201421cf5230ec389a0591c4deb5c817c9d7ccca3b26fe5f31e34"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.8"
@@ -596,6 +666,7 @@ packages:
     dependency: transitive
     description:
       name: universal_io
+      sha256: "79f78ddad839ee3aae3ec7c01eb4575faf0d5c860f8e5223bc9f9c17f7f03cef"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.4"
@@ -603,6 +674,7 @@ packages:
     dependency: "direct main"
     description:
       name: url_launcher
+      sha256: "4f0d5f9bf7efba3da5a7ff03bd33cc898c84bac978c068e1c94483828e709592"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "6.1.5"
@@ -610,6 +682,7 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_android
+      sha256: "2514dc16ac169adf55159268d7bf70317d9f2fc9ef5bb02020bb7ad710c0aeb4"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "6.0.21"
@@ -617,6 +690,7 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_ios
+      sha256: "6ba7dddee26c9fae27c9203c424631109d73c8fa26cfa7bc3e35e751cb87f62e"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "6.0.17"
@@ -624,6 +698,7 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_linux
+      sha256: "360fa359ab06bcb4f7c5cd3123a2a9a4d3364d4575d27c4b33468bd4497dd094"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.0.1"
@@ -631,6 +706,7 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_macos
+      sha256: a9b3ea9043eabfaadfa3fb89de67a11210d85569086d22b3854484beab8b3978
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.0.1"
@@ -638,6 +714,7 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_platform_interface
+      sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.1"
@@ -645,6 +722,7 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_web
+      sha256: "5669882643b96bb6d5786637cac727c6e918a790053b09245fd4513b8a07df2a"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.13"
@@ -652,6 +730,7 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_windows
+      sha256: e3c3b16d3104260c10eea3b0e34272aaa57921f83148b0619f74c2eced9b7ef1
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.0.1"
@@ -659,6 +738,7 @@ packages:
     dependency: transitive
     description:
       name: uuid
+      sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.0.7"
@@ -666,6 +746,7 @@ packages:
     dependency: transitive
     description:
       name: vector_math
+      sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.4"
@@ -682,6 +763,7 @@ packages:
     dependency: transitive
     description:
       name: web_socket_channel
+      sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.2.0"
@@ -689,6 +771,7 @@ packages:
     dependency: transitive
     description:
       name: webview_flutter
+      sha256: "6886b3ceef1541109df5001054aade5ee3c36b5780302e41701c78357233721c"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.8.0"
@@ -696,6 +779,7 @@ packages:
     dependency: transitive
     description:
       name: webview_flutter_android
+      sha256: "8b3b2450e98876c70bfcead876d9390573b34b9418c19e28168b74f6cb252dbd"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.10.4"
@@ -703,6 +787,7 @@ packages:
     dependency: transitive
     description:
       name: webview_flutter_platform_interface
+      sha256: "812165e4e34ca677bdfbfa58c01e33b27fd03ab5fa75b70832d4b7d4ca1fa8cf"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.9.5"
@@ -710,6 +795,7 @@ packages:
     dependency: transitive
     description:
       name: webview_flutter_wkwebview
+      sha256: a5364369c758892aa487cbf59ea41d9edd10f9d9baf06a94e80f1bd1b4c7bbc0
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.9.5"
@@ -717,6 +803,7 @@ packages:
     dependency: "direct main"
     description:
       name: webviewx
+      sha256: d7a7b73e0270c9e48d211dfc4174d19212134de7e8733cdda3d6dea13d7e0177
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.1"
@@ -724,6 +811,7 @@ packages:
     dependency: transitive
     description:
       name: win32
+      sha256: d13ac5deea7327f027b3b97ee19ee210f68256ecf3f1a304bcfb992ee947637c
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.1.1"
@@ -731,6 +819,7 @@ packages:
     dependency: transitive
     description:
       name: xdg_directories
+      sha256: "11541eedefbcaec9de35aa82650b695297ce668662bbd6e3911a7fabdbde589f"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.0+2"
@@ -738,9 +827,10 @@ packages:
     dependency: transitive
     description:
       name: xml
+      sha256: "80d494c09849dc3f899d227a78c30c5b949b985ededf884cb3f3bcd39f4b447a"
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "5.4.1"
 sdks:
-  dart: ">=2.18.2 <3.0.0"
+  dart: ">=2.18.2 <4.0.0"
   flutter: ">=3.0.0"