1 files added
9 files modified
changed files
ios/Podfile.lock patch | view | blame | history
lib/models/message.dart patch | view | blame | history
lib/screens/chat_screen.dart patch | view | blame | history
lib/services/mqtt_service.dart patch | view | blame | history
lib/widgets/message_bubble.dart patch | view | blame | history
lib/widgets/pdf_viewer.dart patch | view | blame | history
linux/flutter/generated_plugins.cmake patch | view | blame | history
pubspec.lock patch | view | blame | history
pubspec.yaml patch | view | blame | history
windows/flutter/generated_plugins.cmake patch | view | blame | history
ios/Podfile.lock
....@@ -53,6 +53,9 @@
5353 - in_app_purchase_storekit (0.0.1):
5454 - Flutter
5555 - FlutterMacOS
56
+ - pdfrx (0.0.6):
57
+ - Flutter
58
+ - FlutterMacOS
5659 - permission_handler_apple (9.3.0):
5760 - Flutter
5861 - push (0.0.1):
....@@ -85,6 +88,7 @@
8588 - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
8689 - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
8790 - in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
91
+ - pdfrx (from `.symlinks/plugins/pdfrx/darwin`)
8892 - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
8993 - push (from `.symlinks/plugins/push/darwin`)
9094 - record_ios (from `.symlinks/plugins/record_ios/ios`)
....@@ -121,6 +125,8 @@
121125 :path: ".symlinks/plugins/image_picker_ios/ios"
122126 in_app_purchase_storekit:
123127 :path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
128
+ pdfrx:
129
+ :path: ".symlinks/plugins/pdfrx/darwin"
124130 permission_handler_apple:
125131 :path: ".symlinks/plugins/permission_handler_apple/ios"
126132 push:
....@@ -149,6 +155,7 @@
149155 flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
150156 image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
151157 in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8
158
+ pdfrx: 310e84d01e06fd2af26e16507a0e48c27e99195c
152159 permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
153160 push: 91373ae39c5341c6de6adefa3fda7f7287d646bf
154161 record_ios: 412daca2350b228e698fffcd08f1f94ceb1e3844
lib/models/message.dart
....@@ -13,6 +13,7 @@
1313 final String content;
1414 final String? audioUri;
1515 final String? imageBase64;
16
+ final String? mimeType;
1617 final int timestamp;
1718 final MessageStatus? status;
1819 final int? duration;
....@@ -25,6 +26,7 @@
2526 required this.timestamp,
2627 this.audioUri,
2728 this.imageBase64,
29
+ this.mimeType,
2830 this.status,
2931 this.duration,
3032 });
....@@ -67,6 +69,7 @@
6769 required MessageRole role,
6870 required String imageBase64,
6971 String content = '',
72
+ String? mimeType,
7073 MessageStatus? status,
7174 }) {
7275 return Message(
....@@ -75,6 +78,7 @@
7578 type: MessageType.image,
7679 content: content,
7780 imageBase64: imageBase64,
81
+ mimeType: mimeType,
7882 timestamp: DateTime.now().millisecondsSinceEpoch,
7983 status: status,
8084 );
....@@ -84,6 +88,7 @@
8488 String? content,
8589 String? audioUri,
8690 String? imageBase64,
91
+ String? mimeType,
8792 MessageStatus? status,
8893 int? duration,
8994 }) {
....@@ -94,6 +99,7 @@
9499 content: content ?? this.content,
95100 audioUri: audioUri ?? this.audioUri,
96101 imageBase64: imageBase64 ?? this.imageBase64,
102
+ mimeType: mimeType ?? this.mimeType,
97103 timestamp: timestamp,
98104 status: status ?? this.status,
99105 duration: duration ?? this.duration,
....@@ -108,6 +114,7 @@
108114 'content': content,
109115 if (audioUri != null) 'audioUri': audioUri,
110116 if (imageBase64 != null) 'imageBase64': imageBase64,
117
+ if (mimeType != null) 'mimeType': mimeType,
111118 'timestamp': timestamp,
112119 if (status != null) 'status': status!.name,
113120 if (duration != null) 'duration': duration,
....@@ -130,6 +137,7 @@
130137 if (duration != null) 'duration': duration,
131138 // Keep imageBase64 — images are typically 50-200 KB and must survive restart.
132139 if (imageBase64 != null) 'imageBase64': imageBase64,
140
+ if (mimeType != null) 'mimeType': mimeType,
133141 };
134142 }
135143
....@@ -141,6 +149,7 @@
141149 content: json['content'] as String? ?? '',
142150 audioUri: json['audioUri'] as String?,
143151 imageBase64: json['imageBase64'] as String?,
152
+ mimeType: json['mimeType'] as String?,
144153 timestamp: json['timestamp'] as int,
145154 status: json['status'] != null
146155 ? MessageStatus.values.byName(json['status'] as String)
lib/screens/chat_screen.dart
....@@ -413,6 +413,7 @@
413413 role: MessageRole.assistant,
414414 imageBase64: imageData,
415415 content: content,
416
+ mimeType: map['mimeType'] as String?,
416417 status: MessageStatus.sent,
417418 );
418419 } else {
....@@ -679,10 +680,12 @@
679680 _screenshotForChat = false;
680681 }
681682
683
+ final mimeType = msg['mimeType'] as String?;
682684 final message = Message.image(
683685 role: MessageRole.assistant,
684686 imageBase64: imageData,
685687 content: content,
688
+ mimeType: mimeType,
686689 status: MessageStatus.sent,
687690 );
688691
lib/services/mqtt_service.dart
....@@ -68,6 +68,13 @@
6868 // (Per-session subscriptions removed — single pailot/out topic now)
6969 static const int _maxSeenIds = 500;
7070
71
+ // Reconnect backoff
72
+ Timer? _reconnectTimer;
73
+ Timer? _stabilityTimer;
74
+ int _reconnectAttempt = 0;
75
+ static const int _maxReconnectDelay = 30000; // 30s cap
76
+ static const int _stabilityThresholdMs = 10000; // 10s stable = reset backoff
77
+
7178 // Callbacks
7279 void Function(ConnectionStatus status)? onStatusChanged;
7380 void Function(String detail)? onStatusDetail; // "Probing local...", "Scanning network..."
....@@ -120,16 +127,16 @@
120127 }
121128
122129 /// Fast reconnect to a known host — skips discovery, short timeout.
123
- Future<void> _fastReconnect(String host) async {
130
+ /// Returns true if connected, false if failed.
131
+ Future<bool> _fastReconnect(String host) async {
124132 _mqttLog('MQTT: fast reconnect to $host');
125133 final clientId = await _getClientId();
126134 if (await _tryConnect(host, clientId, timeout: 2000)) {
127135 connectedHost = host;
128
- return;
136
+ return true;
129137 }
130
- // Fast path failed — fall back to full connect
131
- _mqttLog('MQTT: fast reconnect failed, full connect...');
132
- connect();
138
+ _mqttLog('MQTT: fast reconnect failed');
139
+ return false;
133140 }
134141
135142 /// Connect to the MQTT broker.
....@@ -440,7 +447,9 @@
440447 );
441448 _mqttLog('MQTT: connect result=${result?.state}');
442449 if (result?.state == MqttConnectionState.connected) {
443
- client.autoReconnect = true;
450
+ // Don't use autoReconnect — it has no backoff and causes tight reconnect loops.
451
+ // We handle reconnection manually in _onDisconnected with exponential backoff.
452
+ _reconnectAttempt = 0;
444453 return true;
445454 }
446455 _client = null;
....@@ -454,6 +463,17 @@
454463
455464 void _onConnected() {
456465 _mqttLog('MQTT: _onConnected fired');
466
+ _reconnectTimer?.cancel();
467
+ // Don't reset _reconnectAttempt here — only after the connection has been
468
+ // STABLE for 10+ seconds. This prevents flap loops where each brief connect
469
+ // resets the backoff and we hammer the server every 5s forever.
470
+ _stabilityTimer?.cancel();
471
+ _stabilityTimer = Timer(const Duration(milliseconds: _stabilityThresholdMs), () {
472
+ if (_status == ConnectionStatus.connected) {
473
+ _mqttLog('MQTT: connection stable for ${_stabilityThresholdMs}ms — resetting backoff');
474
+ _reconnectAttempt = 0;
475
+ }
476
+ });
457477 _setStatus(ConnectionStatus.connected);
458478 _subscribe();
459479 _listenMessages();
....@@ -461,6 +481,7 @@
461481 }
462482
463483 void _onDisconnected() {
484
+ _stabilityTimer?.cancel();
464485 _updatesSub?.cancel();
465486 _updatesSub = null;
466487
....@@ -470,15 +491,41 @@
470491 } else {
471492 _setStatus(ConnectionStatus.reconnecting);
472493 onReconnecting?.call();
494
+ _scheduleReconnect();
473495 }
474496 }
475497
498
+ void _scheduleReconnect() {
499
+ _reconnectTimer?.cancel();
500
+ // Exponential backoff: 1s, 2s, 4s, 8s, 16s, 30s cap
501
+ final delayMs = (1000 * (1 << _reconnectAttempt)).clamp(1000, _maxReconnectDelay);
502
+ _reconnectAttempt++;
503
+ _mqttLog('MQTT: scheduling reconnect in ${delayMs}ms (attempt $_reconnectAttempt)');
504
+ _reconnectTimer = Timer(Duration(milliseconds: delayMs), () async {
505
+ if (_intentionalClose || _status == ConnectionStatus.connected) return;
506
+ final host = connectedHost ?? _lastDiscoveredHost;
507
+ if (host != null) {
508
+ _mqttLog('MQTT: reconnect attempt $_reconnectAttempt to $host');
509
+ final ok = await _fastReconnect(host);
510
+ if (!ok && !_intentionalClose) {
511
+ _scheduleReconnect(); // Try again with increased backoff
512
+ }
513
+ } else {
514
+ _mqttLog('MQTT: no known host, running full connect');
515
+ await connect();
516
+ }
517
+ });
518
+ }
519
+
476520 void _onAutoReconnect() {
521
+ // Unused — autoReconnect is disabled, but keep callback for safety
477522 _setStatus(ConnectionStatus.reconnecting);
478523 onReconnecting?.call();
479524 }
480525
481526 void _onAutoReconnected() {
527
+ // Unused — autoReconnect is disabled, but keep callback for safety
528
+ _reconnectAttempt = 0;
482529 _setStatus(ConnectionStatus.connected);
483530 _subscribe();
484531 _listenMessages();
....@@ -764,6 +811,11 @@
764811 /// Disconnect intentionally.
765812 void disconnect() {
766813 _intentionalClose = true;
814
+ _reconnectTimer?.cancel();
815
+ _reconnectTimer = null;
816
+ _stabilityTimer?.cancel();
817
+ _stabilityTimer = null;
818
+ _reconnectAttempt = 0;
767819 _updatesSub?.cancel();
768820 _updatesSub = null;
769821 _connectivitySub?.cancel();
....@@ -799,8 +851,12 @@
799851 case AppLifecycleState.resumed:
800852 if (_intentionalClose) break;
801853 _mqttLog('MQTT: app resumed');
802
- // Let autoReconnect handle dead connections (keepalive timeout).
803
- // Just trigger catch_up to fetch missed messages and rebuild UI.
854
+ // If disconnected, trigger immediate reconnect (reset backoff).
855
+ if (_status != ConnectionStatus.connected) {
856
+ _reconnectAttempt = 0;
857
+ _scheduleReconnect();
858
+ }
859
+ // Trigger catch_up to fetch missed messages and rebuild UI.
804860 onResume?.call();
805861 case AppLifecycleState.paused:
806862 break;
lib/widgets/message_bubble.dart
....@@ -1,16 +1,20 @@
11 import 'dart:convert';
2
+import 'dart:io';
23 import 'dart:math';
34 import 'dart:typed_data';
45
56 import 'package:flutter/material.dart';
67 import 'package:flutter/services.dart';
78 import 'package:flutter_markdown/flutter_markdown.dart';
9
+import 'package:path_provider/path_provider.dart';
10
+import 'package:share_plus/share_plus.dart';
811 import 'package:url_launcher/url_launcher.dart';
912 import 'package:intl/intl.dart';
1013
1114 import '../models/message.dart';
1215 import '../theme/app_theme.dart';
1316 import 'image_viewer.dart';
17
+import 'pdf_viewer.dart';
1418
1519 // Cache decoded image bytes to prevent flicker on widget rebuild
1620 final Map<String, Uint8List> _imageCache = {};
....@@ -263,9 +267,20 @@
263267 );
264268 }
265269
270
+ /// True if the mimeType is a renderable image format.
271
+ bool get _isImageMime {
272
+ final mime = message.mimeType?.toLowerCase() ?? 'image/jpeg';
273
+ return mime.startsWith('image/');
274
+ }
275
+
266276 Widget _buildImageContent(BuildContext context) {
267277 if (message.imageBase64 == null || message.imageBase64!.isEmpty) {
268278 return const Text('Image unavailable');
279
+ }
280
+
281
+ // Non-image files (PDF, CSV, etc.) — show file card instead of Image.memory
282
+ if (!_isImageMime) {
283
+ return _buildFileCard(context);
269284 }
270285
271286 // Cache decoded bytes to prevent flicker on rebuild; evict oldest if over 50 entries
....@@ -322,6 +337,148 @@
322337 );
323338 }
324339
340
+ Widget _buildFileCard(BuildContext context) {
341
+ final mime = message.mimeType ?? 'application/octet-stream';
342
+ final caption = message.content.isNotEmpty ? message.content : 'File';
343
+ final isPdf = mime == 'application/pdf';
344
+
345
+ // File type icon
346
+ IconData icon;
347
+ Color iconColor;
348
+ if (isPdf) {
349
+ icon = Icons.picture_as_pdf;
350
+ iconColor = Colors.red;
351
+ } else if (mime.contains('spreadsheet') || mime.contains('excel') || mime == 'text/csv') {
352
+ icon = Icons.table_chart;
353
+ iconColor = Colors.green;
354
+ } else if (mime.contains('word') || mime.contains('document')) {
355
+ icon = Icons.description;
356
+ iconColor = Colors.blue;
357
+ } else if (mime == 'text/plain' || mime == 'application/json') {
358
+ icon = Icons.text_snippet;
359
+ iconColor = Colors.grey;
360
+ } else {
361
+ icon = Icons.insert_drive_file;
362
+ iconColor = Colors.blueGrey;
363
+ }
364
+
365
+ final sizeKB = ((message.imageBase64?.length ?? 0) * 3 / 4 / 1024).round();
366
+
367
+ return GestureDetector(
368
+ onTap: () => _openFile(context),
369
+ child: Container(
370
+ width: 260,
371
+ padding: const EdgeInsets.all(12),
372
+ decoration: BoxDecoration(
373
+ color: (_isUser ? Colors.white : Theme.of(context).colorScheme.primary).withAlpha(25),
374
+ borderRadius: BorderRadius.circular(8),
375
+ border: Border.all(
376
+ color: (_isUser ? Colors.white : Colors.grey).withAlpha(50),
377
+ ),
378
+ ),
379
+ child: Column(
380
+ crossAxisAlignment: CrossAxisAlignment.start,
381
+ children: [
382
+ Row(
383
+ children: [
384
+ Icon(icon, size: 32, color: iconColor),
385
+ const SizedBox(width: 10),
386
+ Expanded(
387
+ child: Column(
388
+ crossAxisAlignment: CrossAxisAlignment.start,
389
+ children: [
390
+ Text(
391
+ caption,
392
+ style: TextStyle(
393
+ fontSize: 14,
394
+ fontWeight: FontWeight.w600,
395
+ color: _isUser ? Colors.white : null,
396
+ ),
397
+ maxLines: 2,
398
+ overflow: TextOverflow.ellipsis,
399
+ ),
400
+ const SizedBox(height: 2),
401
+ Text(
402
+ '${mime.split('/').last.toUpperCase()} - ${sizeKB} KB',
403
+ style: TextStyle(
404
+ fontSize: 11,
405
+ color: (_isUser ? Colors.white : Colors.grey).withAlpha(180),
406
+ ),
407
+ ),
408
+ ],
409
+ ),
410
+ ),
411
+ Icon(
412
+ Icons.open_in_new,
413
+ size: 20,
414
+ color: (_isUser ? Colors.white : Colors.grey).withAlpha(150),
415
+ ),
416
+ ],
417
+ ),
418
+ ],
419
+ ),
420
+ ),
421
+ );
422
+ }
423
+
424
+ Future<void> _openFile(BuildContext context) async {
425
+ final data = message.imageBase64;
426
+ if (data == null || data.isEmpty) return;
427
+
428
+ try {
429
+ final bytes = Uint8List.fromList(
430
+ base64Decode(data.contains(',') ? data.split(',').last : data),
431
+ );
432
+ final mime = message.mimeType ?? 'application/octet-stream';
433
+
434
+ // PDFs: open inline viewer
435
+ if (mime == 'application/pdf') {
436
+ if (context.mounted) {
437
+ Navigator.of(context).push(
438
+ MaterialPageRoute(
439
+ builder: (_) => PdfViewerScreen(
440
+ pdfBytes: bytes,
441
+ title: message.content.isNotEmpty ? message.content : 'PDF',
442
+ ),
443
+ ),
444
+ );
445
+ }
446
+ return;
447
+ }
448
+
449
+ // Other files: save to temp and share
450
+ final ext = _mimeToExt(mime);
451
+ final dir = await getTemporaryDirectory();
452
+ final fileName = '${message.content.isNotEmpty ? message.content.replaceAll(RegExp(r'[^\w\s.-]'), '').trim() : 'file'}.$ext';
453
+ final file = File('${dir.path}/$fileName');
454
+ await file.writeAsBytes(bytes);
455
+
456
+ await SharePlus.instance.share(
457
+ ShareParams(files: [XFile(file.path, mimeType: mime)]),
458
+ );
459
+ } catch (e) {
460
+ if (context.mounted) {
461
+ ScaffoldMessenger.of(context).showSnackBar(
462
+ SnackBar(content: Text('Could not open file: $e')),
463
+ );
464
+ }
465
+ }
466
+ }
467
+
468
+ String _mimeToExt(String mime) {
469
+ const map = {
470
+ 'application/pdf': 'pdf',
471
+ 'application/msword': 'doc',
472
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
473
+ 'application/vnd.ms-excel': 'xls',
474
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
475
+ 'text/plain': 'txt',
476
+ 'text/csv': 'csv',
477
+ 'application/json': 'json',
478
+ };
479
+ return map[mime] ?? 'bin';
480
+ }
481
+
325482 Widget _buildFooter(BuildContext context) {
326483 final isDark = Theme.of(context).brightness == Brightness.dark;
327484 final dt = DateTime.fromMillisecondsSinceEpoch(message.timestamp);
lib/widgets/pdf_viewer.dart
....@@ -0,0 +1,58 @@
1
+import 'dart:io';
2
+import 'dart:typed_data';
3
+
4
+import 'package:flutter/material.dart';
5
+import 'package:path_provider/path_provider.dart';
6
+import 'package:pdfrx/pdfrx.dart';
7
+import 'package:share_plus/share_plus.dart';
8
+
9
+/// Full-screen PDF viewer with pinch-to-zoom, page navigation, and share/save.
10
+class PdfViewerScreen extends StatelessWidget {
11
+ final Uint8List pdfBytes;
12
+ final String title;
13
+
14
+ const PdfViewerScreen({
15
+ super.key,
16
+ required this.pdfBytes,
17
+ this.title = 'PDF',
18
+ });
19
+
20
+ Future<void> _share(BuildContext context) async {
21
+ try {
22
+ final dir = await getTemporaryDirectory();
23
+ final safeName = title.replaceAll(RegExp(r'[^\w\s.-]'), '').trim();
24
+ final fileName = safeName.endsWith('.pdf') ? safeName : '$safeName.pdf';
25
+ final file = File('${dir.path}/$fileName');
26
+ await file.writeAsBytes(pdfBytes);
27
+ await SharePlus.instance.share(
28
+ ShareParams(files: [XFile(file.path, mimeType: 'application/pdf')]),
29
+ );
30
+ } catch (e) {
31
+ if (context.mounted) {
32
+ ScaffoldMessenger.of(context).showSnackBar(
33
+ SnackBar(content: Text('Share failed: $e')),
34
+ );
35
+ }
36
+ }
37
+ }
38
+
39
+ @override
40
+ Widget build(BuildContext context) {
41
+ return Scaffold(
42
+ appBar: AppBar(
43
+ title: Text(title, style: const TextStyle(fontSize: 16)),
44
+ backgroundColor: Colors.black87,
45
+ foregroundColor: Colors.white,
46
+ actions: [
47
+ IconButton(
48
+ icon: const Icon(Icons.ios_share),
49
+ tooltip: 'Share / Save',
50
+ onPressed: () => _share(context),
51
+ ),
52
+ ],
53
+ ),
54
+ backgroundColor: Colors.grey[900],
55
+ body: PdfViewer.data(pdfBytes, sourceName: title),
56
+ );
57
+ }
58
+}
linux/flutter/generated_plugins.cmake
....@@ -11,6 +11,7 @@
1111 )
1212
1313 list(APPEND FLUTTER_FFI_PLUGIN_LIST
14
+ pdfrx
1415 )
1516
1617 set(PLUGIN_BUNDLED_LIBRARIES)
pubspec.lock
....@@ -1,6 +1,22 @@
11 # Generated by pub
22 # See https://dart.dev/tools/pub/glossary#lockfile
33 packages:
4
+ _fe_analyzer_shared:
5
+ dependency: transitive
6
+ description:
7
+ name: _fe_analyzer_shared
8
+ sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d"
9
+ url: "https://pub.dev"
10
+ source: hosted
11
+ version: "93.0.0"
12
+ analyzer:
13
+ dependency: transitive
14
+ description:
15
+ name: analyzer
16
+ sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b
17
+ url: "https://pub.dev"
18
+ source: hosted
19
+ version: "10.0.1"
420 args:
521 dependency: transitive
622 description:
....@@ -137,6 +153,30 @@
137153 url: "https://pub.dev"
138154 source: hosted
139155 version: "1.4.1"
156
+ checked_yaml:
157
+ dependency: transitive
158
+ description:
159
+ name: checked_yaml
160
+ sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
161
+ url: "https://pub.dev"
162
+ source: hosted
163
+ version: "2.0.4"
164
+ cli_config:
165
+ dependency: transitive
166
+ description:
167
+ name: cli_config
168
+ sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
169
+ url: "https://pub.dev"
170
+ source: hosted
171
+ version: "0.2.0"
172
+ cli_util:
173
+ dependency: transitive
174
+ description:
175
+ name: cli_util
176
+ sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
177
+ url: "https://pub.dev"
178
+ source: hosted
179
+ version: "0.4.2"
140180 clock:
141181 dependency: transitive
142182 description:
....@@ -177,6 +217,22 @@
177217 url: "https://pub.dev"
178218 source: hosted
179219 version: "2.1.0"
220
+ convert:
221
+ dependency: transitive
222
+ description:
223
+ name: convert
224
+ sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
225
+ url: "https://pub.dev"
226
+ source: hosted
227
+ version: "3.1.2"
228
+ coverage:
229
+ dependency: transitive
230
+ description:
231
+ name: coverage
232
+ sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
233
+ url: "https://pub.dev"
234
+ source: hosted
235
+ version: "1.15.0"
180236 cross_file:
181237 dependency: transitive
182238 description:
....@@ -193,6 +249,14 @@
193249 url: "https://pub.dev"
194250 source: hosted
195251 version: "3.0.7"
252
+ csslib:
253
+ dependency: transitive
254
+ description:
255
+ name: csslib
256
+ sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
257
+ url: "https://pub.dev"
258
+ source: hosted
259
+ version: "1.0.2"
196260 cupertino_icons:
197261 dependency: "direct main"
198262 description:
....@@ -201,6 +265,14 @@
201265 url: "https://pub.dev"
202266 source: hosted
203267 version: "1.0.8"
268
+ dart_pubspec_licenses:
269
+ dependency: transitive
270
+ description:
271
+ name: dart_pubspec_licenses
272
+ sha256: "3d579e1aa3ad3b6519f08fce6980799c0a8375bf41e0b8d58ca21f1be64032c9"
273
+ url: "https://pub.dev"
274
+ source: hosted
275
+ version: "3.2.0"
204276 dbus:
205277 dependency: transitive
206278 description:
....@@ -408,6 +480,14 @@
408480 description: flutter
409481 source: sdk
410482 version: "0.0.0"
483
+ frontend_server_client:
484
+ dependency: transitive
485
+ description:
486
+ name: frontend_server_client
487
+ sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
488
+ url: "https://pub.dev"
489
+ source: hosted
490
+ version: "4.0.0"
411491 glob:
412492 dependency: transitive
413493 description:
....@@ -432,6 +512,14 @@
432512 url: "https://pub.dev"
433513 source: hosted
434514 version: "1.0.2"
515
+ html:
516
+ dependency: transitive
517
+ description:
518
+ name: html
519
+ sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
520
+ url: "https://pub.dev"
521
+ source: hosted
522
+ version: "0.15.6"
435523 http:
436524 dependency: transitive
437525 description:
....@@ -440,6 +528,14 @@
440528 url: "https://pub.dev"
441529 source: hosted
442530 version: "1.6.0"
531
+ http_multi_server:
532
+ dependency: transitive
533
+ description:
534
+ name: http_multi_server
535
+ sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
536
+ url: "https://pub.dev"
537
+ source: hosted
538
+ version: "3.2.2"
443539 http_parser:
444540 dependency: transitive
445541 description:
....@@ -552,6 +648,14 @@
552648 url: "https://pub.dev"
553649 source: hosted
554650 version: "0.20.2"
651
+ io:
652
+ dependency: transitive
653
+ description:
654
+ name: io
655
+ sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
656
+ url: "https://pub.dev"
657
+ source: hosted
658
+ version: "1.0.5"
555659 js:
556660 dependency: transitive
557661 description:
....@@ -672,6 +776,14 @@
672776 url: "https://pub.dev"
673777 source: hosted
674778 version: "0.5.0"
779
+ node_preamble:
780
+ dependency: transitive
781
+ description:
782
+ name: node_preamble
783
+ sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
784
+ url: "https://pub.dev"
785
+ source: hosted
786
+ version: "2.0.2"
675787 objective_c:
676788 dependency: transitive
677789 description:
....@@ -680,6 +792,22 @@
680792 url: "https://pub.dev"
681793 source: hosted
682794 version: "9.3.0"
795
+ package_config:
796
+ dependency: transitive
797
+ description:
798
+ name: package_config
799
+ sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
800
+ url: "https://pub.dev"
801
+ source: hosted
802
+ version: "2.2.0"
803
+ pana:
804
+ dependency: transitive
805
+ description:
806
+ name: pana
807
+ sha256: "847ee5df6ac13fdc6c53d641095de1b25886c973d0b0f0469c73521ebe4602fa"
808
+ url: "https://pub.dev"
809
+ source: hosted
810
+ version: "0.23.12"
683811 path:
684812 dependency: transitive
685813 description:
....@@ -736,6 +864,14 @@
736864 url: "https://pub.dev"
737865 source: hosted
738866 version: "2.3.0"
867
+ pdfrx:
868
+ dependency: "direct main"
869
+ description:
870
+ name: pdfrx
871
+ sha256: "94c865686e7e15e93f2a5e3c0255f7d6c2ac39b2e5e82d62a0d278cb4b7d0d3b"
872
+ url: "https://pub.dev"
873
+ source: hosted
874
+ version: "1.3.5"
739875 permission_handler:
740876 dependency: "direct main"
741877 description:
....@@ -808,6 +944,14 @@
808944 url: "https://pub.dev"
809945 source: hosted
810946 version: "2.1.8"
947
+ pool:
948
+ dependency: transitive
949
+ description:
950
+ name: pool
951
+ sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
952
+ url: "https://pub.dev"
953
+ source: hosted
954
+ version: "1.5.2"
811955 pub_semver:
812956 dependency: transitive
813957 description:
....@@ -816,6 +960,14 @@
816960 url: "https://pub.dev"
817961 source: hosted
818962 version: "2.2.0"
963
+ pubspec_parse:
964
+ dependency: transitive
965
+ description:
966
+ name: pubspec_parse
967
+ sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
968
+ url: "https://pub.dev"
969
+ source: hosted
970
+ version: "1.5.0"
819971 push:
820972 dependency: "direct main"
821973 description:
....@@ -888,6 +1040,14 @@
8881040 url: "https://pub.dev"
8891041 source: hosted
8901042 version: "1.0.7"
1043
+ retry:
1044
+ dependency: transitive
1045
+ description:
1046
+ name: retry
1047
+ sha256: "822e118d5b3aafed083109c72d5f484c6dc66707885e07c0fbcb8b986bba7efc"
1048
+ url: "https://pub.dev"
1049
+ source: hosted
1050
+ version: "3.1.2"
8911051 riverpod:
8921052 dependency: transitive
8931053 description:
....@@ -904,6 +1064,22 @@
9041064 url: "https://pub.dev"
9051065 source: hosted
9061066 version: "2.6.1"
1067
+ rxdart:
1068
+ dependency: transitive
1069
+ description:
1070
+ name: rxdart
1071
+ sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
1072
+ url: "https://pub.dev"
1073
+ source: hosted
1074
+ version: "0.28.0"
1075
+ safe_url_check:
1076
+ dependency: transitive
1077
+ description:
1078
+ name: safe_url_check
1079
+ sha256: "49a3e060a7869cbafc8f4845ca1ecbbaaa53179980a32f4fdfeab1607e90f41d"
1080
+ url: "https://pub.dev"
1081
+ source: hosted
1082
+ version: "1.1.2"
9071083 share_plus:
9081084 dependency: "direct main"
9091085 description:
....@@ -976,11 +1152,59 @@
9761152 url: "https://pub.dev"
9771153 source: hosted
9781154 version: "2.4.1"
1155
+ shelf:
1156
+ dependency: transitive
1157
+ description:
1158
+ name: shelf
1159
+ sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
1160
+ url: "https://pub.dev"
1161
+ source: hosted
1162
+ version: "1.4.2"
1163
+ shelf_packages_handler:
1164
+ dependency: transitive
1165
+ description:
1166
+ name: shelf_packages_handler
1167
+ sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
1168
+ url: "https://pub.dev"
1169
+ source: hosted
1170
+ version: "3.0.2"
1171
+ shelf_static:
1172
+ dependency: transitive
1173
+ description:
1174
+ name: shelf_static
1175
+ sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
1176
+ url: "https://pub.dev"
1177
+ source: hosted
1178
+ version: "1.1.3"
1179
+ shelf_web_socket:
1180
+ dependency: transitive
1181
+ description:
1182
+ name: shelf_web_socket
1183
+ sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
1184
+ url: "https://pub.dev"
1185
+ source: hosted
1186
+ version: "3.0.0"
9791187 sky_engine:
9801188 dependency: transitive
9811189 description: flutter
9821190 source: sdk
9831191 version: "0.0.0"
1192
+ source_map_stack_trace:
1193
+ dependency: transitive
1194
+ description:
1195
+ name: source_map_stack_trace
1196
+ sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
1197
+ url: "https://pub.dev"
1198
+ source: hosted
1199
+ version: "2.1.2"
1200
+ source_maps:
1201
+ dependency: transitive
1202
+ description:
1203
+ name: source_maps
1204
+ sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
1205
+ url: "https://pub.dev"
1206
+ source: hosted
1207
+ version: "0.10.13"
9841208 source_span:
9851209 dependency: transitive
9861210 description:
....@@ -1037,6 +1261,14 @@
10371261 url: "https://pub.dev"
10381262 source: hosted
10391263 version: "1.2.2"
1264
+ test:
1265
+ dependency: transitive
1266
+ description:
1267
+ name: test
1268
+ sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
1269
+ url: "https://pub.dev"
1270
+ source: hosted
1271
+ version: "1.30.0"
10401272 test_api:
10411273 dependency: transitive
10421274 description:
....@@ -1045,6 +1277,14 @@
10451277 url: "https://pub.dev"
10461278 source: hosted
10471279 version: "0.7.10"
1280
+ test_core:
1281
+ dependency: transitive
1282
+ description:
1283
+ name: test_core
1284
+ sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
1285
+ url: "https://pub.dev"
1286
+ source: hosted
1287
+ version: "0.6.16"
10481288 typed_data:
10491289 dependency: transitive
10501290 description:
....@@ -1165,6 +1405,14 @@
11651405 url: "https://pub.dev"
11661406 source: hosted
11671407 version: "15.0.2"
1408
+ watcher:
1409
+ dependency: transitive
1410
+ description:
1411
+ name: watcher
1412
+ sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
1413
+ url: "https://pub.dev"
1414
+ source: hosted
1415
+ version: "1.2.1"
11681416 web:
11691417 dependency: transitive
11701418 description:
....@@ -1173,6 +1421,30 @@
11731421 url: "https://pub.dev"
11741422 source: hosted
11751423 version: "1.1.1"
1424
+ web_socket:
1425
+ dependency: transitive
1426
+ description:
1427
+ name: web_socket
1428
+ sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
1429
+ url: "https://pub.dev"
1430
+ source: hosted
1431
+ version: "1.0.1"
1432
+ web_socket_channel:
1433
+ dependency: transitive
1434
+ description:
1435
+ name: web_socket_channel
1436
+ sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
1437
+ url: "https://pub.dev"
1438
+ source: hosted
1439
+ version: "3.0.3"
1440
+ webkit_inspection_protocol:
1441
+ dependency: transitive
1442
+ description:
1443
+ name: webkit_inspection_protocol
1444
+ sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
1445
+ url: "https://pub.dev"
1446
+ source: hosted
1447
+ version: "1.2.1"
11761448 win32:
11771449 dependency: transitive
11781450 description:
pubspec.yaml
....@@ -36,6 +36,7 @@
3636 connectivity_plus: ^7.1.0
3737 in_app_purchase: ^3.2.3
3838 url_launcher: ^6.3.2
39
+ pdfrx: ^1.0.100
3940
4041 dev_dependencies:
4142 flutter_test:
windows/flutter/generated_plugins.cmake
....@@ -15,6 +15,7 @@
1515 )
1616
1717 list(APPEND FLUTTER_FFI_PLUGIN_LIST
18
+ pdfrx
1819 )
1920
2021 set(PLUGIN_BUNDLED_LIBRARIES)