Compare commits

...
1 Commits
24 changed files with 234 additions and 121 deletions

No files matched your search

+14 -3
View File
@@ -1,3 +1,4 @@
import 'dart:io' show Platform;
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -12,11 +13,14 @@ class SoundService {
static bool get isMuted => _muted;
static set isMuted(bool value) => _muted = value;
static bool get _isDesktop =>
!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS);
static void _ensureInitialized() {
if (_isDesktop) return;
if (_initialized && _player != null) return;
try {
_player = AudioPlayer();
_player!.setPlayerMode(PlayerMode.lowLatency);
_player!.setVolume(1.0);
_initialized = true;
} catch (e) {
@@ -27,13 +31,20 @@ class SoundService {
static Future<void> _playSound(String assetFileName, {bool isErrorOrAlert = false}) async {
if (_muted) return;
try {
if (_isDesktop) {
if (isErrorOrAlert) {
await SystemSound.play(SystemSoundType.alert);
} else {
await SystemSound.play(SystemSoundType.click);
}
return;
}
_ensureInitialized();
if (_player != null) {
// Stop current sound if playing, then play new sound
await _player!.stop();
await _player!.play(AssetSource('sounds/$assetFileName'));
} else {
// Fallback to system sound
if (isErrorOrAlert) {
await SystemSound.play(SystemSoundType.alert);
} else {
+26 -7
View File
@@ -17,6 +17,14 @@ class DownloadService {
final dir = await getDownloadsDirectory();
if (dir != null) {
path = '${dir.path}\\TIS_RMS';
} else {
final userProfile = Platform.environment['USERPROFILE'];
if (userProfile != null && userProfile.isNotEmpty) {
path = '$userProfile\\Downloads\\TIS_RMS';
} else {
final docsDir = await getApplicationDocumentsDirectory();
path = '${docsDir.path}\\TIS_RMS';
}
}
}
@@ -78,7 +86,16 @@ class DownloadService {
if (!await file.exists()) {
throw Exception('File does not exist at $filePath');
}
await OpenFilex.open(filePath);
try {
final res = await OpenFilex.open(filePath);
if (res.type != ResultType.done && Platform.isWindows) {
await Process.run('explorer.exe', [filePath]);
}
} catch (_) {
if (Platform.isWindows) {
await Process.run('explorer.exe', [filePath]);
}
}
}
/// Opens the storage directory (Download/TIS_RMS) or highlights the file in File Explorer.
@@ -105,12 +122,14 @@ class DownloadService {
await fallback.launch();
}
} else if (Platform.isWindows) {
if (filePath != null && await File(filePath).exists()) {
await Process.run('explorer.exe', ['/select,', filePath]);
} else {
final dirPath = await getDownloadDirectoryPath();
await Process.run('explorer.exe', [dirPath]);
}
try {
if (filePath != null && await File(filePath).exists()) {
await Process.run('explorer.exe', ['/select,$filePath']);
} else {
final dirPath = await getDownloadDirectoryPath();
await Process.run('explorer.exe', [dirPath]);
}
} catch (_) {}
}
}
}
@@ -61,16 +61,16 @@ class PrintQueueItem {
factory PrintQueueItem.fromJson(Map<String, dynamic> json) {
return PrintQueueItem(
queueId: json['queue_id'] as int,
documentId: json['document_id'] as int,
fileName: json['file_name'] as String? ?? '',
filePath: json['file_path'] as String? ?? '',
documentType: json['document_type'] as String?,
status: json['status'] as String? ?? 'Pending',
studentName: json['student_name'] as String?,
studentLrn: json['student_lrn'] as String?,
queueId: (json['queue_id'] as num?)?.toInt() ?? 0,
documentId: (json['document_id'] as num?)?.toInt() ?? 0,
fileName: json['file_name']?.toString() ?? '',
filePath: json['file_path']?.toString() ?? '',
documentType: json['document_type']?.toString(),
status: json['status']?.toString() ?? 'Pending',
studentName: json['student_name']?.toString(),
studentLrn: json['student_lrn']?.toString(),
addedAt: json['added_at'] != null
? DateTime.parse(json['added_at'] as String)
? (DateTime.tryParse(json['added_at'].toString()) ?? DateTime.now())
: DateTime.now(),
);
}
@@ -99,15 +99,15 @@ class PrintHistoryItem {
factory PrintHistoryItem.fromJson(Map<String, dynamic> json) {
return PrintHistoryItem(
id: json['id'] as int,
documentId: json['document_id'] as int,
documentName: json['document_name'] as String? ?? 'Document',
studentName: json['student_name'] as String? ?? 'Unknown',
studentLrn: json['student_lrn'] as String?,
fileName: json['file_name'] as String?,
documentType: json['document_type'] as String?,
id: (json['id'] as num?)?.toInt() ?? 0,
documentId: (json['document_id'] as num?)?.toInt() ?? 0,
documentName: json['document_name']?.toString() ?? 'Document',
studentName: json['student_name']?.toString() ?? 'Unknown',
studentLrn: json['student_lrn']?.toString(),
fileName: json['file_name']?.toString(),
documentType: json['document_type']?.toString(),
printedAt: json['printed_at'] != null
? DateTime.parse(json['printed_at'] as String)
? (DateTime.tryParse(json['printed_at'].toString()) ?? DateTime.now())
: DateTime.now(),
);
}
+20 -18
View File
@@ -48,25 +48,27 @@ void main() async {
);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
final view = WidgetsBinding.instance.platformDispatcher.views.first;
final physicalSize = view.physicalSize;
final devicePixelRatio = view.devicePixelRatio;
final width = physicalSize.width / devicePixelRatio;
final height = physicalSize.height / devicePixelRatio;
final shortestSide = width < height ? width : height;
final view = WidgetsBinding.instance.platformDispatcher.views.firstOrNull;
if (view != null) {
final physicalSize = view.physicalSize;
final devicePixelRatio = view.devicePixelRatio;
final width = physicalSize.width / devicePixelRatio;
final height = physicalSize.height / devicePixelRatio;
final shortestSide = width < height ? width : height;
if (shortestSide < 600) {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
} else {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
if (shortestSide < 600) {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
} else {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
}
}
await NotificationService().initialize();
@@ -560,7 +560,8 @@ class _ArchivesScreenState extends ConsumerState<ArchivesScreen>
final dirPath = await DownloadService.getDownloadDirectoryPath();
for (final docId in _selectedDocumentIds) {
final doc = docs.firstWhere((d) => d.id == docId);
final doc = docs.where((d) => d.id == docId).firstOrNull;
if (doc == null) continue;
final url =
'${ApiConstants.baseUrl}/documents/${doc.id}/view?token=$token&download=true';
lastSavedPath = await DownloadService.downloadFile(url: url, fileName: doc.fileName);
@@ -1867,8 +1868,9 @@ class _ArchivesScreenState extends ConsumerState<ArchivesScreen>
final studentName =
'${folder.studentLastName ?? ''}, ${folder.studentFirstName ?? ''}';
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final RenderBox? overlay =
Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?;
if (overlay == null) return;
final value = await showMenu<String>(
context: context,
position: RelativeRect.fromRect(
@@ -173,9 +173,11 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
final notificationsAsync = ref.read(notificationsProvider);
final list = notificationsAsync.value ?? [];
final RenderBox button = context.findRenderObject() as RenderBox;
final RenderBox overlay =
Navigator.of(context).overlay!.context.findRenderObject() as RenderBox;
final RenderBox? button = context.findRenderObject() as RenderBox?;
final RenderBox? overlay =
Navigator.of(context).overlay?.context.findRenderObject() as RenderBox?;
if (button == null || overlay == null) return;
final position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(Offset.zero, ancestor: overlay),
@@ -524,7 +524,8 @@ class _DocumentsScreenState extends ConsumerState<DocumentsScreen>
int successCount = 0;
for (final docId in _selectedDocumentIds) {
final doc = docs.firstWhere((d) => d.id == docId);
final doc = docs.where((d) => d.id == docId).firstOrNull;
if (doc == null) continue;
final url =
'${ApiConstants.baseUrl}/documents/${doc.id}/view?token=$token&download=true';
await DownloadService.downloadFile(url: url, fileName: doc.fileName);
@@ -2428,8 +2429,9 @@ class _DocumentsScreenState extends ConsumerState<DocumentsScreen>
) async {
if (folder.studentId == null) return;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final RenderBox? overlay =
Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?;
if (overlay == null) return;
final value = await showMenu<String>(
context: context,
position: RelativeRect.fromRect(
@@ -226,8 +226,9 @@ class _FileFolderCardState extends State<FileFolderCard> {
void _showContextMenu(BuildContext context, Offset position) {
if (widget.isMultiSelectMode) return;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final RenderBox? overlay =
Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?;
if (overlay == null) return;
showMenu<String>(
context: context,
position: RelativeRect.fromRect(
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/constants/app_colors.dart';
import '../../../../core/services/haptic_service.dart';
import '../../../../domain/repositories/document_repository.dart'
show MissingRequirements;
import '../../../../domain/entities/document_requirement_model.dart';
@@ -1182,7 +1183,7 @@ class _CopyableLrnButtonState extends State<_CopyableLrnButton> {
if (lrn.isEmpty) return;
Clipboard.setData(ClipboardData(text: lrn));
HapticFeedback.lightImpact();
HapticService.light();
setState(() {
_copied = true;
@@ -151,6 +151,15 @@ class _UploadOcrModalState extends ConsumerState<UploadOcrModal> {
// Requirements strip expand/collapse state
bool _requirementsExpanded = true;
int _safeFileSize(File file) {
try {
if (file.existsSync()) {
return file.lengthSync();
}
} catch (_) {}
return 0;
}
@override
void initState() {
super.initState();
@@ -171,7 +180,7 @@ class _UploadOcrModalState extends ConsumerState<UploadOcrModal> {
// Pre-populate entries from drag-and-drop files
if (widget.preloadedFiles != null && widget.preloadedFiles!.isNotEmpty) {
_entries = widget.preloadedFiles!.map((f) {
final bytes = f.lengthSync();
final bytes = _safeFileSize(f);
final kb = bytes / 1024;
final size = kb >= 1024
? '${(kb / 1024).toStringAsFixed(1)} MB'
@@ -326,7 +335,7 @@ class _UploadOcrModalState extends ConsumerState<UploadOcrModal> {
final ext = path.split('.').last.toLowerCase();
if (!allowed.contains(ext)) continue;
final file = File(path);
final len = file.lengthSync();
final len = _safeFileSize(file);
final sizeKb = (len / 1024).toStringAsFixed(1);
final sizeLabel = len > 1048576
? '${(len / 1048576).toStringAsFixed(1)} MB'
@@ -569,6 +578,7 @@ class _UploadOcrModalState extends ConsumerState<UploadOcrModal> {
}
Future<void> _scanDocument() async {
if (!Platform.isAndroid && !Platform.isIOS) return;
try {
final scanner = DocumentScanner(
options: DocumentScannerOptions(
@@ -604,9 +614,10 @@ class _UploadOcrModalState extends ConsumerState<UploadOcrModal> {
if (format == ScannedSaveFormat.pdf && hasPdf) {
final file = File(result.pdf!.uri);
final sizeKb = (file.lengthSync() / 1024).toStringAsFixed(1);
final sizeLabel = file.lengthSync() > 1048576
? '${(file.lengthSync() / 1048576).toStringAsFixed(1)} MB'
final len = _safeFileSize(file);
final sizeKb = (len / 1024).toStringAsFixed(1);
final sizeLabel = len > 1048576
? '${(len / 1048576).toStringAsFixed(1)} MB'
: '$sizeKb KB';
final name = 'Scanned_Doc_$timestamp.pdf';
@@ -630,9 +641,10 @@ class _UploadOcrModalState extends ConsumerState<UploadOcrModal> {
final imagePath = result.images![i];
final file = File(imagePath);
if (!file.existsSync()) continue;
final sizeKb = (file.lengthSync() / 1024).toStringAsFixed(1);
final sizeLabel = file.lengthSync() > 1048576
? '${(file.lengthSync() / 1048576).toStringAsFixed(1)} MB'
final len = _safeFileSize(file);
final sizeKb = (len / 1024).toStringAsFixed(1);
final sizeLabel = len > 1048576
? '${(len / 1048576).toStringAsFixed(1)} MB'
: '$sizeKb KB';
final pageSuffix =
result.images!.length > 1 ? '_page${i + 1}' : '';
@@ -3568,9 +3568,11 @@ class _ReportsScreenState extends ConsumerState<ReportsScreen> {
List<String> allYears,
Set<String> selected,
) {
final RenderBox button = context.findRenderObject() as RenderBox;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final RenderBox? button = context.findRenderObject() as RenderBox?;
final RenderBox? overlay =
Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?;
if (button == null || overlay == null) return;
final RelativeRect position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(Offset.zero, ancestor: overlay),
@@ -3742,9 +3744,11 @@ class _ReportsScreenState extends ConsumerState<ReportsScreen> {
List<_StatusOption> options,
Set<String> selected,
) {
final RenderBox button = context.findRenderObject() as RenderBox;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final RenderBox? button = context.findRenderObject() as RenderBox?;
final RenderBox? overlay =
Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?;
if (button == null || overlay == null) return;
final RelativeRect position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(Offset.zero, ancestor: overlay),
@@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/constants/app_sizes.dart';
import '../../../core/services/haptic_service.dart';
import '../../../domain/entities/student_model.dart';
import '../../../domain/entities/setup_models.dart';
import '../../shared/buttons/primary_button.dart';
@@ -353,8 +354,9 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
Offset globalPosition,
StudentModel student,
) async {
final RenderBox overlay =
Overlay.of(ctx).context.findRenderObject()! as RenderBox;
final RenderBox? overlay =
Overlay.maybeOf(ctx)?.context.findRenderObject() as RenderBox?;
if (overlay == null) return;
final RelativeRect position = RelativeRect.fromRect(
Rect.fromLTWH(globalPosition.dx, globalPosition.dy, 0, 0),
Offset.zero & overlay.size,
@@ -1403,7 +1405,7 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
behavior: HitTestBehavior.opaque,
onTap: widget.userRole != 'teacher'
? () {
HapticFeedback.selectionClick();
HapticService.selection();
_updateSelection(() {
if (!_showMultiSelect) {
_showMultiSelect = true;
@@ -1668,7 +1670,7 @@ class _StudentsScreenState extends ConsumerState<StudentsScreen> {
behavior: HitTestBehavior.opaque,
onTap: widget.userRole != 'teacher'
? () {
HapticFeedback.selectionClick();
HapticService.selection();
_updateSelection(() {
if (!_showMultiSelect) {
_showMultiSelect = true;
@@ -34,13 +34,17 @@ Future<bool?> showConfirmDialog(
final effectiveConfirmColor = confirmColor ?? (isDanger ? AppColors.error : AppColors.primaryGreen);
final effectiveIcon = icon ?? (isDanger ? Icons.warning_amber_rounded : Icons.help_outline_rounded);
if (isDanger) {
SoundService.playWarning();
HapticService.warning();
} else {
SoundService.playConfirm();
HapticService.medium();
}
if (!context.mounted) return Future.value(false);
try {
if (isDanger) {
SoundService.playWarning();
HapticService.warning();
} else {
SoundService.playConfirm();
HapticService.medium();
}
} catch (_) {}
return showDialog<bool>(
context: context,
@@ -22,10 +22,13 @@ class DisconnectedDialog extends StatefulWidget {
BuildContext context, {
VoidCallback? onReconnected,
}) async {
if (!context.mounted) return;
if (_isShowing) return;
_isShowing = true;
SoundService.playError();
HapticService.error();
try {
SoundService.playError();
HapticService.error();
} catch (_) {}
await showDialog<void>(
context: context,
@@ -18,6 +18,7 @@ class DocumentPropertiesDialog extends StatelessWidget {
BuildContext context, {
required DocumentModel document,
}) {
if (!context.mounted) return Future.value();
return showDialog(
context: context,
builder: (ctx) => DocumentPropertiesDialog(document: document),
@@ -19,8 +19,15 @@ void showErrorDialog(
String buttonLabel = 'OK',
VoidCallback? onDismissed,
}) {
SoundService.playError();
HapticService.error();
try {
SoundService.playError();
} catch (_) {}
try {
HapticService.error();
} catch (_) {}
if (!context.mounted) return;
showDialog(
context: context,
barrierDismissible: false,
@@ -111,6 +111,7 @@ Future<bool?> showFileSavePreviewDialog(
File? imageFile,
required Future<void> Function(String resolvedFileName) onSave,
}) {
if (!context.mounted) return Future.value(null);
return showDialog<bool>(
context: context,
barrierDismissible: false,
+15 -11
View File
@@ -27,17 +27,21 @@ void showInfoDialog(
}) {
final btnColor = buttonColor ?? iconColor;
if (iconColor == AppColors.error ||
iconColor == Colors.red ||
iconColor == Colors.orange ||
title.toLowerCase().contains('warning') ||
title.toLowerCase().contains('alert')) {
SoundService.playWarning();
HapticService.warning();
} else {
SoundService.playInfo();
HapticService.info();
}
if (!context.mounted) return;
try {
if (iconColor == AppColors.error ||
iconColor == Colors.red ||
iconColor == Colors.orange ||
title.toLowerCase().contains('warning') ||
title.toLowerCase().contains('alert')) {
SoundService.playWarning();
HapticService.warning();
} else {
SoundService.playInfo();
HapticService.info();
}
} catch (_) {}
showDialog(
context: context,
@@ -8,8 +8,11 @@ import '../../screens/login/login_screen.dart';
/// A reusable function to show the logout confirmation and handle the logout process.
Future<void> showLogoutConfirmationDialog(BuildContext context) async {
SoundService.playWarning();
HapticService.warning();
if (!context.mounted) return;
try {
SoundService.playWarning();
HapticService.warning();
} catch (_) {}
return showDialog(
context: context,
builder: (ctx) => Consumer(
@@ -24,8 +24,15 @@ Future<void> showSuccessDialog(
String? filePath,
String? notes,
}) {
SoundService.playSuccess();
HapticService.success();
try {
SoundService.playSuccess();
} catch (_) {}
try {
HapticService.success();
} catch (_) {}
if (!context.mounted) return Future.value();
return showDialog<void>(
context: context,
barrierDismissible: false,
@@ -19,8 +19,14 @@ void showWarningDialog(
String buttonLabel = 'Got It',
VoidCallback? onDismissed,
}) {
SoundService.playWarning();
HapticService.warning();
try {
SoundService.playWarning();
} catch (_) {}
try {
HapticService.warning();
} catch (_) {}
if (!context.mounted) return;
final isDark = Theme.of(context).brightness == Brightness.dark;
final textPrimary = isDark ? AppColors.darkTextPrimary : AppColors.textPrimary;
@@ -36,6 +36,15 @@ class _DocumentSourcePickerState extends State<DocumentSourcePicker> {
super.dispose();
}
int _safeFileSize(File file) {
try {
if (file.existsSync()) {
return file.lengthSync();
}
} catch (_) {}
return 0;
}
Future<void> _pickFile() async {
try {
final allowed =
@@ -46,9 +55,10 @@ class _DocumentSourcePickerState extends State<DocumentSourcePicker> {
allowedExtensions: allowed,
);
if (result != null) {
if (result != null && result.files.single.path != null) {
final file = File(result.files.single.path!);
final size = (file.lengthSync() / (1024 * 1024)).toStringAsFixed(2);
final bytes = _safeFileSize(file);
final size = (bytes / (1024 * 1024)).toStringAsFixed(2);
// Send the data back to the parent screen
widget.onFileSelected(file, result.files.single.name, '$size MB');
}
@@ -58,6 +68,7 @@ class _DocumentSourcePickerState extends State<DocumentSourcePicker> {
}
Future<void> _takePhoto() async {
if (!Platform.isAndroid && !Platform.isIOS) return;
try {
final scanner = DocumentScanner(
options: DocumentScannerOptions(
@@ -75,7 +86,8 @@ class _DocumentSourcePickerState extends State<DocumentSourcePicker> {
final images = result.images;
if (images != null && images.isNotEmpty) {
final file = File(images.first);
final size = (file.lengthSync() / (1024 * 1024)).toStringAsFixed(2);
final bytes = _safeFileSize(file);
final size = (bytes / (1024 * 1024)).toStringAsFixed(2);
final name =
'Scanned_Doc_${DateTime.now().millisecondsSinceEpoch}.jpg';
@@ -255,18 +267,23 @@ class _DocumentSourcePickerState extends State<DocumentSourcePicker> {
onDragDone: (details) {
_dragResetTimer?.cancel();
if (mounted) setState(() => _isDragOver = false);
if (details.files.isNotEmpty) {
final xfile = details.files.first;
final ext = xfile.path.split('.').last.toLowerCase();
if (allowed.contains(ext)) {
final file = File(xfile.path);
final size =
(file.lengthSync() / (1024 * 1024)).toStringAsFixed(2);
widget.onFileSelected(file, xfile.name, '$size MB');
} else {
widget.onError?.call(
'Unsupported file format: .$ext. Allowed: ${allowed.join(', ')}');
try {
if (details.files.isNotEmpty) {
final xfile = details.files.first;
final ext = xfile.path.split('.').last.toLowerCase();
if (allowed.contains(ext)) {
final file = File(xfile.path);
final bytes = _safeFileSize(file);
final size =
(bytes / (1024 * 1024)).toStringAsFixed(2);
widget.onFileSelected(file, xfile.name, '$size MB');
} else {
widget.onError?.call(
'Unsupported file format: .$ext. Allowed: ${allowed.join(', ')}');
}
}
} catch (e) {
widget.onError?.call('Failed to read dropped file: $e');
}
},
child: content,
@@ -24,9 +24,11 @@ void showNotificationDropdownMenu(BuildContext context, WidgetRef ref) {
final notificationsAsync = ref.read(notificationsProvider);
final list = notificationsAsync.value ?? [];
final RenderBox button = context.findRenderObject() as RenderBox;
final RenderBox overlay =
Navigator.of(context).overlay!.context.findRenderObject() as RenderBox;
final RenderBox? button = context.findRenderObject() as RenderBox?;
final RenderBox? overlay =
Navigator.of(context).overlay?.context.findRenderObject() as RenderBox?;
if (button == null || overlay == null) return;
final position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(
+1 -1
View File
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.13+13
version: 1.0.22+22
environment:
sdk: ^3.11.5