Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ PODS:
- local_auth_darwin (0.0.1):
- Flutter
- FlutterMacOS
- mobile_scanner (7.0.0):
- Flutter
- FlutterMacOS
- no_screenshot (0.10.0):
- Flutter
- open_file_ios (1.0.3):
Expand All @@ -49,6 +52,7 @@ DEPENDENCIES:
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
- no_screenshot (from `.symlinks/plugins/no_screenshot/ios`)
- open_file_ios (from `.symlinks/plugins/open_file_ios/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
Expand Down Expand Up @@ -78,6 +82,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/image_picker_ios/ios"
local_auth_darwin:
:path: ".symlinks/plugins/local_auth_darwin/darwin"
mobile_scanner:
:path: ".symlinks/plugins/mobile_scanner/darwin"
no_screenshot:
:path: ".symlinks/plugins/no_screenshot/ios"
open_file_ios:
Expand All @@ -99,6 +105,7 @@ SPEC CHECKSUMS:
IdensicMobileSDK: a8ec2cf5c216ae138b00e2ff32e0e31c5e366cec
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
no_screenshot: 03c8ac6586f9652cd45e3d12d74e5992256403ac
open_file_ios: 46184d802ee7959203f6392abcfa0dd49fdb5be0
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/pay/pay_scan_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class PayScanView extends StatelessWidget {
appBar: AppBar(title: Text(S.of(context).payScanTitle)),
body: QrScannerView(
onDetect: (raw) => context.read<PayScanCubit>().onCodeDetected(raw),
errorBuilder: (context, error, child) {
errorBuilder: (context, error) {
final message = error.errorCode == MobileScannerErrorCode.permissionDenied
? S.of(context).payScanCameraPermissionDenied
: S.of(context).payScanCameraUnavailable;
Expand Down
28 changes: 24 additions & 4 deletions lib/widgets/scanner/qr_scanner_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,26 @@
// pay flow (LNURL decode) and the wallet-to-wallet send flow (EVM address
// decode); the per-flow decode logic it feeds is unit-tested in the respective
// cubit tests.
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:realunit_wallet/styles/colors.dart';

/// Compact icon-only error placeholder used when [QrScannerView.errorBuilder]
/// is null. Replaces mobile_scanner 7.x's own (taller, textScale-scaling)
/// default to avoid overflow in bounded layouts like send_recipient_page.dart's
/// Expanded.
Widget _defaultErrorBuilder(BuildContext context, MobileScannerException error) {
return ColoredBox(
color: RealUnitColors.basic.black,
child: Center(
child: Icon(
Icons.error_outline,
size: 48,
color: RealUnitColors.status.red600,
),
),
);
}

/// Thin wrapper around [MobileScanner] that surfaces the first raw barcode
/// value of each capture via [onDetect]. Keeping the camera/MethodChannel
Expand All @@ -17,8 +35,10 @@ class QrScannerView extends StatelessWidget {
final ValueChanged<String> onDetect;

/// Optional error UI builder forwarded to [MobileScanner.errorBuilder].
/// When null, MobileScanner's own default error handling is used.
final Widget Function(BuildContext, MobileScannerException, Widget?)? errorBuilder;
/// When null, this compact icon-only placeholder is used instead of
/// mobile_scanner 7.x's own (taller, textScale-scaling) default, to avoid
/// overflow in bounded layouts like send_recipient_page.dart's Expanded.
final Widget Function(BuildContext, MobileScannerException)? errorBuilder;

const QrScannerView({
super.key,
Expand All @@ -33,7 +53,7 @@ class QrScannerView extends StatelessWidget {
final raw = capture.barcodes.firstOrNull?.rawValue;
if (raw != null) onDetect(raw);
},
errorBuilder: errorBuilder,
errorBuilder: errorBuilder ?? _defaultErrorBuilder,
);
}
}
6 changes: 3 additions & 3 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ packages:
description:
path: "."
ref: "v0.0.10"
resolved-ref: "cd99ce656410e8df6c6585076d6ee0205a67b34c"
resolved-ref: cd99ce656410e8df6c6585076d6ee0205a67b34c
url: "https://github.com/DFXswiss/bitbox_flutter.git"
source: git
version: "0.0.1"
Expand Down Expand Up @@ -955,10 +955,10 @@ packages:
dependency: "direct main"
description:
name: mobile_scanner
sha256: d234581c090526676fd8fab4ada92f35c6746e3fb4f05a399665d75a399fb760
sha256: ce3f059ebd6dbfab7292bba0e893e354b46730636820d3c9ef69005ce2d55bce
url: "https://pub.dev"
source: hosted
version: "5.2.3"
version: "7.4.0"
mocktail:
dependency: "direct dev"
description:
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ dependencies:
http: ^1.1.0
intl: any
local_auth: ^3.0.0
mobile_scanner: ^5.2.3
mobile_scanner: ^7.4.0
no_screenshot: ^1.1.0
open_file: ^3.5.11
path: ^1.9.0
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions test/helper/golden_plugin_stubs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,10 @@ void stubMobileScannerChannel() {
const EventChannel('dev.steenbakker.mobile_scanner/scanner/event'),
MockStreamHandler.inline(onListen: (arguments, sink) {}),
);
// mobile_scanner 7.x additionally subscribes to a device-orientation event
// stream; stub it as a no-op so `listen`/`cancel` don't throw MissingPluginException.
messenger.setMockStreamHandler(
const EventChannel('dev.steenbakker.mobile_scanner/scanner/deviceOrientation'),
MockStreamHandler.inline(onListen: (arguments, sink) {}),
);
}
1 change: 0 additions & 1 deletion test/screens/pay/pay_scan_page_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ void main() {
final errorWidget = scanner.errorBuilder!(
context,
MobileScannerException(errorCode: nonPermissionCode),
null,
);

await tester.pumpWidget(MaterialApp(home: errorWidget));
Expand Down
40 changes: 40 additions & 0 deletions test/widgets/scanner/qr_scanner_view_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:realunit_wallet/widgets/scanner/qr_scanner_view.dart';

import '../../helper/helper.dart';

void main() {
setUpAll(stubMobileScannerChannel);

group('$QrScannerView default error placeholder', () {
testWidgets(
'renders the compact icon placeholder without overflow at high textScale in a tight box',
(tester) async {
await tester.pumpApp(
MediaQuery(
data: const MediaQueryData(size: Size(400, 800))
.copyWith(textScaler: const TextScaler.linear(3.0)),
child: Center(
child: SizedBox(
width: 60,
height: 60,
child: QrScannerView(onDetect: (_) {}),
),
),
),
);

// Let the stubbed permission handshake resolve into the
// permission-denied error state so the default error builder paints.
await tester.pump();
await tester.pump(const Duration(milliseconds: 50));

expect(tester.takeException(), isNull);
expect(find.byIcon(Icons.error_outline), findsOneWidget);
final icon = tester.widget<Icon>(find.byIcon(Icons.error_outline));
expect(icon.size, 48);
},
);
});
}
Loading