diff --git a/mobile-app/lib/utils/feature_flags.dart b/mobile-app/lib/utils/feature_flags.dart index 02547118..84849f17 100644 --- a/mobile-app/lib/utils/feature_flags.dart +++ b/mobile-app/lib/utils/feature_flags.dart @@ -4,4 +4,5 @@ class FeatureFlags { static const bool enableKeystoneHardwareWallet = false; // turn keystone hw wallet on and off static const bool enableHighSecurity = true; // turn keystone hw wallet on and off static const bool enableRemoteNotifications = false; // turn remote notifications on and off + static const bool enableSwap = false; } diff --git a/mobile-app/lib/v2/components/qr_scanner_page.dart b/mobile-app/lib/v2/components/qr_scanner_page.dart new file mode 100644 index 00000000..472d3ddd --- /dev/null +++ b/mobile-app/lib/v2/components/qr_scanner_page.dart @@ -0,0 +1,194 @@ +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:resonance_network_wallet/v2/components/glass_container.dart'; +import 'package:resonance_network_wallet/v2/theme/app_colors.dart'; +import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart'; + +class QrScannerPage extends StatefulWidget { + const QrScannerPage({super.key}); + + @override + State createState() => _QrScannerPageState(); +} + +class _QrScannerPageState extends State { + final _controller = MobileScannerController(); + bool _scanned = false; + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _onDetect(BarcodeCapture capture) { + if (_scanned) return; + final code = capture.barcodes.firstOrNull?.rawValue; + if (code != null && code.isNotEmpty) { + _scanned = true; + Navigator.pop(context, code); + } + } + + Future _pickImage() async { + final image = await ImagePicker().pickImage(source: ImageSource.gallery); + if (image == null || !mounted) return; + final capture = await _controller.analyzeImage(image.path); + if (!mounted) return; + if (capture != null) { + _onDetect(capture); + } else { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('No QR code found in image'))); + } + } + + @override + Widget build(BuildContext context) { + final colors = context.colors; + final text = context.themeText; + final screen = MediaQuery.of(context).size; + final frameSize = (screen.width - 112).clamp(220.0, 280.0); + + return Scaffold( + backgroundColor: Colors.black, + body: Stack( + children: [ + MobileScanner(controller: _controller, onDetect: _onDetect), + CustomPaint( + size: Size(screen.width, screen.height), + painter: _OverlayPainter(frameSize: frameSize, screenSize: screen), + ), + Center(child: _ScanFrame(size: frameSize)), + Positioned( + left: 0, + right: 0, + top: screen.height / 2 + frameSize / 2 + 24, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _actionButton(icon: Icons.image_outlined, onTap: _pickImage, colors: colors), + const SizedBox(width: 8), + ValueListenableBuilder( + valueListenable: _controller, + builder: (_, state, _) { + final isOn = state.torchState == TorchState.on; + return _actionButton( + icon: isOn ? Icons.flash_on : Icons.flash_off, + onTap: _controller.toggleTorch, + colors: colors, + ); + }, + ), + ], + ), + ), + Positioned( + bottom: 58, + left: 24, + right: 24, + child: GestureDetector( + onTap: () => Navigator.pop(context), + child: GlassContainer( + asset: GlassContainer.wideAsset, + child: Text( + 'Cancel', + textAlign: TextAlign.center, + style: text.paragraph?.copyWith(color: colors.textPrimary, fontWeight: FontWeight.w500), + ), + ), + ), + ), + ], + ), + ); + } + + Widget _actionButton({required IconData icon, required VoidCallback onTap, required AppColorsV2 colors}) { + return GestureDetector( + onTap: onTap, + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration(color: Colors.white.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8)), + child: Icon(icon, size: 20, color: colors.textPrimary), + ), + ); + } +} + +class _OverlayPainter extends CustomPainter { + final double frameSize; + final Size screenSize; + + _OverlayPainter({required this.frameSize, required this.screenSize}); + + @override + void paint(Canvas canvas, Size size) { + final rect = Offset.zero & size; + final frameRect = Rect.fromCenter( + center: Offset(size.width / 2, size.height / 2), + width: frameSize, + height: frameSize, + ); + final path = Path() + ..addRect(rect) + ..addRRect(RRect.fromRectAndRadius(frameRect, const Radius.circular(16))); + path.fillType = PathFillType.evenOdd; + canvas.drawPath(path, Paint()..color = Colors.black.withValues(alpha: 0.6)); + } + + @override + bool shouldRepaint(_OverlayPainter old) => frameSize != old.frameSize; +} + +class _ScanFrame extends StatelessWidget { + final double size; + const _ScanFrame({required this.size}); + + @override + Widget build(BuildContext context) { + final color = Colors.white.withValues(alpha: 0.92); + return SizedBox( + width: size, + height: size, + child: Stack( + children: [ + _corner(top: true, left: true, color: color), + _corner(top: true, left: false, color: color), + _corner(top: false, left: true, color: color), + _corner(top: false, left: false, color: color), + ], + ), + ); + } + + Widget _corner({required bool top, required bool left, required Color color}) { + return Positioned( + top: top ? 0 : null, + bottom: top ? null : 0, + left: left ? 0 : null, + right: left ? null : 0, + child: SizedBox( + width: 41, + height: 41, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: top && left ? const Radius.circular(16) : Radius.zero, + topRight: top && !left ? const Radius.circular(16) : Radius.zero, + bottomLeft: !top && left ? const Radius.circular(16) : Radius.zero, + bottomRight: !top && !left ? const Radius.circular(16) : Radius.zero, + ), + border: Border( + top: top ? BorderSide(color: color, width: 1.6) : BorderSide.none, + bottom: !top ? BorderSide(color: color, width: 1.6) : BorderSide.none, + left: left ? BorderSide(color: color, width: 1.6) : BorderSide.none, + right: !left ? BorderSide(color: color, width: 1.6) : BorderSide.none, + ), + ), + ), + ), + ); + } +} diff --git a/mobile-app/lib/v2/components/toaster_helper.dart b/mobile-app/lib/v2/components/toaster_helper.dart index 2ad6419c..c98c21a9 100644 --- a/mobile-app/lib/v2/components/toaster_helper.dart +++ b/mobile-app/lib/v2/components/toaster_helper.dart @@ -9,14 +9,14 @@ Future showToaster( required String message, required IconData iconData, Color? iconColor, - Duration duration = const Duration(seconds: 3), + Duration duration = const Duration(seconds: 2), FlashBehavior behavior = FlashBehavior.floating, }) async { if (!context.mounted) return; await context.showFlash( duration: duration, - persistent: false, + persistent: true, builder: (context, controller) { return FlashBar( controller: controller, diff --git a/mobile-app/lib/v2/screens/home/home_screen.dart b/mobile-app/lib/v2/screens/home/home_screen.dart index 5f28e255..0cb59e46 100644 --- a/mobile-app/lib/v2/screens/home/home_screen.dart +++ b/mobile-app/lib/v2/screens/home/home_screen.dart @@ -10,6 +10,7 @@ import 'package:resonance_network_wallet/v2/screens/home/accounts_sheet.dart'; import 'package:resonance_network_wallet/v2/screens/receive/receive_sheet.dart'; import 'package:resonance_network_wallet/v2/screens/send/send_sheet.dart'; import 'package:resonance_network_wallet/v2/screens/settings/settings_screen.dart'; +import 'package:resonance_network_wallet/utils/feature_flags.dart'; import 'package:resonance_network_wallet/v2/screens/swap/swap_screen.dart'; import 'package:resonance_network_wallet/providers/account_id_list_cache.dart'; import 'package:resonance_network_wallet/providers/account_providers.dart'; @@ -171,75 +172,89 @@ class _HomeScreenState extends ConsumerState { } Widget _buildBalance(AsyncValue balanceAsync, bool isBalanceHidden, AppColorsV2 colors, AppTextTheme text) { - const stableHeight = 96.0; - - return SizedBox( - height: stableHeight, - child: Column( - children: [ - balanceAsync.when( - data: (balance) { - final formatted = isBalanceHidden ? '-----' : _fmt.formatBalance(balance); - return Stack( - alignment: Alignment.center, - children: [ - ImageFiltered( - imageFilter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), - child: Text( + return Column( + children: [ + balanceAsync.when( + data: (balance) { + final formatted = isBalanceHidden ? '-----' : _fmt.formatBalance(balance); + final usdFormatted = isBalanceHidden ? '-----' : '\$${_fmt.formatBalance(balance)}'; + return Column( + children: [ + Stack( + alignment: Alignment.center, + children: [ + ImageFiltered( + imageFilter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), + child: Text( + '$formatted ${AppConstants.tokenSymbol}', + style: text.extraLargeTitle?.copyWith(color: colors.textSecondary), + ), + ), + Text( '$formatted ${AppConstants.tokenSymbol}', - style: text.extraLargeTitle?.copyWith(color: colors.textSecondary), + style: text.extraLargeTitle?.copyWith(color: colors.textPrimary), ), - ), - Text( - '$formatted ${AppConstants.tokenSymbol}', - style: text.extraLargeTitle?.copyWith(color: colors.textPrimary), - ), - ], - ); - }, - loading: () => Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Skeleton(width: 200, height: 36), - Text(' ${AppConstants.tokenSymbol}', style: text.smallTitle?.copyWith(color: colors.textPrimary)), + ], + ), + const SizedBox(height: 6), + Text('≈ $usdFormatted', style: text.paragraph?.copyWith(color: colors.textSecondary)), ], - ), - error: (_, _) => Text('Error loading balance', style: text.detail?.copyWith(color: colors.textError)), + ); + }, + loading: () => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Skeleton(width: 200, height: 36), + Text(' ${AppConstants.tokenSymbol}', style: text.smallTitle?.copyWith(color: colors.textPrimary)), + ], ), - // if (!isBalanceHidden) ...[ - // const SizedBox(height: 6), - // Text('≈ \$0.00', style: text.paragraph?.copyWith(color: colors.textSecondary)), - // ], - ], - ), + error: (_, _) => Text('Error loading balance', style: text.detail?.copyWith(color: colors.textError)), + ), + ], ); } Widget _buildActionButtons(AppColorsV2 colors, AppTextTheme text) { + final receiveCard = _actionCard( + iconAsset: 'assets/v2/action_receive.svg', + label: 'Receive', + colors: colors, + text: text, + onTap: () => showReceiveSheetV2(context), + ); + final sendCard = _actionCard( + iconAsset: 'assets/v2/action_send.svg', + label: 'Send', + colors: colors, + text: text, + onTap: () => showSendSheetV2(context), + ); + + if (!FeatureFlags.enableSwap) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(width: 104, child: receiveCard), + const SizedBox(width: 32), + SizedBox(width: 104, child: sendCard), + ], + ); + } + return Row( children: [ - _actionCard( - iconAsset: 'assets/v2/action_receive.svg', - label: 'Receive', - colors: colors, - text: text, - onTap: () => showReceiveSheetV2(context), - ), + Expanded(child: receiveCard), const SizedBox(width: 15), - _actionCard( - iconAsset: 'assets/v2/action_send.svg', - label: 'Send', - colors: colors, - text: text, - onTap: () => showSendSheetV2(context), - ), + Expanded(child: sendCard), const SizedBox(width: 15), - _actionCard( - iconAsset: 'assets/v2/action_swap.svg', - label: 'Swap', - colors: colors, - text: text, - onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SwapScreen())), + Expanded( + child: _actionCard( + iconAsset: 'assets/v2/action_swap.svg', + label: 'Swap', + colors: colors, + text: text, + onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SwapScreen())), + ), ), ], ); @@ -252,34 +267,32 @@ class _HomeScreenState extends ConsumerState { required AppTextTheme text, required VoidCallback onTap, }) { - return Expanded( - child: GestureDetector( - onTap: onTap, - child: SizedBox( - height: 80, - child: Stack( - fit: StackFit.expand, - children: [ - Image.asset(_actionButtonBgAsset, fit: BoxFit.fill), - Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - SvgPicture.asset(iconAsset, width: 24, height: 24), - const SizedBox(height: 6), - Text( - label, - maxLines: 1, - overflow: TextOverflow.clip, - style: text.paragraph?.copyWith(color: colors.textPrimary, height: 1.0), - ), - ], - ), + return GestureDetector( + onTap: onTap, + child: SizedBox( + height: 80, + child: Stack( + fit: StackFit.expand, + children: [ + Image.asset(_actionButtonBgAsset, fit: BoxFit.fill), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset(iconAsset, width: 24, height: 24), + const SizedBox(height: 6), + Text( + label, + maxLines: 1, + overflow: TextOverflow.clip, + style: text.paragraph?.copyWith(color: colors.textPrimary, height: 1.0), + ), + ], ), - ], - ), + ), + ], ), ), ); diff --git a/mobile-app/lib/v2/screens/receive/receive_sheet.dart b/mobile-app/lib/v2/screens/receive/receive_sheet.dart index c948378f..eedab7ae 100644 --- a/mobile-app/lib/v2/screens/receive/receive_sheet.dart +++ b/mobile-app/lib/v2/screens/receive/receive_sheet.dart @@ -175,11 +175,15 @@ class _ReceiveSheetState extends State { } Widget _copyButton(AppColorsV2 colors) { - return Container( + return SizedBox( width: 20, height: 20, - decoration: BoxDecoration(color: colors.surfaceGlass, borderRadius: BorderRadius.circular(4)), - child: Icon(Icons.copy, size: 12, color: colors.textPrimary), + child: GlassContainer( + asset: GlassContainer.tinyAsset, + filled: false, + onTap: _copyAddress, + child: Icon(Icons.copy, size: 12, color: colors.textPrimary), + ), ); } diff --git a/mobile-app/lib/v2/screens/send/send_sheet.dart b/mobile-app/lib/v2/screens/send/send_sheet.dart index d11a066f..fc7a5305 100644 --- a/mobile-app/lib/v2/screens/send/send_sheet.dart +++ b/mobile-app/lib/v2/screens/send/send_sheet.dart @@ -1,7 +1,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:resonance_network_wallet/v2/components/qr_scanner_page.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/features/main/screens/send/send_providers.dart'; import 'package:resonance_network_wallet/features/main/screens/send/send_screen_logic.dart'; @@ -117,7 +117,7 @@ class _SendSheetState extends ConsumerState { Future _scanQr() async { final address = await Navigator.push( context, - MaterialPageRoute(fullscreenDialog: true, builder: (_) => const _QrScanPage()), + MaterialPageRoute(fullscreenDialog: true, builder: (_) => const QrScannerPage()), ); if (address != null && mounted) { _recipientController.text = address; @@ -510,64 +510,3 @@ void showSendSheetV2(BuildContext context, {String? address}) { ), ); } - -class _QrScanPage extends StatefulWidget { - const _QrScanPage(); - - @override - State<_QrScanPage> createState() => _QrScanPageState(); -} - -class _QrScanPageState extends State<_QrScanPage> { - final _controller = MobileScannerController(); - bool _scanned = false; - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.black, - body: Stack( - children: [ - MobileScanner( - controller: _controller, - onDetect: (capture) { - if (_scanned) return; - for (final barcode in capture.barcodes) { - final v = barcode.rawValue; - if (v != null && v.isNotEmpty) { - _scanned = true; - Navigator.pop(context, v); - return; - } - } - }, - ), - Positioned( - bottom: 60, - left: 0, - right: 0, - child: Center( - child: GestureDetector( - onTap: () => Navigator.pop(context), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 16), - decoration: BoxDecoration(color: const Color(0xFF1A1A1A), borderRadius: BorderRadius.circular(14)), - child: const Text( - 'Cancel', - style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500), - ), - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/mobile-app/lib/v2/screens/swap/swap_screen.dart b/mobile-app/lib/v2/screens/swap/swap_screen.dart index 7f1317cd..e8873193 100644 --- a/mobile-app/lib/v2/screens/swap/swap_screen.dart +++ b/mobile-app/lib/v2/screens/swap/swap_screen.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:resonance_network_wallet/v2/components/qr_scanner_page.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/v2/components/back_button.dart'; import 'package:resonance_network_wallet/v2/components/glass_container.dart'; @@ -108,18 +108,12 @@ class _SwapScreenState extends State { } } - void _scanQr() { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => _QrScanPage( - onScanned: (v) { - _addressController.text = v; - Navigator.pop(context); - }, - ), - ), - ); + void _scanQr() async { + final address = await Navigator.push(context, MaterialPageRoute(builder: (_) => const QrScannerPage())); + if (address != null && mounted) { + _addressController.text = address; + setState(() {}); + } } @override @@ -462,157 +456,3 @@ class _SwapScreenState extends State { ); } } - -class _QrScanPage extends StatefulWidget { - final ValueChanged onScanned; - const _QrScanPage({required this.onScanned}); - - @override - State<_QrScanPage> createState() => _QrScanPageState(); -} - -class _QrScanPageState extends State<_QrScanPage> { - final _controller = MobileScannerController(); - bool _scanned = false; - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final colors = context.colors; - final frameSize = (MediaQuery.of(context).size.width - 111.5).clamp(220.0, 280.0); - - return Scaffold( - backgroundColor: Colors.black, - body: Stack( - children: [ - MobileScanner( - controller: _controller, - onDetect: (capture) { - if (_scanned) return; - final code = capture.barcodes.firstOrNull?.rawValue; - if (code != null && code.isNotEmpty) { - _scanned = true; - widget.onScanned(code); - } - }, - ), - ColoredBox(color: Colors.black.withValues(alpha: 0.24)), - Center(child: _ScanFrame(size: frameSize)), - Positioned( - left: 0, - right: 0, - bottom: 228, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _scanActionButton(icon: Icons.image_outlined, onTap: () {}, colors: colors), - const SizedBox(width: 8), - ValueListenableBuilder( - valueListenable: _controller, - builder: (_, state, _) { - final isOn = state.torchState == TorchState.on; - return _scanActionButton( - icon: isOn ? Icons.flash_on : Icons.flash_off, - onTap: _controller.toggleTorch, - colors: colors, - ); - }, - ), - ], - ), - ), - Positioned( - bottom: 58, - left: 24, - right: 24, - child: GestureDetector( - onTap: () => Navigator.pop(context), - child: Container( - padding: const EdgeInsets.symmetric(vertical: 20), - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.2), - border: Border.all(color: Colors.white.withValues(alpha: 0.44), width: 0.9), - borderRadius: BorderRadius.circular(14), - ), - child: Center( - child: Text( - 'Cancel', - style: TextStyle(color: colors.textPrimary, fontSize: 16, fontWeight: FontWeight.w500), - ), - ), - ), - ), - ), - ], - ), - ); - } - - Widget _scanActionButton({required IconData icon, required VoidCallback onTap, required AppColorsV2 colors}) { - return GestureDetector( - onTap: onTap, - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration(color: Colors.white.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8)), - child: Icon(icon, size: 20, color: colors.textPrimary), - ), - ); - } -} - -class _ScanFrame extends StatelessWidget { - final double size; - const _ScanFrame({required this.size}); - - @override - Widget build(BuildContext context) { - final corner = Colors.white.withValues(alpha: 0.92); - return SizedBox( - width: size, - height: size, - child: Stack( - children: [ - _corner(top: true, left: true, color: corner), - _corner(top: true, left: false, color: corner), - _corner(top: false, left: true, color: corner), - _corner(top: false, left: false, color: corner), - ], - ), - ); - } - - Widget _corner({required bool top, required bool left, required Color color}) { - return Positioned( - top: top ? 0 : null, - bottom: top ? null : 0, - left: left ? 0 : null, - right: left ? null : 0, - child: SizedBox( - width: 41, - height: 41, - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: top && left ? const Radius.circular(16) : Radius.zero, - topRight: top && !left ? const Radius.circular(16) : Radius.zero, - bottomLeft: !top && left ? const Radius.circular(16) : Radius.zero, - bottomRight: !top && !left ? const Radius.circular(16) : Radius.zero, - ), - border: Border( - top: top ? BorderSide(color: color, width: 1.6) : BorderSide.none, - bottom: !top ? BorderSide(color: color, width: 1.6) : BorderSide.none, - left: left ? BorderSide(color: color, width: 1.6) : BorderSide.none, - right: !left ? BorderSide(color: color, width: 1.6) : BorderSide.none, - ), - ), - ), - ), - ); - } -} diff --git a/mobile-app/pubspec.lock b/mobile-app/pubspec.lock index b6cd5527..f604a6d3 100644 --- a/mobile-app/pubspec.lock +++ b/mobile-app/pubspec.lock @@ -433,6 +433,38 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0" + url: "https://pub.dev" + source: hosted + version: "0.9.4" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a" + url: "https://pub.dev" + source: hosted + version: "0.9.5" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85" + url: "https://pub.dev" + source: hosted + version: "2.7.0" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "62197474ae75893a62df75939c777763d39c2bc5f73ce5b88497208bc269abfd" + url: "https://pub.dev" + source: hosted + version: "0.9.3+5" firebase_core: dependency: "direct main" description: @@ -809,6 +841,70 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.4" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "784210112be18ea55f69d7076e2c656a4e24949fa9e76429fe53af0c0f4fa320" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "518a16108529fc18657a3e6dde4a043dc465d16596d20ab2abd49a4cac2e703d" + url: "https://pub.dev" + source: hosted + version: "0.8.13+13" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: b9c4a438a9ff4f60808c9cf0039b93a42bb6c2211ef6ebb647394b2b3fa84588 + url: "https://pub.dev" + source: hosted + version: "0.8.13+6" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" + url: "https://pub.dev" + source: hosted + version: "0.2.2" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" + url: "https://pub.dev" + source: hosted + version: "0.2.2+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" + url: "https://pub.dev" + source: hosted + version: "2.11.1" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae + url: "https://pub.dev" + source: hosted + version: "0.2.2" intl: dependency: "direct main" description: @@ -1869,5 +1965,5 @@ packages: source: hosted version: "2.1.0" sdks: - dart: ">=3.9.0 <4.0.0" - flutter: ">=3.35.0" + dart: ">=3.10.0 <4.0.0" + flutter: ">=3.38.0" diff --git a/mobile-app/pubspec.yaml b/mobile-app/pubspec.yaml index 9e048be6..2c09fe2b 100644 --- a/mobile-app/pubspec.yaml +++ b/mobile-app/pubspec.yaml @@ -60,6 +60,7 @@ dependencies: glass_kit: ^4.0.2 firebase_core: ^4.4.0 firebase_messaging: ^16.1.1 + image_picker: ^1.2.1 dev_dependencies: flutter_test: