From 4d4d73c6154f00f39106f247212192ae87856381 Mon Sep 17 00:00:00 2001 From: GT610 Date: Mon, 1 Jun 2026 22:56:37 +0800 Subject: [PATCH 1/6] Update fl_lib and gitignore --- .gitignore | 2 +- packages/fl_lib | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 542d6fb..c0d7c58 100644 --- a/.gitignore +++ b/.gitignore @@ -17,11 +17,11 @@ migrate_working_dir/ *.ipr *.iws .idea/ -.trae/ # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line # is commented out by default. +.vscode/ # Flutter/Dart/Pub related diff --git a/packages/fl_lib b/packages/fl_lib index 27b3d16..654b1f1 160000 --- a/packages/fl_lib +++ b/packages/fl_lib @@ -1 +1 @@ -Subproject commit 27b3d16f92a958412a87e55c5362c7628d2f8c5d +Subproject commit 654b1f1eb5f06154c45905351dc092fb23f87082 From a42154fce3d29525c6bf683b7f867fea933d1fcc Mon Sep 17 00:00:00 2001 From: GT610 Date: Mon, 1 Jun 2026 23:03:03 +0800 Subject: [PATCH 2/6] perf: optimize widget rebuilds and reduce redundant computation --- lib/app.dart | 174 +++++++++++---------- lib/pages/download_page/download_page.dart | 90 +++++++---- 2 files changed, 154 insertions(+), 110 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index ca4f7b9..9b557c5 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -537,30 +537,27 @@ class _MainWindowState extends State with WindowListener, Loggable { Provider.of(context, listen: false); final connectedCount = instanceManager.getConnectedInstances().length; final tasks = downloadDataService.tasks; - final activeCount = tasks - .where((task) => task.status == DownloadStatus.active) - .length; - final waitingCount = tasks - .where((task) => task.status == DownloadStatus.waiting) - .length; - final resumableCount = tasks - .where( - (task) => - task.status == DownloadStatus.waiting && - task.taskStatus == 'paused', - ) - .length; - final pausableCount = tasks - .where( - (task) => - (task.status == DownloadStatus.active || - task.status == DownloadStatus.waiting) && - task.taskStatus != 'paused', - ) - .length; - final totalDownloadSpeed = tasks - .where((task) => task.status == DownloadStatus.active) - .fold(0, (sum, task) => sum + task.downloadSpeedBytes); + var activeCount = 0; + var waitingCount = 0; + var resumableCount = 0; + var pausableCount = 0; + var totalDownloadSpeed = 0; + for (final task in tasks) { + if (task.status == DownloadStatus.active) { + activeCount++; + totalDownloadSpeed += task.downloadSpeedBytes; + } else if (task.status == DownloadStatus.waiting) { + waitingCount++; + } + if (task.status == DownloadStatus.waiting && task.taskStatus == 'paused') { + resumableCount++; + } + if ((task.status == DownloadStatus.active || + task.status == DownloadStatus.waiting) && + task.taskStatus != 'paused') { + pausableCount++; + } + } final settings = _settings ?? Provider.of(context, listen: false); if (settings.runMode == AppRunMode.hideTray) { return; @@ -853,17 +850,6 @@ class _MainWindowState extends State with WindowListener, Loggable { const InstancePage(), const SettingsPage(), ]; - final tasks = context.watch().tasks; - final activeTasks = tasks - .where((task) => task.status == DownloadStatus.active) - .toList(); - final waitingTasks = tasks - .where((task) => task.status == DownloadStatus.waiting) - .length; - final totalDownloadSpeed = activeTasks.fold( - 0, - (sum, task) => sum + task.downloadSpeedBytes, - ); final l10n = AppLocalizations.of(context)!; final theme = Theme.of(context); final colorScheme = theme.colorScheme; @@ -928,53 +914,79 @@ class _MainWindowState extends State with WindowListener, Loggable { ), ), // Bottom status bar - Material You style - Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - decoration: BoxDecoration( - color: colorScheme.surfaceContainer, - border: Border( - top: BorderSide(color: colorScheme.surfaceContainerHighest), - ), - boxShadow: [ - BoxShadow( - color: colorScheme.shadow, - offset: const Offset(0, -1), - blurRadius: 3, - spreadRadius: 0, - ), - ], + const _StatusBar(), + ], + ), + ); + } +} + +class _StatusBar extends StatelessWidget { + const _StatusBar(); + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final colorScheme = Theme.of(context).colorScheme; + final counts = context.select((service) { + var active = 0; + var waiting = 0; + var speed = 0; + for (final task in service.tasks) { + if (task.status == DownloadStatus.active) { + active++; + speed += task.downloadSpeedBytes; + } else if (task.status == DownloadStatus.waiting) { + waiting++; + } + } + return (active: active, waiting: waiting, speed: speed); + }); + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: colorScheme.surfaceContainer, + border: Border( + top: BorderSide(color: colorScheme.surfaceContainerHighest), + ), + boxShadow: [ + BoxShadow( + color: colorScheme.shadow, + offset: const Offset(0, -1), + blurRadius: 3, + spreadRadius: 0, + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Chip( + label: Text(l10n.totalSpeed(formatSpeed(counts.speed))), + avatar: const Icon(Icons.speed, size: 16), + backgroundColor: colorScheme.surfaceContainerHighest, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Chip( - label: Text(l10n.totalSpeed(formatSpeed(totalDownloadSpeed))), - avatar: const Icon(Icons.speed, size: 16), - backgroundColor: colorScheme.surfaceContainerHighest, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 6, - ), - ), - Chip( - label: Text(l10n.activeTasks(activeTasks.length.toString())), - avatar: const Icon(Icons.task_alt, size: 16), - backgroundColor: colorScheme.surfaceContainerHighest, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 6, - ), - ), - Chip( - label: Text(l10n.waitingTasks(waitingTasks.toString())), - avatar: const Icon(Icons.pending, size: 16), - backgroundColor: colorScheme.surfaceContainerHighest, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 6, - ), - ), - ], + ), + Chip( + label: Text(l10n.activeTasks(counts.active.toString())), + avatar: const Icon(Icons.task_alt, size: 16), + backgroundColor: colorScheme.surfaceContainerHighest, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, + ), + ), + Chip( + label: Text(l10n.waitingTasks(counts.waiting.toString())), + avatar: const Icon(Icons.pending, size: 16), + backgroundColor: colorScheme.surfaceContainerHighest, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, ), ), ], diff --git a/lib/pages/download_page/download_page.dart b/lib/pages/download_page/download_page.dart index 6313e4d..0fe3826 100644 --- a/lib/pages/download_page/download_page.dart +++ b/lib/pages/download_page/download_page.dart @@ -48,6 +48,16 @@ class DownloadPageState extends State bool _isHandlingPendingProtocolLink = false; bool _isDropTargetHighlighted = false; + List? _cachedFilteredTasks; + List? _cachedTasksRef; + FilterOption? _cachedFilter; + CategoryType? _cachedCategory; + String? _cachedSelectedInstanceId; + String? _cachedSearchQuery; + TaskSortOption? _cachedSortOption; + bool? _cachedSortDescending; + Map? _cachedInstanceNames; + @override void initState() { super.initState(); @@ -108,7 +118,6 @@ class DownloadPageState extends State _loadInstanceNames(instanceManager!); } _schedulePendingProtocolLinkHandling(); - setState(() {}); } void _handleDownloadDataChanges() { @@ -282,11 +291,28 @@ class DownloadPageState extends State .toList(); } - int _countActionableTasks( + Map _countAllActionableTasks( List tasks, - TaskActionType actionType, ) { - return TaskActionDialogs.actionableTasks(tasks, actionType).length; + var pauseable = 0; + var resumable = 0; + var deletable = 0; + for (final task in tasks) { + if (TaskActionDialogs.canPerformAction(task, TaskActionType.pause)) { + pauseable++; + } + if (TaskActionDialogs.canPerformAction(task, TaskActionType.resume)) { + resumable++; + } + if (TaskActionDialogs.canPerformAction(task, TaskActionType.delete)) { + deletable++; + } + } + return { + TaskActionType.pause: pauseable, + TaskActionType.resume: resumable, + TaskActionType.delete: deletable, + }; } void _pruneSelection() { @@ -303,7 +329,20 @@ class DownloadPageState extends State List _filterTasks() { if (downloadDataService == null) return []; - var tasks = List.from(downloadDataService!.tasks); + final tasksRef = downloadDataService!.tasks; + if (_cachedFilteredTasks != null && + identical(_cachedTasksRef, tasksRef) && + _cachedFilter == _selectedFilter && + _cachedCategory == _currentCategoryType && + _cachedSelectedInstanceId == _selectedInstanceId && + _cachedSearchQuery == _searchQuery && + _cachedSortOption == _sortOption && + _cachedSortDescending == _sortDescending && + identical(_cachedInstanceNames, _instanceNames)) { + return _cachedFilteredTasks!; + } + + var tasks = List.from(tasksRef); if (_currentCategoryType == CategoryType.byInstance && _selectedInstanceId != null) { @@ -381,6 +420,15 @@ class DownloadPageState extends State return _sortDescending ? -result : result; }); + _cachedTasksRef = tasksRef; + _cachedFilteredTasks = tasks; + _cachedFilter = _selectedFilter; + _cachedCategory = _currentCategoryType; + _cachedSelectedInstanceId = _selectedInstanceId; + _cachedSearchQuery = _searchQuery; + _cachedSortOption = _sortOption; + _cachedSortDescending = _sortDescending; + _cachedInstanceNames = _instanceNames; return tasks; } @@ -537,30 +585,14 @@ class DownloadPageState extends State _currentCategoryType != CategoryType.all || _selectedFilter != FilterOption.all || _selectedInstanceId != null; - final pauseableVisibleCount = _countActionableTasks( - filteredTasks, - TaskActionType.pause, - ); - final resumableVisibleCount = _countActionableTasks( - filteredTasks, - TaskActionType.resume, - ); - final deletableVisibleCount = _countActionableTasks( - filteredTasks, - TaskActionType.delete, - ); - final pauseableSelectedCount = _countActionableTasks( - selectedTasks, - TaskActionType.pause, - ); - final resumableSelectedCount = _countActionableTasks( - selectedTasks, - TaskActionType.resume, - ); - final deletableSelectedCount = _countActionableTasks( - selectedTasks, - TaskActionType.delete, - ); + final visibleCounts = _countAllActionableTasks(filteredTasks); + final selectedCounts = _countAllActionableTasks(selectedTasks); + final pauseableVisibleCount = visibleCounts[TaskActionType.pause]!; + final resumableVisibleCount = visibleCounts[TaskActionType.resume]!; + final deletableVisibleCount = visibleCounts[TaskActionType.delete]!; + final pauseableSelectedCount = selectedCounts[TaskActionType.pause]!; + final resumableSelectedCount = selectedCounts[TaskActionType.resume]!; + final deletableSelectedCount = selectedCounts[TaskActionType.delete]!; return DropTarget( onDragEntered: (_) { From 3fd966fc9053df0740c6c367d622f7c35dee98cc Mon Sep 17 00:00:00 2001 From: GT610 Date: Mon, 1 Jun 2026 23:08:52 +0800 Subject: [PATCH 3/6] refactor: remove dead code and extract shared settings page helpers --- lib/pages/builtin_instance_settings_page.dart | 98 +++-------------- lib/pages/components/settings_helpers.dart | 103 ++++++++++++++++++ .../download_page/models/download_task.dart | 89 --------------- lib/pages/remote_instance_settings_page.dart | 92 +++------------- lib/services/builtin_instance_service.dart | 2 - lib/services/instance_manager.dart | 13 --- 6 files changed, 132 insertions(+), 265 deletions(-) create mode 100644 lib/pages/components/settings_helpers.dart diff --git a/lib/pages/builtin_instance_settings_page.dart b/lib/pages/builtin_instance_settings_page.dart index 7802e0a..c590dff 100644 --- a/lib/pages/builtin_instance_settings_page.dart +++ b/lib/pages/builtin_instance_settings_page.dart @@ -11,6 +11,7 @@ import '../services/settings_service.dart'; import '../services/tracker_sync_service.dart'; import '../utils/logging.dart'; import 'components/builtin_settings_apply_hint_card.dart'; +import 'components/settings_helpers.dart'; import 'download_page/components/directory_picker.dart'; class BuiltinInstanceSettingsPage extends StatefulWidget { @@ -27,19 +28,18 @@ enum _BuiltinSettingsTab { filesAndMaintenance, } -class _BuiltinSettingsSection { +class _BuiltinSettingsSection implements SettingsSection { const _BuiltinSettingsSection({required this.title, required this.child}); + @override final String title; + @override final Widget child; } class _BuiltinInstanceSettingsPageState extends State - with SingleTickerProviderStateMixin { - static const _kSettingCardSpacing = 10.0; - static const _kSettingTilePadding = EdgeInsets.fromLTRB(16, 6, 16, 6); - + with SingleTickerProviderStateMixin, SettingsPageHelpers { final _logger = taggedLogger('BuiltinInstanceSettingsPage'); bool _hasChanges = false; bool _isSaving = false; @@ -311,62 +311,7 @@ class _BuiltinInstanceSettingsPageState } Widget _buildSettingsTabView(List<_BuiltinSettingsSection> sections) { - return LayoutBuilder( - builder: (context, constraints) { - final width = constraints.maxWidth; - final columns = width >= 1440 - ? 3 - : width >= 900 - ? 2 - : 1; - const gap = 16.0; - final distributedSections = List.generate( - columns, - (_) => <_BuiltinSettingsSection>[], - ); - - for (var index = 0; index < sections.length; index++) { - distributedSections[index % columns].add(sections[index]); - } - - return SingleChildScrollView( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 16), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (var i = 0; i < distributedSections.length; i++) ...[ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: distributedSections[i] - .map( - (section) => Padding( - padding: const EdgeInsets.only(bottom: gap), - child: _buildSectionBlock(section), - ), - ) - .toList(growable: false), - ), - ), - if (i < distributedSections.length - 1) - const SizedBox(width: gap), - ], - ], - ), - ); - }, - ); - } - - Widget _buildSectionBlock(_BuiltinSettingsSection section) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - fl.CenterGreyTitle(section.title), - const SizedBox(height: 4), - section.child, - ], - ); + return buildSettingsTabView(sections); } List<_BuiltinSettingsSection> _buildConnectionAndTransferSections() { @@ -756,30 +701,19 @@ class _BuiltinInstanceSettingsPageState required List children, required ThemeData theme, }) { - return Column( - children: children - .map( - (child) => Padding( - padding: const EdgeInsets.only(bottom: _kSettingCardSpacing), - child: fl.CardX(child: child), - ), - ) - .toList(growable: false), - ); + return buildSettingsCard(children: children, theme: theme); } TextStyle? _settingTitleStyle(ThemeData theme) { - return theme.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500); + return settingTitleStyle(theme); } TextStyle? _settingBodyStyle(ThemeData theme) { - return theme.textTheme.bodyMedium; + return settingBodyStyle(theme); } TextStyle? _settingHintStyle(ThemeData theme) { - return theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ); + return settingHintStyle(theme); } Widget _buildDangerActionSetting({ @@ -801,7 +735,7 @@ class _BuiltinInstanceSettingsPageState style: OutlinedButton.styleFrom(foregroundColor: colorScheme.error), child: Text(actionLabel), ), - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), ); } @@ -853,7 +787,7 @@ class _BuiltinInstanceSettingsPageState ), ], ), - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), ); } @@ -874,7 +808,7 @@ class _BuiltinInstanceSettingsPageState : null, value: value, onChanged: enabled ? onChanged : null, - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), ); } @@ -895,7 +829,7 @@ class _BuiltinInstanceSettingsPageState subtitle: suffix.isNotEmpty ? Text(suffix, style: _settingHintStyle(theme)) : null, - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, trailing: SizedBox( width: 130, child: Row( @@ -957,7 +891,7 @@ class _BuiltinInstanceSettingsPageState return ListTile( title: Text(title, style: _settingTitleStyle(theme)), - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, subtitle: Padding( padding: const EdgeInsets.only(right: 0), child: TextFormField( @@ -987,7 +921,7 @@ class _BuiltinInstanceSettingsPageState return ListTile( title: Text(title, style: _settingTitleStyle(theme)), - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, subtitle: Padding( padding: const EdgeInsets.only(top: 8), child: DirectoryPicker( diff --git a/lib/pages/components/settings_helpers.dart b/lib/pages/components/settings_helpers.dart new file mode 100644 index 0000000..0a2a2c5 --- /dev/null +++ b/lib/pages/components/settings_helpers.dart @@ -0,0 +1,103 @@ +import 'package:flutter/material.dart'; +import 'package:fl_lib/fl_lib.dart' as fl; + +abstract class SettingsSection { + String get title; + Widget get child; +} + +mixin SettingsPageHelpers on State { + static const kSettingCardSpacing = 10.0; + static const kSettingTilePadding = EdgeInsets.fromLTRB(16, 6, 16, 6); + + TextStyle? settingTitleStyle(ThemeData theme) { + return theme.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500); + } + + TextStyle? settingBodyStyle(ThemeData theme) { + return theme.textTheme.bodyMedium; + } + + TextStyle? settingHintStyle(ThemeData theme) { + return theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ); + } + + Widget buildSettingsCard({ + required List children, + required ThemeData theme, + }) { + return Column( + children: children + .map( + (child) => Padding( + padding: const EdgeInsets.only(bottom: kSettingCardSpacing), + child: fl.CardX(child: child), + ), + ) + .toList(growable: false), + ); + } + + Widget buildSettingsSectionBlock(S section) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + fl.CenterGreyTitle(section.title), + const SizedBox(height: 4), + section.child, + ], + ); + } + + Widget buildSettingsTabView( + List sections, + ) { + return LayoutBuilder( + builder: (context, constraints) { + final width = constraints.maxWidth; + final columns = width >= 1440 + ? 3 + : width >= 900 + ? 2 + : 1; + const gap = 16.0; + final distributedSections = List.generate( + columns, + (_) => [], + ); + + for (var index = 0; index < sections.length; index++) { + distributedSections[index % columns].add(sections[index]); + } + + return SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (var i = 0; i < distributedSections.length; i++) ...[ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: distributedSections[i] + .map( + (section) => Padding( + padding: const EdgeInsets.only(bottom: gap), + child: buildSettingsSectionBlock(section), + ), + ) + .toList(growable: false), + ), + ), + if (i < distributedSections.length - 1) + const SizedBox(width: gap), + ], + ], + ), + ); + }, + ); + } +} diff --git a/lib/pages/download_page/models/download_task.dart b/lib/pages/download_page/models/download_task.dart index 22a787b..5923c2c 100644 --- a/lib/pages/download_page/models/download_task.dart +++ b/lib/pages/download_page/models/download_task.dart @@ -64,93 +64,4 @@ class DownloadTask { this.numPieces, this.isSeeder = false, }); - - factory DownloadTask.fromJson(Map json) { - return DownloadTask( - id: json['id'] ?? '', - name: json['name'] ?? '', - status: _parseDownloadStatus(json['status']), - taskStatus: json['taskStatus'], - progress: (json['progress'] ?? 0.0).toDouble(), - downloadSpeed: json['downloadSpeed'] ?? '0 B/s', - uploadSpeed: json['uploadSpeed'] ?? '0 B/s', - size: json['size'] ?? '0 B', - completedSize: json['completedSize'] ?? '0 B', - isLocal: json['isLocal'] ?? false, - instanceId: json['instanceId'] ?? '', - connections: json['connections'], - numSeeders: json['numSeeders'], - dir: json['dir'], - totalLengthBytes: json['totalLengthBytes'] ?? 0, - completedLengthBytes: json['completedLengthBytes'] ?? 0, - uploadLengthBytes: json['uploadLengthBytes'] ?? 0, - downloadSpeedBytes: json['downloadSpeedBytes'] ?? 0, - uploadSpeedBytes: json['uploadSpeedBytes'] ?? 0, - files: json['files'] != null - ? List>.from(json['files']) - : null, - bittorrentInfo: json['bittorrentInfo'], - trackers: json['trackers'] != null - ? List.from(json['trackers']) - : null, - uris: json['uris'] != null ? List.from(json['uris']) : null, - errorMessage: json['errorMessage'], - startTime: json['startTime'] != null - ? DateTime.tryParse(json['startTime']) - : null, - bitfield: json['bitfield'], - infoHash: json['infoHash'], - pieceLength: json['pieceLength'], - numPieces: json['numPieces'], - isSeeder: json['isSeeder'] ?? false, - ); - } - - static DownloadStatus _parseDownloadStatus(dynamic value) { - if (value == null) return DownloadStatus.stopped; - if (value is DownloadStatus) return value; - if (value is String) { - try { - return DownloadStatus.values.byName(value); - } catch (_) { - return DownloadStatus.stopped; - } - } - return DownloadStatus.stopped; - } - - Map toJson() { - return { - 'id': id, - 'name': name, - 'status': status.name, - 'taskStatus': taskStatus, - 'progress': progress, - 'downloadSpeed': downloadSpeed, - 'uploadSpeed': uploadSpeed, - 'size': size, - 'completedSize': completedSize, - 'isLocal': isLocal, - 'instanceId': instanceId, - 'connections': connections, - 'numSeeders': numSeeders, - 'dir': dir, - 'totalLengthBytes': totalLengthBytes, - 'completedLengthBytes': completedLengthBytes, - 'uploadLengthBytes': uploadLengthBytes, - 'downloadSpeedBytes': downloadSpeedBytes, - 'uploadSpeedBytes': uploadSpeedBytes, - 'files': files, - 'bittorrentInfo': bittorrentInfo, - 'trackers': trackers, - 'uris': uris, - 'errorMessage': errorMessage, - 'startTime': startTime?.toIso8601String(), - 'bitfield': bitfield, - 'infoHash': infoHash, - 'pieceLength': pieceLength, - 'numPieces': numPieces, - 'isSeeder': isSeeder, - }; - } } diff --git a/lib/pages/remote_instance_settings_page.dart b/lib/pages/remote_instance_settings_page.dart index 79aff8a..001f91a 100644 --- a/lib/pages/remote_instance_settings_page.dart +++ b/lib/pages/remote_instance_settings_page.dart @@ -5,6 +5,7 @@ import '../generated/l10n/l10n.dart'; import '../models/aria2_instance.dart'; import '../services/aria2_rpc_client.dart'; import '../utils/format_utils.dart'; +import 'components/settings_helpers.dart'; class RemoteInstanceSettingsPage extends StatefulWidget { final Aria2Instance instance; @@ -22,18 +23,17 @@ enum _RemoteSettingsTab { filesAndMaintenance, } -class _RemoteSettingsSection { +class _RemoteSettingsSection implements SettingsSection { const _RemoteSettingsSection({required this.title, required this.child}); + @override final String title; + @override final Widget child; } class _RemoteInstanceSettingsPageState extends State - with SingleTickerProviderStateMixin { - static const _kSettingCardSpacing = 10.0; - static const _kSettingTilePadding = EdgeInsets.fromLTRB(16, 6, 16, 6); - + with SingleTickerProviderStateMixin, SettingsPageHelpers { bool _isLoading = true; bool _isSaving = false; bool _hasLoaded = false; @@ -528,62 +528,7 @@ class _RemoteInstanceSettingsPageState extends State } Widget _buildSettingsTabView(List<_RemoteSettingsSection> sections) { - return LayoutBuilder( - builder: (context, constraints) { - final width = constraints.maxWidth; - final columns = width >= 1440 - ? 3 - : width >= 900 - ? 2 - : 1; - const gap = 16.0; - final distributedSections = List.generate( - columns, - (_) => <_RemoteSettingsSection>[], - ); - - for (var index = 0; index < sections.length; index++) { - distributedSections[index % columns].add(sections[index]); - } - - return SingleChildScrollView( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 16), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (var i = 0; i < distributedSections.length; i++) ...[ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: distributedSections[i] - .map( - (section) => Padding( - padding: const EdgeInsets.only(bottom: gap), - child: _buildSectionBlock(section), - ), - ) - .toList(growable: false), - ), - ), - if (i < distributedSections.length - 1) - const SizedBox(width: gap), - ], - ], - ), - ); - }, - ); - } - - Widget _buildSectionBlock(_RemoteSettingsSection section) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - fl.CenterGreyTitle(section.title), - const SizedBox(height: 4), - section.child, - ], - ); + return buildSettingsTabView(sections); } List<_RemoteSettingsSection> _buildConnectionAndTransferSections( @@ -809,30 +754,19 @@ class _RemoteInstanceSettingsPageState extends State required ThemeData theme, required List children, }) { - return Column( - children: children - .map( - (child) => Padding( - padding: const EdgeInsets.only(bottom: _kSettingCardSpacing), - child: fl.CardX(child: child), - ), - ) - .toList(growable: false), - ); + return buildSettingsCard(children: children, theme: theme); } TextStyle? _settingTitleStyle(ThemeData theme) { - return theme.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500); + return settingTitleStyle(theme); } TextStyle? _settingBodyStyle(ThemeData theme) { - return theme.textTheme.bodyMedium; + return settingBodyStyle(theme); } TextStyle? _settingHintStyle(ThemeData theme) { - return theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ); + return settingHintStyle(theme); } Widget _buildSwitchSetting( @@ -845,7 +779,7 @@ class _RemoteInstanceSettingsPageState extends State title: Text(title, style: _settingTitleStyle(theme)), value: value, onChanged: _isSaving ? null : onChanged, - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), ); } @@ -866,7 +800,7 @@ class _RemoteInstanceSettingsPageState extends State subtitle: suffix.isNotEmpty ? Text(suffix, style: _settingHintStyle(theme)) : null, - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, trailing: SizedBox( width: 130, child: Row( @@ -928,7 +862,7 @@ class _RemoteInstanceSettingsPageState extends State return ListTile( title: Text(title, style: _settingTitleStyle(theme)), - contentPadding: _kSettingTilePadding, + contentPadding: SettingsPageHelpers.kSettingTilePadding, subtitle: Padding( padding: const EdgeInsets.only(top: 4), child: TextFormField( diff --git a/lib/services/builtin_instance_service.dart b/lib/services/builtin_instance_service.dart index 5e40df8..c6315ae 100644 --- a/lib/services/builtin_instance_service.dart +++ b/lib/services/builtin_instance_service.dart @@ -475,8 +475,6 @@ class BuiltinInstanceService with Loggable { return _aria2Process != null; } - int? get pid => _aria2Process?.pid; - Future _shutdownThroughRpcIfPossible() async { final client = Aria2RpcClient(getBuiltinInstanceConfig()); try { diff --git a/lib/services/instance_manager.dart b/lib/services/instance_manager.dart index 2d4b0e7..3d871c1 100644 --- a/lib/services/instance_manager.dart +++ b/lib/services/instance_manager.dart @@ -16,21 +16,8 @@ class InstanceManager extends ChangeNotifier with Loggable { final BuiltinInstanceService _builtinInstanceService = BuiltinInstanceService(); - InstanceManager() {} - List get instances => _instances; - /// Get the first connected instance - Aria2Instance? getConnectedInstance() { - try { - return _instances.firstWhere( - (instance) => instance.status == ConnectionStatus.connected, - ); - } catch (e) { - return null; - } - } - /// Get all connected instances List getConnectedInstances() { return _instances From 15998c90a25c95432904b805078cf4d85d3b321d Mon Sep 17 00:00:00 2001 From: GT610 Date: Mon, 1 Jun 2026 23:14:22 +0800 Subject: [PATCH 4/6] perf: narrow Settings provider scope, cache connected instances, pre-compute sort keys --- lib/app.dart | 12 ++--- lib/pages/download_page/download_page.dart | 56 +++++++++++----------- lib/services/instance_manager.dart | 20 ++++++-- 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index 9b557c5..7eb0f08 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -53,33 +53,33 @@ class _ThemeProviderState extends State<_ThemeProvider> { @override Widget build(BuildContext context) { - final settings = Provider.of(context); + final display = context.select((s) => (locale: s.locale, hideTitleBar: s.hideTitleBar, primaryColor: s.primaryColor, themeMode: s.themeMode)); return MaterialApp( title: kAppName, - locale: settings.locale, + locale: display.locale, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, builder: (context, child) => fl.VirtualWindowFrame( title: kAppName, - showCaption: settings.hideTitleBar, + showCaption: display.hideTitleBar, child: ClipRect(child: child ?? const SizedBox.shrink()), ), theme: ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( - seedColor: settings.primaryColor, + seedColor: display.primaryColor, brightness: Brightness.light, ), ).fixWindowsFont, darkTheme: ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( - seedColor: settings.primaryColor, + seedColor: display.primaryColor, brightness: Brightness.dark, ), ).fixWindowsFont, - themeMode: settings.themeMode, + themeMode: display.themeMode, home: MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => InstanceManager()), diff --git a/lib/pages/download_page/download_page.dart b/lib/pages/download_page/download_page.dart index 0fe3826..ad65082 100644 --- a/lib/pages/download_page/download_page.dart +++ b/lib/pages/download_page/download_page.dart @@ -391,34 +391,36 @@ class DownloadPageState extends State }).toList(); } - tasks.sort((left, right) { - int result; - switch (_sortOption) { - case TaskSortOption.name: - result = left.name.toLowerCase().compareTo(right.name.toLowerCase()); - break; - case TaskSortOption.progress: - result = left.progress.compareTo(right.progress); - break; - case TaskSortOption.size: - result = left.totalLengthBytes.compareTo(right.totalLengthBytes); - break; - case TaskSortOption.speed: - result = left.downloadSpeedBytes.compareTo(right.downloadSpeedBytes); - break; - case TaskSortOption.instance: - final leftName = _instanceNames[left.instanceId] ?? left.instanceId; - final rightName = - _instanceNames[right.instanceId] ?? right.instanceId; - result = leftName.toLowerCase().compareTo(rightName.toLowerCase()); - break; + if (_sortOption == TaskSortOption.name || + _sortOption == TaskSortOption.instance) { + final sortKeys = {}; + for (final task in tasks) { + final key = '${task.instanceId}::${task.id}'; + sortKeys[key] = _sortOption == TaskSortOption.name + ? task.name.toLowerCase() + : (_instanceNames[task.instanceId] ?? task.instanceId).toLowerCase(); } - - if (result == 0) { - result = left.id.compareTo(right.id); - } - return _sortDescending ? -result : result; - }); + tasks.sort((left, right) { + final leftKey = sortKeys['${left.instanceId}::${left.id}'] ?? ''; + final rightKey = sortKeys['${right.instanceId}::${right.id}'] ?? ''; + final result = leftKey.compareTo(rightKey); + if (result != 0) return _sortDescending ? -result : result; + final idResult = left.id.compareTo(right.id); + return _sortDescending ? -idResult : idResult; + }); + } else { + tasks.sort((left, right) { + final result = switch (_sortOption) { + TaskSortOption.progress => left.progress.compareTo(right.progress), + TaskSortOption.size => left.totalLengthBytes.compareTo(right.totalLengthBytes), + TaskSortOption.speed => left.downloadSpeedBytes.compareTo(right.downloadSpeedBytes), + _ => 0, + }; + if (result != 0) return _sortDescending ? -result : result; + final idResult = left.id.compareTo(right.id); + return _sortDescending ? -idResult : idResult; + }); + } _cachedTasksRef = tasksRef; _cachedFilteredTasks = tasks; diff --git a/lib/services/instance_manager.dart b/lib/services/instance_manager.dart index 3d871c1..df14f1c 100644 --- a/lib/services/instance_manager.dart +++ b/lib/services/instance_manager.dart @@ -15,14 +15,19 @@ class InstanceManager extends ChangeNotifier with Loggable { final String _fileName = 'aria2_instances.json'; final BuiltinInstanceService _builtinInstanceService = BuiltinInstanceService(); + List? _cachedConnectedInstances; List get instances => _instances; + void _invalidateConnectedCache() { + _cachedConnectedInstances = null; + } + /// Get all connected instances List getConnectedInstances() { - return _instances + return _cachedConnectedInstances ??= _instances .where((instance) => instance.status == ConnectionStatus.connected) - .toList(); + .toList(growable: false); } /// Get the built-in instance if it exists @@ -76,6 +81,7 @@ class InstanceManager extends ChangeNotifier with Loggable { status: ConnectionStatus.disconnected, ), ); + _invalidateConnectedCache(); await _saveInstances(); this.i('Added missing built-in instance record'); } @@ -88,6 +94,7 @@ class InstanceManager extends ChangeNotifier with Loggable { _instances[builtinIndex] = _instances[builtinIndex].copyWith( protocol: 'ws', ); + _invalidateConnectedCache(); await _saveInstances(); this.i('Migrated built-in instance protocol from http to ws'); } @@ -138,6 +145,7 @@ class InstanceManager extends ChangeNotifier with Loggable { instance.copyWith(status: ConnectionStatus.disconnected), ) .toList(); + _invalidateConnectedCache(); this.i('Loaded ${_instances.length} instance records'); } else { @@ -163,6 +171,7 @@ class InstanceManager extends ChangeNotifier with Loggable { status: ConnectionStatus.disconnected, ), ]; + _invalidateConnectedCache(); await _saveInstances(); this.i('Created default built-in instance record'); } @@ -214,10 +223,9 @@ class InstanceManager extends ChangeNotifier with Loggable { ); _instances.add(newInstance); + _invalidateConnectedCache(); await _saveInstances(); this.i('Added instance ${newInstance.name}'); - - // Notify listeners notifyListeners(); } catch (e, stackTrace) { this.e('Failed to add instance', error: e, stackTrace: stackTrace); @@ -236,6 +244,7 @@ class InstanceManager extends ChangeNotifier with Loggable { final index = _instances.indexWhere((i) => i.id == updatedInstance.id); if (index != -1) { _instances[index] = updatedInstance; + _invalidateConnectedCache(); await _saveInstances(); this.i('Updated instance ${updatedInstance.name}'); @@ -265,6 +274,7 @@ class InstanceManager extends ChangeNotifier with Loggable { } _instances.removeWhere((i) => i.id == instanceId); + _invalidateConnectedCache(); await _saveInstances(); notifyListeners(); } @@ -444,6 +454,7 @@ class InstanceManager extends ChangeNotifier with Loggable { version: version, errorMessage: errorMessage, ); + _invalidateConnectedCache(); // Schedule notifyListeners to run after the current frame is built SchedulerBinding.instance.addPostFrameCallback((_) { notifyListeners(); @@ -481,6 +492,7 @@ class InstanceManager extends ChangeNotifier with Loggable { ); _instances[builtinIndex] = refreshed; + _invalidateConnectedCache(); await _saveInstances(); notifyListeners(); } From 8214cf647271a6bf8f34cb756f1ad3e6c66864e1 Mon Sep 17 00:00:00 2001 From: GT610 Date: Mon, 1 Jun 2026 23:17:56 +0800 Subject: [PATCH 5/6] chore: code quality - unmodifiable tasks, pin intl, remove stale code, localize hardcoded strings --- lib/models/settings.dart | 2 +- lib/services/aria2_rpc_client.dart | 7 +- lib/services/download_data_service.dart | 3 +- lib/services/instance_manager.dart | 8 +- lib/utils/default_download_directory.dart | 4 - pubspec.lock | 386 +++++++++++----------- pubspec.yaml | 2 +- 7 files changed, 204 insertions(+), 208 deletions(-) diff --git a/lib/models/settings.dart b/lib/models/settings.dart index 6065ca9..d1f8bb3 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -95,7 +95,7 @@ class Settings extends ChangeNotifier with Loggable { } Future _defaultDownloadDirectory() async { - return getDefaultDownloadDirectory(); + return getDefaultDownloadDirectorySync(); } void _assignDefaultSettings({required String defaultDownloadDir}) { diff --git a/lib/services/aria2_rpc_client.dart b/lib/services/aria2_rpc_client.dart index cf7f7bc..e093936 100644 --- a/lib/services/aria2_rpc_client.dart +++ b/lib/services/aria2_rpc_client.dart @@ -8,12 +8,12 @@ import '../utils/logging.dart'; // Custom exception classes class ConnectionFailedException implements Exception { @override - String toString() => '连接实例失败'; + String toString() => 'Failed to connect to instance'; } class UnauthorizedException implements Exception { @override - String toString() => '认证未通过'; + String toString() => 'Authentication failed'; } /// Aria2 RPC client service @@ -382,9 +382,6 @@ class Aria2RpcClient with Loggable { return response['result'] as String; // Returns the GID of the resumed task } - // tellStatus method removed as main loop already gets complete task data - // through tellActive, tellWaiting and tellStopped calls in getTasksMulticall - /// Remove a download task Future removeTask(String gid) async { final response = await callRpc('aria2.remove', [gid]); diff --git a/lib/services/download_data_service.dart b/lib/services/download_data_service.dart index dc78743..db35dd1 100644 --- a/lib/services/download_data_service.dart +++ b/lib/services/download_data_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:collection'; import 'package:flutter/foundation.dart'; import '../pages/download_page/models/download_task.dart'; import '../pages/download_page/enums.dart'; @@ -42,7 +43,7 @@ class DownloadDataService extends ChangeNotifier with Loggable { final Map _clientCache = {}; List Function()? _connectedInstancesProvider; - List get tasks => _tasks; + List get tasks => UnmodifiableListView(_tasks); bool get isRefreshing => _isRefreshing; String? get lastError => _lastError; diff --git a/lib/services/instance_manager.dart b/lib/services/instance_manager.dart index df14f1c..6346fc6 100644 --- a/lib/services/instance_manager.dart +++ b/lib/services/instance_manager.dart @@ -72,7 +72,7 @@ class InstanceManager extends ChangeNotifier with Loggable { 0, Aria2Instance( id: 'builtin', - name: '内建实例', + name: 'Built-in', type: InstanceType.builtin, protocol: 'ws', host: '127.0.0.1', @@ -207,7 +207,7 @@ class InstanceManager extends ChangeNotifier with Loggable { try { // Only allow adding remote instances if (instance.type != InstanceType.remote) { - throw Exception('只能添加远程实例'); + throw Exception('Only remote instances can be added'); } // Ensure ID is unique @@ -238,7 +238,7 @@ class InstanceManager extends ChangeNotifier with Loggable { try { // Can't update built-in instance if (updatedInstance.id == 'builtin') { - throw Exception('不能编辑内建实例'); + throw Exception('Cannot edit the built-in instance'); } final index = _instances.indexWhere((i) => i.id == updatedInstance.id); @@ -265,7 +265,7 @@ class InstanceManager extends ChangeNotifier with Loggable { Future deleteInstance(String instanceId) async { // Can't delete built-in instance if (instanceId == 'builtin') { - throw Exception('不能删除内建实例'); + throw Exception('Cannot delete the built-in instance'); } // Can't delete the last instance diff --git a/lib/utils/default_download_directory.dart b/lib/utils/default_download_directory.dart index e0076ac..ab139c7 100644 --- a/lib/utils/default_download_directory.dart +++ b/lib/utils/default_download_directory.dart @@ -21,7 +21,3 @@ String getDefaultDownloadDirectorySync() { return p.normalize(p.join(getAppDataDirectory().path, 'downloads')); } - -Future getDefaultDownloadDirectory() async { - return getDefaultDownloadDirectorySync(); -} diff --git a/pubspec.lock b/pubspec.lock index 798e770..030683e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: _fe_analyzer_shared sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "93.0.0" analyzer: @@ -14,7 +14,7 @@ packages: description: name: analyzer sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "10.0.1" archive: @@ -22,7 +22,7 @@ packages: description: name: archive sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.9" args: @@ -30,7 +30,7 @@ packages: description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.7.0" async: @@ -38,7 +38,7 @@ packages: description: name: async sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.13.1" boolean_selector: @@ -46,7 +46,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" camera: @@ -54,7 +54,7 @@ packages: description: name: camera sha256: "4142a19a38e388d3bab444227636610ba88982e36dff4552d5191a86f65dc437" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.11.4" camera_android_camerax: @@ -62,7 +62,7 @@ packages: description: name: camera_android_camerax sha256: "8516fe308bc341a5067fb1a48edff0ddfa57c0d3cdcc9dbe7ceca3ba119e2577" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.6.30" camera_avfoundation: @@ -70,7 +70,7 @@ packages: description: name: camera_avfoundation sha256: "11b4aee2f5e5e038982e152b4a342c749b414aa27857899d20f4323e94cb5f0b" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.9.23+2" camera_platform_interface: @@ -78,7 +78,7 @@ packages: description: name: camera_platform_interface sha256: "7ac852d77699acee79f0d438b793feee26721841e50973576419ff5c6d95e9b7" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.13.0" camera_web: @@ -86,7 +86,7 @@ packages: description: name: camera_web sha256: "57f49a635c8bf249d07fb95eb693d7e4dda6796dedb3777f9127fb54847beba7" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.3.5+3" characters: @@ -94,7 +94,7 @@ packages: description: name: characters sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.1" charset: @@ -102,7 +102,7 @@ packages: description: name: charset sha256: "27802032a581e01ac565904ece8c8962564b1070690794f0072f6865958ce8b9" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.1" choice: @@ -110,7 +110,7 @@ packages: description: name: choice sha256: "52d07065e8056beba5b26cff7786134cbfa24927b1f5bf60a05d50058597b2d9" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.2" cli_config: @@ -118,7 +118,7 @@ packages: description: name: cli_config sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" clock: @@ -126,7 +126,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.2" code_assets: @@ -134,7 +134,7 @@ packages: description: name: code_assets sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" collection: @@ -142,7 +142,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.19.1" computer: @@ -159,7 +159,7 @@ packages: description: name: convert sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.2" coverage: @@ -167,7 +167,7 @@ packages: description: name: coverage sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.15.0" cross_file: @@ -175,7 +175,7 @@ packages: description: name: cross_file sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.3.5+2" crypto: @@ -183,7 +183,7 @@ packages: description: name: crypto sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.7" dbus: @@ -191,7 +191,7 @@ packages: description: name: dbus sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.12" desktop_drop: @@ -199,7 +199,7 @@ packages: description: name: desktop_drop sha256: "927511f590ce01ee90d0d80f79bc71b9c919d8522d01e495e89a00c6f4a4fb5b" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.6.1" dio: @@ -207,7 +207,7 @@ packages: description: name: dio sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.9.2" dio_web_adapter: @@ -215,7 +215,7 @@ packages: description: name: dio_web_adapter sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" equatable: @@ -223,7 +223,7 @@ packages: description: name: equatable sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.8" extended_image: @@ -231,7 +231,7 @@ packages: description: name: extended_image sha256: f6cbb1d798f51262ed1a3d93b4f1f2aa0d76128df39af18ecb77fa740f88b2e0 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "10.0.1" extended_image_library: @@ -239,7 +239,7 @@ packages: description: name: extended_image_library sha256: "1f9a24d3a00c2633891c6a7b5cab2807999eb2d5b597e5133b63f49d113811fe" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.0.1" fake_async: @@ -247,7 +247,7 @@ packages: description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.3" ffi: @@ -255,7 +255,7 @@ packages: description: name: ffi sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" file: @@ -263,7 +263,7 @@ packages: description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.1" file_picker: @@ -271,7 +271,7 @@ packages: description: name: file_picker sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "10.3.10" fixnum: @@ -279,7 +279,7 @@ packages: description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" fl_lib: @@ -299,7 +299,7 @@ packages: description: name: flutter_highlight sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.0" flutter_lints: @@ -307,7 +307,7 @@ packages: description: name: flutter_lints sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.0.0" flutter_localizations: @@ -320,7 +320,7 @@ packages: description: name: flutter_markdown_plus sha256: "039177906850278e8fb1cd364115ee0a46281135932fa8ecea8455522166d2de" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.7" flutter_markdown_plus_latex: @@ -328,7 +328,7 @@ packages: description: name: flutter_markdown_plus_latex sha256: "2e7698b291f0657ca445efab730bb25a8c5851037e882cb7bf47d16a5c218de7" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.5" flutter_math_fork: @@ -336,7 +336,7 @@ packages: description: name: flutter_math_fork sha256: "6d5f2f1aa57ae539ffb0a04bb39d2da67af74601d685a161aff7ce5bda5fa407" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.4" flutter_plugin_android_lifecycle: @@ -344,7 +344,7 @@ packages: description: name: flutter_plugin_android_lifecycle sha256: "38d1c268de9097ff59cf0e844ac38759fc78f76836d37edad06fa21e182055a0" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.34" flutter_riverpod: @@ -352,7 +352,7 @@ packages: description: name: flutter_riverpod sha256: "4e166be88e1dbbaa34a280bdb744aeae73b7ef25fdf8db7a3bb776760a3648e2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.3.1" flutter_secure_storage: @@ -360,7 +360,7 @@ packages: description: name: flutter_secure_storage sha256: da922f2aab2d733db7e011a6bcc4a825b844892d4edd6df83ff156b09a9b2e40 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "10.0.0" flutter_secure_storage_darwin: @@ -368,7 +368,7 @@ packages: description: name: flutter_secure_storage_darwin sha256: "8878c25136a79def1668c75985e8e193d9d7d095453ec28730da0315dc69aee3" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" flutter_secure_storage_linux: @@ -376,7 +376,7 @@ packages: description: name: flutter_secure_storage_linux sha256: "2b5c76dce569ab752d55a1cee6a2242bcc11fdba927078fb88c503f150767cda" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.0" flutter_secure_storage_platform_interface: @@ -384,7 +384,7 @@ packages: description: name: flutter_secure_storage_platform_interface sha256: "8ceea1223bee3c6ac1a22dabd8feefc550e4729b3675de4b5900f55afcb435d6" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_secure_storage_web: @@ -392,7 +392,7 @@ packages: description: name: flutter_secure_storage_web sha256: "6a1137df62b84b54261dca582c1c09ea72f4f9a4b2fcee21b025964132d5d0c3" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.0" flutter_secure_storage_windows: @@ -400,7 +400,7 @@ packages: description: name: flutter_secure_storage_windows sha256: "3b7c8e068875dfd46719ff57c90d8c459c87f2302ed6b00ff006b3c9fcad1613" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.1.0" flutter_svg: @@ -408,7 +408,7 @@ packages: description: name: flutter_svg sha256: "1ded017b39c8e15c8948ea855070a5ff8ff8b3d5e83f3446e02d6bb12add7ad9" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.4" flutter_test: @@ -426,7 +426,7 @@ packages: description: name: freezed_annotation sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.0" frontend_server_client: @@ -434,7 +434,7 @@ packages: description: name: frontend_server_client sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.0" glob: @@ -442,7 +442,7 @@ packages: description: name: glob sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.3" highlight: @@ -450,7 +450,7 @@ packages: description: name: highlight sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.0" hive_ce: @@ -458,7 +458,7 @@ packages: description: name: hive_ce sha256: "8e9980e68643afb1e765d3af32b47996552a64e190d03faf622cea07c1294418" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.19.3" hive_ce_flutter: @@ -466,7 +466,7 @@ packages: description: name: hive_ce_flutter sha256: "2677e95a333ff15af43ccd06af7eb7abbf1a4f154ea071997f3de4346cae913a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.4" hooks: @@ -474,7 +474,7 @@ packages: description: name: hooks sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.2" http: @@ -482,7 +482,7 @@ packages: description: name: http sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.6.0" http_client_helper: @@ -490,7 +490,7 @@ packages: description: name: http_client_helper sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.0" http_multi_server: @@ -498,7 +498,7 @@ packages: description: name: http_multi_server sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.2" http_parser: @@ -506,7 +506,7 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.1.2" icloud_storage: @@ -514,23 +514,24 @@ packages: description: name: icloud_storage sha256: fa91d9c3b4264651f01a4f5b99cffa354ffe455623b13ecf92be86d88b1e26ea - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" icons_plus: dependency: transitive description: - name: icons_plus - sha256: "8e2f601b8605d45dd55b106a0da084a1809125077a49574ca22e8bcd5b6e86f0" - url: "https://pub.flutter-io.cn" - source: hosted + path: "." + ref: main + resolved-ref: "42670d29c8c1cffb7203dd110e0009ba98709667" + url: "https://github.com/lollipopkit/icons_plus" + source: git version: "5.0.0" image: dependency: transitive description: name: image sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.8.0" intl: @@ -538,7 +539,7 @@ packages: description: name: intl sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.20.2" io: @@ -546,7 +547,7 @@ packages: description: name: io sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.5" isolate_channel: @@ -554,7 +555,7 @@ packages: description: name: isolate_channel sha256: a9d3d620695bc984244dafae00b95e4319d6974b2d77f4b9e1eb4f2efe099094 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.6.1" isolate_contactor: @@ -562,7 +563,7 @@ packages: description: name: isolate_contactor sha256: "6ba8434ceb58238a1389d6365111a3efe7baa1c68a66f4db6d63d351cf6c3a0f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.1.0" isolate_manager: @@ -570,7 +571,7 @@ packages: description: name: isolate_manager sha256: "22ed0c25f80ec3b5f21e3a55d060f4650afff33f27c2dff34c0f9409d5759ae5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.1.5+1" jni: @@ -578,7 +579,7 @@ packages: description: name: jni sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" jni_flutter: @@ -586,7 +587,7 @@ packages: description: name: jni_flutter sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.1" js: @@ -594,7 +595,7 @@ packages: description: name: js sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.2" json_annotation: @@ -602,7 +603,7 @@ packages: description: name: json_annotation sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.11.0" launch_at_startup: @@ -610,7 +611,7 @@ packages: description: name: launch_at_startup sha256: "7db33398b76ec0ed9e27f9f4640553e239977437564046625e215be89c91f084" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.5.1" leak_tracker: @@ -618,7 +619,7 @@ packages: description: name: leak_tracker sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "11.0.2" leak_tracker_flutter_testing: @@ -626,7 +627,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.10" leak_tracker_testing: @@ -634,7 +635,7 @@ packages: description: name: leak_tracker_testing sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.2" lints: @@ -642,7 +643,7 @@ packages: description: name: lints sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.1.0" local_auth: @@ -650,7 +651,7 @@ packages: description: name: local_auth sha256: ae6f382f638108c6becd134318d7c3f0a93875383a54010f61d7c97ac05d5137 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.1" local_auth_android: @@ -658,7 +659,7 @@ packages: description: name: local_auth_android sha256: b201c006fa769c23386f89aa6837ec0eb8179fcfb212eadcf87b422b3f9a6a78 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.8" local_auth_darwin: @@ -666,7 +667,7 @@ packages: description: name: local_auth_darwin sha256: a8c3d4e17454111f7fd31ff72a31222359f6059f7fe956c2dcfe0f88f49826d4 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.3" local_auth_platform_interface: @@ -674,7 +675,7 @@ packages: description: name: local_auth_platform_interface sha256: f98b8e388588583d3f781f6806e4f4c9f9e189d898d27f0c249b93a1973dd122 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" local_auth_windows: @@ -682,7 +683,7 @@ packages: description: name: local_auth_windows sha256: be12c5b8ba5e64896983123655c5f67d2484ecfcc95e367952ad6e3bff94cb16 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.1" local_notifier: @@ -690,7 +691,7 @@ packages: description: name: local_notifier sha256: f6cfc933c6fbc961f4e52b5c880f68e41b2d3cd29aad557cc654fd211093a025 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.6" locale_names: @@ -698,7 +699,7 @@ packages: description: name: locale_names sha256: "7a89ca54072f4f13d0f5df5a9ba69337554bf2fd057d1dd2a238898f3f159374" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" logging: @@ -706,7 +707,7 @@ packages: description: name: logging sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.0" markdown: @@ -714,7 +715,7 @@ packages: description: name: markdown sha256: ee85086ad7698b42522c6ad42fe195f1b9898e4d974a1af4576c1a3a176cada9 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.3.1" matcher: @@ -722,7 +723,7 @@ packages: description: name: matcher sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.12.19" material_color_utilities: @@ -730,7 +731,7 @@ packages: description: name: material_color_utilities sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.13.0" menu_base: @@ -738,7 +739,7 @@ packages: description: name: menu_base sha256: "820368014a171bd1241030278e6c2617354f492f5c703d7b7d4570a6b8b84405" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.1" meta: @@ -746,7 +747,7 @@ packages: description: name: meta sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.17.0" mime: @@ -754,7 +755,7 @@ packages: description: name: mime sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.0" multi_split_view: @@ -762,7 +763,7 @@ packages: description: name: multi_split_view sha256: "06f5126a65d3010ce0a9d5c003e793041fe99377b23e3534bb05059f79a580e9" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.6.1" native_toolchain_c: @@ -770,7 +771,7 @@ packages: description: name: native_toolchain_c sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.17.6" nested: @@ -778,7 +779,7 @@ packages: description: name: nested sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" node_preamble: @@ -786,7 +787,7 @@ packages: description: name: node_preamble sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.2" objective_c: @@ -794,7 +795,7 @@ packages: description: name: objective_c sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "9.3.0" package_config: @@ -802,7 +803,7 @@ packages: description: name: package_config sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" package_info_plus: @@ -810,7 +811,7 @@ packages: description: name: package_info_plus sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "9.0.1" package_info_plus_platform_interface: @@ -818,7 +819,7 @@ packages: description: name: package_info_plus_platform_interface sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.1" path: @@ -826,7 +827,7 @@ packages: description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.9.1" path_parsing: @@ -834,7 +835,7 @@ packages: description: name: path_parsing sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" path_provider: @@ -842,7 +843,7 @@ packages: description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.5" path_provider_android: @@ -850,7 +851,7 @@ packages: description: name: path_provider_android sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.1" path_provider_foundation: @@ -858,7 +859,7 @@ packages: description: name: path_provider_foundation sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.6.0" path_provider_linux: @@ -866,7 +867,7 @@ packages: description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.1" path_provider_platform_interface: @@ -874,7 +875,7 @@ packages: description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" path_provider_windows: @@ -882,7 +883,7 @@ packages: description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.0" petitparser: @@ -890,7 +891,7 @@ packages: description: name: petitparser sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.2" platform: @@ -898,7 +899,7 @@ packages: description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.6" plugin_platform_interface: @@ -906,7 +907,7 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.8" pointycastle: @@ -914,7 +915,7 @@ packages: description: name: pointycastle sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.0" pool: @@ -922,7 +923,7 @@ packages: description: name: pool sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.5.2" port_forwarder: @@ -930,7 +931,7 @@ packages: description: name: port_forwarder sha256: "80f762d6bba492aa6f458a4cb00ecdbac02007bfa6435907397212370962ca7d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" posix: @@ -938,7 +939,7 @@ packages: description: name: posix sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.5.0" pretty_qr_code: @@ -946,7 +947,7 @@ packages: description: name: pretty_qr_code sha256: "474f8a4512113fba06f14a6ec9bbf42353b4e651d7a520e3096f2a9b6bbe7a8a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.6.0" provider: @@ -954,7 +955,7 @@ packages: description: name: provider sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.1.5+1" pub_semver: @@ -962,7 +963,7 @@ packages: description: name: pub_semver sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" qr: @@ -970,7 +971,7 @@ packages: description: name: qr sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.2" qr_code_dart_decoder: @@ -978,7 +979,7 @@ packages: description: name: qr_code_dart_decoder sha256: "4044f13a071da6102f7e9bc44a6b1ce577604d7846bcbeb1be412a137b825017" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.2" qr_code_dart_scan: @@ -986,23 +987,24 @@ packages: description: name: qr_code_dart_scan sha256: "81443d940f8f27baaa4b9aeaa8d3d2155ad2c0b9842a9bacb03dab85c111e2f6" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.11.5" re_editor: dependency: transitive description: - name: re_editor - sha256: dd4e6ca7350a8fa0cda4e425b82a0c3c010f0f6b3f618c74223e05b8129ab629 - url: "https://pub.flutter-io.cn" - source: hosted + path: "." + ref: main + resolved-ref: d5d70020da74082d182301a49e2bb87d30f0ab66 + url: "https://github.com/lollipopkit/re-editor" + source: git version: "0.8.0" re_highlight: dependency: transitive description: name: re_highlight sha256: "6c4ac3f76f939fb7ca9df013df98526634e17d8f7460e028bd23a035870024f2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.0.3" responsive_framework: @@ -1010,7 +1012,7 @@ packages: description: name: responsive_framework sha256: a8e1c13d4ba980c60cbf6fa1e9907cd60662bf2585184d7c96ca46c43de91552 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.5.1" riverpod: @@ -1018,7 +1020,7 @@ packages: description: name: riverpod sha256: "8c22216be8ad3ef2b44af3a329693558c98eca7b8bd4ef495c92db0bba279f83" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.1" riverpod_annotation: @@ -1026,7 +1028,7 @@ packages: description: name: riverpod_annotation sha256: "16471a1260b94e939394d78f1c63a9350936ac4a68c9fbdab40be47268c0b04f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.2" screen_retriever: @@ -1034,7 +1036,7 @@ packages: description: name: screen_retriever sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" screen_retriever_linux: @@ -1042,7 +1044,7 @@ packages: description: name: screen_retriever_linux sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" screen_retriever_macos: @@ -1050,7 +1052,7 @@ packages: description: name: screen_retriever_macos sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" screen_retriever_platform_interface: @@ -1058,7 +1060,7 @@ packages: description: name: screen_retriever_platform_interface sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" screen_retriever_windows: @@ -1066,7 +1068,7 @@ packages: description: name: screen_retriever_windows sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.0" share_plus: @@ -1074,7 +1076,7 @@ packages: description: name: share_plus sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "12.0.2" share_plus_platform_interface: @@ -1082,7 +1084,7 @@ packages: description: name: share_plus_platform_interface sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.1.0" shared_preferences: @@ -1090,7 +1092,7 @@ packages: description: name: shared_preferences sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.5.5" shared_preferences_android: @@ -1098,7 +1100,7 @@ packages: description: name: shared_preferences_android sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.23" shared_preferences_foundation: @@ -1106,7 +1108,7 @@ packages: description: name: shared_preferences_foundation sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.5.6" shared_preferences_linux: @@ -1114,7 +1116,7 @@ packages: description: name: shared_preferences_linux sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.1" shared_preferences_platform_interface: @@ -1122,7 +1124,7 @@ packages: description: name: shared_preferences_platform_interface sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.2" shared_preferences_web: @@ -1130,7 +1132,7 @@ packages: description: name: shared_preferences_web sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.3" shared_preferences_windows: @@ -1138,7 +1140,7 @@ packages: description: name: shared_preferences_windows sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.1" shelf: @@ -1146,7 +1148,7 @@ packages: description: name: shelf sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.2" shelf_packages_handler: @@ -1154,7 +1156,7 @@ packages: description: name: shelf_packages_handler sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.2" shelf_static: @@ -1162,7 +1164,7 @@ packages: description: name: shelf_static sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.3" shelf_web_socket: @@ -1170,7 +1172,7 @@ packages: description: name: shelf_web_socket sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.0" shortid: @@ -1178,7 +1180,7 @@ packages: description: name: shortid sha256: d0b40e3dbb50497dad107e19c54ca7de0d1a274eb9b4404991e443dadb9ebedb - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.2" sky_engine: @@ -1191,7 +1193,7 @@ packages: description: name: source_map_stack_trace sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" source_maps: @@ -1199,7 +1201,7 @@ packages: description: name: source_maps sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.10.13" source_span: @@ -1207,7 +1209,7 @@ packages: description: name: source_span sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.10.2" stack_trace: @@ -1215,7 +1217,7 @@ packages: description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.12.1" state_notifier: @@ -1223,7 +1225,7 @@ packages: description: name: state_notifier sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" stream_channel: @@ -1231,7 +1233,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.4" stream_transform: @@ -1239,7 +1241,7 @@ packages: description: name: stream_transform sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.1" string_scanner: @@ -1247,7 +1249,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.1" term_glyph: @@ -1255,7 +1257,7 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.2" test: @@ -1263,7 +1265,7 @@ packages: description: name: test sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.30.0" test_api: @@ -1271,7 +1273,7 @@ packages: description: name: test_api sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.10" test_core: @@ -1279,7 +1281,7 @@ packages: description: name: test_core sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.6.16" tray_manager: @@ -1287,7 +1289,7 @@ packages: description: name: tray_manager sha256: c5fd83b0ae4d80be6eaedfad87aaefab8787b333b8ebd064b0e442a81006035b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.5.2" tuple: @@ -1295,7 +1297,7 @@ packages: description: name: tuple sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.2" typed_data: @@ -1303,7 +1305,7 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.0" universal_platform: @@ -1311,7 +1313,7 @@ packages: description: name: universal_platform sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" url_launcher: @@ -1319,7 +1321,7 @@ packages: description: name: url_launcher sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.3.2" url_launcher_android: @@ -1327,7 +1329,7 @@ packages: description: name: url_launcher_android sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.3.29" url_launcher_ios: @@ -1335,7 +1337,7 @@ packages: description: name: url_launcher_ios sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.4.1" url_launcher_linux: @@ -1343,7 +1345,7 @@ packages: description: name: url_launcher_linux sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.2" url_launcher_macos: @@ -1351,7 +1353,7 @@ packages: description: name: url_launcher_macos sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.5" url_launcher_platform_interface: @@ -1359,7 +1361,7 @@ packages: description: name: url_launcher_platform_interface sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.2" url_launcher_web: @@ -1367,7 +1369,7 @@ packages: description: name: url_launcher_web sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.2" url_launcher_windows: @@ -1375,7 +1377,7 @@ packages: description: name: url_launcher_windows sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.5" uuid: @@ -1383,7 +1385,7 @@ packages: description: name: uuid sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.5.3" vector_graphics: @@ -1391,7 +1393,7 @@ packages: description: name: vector_graphics sha256: "81da85e9ca8885ade47f9685b953cb098970d11be4821ac765580a6607ea4373" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.21" vector_graphics_codec: @@ -1399,7 +1401,7 @@ packages: description: name: vector_graphics_codec sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.13" vector_graphics_compiler: @@ -1407,7 +1409,7 @@ packages: description: name: vector_graphics_compiler sha256: "5a88dd14c0954a5398af544651c7fb51b457a2a556949bfb25369b210ef73a74" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.0" vector_math: @@ -1415,7 +1417,7 @@ packages: description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" vm_service: @@ -1423,7 +1425,7 @@ packages: description: name: vm_service sha256: "046d3928e16fa4dc46e8350415661755ab759d9fc97fc21b5ab295f71e4f0499" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "15.1.0" watcher: @@ -1431,7 +1433,7 @@ packages: description: name: watcher sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.1" web: @@ -1439,7 +1441,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" web_socket: @@ -1447,7 +1449,7 @@ packages: description: name: web_socket sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.1" web_socket_channel: @@ -1455,7 +1457,7 @@ packages: description: name: web_socket_channel sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.3" webdav_client_plus: @@ -1463,7 +1465,7 @@ packages: description: name: webdav_client_plus sha256: "0f992fe05a46674a800d9fd8fdc5c54952ff739da155c558776a62ca0c2bed3a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.2" webkit_inspection_protocol: @@ -1471,7 +1473,7 @@ packages: description: name: webkit_inspection_protocol sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.1" win32: @@ -1479,7 +1481,7 @@ packages: description: name: win32 sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.15.0" win32_registry: @@ -1487,7 +1489,7 @@ packages: description: name: win32_registry sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.0" window_manager: @@ -1495,7 +1497,7 @@ packages: description: name: window_manager sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.5.1" xdg_directories: @@ -1503,7 +1505,7 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" xml: @@ -1511,7 +1513,7 @@ packages: description: name: xml sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.6.1" yaml: @@ -1519,7 +1521,7 @@ packages: description: name: yaml sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.3" zxing_lib: @@ -1527,7 +1529,7 @@ packages: description: name: zxing_lib sha256: f9170470b6bc947d21a6783486f88ef48aad66fc1380c8acd02b118418ec0ce0 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.4" sdks: diff --git a/pubspec.yaml b/pubspec.yaml index 7121676..024a5ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,7 +51,7 @@ dependencies: window_manager: ^0.5.1 # Internationalization - intl: any + intl: ^0.20.2 # Local packages fl_lib: From b5ff5d2b820a1a320755bb83ddbe9304c79c2f53 Mon Sep 17 00:00:00 2001 From: GT610 Date: Mon, 1 Jun 2026 23:44:35 +0800 Subject: [PATCH 6/6] fix: address review findings - cache invalidation, column clamping, dedup task summary --- .gitignore | 4 +- lib/app.dart | 89 +++++++++++----------- lib/models/settings.dart | 4 +- lib/pages/components/settings_helpers.dart | 3 +- lib/pages/download_page/download_page.dart | 7 +- lib/services/download_data_service.dart | 4 + lib/services/instance_manager.dart | 2 +- 7 files changed, 60 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index c0d7c58..4e05836 100644 --- a/.gitignore +++ b/.gitignore @@ -19,8 +19,8 @@ migrate_working_dir/ .idea/ # The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. +# VS Code. This project intentionally ignores it to keep personal editor +# settings out of version control. .vscode/ diff --git a/lib/app.dart b/lib/app.dart index 7eb0f08..2f48877 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -537,27 +537,7 @@ class _MainWindowState extends State with WindowListener, Loggable { Provider.of(context, listen: false); final connectedCount = instanceManager.getConnectedInstances().length; final tasks = downloadDataService.tasks; - var activeCount = 0; - var waitingCount = 0; - var resumableCount = 0; - var pausableCount = 0; - var totalDownloadSpeed = 0; - for (final task in tasks) { - if (task.status == DownloadStatus.active) { - activeCount++; - totalDownloadSpeed += task.downloadSpeedBytes; - } else if (task.status == DownloadStatus.waiting) { - waitingCount++; - } - if (task.status == DownloadStatus.waiting && task.taskStatus == 'paused') { - resumableCount++; - } - if ((task.status == DownloadStatus.active || - task.status == DownloadStatus.waiting) && - task.taskStatus != 'paused') { - pausableCount++; - } - } + final summary = _computeTaskSummary(tasks); final settings = _settings ?? Provider.of(context, listen: false); if (settings.runMode == AppRunMode.hideTray) { return; @@ -573,9 +553,9 @@ class _MainWindowState extends State with WindowListener, Loggable { ? l10n.notConnected : '${l10n.connected}: $connectedCount', if (settings.showTraySpeed) - l10n.totalSpeed(formatSpeed(totalDownloadSpeed)), - l10n.activeTasks(activeCount.toString()), - l10n.waitingTasks(waitingCount.toString()), + l10n.totalSpeed(formatSpeed(summary.speed)), + l10n.activeTasks(summary.active.toString()), + l10n.waitingTasks(summary.waiting.toString()), ]; final trayService = SystemTrayService(); unawaited(trayService.updateTooltip(tooltipLines.join('\n'))); @@ -588,11 +568,11 @@ class _MainWindowState extends State with WindowListener, Loggable { toggleWindowLabel: isWindowVisible ? l10n.hideMainWindow : l10n.showMainWindow, - resumeAllLabel: '${l10n.resumeTasks} ($resumableCount)', - pauseAllLabel: '${l10n.pauseTasks} ($pausableCount)', + resumeAllLabel: '${l10n.resumeTasks} (${summary.resumable})', + pauseAllLabel: '${l10n.pauseTasks} (${summary.pausable})', quitLabel: l10n.quitApp, - resumeAllDisabled: resumableCount == 0, - pauseAllDisabled: pausableCount == 0, + resumeAllDisabled: summary.resumable == 0, + pauseAllDisabled: summary.pausable == 0, ), ); } @@ -921,6 +901,38 @@ class _MainWindowState extends State with WindowListener, Loggable { } } +({int active, int waiting, int resumable, int pausable, int speed}) + _computeTaskSummary(List tasks) { + var active = 0; + var waiting = 0; + var resumable = 0; + var pausable = 0; + var speed = 0; + for (final task in tasks) { + if (task.status == DownloadStatus.active) { + active++; + speed += task.downloadSpeedBytes; + } else if (task.status == DownloadStatus.waiting) { + waiting++; + } + if (task.status == DownloadStatus.waiting && task.taskStatus == 'paused') { + resumable++; + } + if ((task.status == DownloadStatus.active || + task.status == DownloadStatus.waiting) && + task.taskStatus != 'paused') { + pausable++; + } + } + return ( + active: active, + waiting: waiting, + resumable: resumable, + pausable: pausable, + speed: speed, + ); +} + class _StatusBar extends StatelessWidget { const _StatusBar(); @@ -928,19 +940,8 @@ class _StatusBar extends StatelessWidget { Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final colorScheme = Theme.of(context).colorScheme; - final counts = context.select((service) { - var active = 0; - var waiting = 0; - var speed = 0; - for (final task in service.tasks) { - if (task.status == DownloadStatus.active) { - active++; - speed += task.downloadSpeedBytes; - } else if (task.status == DownloadStatus.waiting) { - waiting++; - } - } - return (active: active, waiting: waiting, speed: speed); + final summary = context.select((service) { + return _computeTaskSummary(service.tasks); }); return Container( @@ -963,7 +964,7 @@ class _StatusBar extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Chip( - label: Text(l10n.totalSpeed(formatSpeed(counts.speed))), + label: Text(l10n.totalSpeed(formatSpeed(summary.speed))), avatar: const Icon(Icons.speed, size: 16), backgroundColor: colorScheme.surfaceContainerHighest, padding: const EdgeInsets.symmetric( @@ -972,7 +973,7 @@ class _StatusBar extends StatelessWidget { ), ), Chip( - label: Text(l10n.activeTasks(counts.active.toString())), + label: Text(l10n.activeTasks(summary.active.toString())), avatar: const Icon(Icons.task_alt, size: 16), backgroundColor: colorScheme.surfaceContainerHighest, padding: const EdgeInsets.symmetric( @@ -981,7 +982,7 @@ class _StatusBar extends StatelessWidget { ), ), Chip( - label: Text(l10n.waitingTasks(counts.waiting.toString())), + label: Text(l10n.waitingTasks(summary.waiting.toString())), avatar: const Icon(Icons.pending, size: 16), backgroundColor: colorScheme.surfaceContainerHighest, padding: const EdgeInsets.symmetric( diff --git a/lib/models/settings.dart b/lib/models/settings.dart index d1f8bb3..3dc1278 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -94,8 +94,8 @@ class Settings extends ChangeNotifier with Loggable { return getAppDataDirectory(); } - Future _defaultDownloadDirectory() async { - return getDefaultDownloadDirectorySync(); + Future _defaultDownloadDirectory() { + return Future.value(getDefaultDownloadDirectorySync()); } void _assignDefaultSettings({required String defaultDownloadDir}) { diff --git a/lib/pages/components/settings_helpers.dart b/lib/pages/components/settings_helpers.dart index 0a2a2c5..7d2238b 100644 --- a/lib/pages/components/settings_helpers.dart +++ b/lib/pages/components/settings_helpers.dart @@ -57,11 +57,12 @@ mixin SettingsPageHelpers on State { return LayoutBuilder( builder: (context, constraints) { final width = constraints.maxWidth; - final columns = width >= 1440 + final maxColumns = width >= 1440 ? 3 : width >= 900 ? 2 : 1; + final columns = sections.isEmpty ? 1 : maxColumns.clamp(1, sections.length); const gap = 16.0; final distributedSections = List.generate( columns, diff --git a/lib/pages/download_page/download_page.dart b/lib/pages/download_page/download_page.dart index ad65082..65b5d4f 100644 --- a/lib/pages/download_page/download_page.dart +++ b/lib/pages/download_page/download_page.dart @@ -49,7 +49,7 @@ class DownloadPageState extends State bool _isDropTargetHighlighted = false; List? _cachedFilteredTasks; - List? _cachedTasksRef; + int? _cachedTasksVersion; FilterOption? _cachedFilter; CategoryType? _cachedCategory; String? _cachedSelectedInstanceId; @@ -330,8 +330,9 @@ class DownloadPageState extends State if (downloadDataService == null) return []; final tasksRef = downloadDataService!.tasks; + final tasksVersion = downloadDataService!.tasksVersion; if (_cachedFilteredTasks != null && - identical(_cachedTasksRef, tasksRef) && + _cachedTasksVersion == tasksVersion && _cachedFilter == _selectedFilter && _cachedCategory == _currentCategoryType && _cachedSelectedInstanceId == _selectedInstanceId && @@ -422,7 +423,7 @@ class DownloadPageState extends State }); } - _cachedTasksRef = tasksRef; + _cachedTasksVersion = tasksVersion; _cachedFilteredTasks = tasks; _cachedFilter = _selectedFilter; _cachedCategory = _currentCategoryType; diff --git a/lib/services/download_data_service.dart b/lib/services/download_data_service.dart index db35dd1..eb32b92 100644 --- a/lib/services/download_data_service.dart +++ b/lib/services/download_data_service.dart @@ -37,6 +37,7 @@ class DownloadDataService extends ChangeNotifier with Loggable { bool _isRefreshing = false; String? _lastError; final List _pendingNotifications = []; + int _tasksVersion = 0; final int _refreshInterval = 1000; @@ -44,6 +45,7 @@ class DownloadDataService extends ChangeNotifier with Loggable { List Function()? _connectedInstancesProvider; List get tasks => UnmodifiableListView(_tasks); + int get tasksVersion => _tasksVersion; bool get isRefreshing => _isRefreshing; String? get lastError => _lastError; @@ -84,6 +86,7 @@ class DownloadDataService extends ChangeNotifier with Loggable { final hadTasks = _tasks.isNotEmpty; final hadError = _lastError != null; _tasks = []; + _tasksVersion++; _lastError = null; if (hadTasks || hadError) { notifyListeners(); @@ -107,6 +110,7 @@ class DownloadDataService extends ChangeNotifier with Loggable { newTasks, ); _tasks = newTasks; + _tasksVersion++; _saveSessionsForTerminalTransitions( connectedInstances, terminalTransitionInstanceIds, diff --git a/lib/services/instance_manager.dart b/lib/services/instance_manager.dart index 6346fc6..51717fd 100644 --- a/lib/services/instance_manager.dart +++ b/lib/services/instance_manager.dart @@ -162,7 +162,7 @@ class InstanceManager extends ChangeNotifier with Loggable { _instances = [ Aria2Instance( id: 'builtin', - name: '内建实例', + name: 'Built-in', type: InstanceType.builtin, protocol: 'ws', host: '127.0.0.1',