From 0a17aad6e96d8be31a053fa2e1efb8fcb73bceb4 Mon Sep 17 00:00:00 2001 From: joshua Date: Wed, 3 Jun 2026 19:18:25 +0200 Subject: [PATCH 1/2] =?UTF-8?q?ci:=20harden=20static=20analysis=20?= =?UTF-8?q?=E2=80=94=20strict=20casts=20+=20null/async=20lints,=20--fatal-?= =?UTF-8?q?infos/-warnings,=20FD-limit=20fix=20for=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request.yaml | 4 ++-- analysis_options.yaml | 12 ++++++++++++ lib/main.dart | 2 +- .../repository/supported_fiat_repository.dart | 3 ++- lib/packages/service/balance_service.dart | 2 +- lib/packages/service/debug_auth_service.dart | 2 +- .../service/dfx/dfx_auth_service.dart | 5 +++-- .../service/dfx/dfx_bank_account_service.dart | 9 +++++---- .../service/dfx/dfx_brokerbot_service.dart | 8 ++++---- .../service/dfx/dfx_country_service.dart | 4 ++-- .../service/dfx/dfx_fiat_service.dart | 5 +++-- lib/packages/service/dfx/dfx_kyc_service.dart | 6 +++--- .../service/dfx/dfx_language_service.dart | 4 ++-- .../service/dfx/dfx_price_service.dart | 19 ++++++++++--------- .../real_unit_buy_payment_info_service.dart | 2 +- .../service/dfx/real_unit_pdf_service.dart | 6 +++--- .../dfx/real_unit_registration_service.dart | 11 +++++++---- .../real_unit_sell_payment_info_service.dart | 2 +- .../service/transaction_history_service.dart | 6 +++--- lib/packages/utils/format_fixed.dart | 4 ++-- lib/packages/utils/parse_fixed.dart | 4 ++-- ...yment_additional_action_needed_button.dart | 13 +++++++------ .../widgets/payment_information_details.dart | 2 +- .../bloc/connect_bitbox_cubit.dart | 4 ++-- lib/screens/home/bloc/home_bloc.dart | 5 +++-- .../kyc/steps/email/kyc_email_page.dart | 5 +++-- .../registration/kyc_registration_page.dart | 6 +++--- lib/screens/pin/verify_pin_page.dart | 4 ++-- .../validate_seed/validate_seed_cubit.dart | 2 +- .../restore_wallet/restore_wallet_view.dart | 2 +- .../sell/widgets/sell_bank_account_field.dart | 7 ++++--- .../sell_bank_account_selection_page.dart | 2 +- lib/screens/sell/widgets/sell_button.dart | 11 ++++++----- lib/screens/sell_bitbox/sell_bitbox_page.dart | 3 ++- lib/screens/settings/settings_page.dart | 4 ++-- .../settings_contact_page.dart | 9 +++++---- .../settings_user_data_page.dart | 7 ++++--- .../settings_edit_address_page.dart | 5 +++-- .../edit_name/settings_edit_name_page.dart | 5 +++-- lib/screens/verify_seed/verify_seed_page.dart | 2 +- lib/screens/welcome/welcome_page.dart | 2 +- lib/setup/routing/router_config.dart | 10 +++++----- lib/widgets/buttons/app_filled_button.dart | 6 +++--- lib/widgets/buttons/app_text_button.dart | 2 +- test/packages/config/network_mode_test.dart | 4 ++-- .../dfx/dfx_blockchain_api_service_test.dart | 6 +++--- .../registration/registration_dtos_test.dart | 4 ++-- test/screens/buy/buy_page_test.dart | 4 ++-- .../cubit/legal_disclaimer_cubit_test.dart | 2 +- test/screens/pin/setup_pin_cubit_test.dart | 4 ++-- test/screens/pin/setup_pin_page_test.dart | 2 +- test/screens/pin/verify_pin_cubit_test.dart | 6 +++--- test/screens/pin/verify_pin_page_test.dart | 4 ++-- .../pin/widgets/pin_indicator_test.dart | 8 ++++---- .../welcome/widgets/welcome_card_test.dart | 2 +- .../text_substring_highlighting_test.dart | 2 +- 56 files changed, 158 insertions(+), 128 deletions(-) diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 43276344..2626ec07 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -70,7 +70,7 @@ jobs: - run: dart run tool/generate_localization.dart - run: dart run tool/generate_release_info.dart - run: flutter pub run build_runner build - - run: flutter analyze + - run: flutter analyze --fatal-infos --fatal-warnings # Excludes the `golden` tag: visual-regression tests live under # `test/goldens/` and are validated on the dfx01 self-hosted runner # in the parallel `golden-tests` job (Hardware-Determinismus, see @@ -78,7 +78,7 @@ jobs: # both duplicate work and erroneously red this job on macos-latest # where the Skia/font-rendering does not match the committed # baselines. - - run: flutter test --coverage --exclude-tags golden + - run: ulimit -n 16384 && flutter test --coverage --exclude-tags golden # Narrow the coverage report to the README-defined activated surface: # lib/packages/** — services, repositories, signers, utils diff --git a/analysis_options.yaml b/analysis_options.yaml index e9380ff9..b991a218 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,9 +4,21 @@ formatter: page_width: 100 trailing_commas: preserve +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true + linter: rules: annotate_overrides: true avoid_print: true prefer_const_constructors: true prefer_single_quotes: true + unawaited_futures: true + avoid_dynamic_calls: true + cast_nullable_to_non_nullable: true + null_check_on_nullable_type_parameter: true + unnecessary_null_checks: true + prefer_final_locals: true diff --git a/lib/main.dart b/lib/main.dart index 4efe297c..e9051415 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -41,7 +41,7 @@ Future _initialize() async { Future _initializeWithSplashDuration() async { await Future.wait([ _initialize(), - Future.delayed(const Duration(seconds: 3)), + Future.delayed(const Duration(seconds: 3)), ]); } diff --git a/lib/packages/repository/supported_fiat_repository.dart b/lib/packages/repository/supported_fiat_repository.dart index 4ba5ea21..8045a852 100644 --- a/lib/packages/repository/supported_fiat_repository.dart +++ b/lib/packages/repository/supported_fiat_repository.dart @@ -1,6 +1,7 @@ import 'dart:developer' as developer; import 'package:realunit_wallet/packages/service/dfx/dfx_fiat_service.dart'; +import 'package:realunit_wallet/packages/service/dfx/models/fiat/dto/dfx_fiat_dto.dart'; import 'package:realunit_wallet/styles/currency.dart'; /// Translates the backend's per-user fiat list into the local [Currency] @@ -23,7 +24,7 @@ class SupportedFiatRepository { Future> getAll() => _resolve((_) => true); - Future> _resolve(bool Function(dynamic) filter) async { + Future> _resolve(bool Function(DfxFiatDto) filter) async { final fiats = await _fiatService.getAllFiats(); final result = []; for (final fiat in fiats) { diff --git a/lib/packages/service/balance_service.dart b/lib/packages/service/balance_service.dart index 771fdab2..6457bd45 100644 --- a/lib/packages/service/balance_service.dart +++ b/lib/packages/service/balance_service.dart @@ -36,7 +36,7 @@ class BalanceService { final response = await _appStore.httpClient.get(uri); if (response.statusCode == 200) { - final json = jsonDecode(response.body); + final json = jsonDecode(response.body) as Map; final balanceString = json['balance'] as String?; if (balanceString != null) { diff --git a/lib/packages/service/debug_auth_service.dart b/lib/packages/service/debug_auth_service.dart index ed8c17ed..b2d643fe 100644 --- a/lib/packages/service/debug_auth_service.dart +++ b/lib/packages/service/debug_auth_service.dart @@ -30,7 +30,7 @@ class DebugAuthService { ); if (response.statusCode == 200) { - final body = jsonDecode(response.body); + final body = jsonDecode(response.body) as Map; return body['message'] as String; } throw Exception('Failed to fetch sign message (${response.statusCode})'); diff --git a/lib/packages/service/dfx/dfx_auth_service.dart b/lib/packages/service/dfx/dfx_auth_service.dart index 0ac44478..97feb8fd 100644 --- a/lib/packages/service/dfx/dfx_auth_service.dart +++ b/lib/packages/service/dfx/dfx_auth_service.dart @@ -146,8 +146,9 @@ abstract class DFXAuthService { final responseBody = jsonDecode(response.body); return responseBody as Map; } else if (response.statusCode == 403) { - final responseBody = jsonDecode(response.body); - final message = responseBody['message'] ?? 'Service unavailable in your country'; + final responseBody = jsonDecode(response.body) as Map; + final message = + responseBody['message'] ?? 'Service unavailable in your country'; throw Exception(message); } else { throw Exception('Failed to sign up. Status: ${response.statusCode} ${response.body}'); diff --git a/lib/packages/service/dfx/dfx_bank_account_service.dart b/lib/packages/service/dfx/dfx_bank_account_service.dart index 6dd21d93..95878781 100644 --- a/lib/packages/service/dfx/dfx_bank_account_service.dart +++ b/lib/packages/service/dfx/dfx_bank_account_service.dart @@ -24,8 +24,9 @@ class DfxBankAccountService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - final List jsonList = jsonDecode(response.body); - final bankAccounts = jsonList.map((json) => BankAccountDto.fromJson(json)).toList(); + final jsonList = jsonDecode(response.body) as List; + final bankAccounts = + jsonList.map((json) => BankAccountDto.fromJson(json as Map)).toList(); return bankAccounts; } @@ -47,7 +48,7 @@ class DfxBankAccountService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - return BankAccountDto.fromJson(jsonDecode(response.body)); + return BankAccountDto.fromJson(jsonDecode(response.body) as Map); } Future updateBankAccount({ @@ -74,6 +75,6 @@ class DfxBankAccountService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - return BankAccountDto.fromJson(jsonDecode(response.body)); + return BankAccountDto.fromJson(jsonDecode(response.body) as Map); } } diff --git a/lib/packages/service/dfx/dfx_brokerbot_service.dart b/lib/packages/service/dfx/dfx_brokerbot_service.dart index 01e9f30c..38760f8a 100644 --- a/lib/packages/service/dfx/dfx_brokerbot_service.dart +++ b/lib/packages/service/dfx/dfx_brokerbot_service.dart @@ -34,7 +34,7 @@ class DfxBrokerbotService extends DFXAuthService { throw Exception('BuyPrice request failed: ${res.body}'); } - return BrokerbotBuyPriceDto.fromJson(jsonDecode(res.body)); + return BrokerbotBuyPriceDto.fromJson(jsonDecode(res.body) as Map); } /// Convert CHF → REALU shares @@ -54,7 +54,7 @@ class DfxBrokerbotService extends DFXAuthService { throw Exception('Shares request failed: ${res.body}'); } - return BrokerbotBuySharesDto.fromJson(jsonDecode(res.body)); + return BrokerbotBuySharesDto.fromJson(jsonDecode(res.body) as Map); } /// Convert REALU shares → CHF (with fees) @@ -75,7 +75,7 @@ class DfxBrokerbotService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: res.statusCode); } - return BrokerbotSellPriceDto.fromJson(jsonDecode(res.body)); + return BrokerbotSellPriceDto.fromJson(jsonDecode(res.body) as Map); } /// Convert CHF → REALU shares (with fees) @@ -96,6 +96,6 @@ class DfxBrokerbotService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: res.statusCode); } - return BrokerbotSellSharesDto.fromJson(jsonDecode(res.body)); + return BrokerbotSellSharesDto.fromJson(jsonDecode(res.body) as Map); } } diff --git a/lib/packages/service/dfx/dfx_country_service.dart b/lib/packages/service/dfx/dfx_country_service.dart index dc35a42a..d4810fd7 100644 --- a/lib/packages/service/dfx/dfx_country_service.dart +++ b/lib/packages/service/dfx/dfx_country_service.dart @@ -23,9 +23,9 @@ class DfxCountryService { final response = await _appStore.httpClient.get(uri); if (response.statusCode == 200) { - final List jsonList = jsonDecode(response.body); + final jsonList = jsonDecode(response.body) as List; final countries = jsonList - .map((json) => DfxCountryDto.fromJson(json)) + .map((json) => DfxCountryDto.fromJson(json as Map)) .map((dto) => dto.toCountry()) .toList(); cachedCountries = countries; diff --git a/lib/packages/service/dfx/dfx_fiat_service.dart b/lib/packages/service/dfx/dfx_fiat_service.dart index f2a25c50..7f7784a0 100644 --- a/lib/packages/service/dfx/dfx_fiat_service.dart +++ b/lib/packages/service/dfx/dfx_fiat_service.dart @@ -40,8 +40,9 @@ class DfxFiatService { throw Exception('Failed to fetch fiats: ${response.statusCode}'); } - final List jsonList = jsonDecode(response.body); - final fiats = jsonList.map((json) => DfxFiatDto.fromJson(json)).toList(); + final jsonList = jsonDecode(response.body) as List; + final fiats = + jsonList.map((json) => DfxFiatDto.fromJson(json as Map)).toList(); _cachedFiats = fiats; _cachedAt = clock.now(); return fiats; diff --git a/lib/packages/service/dfx/dfx_kyc_service.dart b/lib/packages/service/dfx/dfx_kyc_service.dart index d7acd58b..b6cf1d72 100644 --- a/lib/packages/service/dfx/dfx_kyc_service.dart +++ b/lib/packages/service/dfx/dfx_kyc_service.dart @@ -53,7 +53,7 @@ class DfxKycService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - final json = jsonDecode(response.body); + final json = jsonDecode(response.body) as Map; return KycLevelDto.fromJson(json); } @@ -76,7 +76,7 @@ class DfxKycService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - final json = jsonDecode(response.body); + final json = jsonDecode(response.body) as Map; return KycSessionDto.fromJson(json); } @@ -98,7 +98,7 @@ class DfxKycService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - final json = jsonDecode(response.body); + final json = jsonDecode(response.body) as Map; return KycSessionDto.fromJson(json); } diff --git a/lib/packages/service/dfx/dfx_language_service.dart b/lib/packages/service/dfx/dfx_language_service.dart index 21302a6f..e5eaa115 100644 --- a/lib/packages/service/dfx/dfx_language_service.dart +++ b/lib/packages/service/dfx/dfx_language_service.dart @@ -39,9 +39,9 @@ class DfxLanguageService { throw Exception('Failed to fetch languages: ${response.statusCode}'); } - final List jsonList = jsonDecode(response.body); + final jsonList = jsonDecode(response.body) as List; final languages = jsonList - .map((json) => DfxLanguageDto.fromJson(json)) + .map((json) => DfxLanguageDto.fromJson(json as Map)) .toList(); _cachedLanguages = languages; _cachedAt = clock.now(); diff --git a/lib/packages/service/dfx/dfx_price_service.dart b/lib/packages/service/dfx/dfx_price_service.dart index dba6fe7b..59c5e787 100644 --- a/lib/packages/service/dfx/dfx_price_service.dart +++ b/lib/packages/service/dfx/dfx_price_service.dart @@ -24,25 +24,26 @@ class DFXPriceService extends APriceService { if (response.statusCode != 200) throw Exception(response.body); - final body = jsonDecode(response.body) as List; + final body = jsonDecode(response.body) as List; final result = []; - for (final entry in body) { + for (final raw in body) { + final entry = raw as Map; BigInt price; switch (currency) { case Currency.eur: - price = BigInt.from(entry['eur'] * 100); + price = BigInt.from((entry['eur'] as num) * 100); break; case Currency.chf: - price = BigInt.from(entry['chf'] * 100); + price = BigInt.from((entry['chf'] as num) * 100); break; } result.add( PricePoint( asset: asset, price: price, - time: DateTime.parse(entry['timestamp']), + time: DateTime.parse(entry['timestamp'] as String), ), ); } @@ -57,13 +58,13 @@ class DFXPriceService extends APriceService { if (response.statusCode != 200) throw Exception(response.body); - final body = jsonDecode(response.body); + final body = jsonDecode(response.body) as Map; switch (currency) { case Currency.eur: - return BigInt.from(body['eur'] * 100); + return BigInt.from((body['eur'] as num) * 100); case Currency.chf: - return BigInt.from(body['chf'] * 100); + return BigInt.from((body['chf'] as num) * 100); } } @@ -74,7 +75,7 @@ class DFXPriceService extends APriceService { if (response.statusCode != 200) throw Exception(response.body); - final body = jsonDecode(response.body); + final body = jsonDecode(response.body) as Map; final chf = (body['chf'] as num).toDouble(); final eur = (body['eur'] as num).toDouble(); diff --git a/lib/packages/service/dfx/real_unit_buy_payment_info_service.dart b/lib/packages/service/dfx/real_unit_buy_payment_info_service.dart index 86e50d82..bbad63d9 100644 --- a/lib/packages/service/dfx/real_unit_buy_payment_info_service.dart +++ b/lib/packages/service/dfx/real_unit_buy_payment_info_service.dart @@ -28,7 +28,7 @@ class RealUnitBuyPaymentInfoService extends DFXAuthService { ); if (response.statusCode == 200) { - final json = jsonDecode(response.body); + final json = jsonDecode(response.body) as Map; final responseDto = RealUnitBuyPaymentInfoDto.fromJson(json); return BuyPaymentInfo( diff --git a/lib/packages/service/dfx/real_unit_pdf_service.dart b/lib/packages/service/dfx/real_unit_pdf_service.dart index 50e4b6a1..0ff63d24 100644 --- a/lib/packages/service/dfx/real_unit_pdf_service.dart +++ b/lib/packages/service/dfx/real_unit_pdf_service.dart @@ -43,7 +43,7 @@ class RealUnitPdfService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - return PdfDto.fromJson(jsonDecode(response.body)); + return PdfDto.fromJson(jsonDecode(response.body) as Map); } Future getTransactionsReceipt( @@ -64,7 +64,7 @@ class RealUnitPdfService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - return PdfDto.fromJson(jsonDecode(response.body)); + return PdfDto.fromJson(jsonDecode(response.body) as Map); } Future getTransactionReceipt(String id, {Currency currency = Currency.chf}) async { @@ -82,6 +82,6 @@ class RealUnitPdfService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - return PdfDto.fromJson(jsonDecode(response.body)); + return PdfDto.fromJson(jsonDecode(response.body) as Map); } } diff --git a/lib/packages/service/dfx/real_unit_registration_service.dart b/lib/packages/service/dfx/real_unit_registration_service.dart index ab4f1711..76bb80ef 100644 --- a/lib/packages/service/dfx/real_unit_registration_service.dart +++ b/lib/packages/service/dfx/real_unit_registration_service.dart @@ -45,7 +45,7 @@ class RealUnitRegistrationService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - return RealUnitRegistrationInfoDto.fromJson(jsonDecode(response.body)); + return RealUnitRegistrationInfoDto.fromJson(jsonDecode(response.body) as Map); } /// registers an email on the wallet. Should always be called first when registering @@ -67,7 +67,8 @@ class RealUnitRegistrationService extends DFXAuthService { final errorJson = jsonDecode(response.body) as Map; throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - final responseDto = RealUnitRegistrationEmailResponseDto.fromJson(jsonDecode(response.body)); + final responseDto = + RealUnitRegistrationEmailResponseDto.fromJson(jsonDecode(response.body) as Map); return responseDto.status; } @@ -166,7 +167,8 @@ class RealUnitRegistrationService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - final responseDto = RealUnitRegistrationResponseDto.fromJson(jsonDecode(response.body)); + final responseDto = + RealUnitRegistrationResponseDto.fromJson(jsonDecode(response.body) as Map); return responseDto.status; } @@ -223,7 +225,8 @@ class RealUnitRegistrationService extends DFXAuthService { throw ApiException.fromJson(errorJson, httpStatusCode: response.statusCode); } - final responseDto = RealUnitRegistrationResponseDto.fromJson(jsonDecode(response.body)); + final responseDto = + RealUnitRegistrationResponseDto.fromJson(jsonDecode(response.body) as Map); return responseDto.status; } } diff --git a/lib/packages/service/dfx/real_unit_sell_payment_info_service.dart b/lib/packages/service/dfx/real_unit_sell_payment_info_service.dart index 87158095..5c1b2021 100644 --- a/lib/packages/service/dfx/real_unit_sell_payment_info_service.dart +++ b/lib/packages/service/dfx/real_unit_sell_payment_info_service.dart @@ -49,7 +49,7 @@ class RealUnitSellPaymentInfoService extends DFXAuthService { ); if (response.statusCode == 200) { - final json = jsonDecode(response.body); + final json = jsonDecode(response.body) as Map; final responseDto = RealUnitSellPaymentInfoDto.fromJson(json); return SellPaymentInfo( diff --git a/lib/packages/service/transaction_history_service.dart b/lib/packages/service/transaction_history_service.dart index 2212fa5d..18fdafbc 100644 --- a/lib/packages/service/transaction_history_service.dart +++ b/lib/packages/service/transaction_history_service.dart @@ -25,7 +25,7 @@ class TransactionHistoryService extends DFXAuthService { ]); final accountHistory = results.elementAt(0) as AccountHistoryDto?; - final transactions = results.elementAt(1) as List; + final transactions = results.elementAt(1)! as List; if (accountHistory == null) return; @@ -105,7 +105,7 @@ class TransactionHistoryService extends DFXAuthService { final response = await appStore.httpClient.get(uri); if (response.statusCode != 200) return []; - final List json = jsonDecode(response.body); + final json = jsonDecode(response.body) as List; return json.map((e) => TransactionDto.fromJson(e as Map)).toList(); } @@ -115,7 +115,7 @@ class TransactionHistoryService extends DFXAuthService { if (response.statusCode != 200) return []; - final List json = jsonDecode(response.body); + final json = jsonDecode(response.body) as List; final transactions = json .map((e) => TransactionDto.fromJson(e as Map)) .toList(); diff --git a/lib/packages/utils/format_fixed.dart b/lib/packages/utils/format_fixed.dart index 79583ae9..e53a7648 100644 --- a/lib/packages/utils/format_fixed.dart +++ b/lib/packages/utils/format_fixed.dart @@ -4,9 +4,9 @@ String formatFixed(BigInt value, int? decimals, {int? fractionalDigits, bool tri decimals ??= 0; fractionalDigits ??= decimals; - var multiplier = getMultiplier(decimals); + final multiplier = getMultiplier(decimals); // Make sure wei is a big number (convert as necessary) - var negative = value.isNegative; + final negative = value.isNegative; if (negative) value = value * BigInt.from(-1); var fraction = diff --git a/lib/packages/utils/parse_fixed.dart b/lib/packages/utils/parse_fixed.dart index 94fbc817..e4bc407b 100644 --- a/lib/packages/utils/parse_fixed.dart +++ b/lib/packages/utils/parse_fixed.dart @@ -14,8 +14,8 @@ BigInt parseFixed(String value, int? decimals) { throw Exception('too many decimal points, value, $value'); } - var whole = comps.isNotEmpty ? comps[0] : '0'; - var fraction = (comps.length == 2 ? comps[1] : '0').padRight(decimals, '0'); + final whole = comps.isNotEmpty ? comps[0] : '0'; + final fraction = (comps.length == 2 ? comps[1] : '0').padRight(decimals, '0'); // Check the fraction doesn't exceed our decimals size if (fraction.length > multiplier.length - 1) { diff --git a/lib/screens/buy/widgets/payment_additional_action_needed_button.dart b/lib/screens/buy/widgets/payment_additional_action_needed_button.dart index 13d71832..2d7082ea 100644 --- a/lib/screens/buy/widgets/payment_additional_action_needed_button.dart +++ b/lib/screens/buy/widgets/payment_additional_action_needed_button.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -57,10 +58,10 @@ class PaymentAdditionalActionNeededButton extends StatelessWidget { onPressed: () async { await context.pushNamed(AppRoutes.kyc, extra: paymentState.context); if (context.mounted) { - context.read().getPaymentInfo( + unawaited(context.read().getPaymentInfo( amount: amountController.text, currency: context.read().state.currency, - ); + )); } }, label: S.of(context).next, @@ -74,10 +75,10 @@ class PaymentAdditionalActionNeededButton extends StatelessWidget { onPressed: () async { await context.pushNamed(AppRoutes.kyc, extra: paymentState.context); if (context.mounted) { - context.read().getPaymentInfo( + unawaited(context.read().getPaymentInfo( amount: amountController.text, currency: context.read().state.currency, - ); + )); } }, label: S.of(context).next, @@ -92,10 +93,10 @@ class PaymentAdditionalActionNeededButton extends StatelessWidget { final paymentInfoCubit = context.read(); final converterCubit = context.read(); await showBitboxReconnectSheet(context); - paymentInfoCubit.getPaymentInfo( + unawaited(paymentInfoCubit.getPaymentInfo( amount: amountController.text, currency: converterCubit.state.currency, - ); + )); }, label: S.of(context).bitboxReconnect, ), diff --git a/lib/screens/buy/widgets/payment_information_details.dart b/lib/screens/buy/widgets/payment_information_details.dart index d1e794b1..5642527c 100644 --- a/lib/screens/buy/widgets/payment_information_details.dart +++ b/lib/screens/buy/widgets/payment_information_details.dart @@ -62,7 +62,7 @@ class PaymentInformationDetailsView extends StatelessWidget { return BlocListener( listener: (context, state) async { if (state is BuyConfirmSuccess) { - await showModalBottomSheet( + await showModalBottomSheet( context: context, builder: (_) => PaymentExecutedSheet(reference: state.reference), ); diff --git a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart index 60170bb6..26cdc5a7 100644 --- a/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart +++ b/lib/screens/hardware_connect_bitbox/bloc/connect_bitbox_cubit.dart @@ -59,7 +59,7 @@ class ConnectBitboxCubit extends Cubit { if (devices.isNotEmpty) { emit(BitboxFound(devices.first)); _checkForTimer?.cancel(); - connectToBitbox(devices.first); + unawaited(connectToBitbox(devices.first)); } } @@ -98,7 +98,7 @@ class ConnectBitboxCubit extends Cubit { // First sleep is longer so the SDK can finish setting up its Go-side // device pointer before we call into it; subsequent iterations stay // tight so the post-PIN handover feels snappy. - await Future.delayed( + await Future.delayed( firstIteration ? const Duration(milliseconds: 500) : const Duration(milliseconds: 100), ); firstIteration = false; diff --git a/lib/screens/home/bloc/home_bloc.dart b/lib/screens/home/bloc/home_bloc.dart index 2ddc90f6..0e97f15f 100644 --- a/lib/screens/home/bloc/home_bloc.dart +++ b/lib/screens/home/bloc/home_bloc.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:developer' as developer; import 'package:equatable/equatable.dart'; @@ -73,9 +74,9 @@ class HomeBloc extends Bloc { return; } - _balanceService.updateBalance(_appStore.primaryAddress); + unawaited(_balanceService.updateBalance(_appStore.primaryAddress)); _balanceService.startSync(_appStore.primaryAddress); - _transactionHistoryService.apiBasedSync(); + unawaited(_transactionHistoryService.apiBasedSync()); } Future _onDeleteCurrentWallet( diff --git a/lib/screens/kyc/steps/email/kyc_email_page.dart b/lib/screens/kyc/steps/email/kyc_email_page.dart index 9710e06c..11f399fb 100644 --- a/lib/screens/kyc/steps/email/kyc_email_page.dart +++ b/lib/screens/kyc/steps/email/kyc_email_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:realunit_wallet/generated/i18n.dart'; @@ -70,7 +71,7 @@ class _KycEmailFormState extends State { } if (state is KycEmailStepSuccess) { if (state.status == .emailRegistered) { - context.read().checkKyc(); + unawaited(context.read().checkKyc()); } if (state.status == .mergeRequested) { final isConfirmed = await Navigator.push( @@ -86,7 +87,7 @@ class _KycEmailFormState extends State { // `checkKyc()` re-fetches `getRegistrationInfo`, sees // `AlreadyRegistered`, and routes forward — no local sign-gate // flag needed. - context.read().checkKyc(); + unawaited(context.read().checkKyc()); } } } diff --git a/lib/screens/kyc/steps/registration/kyc_registration_page.dart b/lib/screens/kyc/steps/registration/kyc_registration_page.dart index f73be532..e4ee5bff 100644 --- a/lib/screens/kyc/steps/registration/kyc_registration_page.dart +++ b/lib/screens/kyc/steps/registration/kyc_registration_page.dart @@ -167,7 +167,7 @@ class _KycRegistrationViewState extends State { // alreadyRegistered). The backend now reflects the new wallet, // so re-fetching `getRegistrationInfo` in `_runCheckKyc` will return // `AlreadyRegistered` and dispatch the next KYC step. - context.read().checkKyc(); + unawaited(context.read().checkKyc()); if (state.status == RegistrationStatus.forwardingFailed) { ScaffoldMessenger.of(context).showSnackBar( @@ -191,7 +191,7 @@ class _KycRegistrationViewState extends State { } if (state is KycRegistrationSubmitBitboxRequired) { final registration = state.registration; - final result = await showModalBottomSheet( + final result = await showModalBottomSheet( context: context, isScrollControlled: true, builder: (_) => ConnectBitboxPage( @@ -202,7 +202,7 @@ class _KycRegistrationViewState extends State { ), ); if (context.mounted && result == true) { - context.read().retrySubmit(registration); + unawaited(context.read().retrySubmit(registration)); } } }, diff --git a/lib/screens/pin/verify_pin_page.dart b/lib/screens/pin/verify_pin_page.dart index 9b2d9384..f0af94a9 100644 --- a/lib/screens/pin/verify_pin_page.dart +++ b/lib/screens/pin/verify_pin_page.dart @@ -152,7 +152,7 @@ class _VerifyPinViewState extends State { height: 40.0, child: Text( switch (state) { - VerifyPinTemporarilyLocked s => + final VerifyPinTemporarilyLocked s => S .of(context) .pinVerifyLockedTemporarily( @@ -243,7 +243,7 @@ class _ForgotPinButton extends StatelessWidget { builder: (_) => const ForgotPinBottomSheet(), ); if (isReset == true) { - await Future.delayed(const Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: 300)); if (context.mounted) { await context.read().reset(); if (context.mounted) { diff --git a/lib/screens/restore_wallet/cubit/validate_seed/validate_seed_cubit.dart b/lib/screens/restore_wallet/cubit/validate_seed/validate_seed_cubit.dart index a1910b24..c6a8e1ad 100644 --- a/lib/screens/restore_wallet/cubit/validate_seed/validate_seed_cubit.dart +++ b/lib/screens/restore_wallet/cubit/validate_seed/validate_seed_cubit.dart @@ -27,7 +27,7 @@ class ValidateSeedCubit extends Cubit { } } - bool _containsAll(Iterable a, Iterable b) { + bool _containsAll(Iterable a, Iterable b) { for (final element in b) { if (!a.contains(element)) return false; } diff --git a/lib/screens/restore_wallet/restore_wallet_view.dart b/lib/screens/restore_wallet/restore_wallet_view.dart index b72484a7..6fecc904 100644 --- a/lib/screens/restore_wallet/restore_wallet_view.dart +++ b/lib/screens/restore_wallet/restore_wallet_view.dart @@ -30,7 +30,7 @@ class _RestoreWalletViewState extends State { listenWhen: (previous, current) => previous.wallet != current.wallet, listener: (context, state) async { if (state.wallet != null) { - await Future.delayed(const Duration(seconds: 2)); + await Future.delayed(const Duration(seconds: 2)); if (context.mounted) context.read().add(LoadWalletEvent(state.wallet!)); } }, diff --git a/lib/screens/sell/widgets/sell_bank_account_field.dart b/lib/screens/sell/widgets/sell_bank_account_field.dart index 21149ae4..b6e50d47 100644 --- a/lib/screens/sell/widgets/sell_bank_account_field.dart +++ b/lib/screens/sell/widgets/sell_bank_account_field.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:developer' as developer; import 'package:collection/collection.dart'; @@ -140,7 +141,7 @@ class BankAccountFieldView extends StatelessWidget { final sellSelectedBankAccountCubit = context.read(); if (sellBankAccountsCubit.state.accounts.isEmpty) { - await showModalBottomSheet( + await showModalBottomSheet( isScrollControlled: true, context: context, builder: (_) => BlocProvider.value( @@ -149,7 +150,7 @@ class BankAccountFieldView extends StatelessWidget { ), ); } else { - Navigator.push( + unawaited(Navigator.push( context, MaterialPageRoute( builder: (_) => MultiBlocProvider( @@ -164,7 +165,7 @@ class BankAccountFieldView extends StatelessWidget { child: const SellBankAccountSelectionPage(), ), ), - ); + )); } } } diff --git a/lib/screens/sell/widgets/sell_bank_account_selection_page.dart b/lib/screens/sell/widgets/sell_bank_account_selection_page.dart index 1d7c32a3..6a83f698 100644 --- a/lib/screens/sell/widgets/sell_bank_account_selection_page.dart +++ b/lib/screens/sell/widgets/sell_bank_account_selection_page.dart @@ -108,7 +108,7 @@ class SellBankAccountSelectionPage extends StatelessWidget { Future _onAddBankAccountPressed(BuildContext context) async { final sellBankAccountsCubit = context.read(); - await showModalBottomSheet( + await showModalBottomSheet( isScrollControlled: true, context: context, builder: (_) => BlocProvider.value( diff --git a/lib/screens/sell/widgets/sell_button.dart b/lib/screens/sell/widgets/sell_button.dart index e3ea0581..2946a3e2 100644 --- a/lib/screens/sell/widgets/sell_button.dart +++ b/lib/screens/sell/widgets/sell_button.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -36,11 +37,11 @@ class SellButton extends StatelessWidget { final converterCurrency = context.read().state.currency; await showBitboxReconnectSheet(context); if (bankAccount != null && amount.isNotEmpty) { - paymentInfoCubit.getPaymentInfo( + unawaited(paymentInfoCubit.getPaymentInfo( amount: amount, iban: bankAccount!.iban, currency: converterCurrency, - ); + )); } return; } @@ -55,10 +56,10 @@ class SellButton extends StatelessWidget { } if (state is SellPaymentInfoSuccess) { if (state.isBitbox && context.mounted) { - context.pushNamed(AppRoutes.sellBitbox, extra: state.sellPaymentInfo); + unawaited(context.pushNamed(AppRoutes.sellBitbox, extra: state.sellPaymentInfo)); return; } else { - final bool? confirmedSuccess = await showModalBottomSheet( + final bool? confirmedSuccess = await showModalBottomSheet( isScrollControlled: true, context: context, builder: (_) => SellConfirmSheet( @@ -67,7 +68,7 @@ class SellButton extends StatelessWidget { ); if (confirmedSuccess ?? false) { if (context.mounted) { - await showModalBottomSheet( + await showModalBottomSheet( context: context, builder: (_) => const SellExecutedSheet(), ); diff --git a/lib/screens/sell_bitbox/sell_bitbox_page.dart b/lib/screens/sell_bitbox/sell_bitbox_page.dart index 8a3effdc..041e309c 100644 --- a/lib/screens/sell_bitbox/sell_bitbox_page.dart +++ b/lib/screens/sell_bitbox/sell_bitbox_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -48,7 +49,7 @@ class SellBitboxView extends StatelessWidget { if (state is SellBitboxBitboxRequired) { final reconnected = await showBitboxReconnectSheet(context); if (reconnected && context.mounted) { - context.read().retryAfterConnection(); + unawaited(context.read().retryAfterConnection()); } return; } diff --git a/lib/screens/settings/settings_page.dart b/lib/screens/settings/settings_page.dart index d66313b7..2e98adb9 100644 --- a/lib/screens/settings/settings_page.dart +++ b/lib/screens/settings/settings_page.dart @@ -125,13 +125,13 @@ class SettingsPage extends StatelessWidget { title: S.of(context).settingsDeleteWallet, leading: const XCircleIcon(size: 24), onTap: () async { - bool? isLogout = await showModalBottomSheet( + final bool? isLogout = await showModalBottomSheet( context: context, isScrollControlled: true, builder: (_) => const SettingsConfirmLogoutWalletSheet(), ); if (isLogout ?? false) { - await Future.delayed(const Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: 300)); if (context.mounted) { await context.read().reset(); if (context.mounted) { diff --git a/lib/screens/settings_contact/settings_contact_page.dart b/lib/screens/settings_contact/settings_contact_page.dart index 9aae9606..04d20b40 100644 --- a/lib/screens/settings_contact/settings_contact_page.dart +++ b/lib/screens/settings_contact/settings_contact_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -142,13 +143,13 @@ class SettingsContactView extends StatelessWidget { // / Failure). API is the authority — if the call is not allowed, // the Support page surfaces the API error. if (capability == null) { - context.pushNamed(SupportRoutes.support); + unawaited(context.pushNamed(SupportRoutes.support)); return; } // Branch 2: explicitly available → straight to Support. if (capability.available) { - context.pushNamed(SupportRoutes.support); + unawaited(context.pushNamed(SupportRoutes.support)); return; } @@ -164,7 +165,7 @@ class SettingsContactView extends StatelessWidget { // Defensive: API reported `available: false` without a // prerequisite this app version routes for. Push Support // directly and let the API render the error. - context.pushNamed(SupportRoutes.support); + unawaited(context.pushNamed(SupportRoutes.support)); } } @@ -185,7 +186,7 @@ class SettingsContactView extends StatelessWidget { if (state is! SettingsContactSuccess) return; final refreshed = state.capability; if (refreshed == null || refreshed.available) { - context.pushNamed(SupportRoutes.support); + unawaited(context.pushNamed(SupportRoutes.support)); } } } diff --git a/lib/screens/settings_user_data/settings_user_data_page.dart b/lib/screens/settings_user_data/settings_user_data_page.dart index 4c61451f..a7057aef 100644 --- a/lib/screens/settings_user_data/settings_user_data_page.dart +++ b/lib/screens/settings_user_data/settings_user_data_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -70,7 +71,7 @@ class SettingsUserDataView extends StatelessWidget { SettingsRoutes.editName, ); if (isEdited == true && context.mounted) { - context.read().getUserData(); + unawaited(context.read().getUserData()); } } : null, @@ -93,7 +94,7 @@ class SettingsUserDataView extends StatelessWidget { SettingsRoutes.editPhone, ); if (isEdited == true && context.mounted) { - context.read().getUserData(); + unawaited(context.read().getUserData()); } } : null, @@ -111,7 +112,7 @@ class SettingsUserDataView extends StatelessWidget { SettingsRoutes.editAddress, ); if (isEdited == true && context.mounted) { - context.read().getUserData(); + unawaited(context.read().getUserData()); } } : null, diff --git a/lib/screens/settings_user_data/subpages/edit_address/settings_edit_address_page.dart b/lib/screens/settings_user_data/subpages/edit_address/settings_edit_address_page.dart index a72cd9cf..2fc62e25 100644 --- a/lib/screens/settings_user_data/subpages/edit_address/settings_edit_address_page.dart +++ b/lib/screens/settings_user_data/subpages/edit_address/settings_edit_address_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -219,7 +220,7 @@ class _SettingsEditAddressViewState extends State { if ((_formKey.currentState?.validate() ?? false) && _selectedFile != null && country != null) { final fileBase64 = await _selectedFile!.toBase64DataUri(); if (mounted) { - context.read().submitAddress( + unawaited(context.read().submitAddress( street: _streetCtrl.text, houseNumber: _numberCtrl.text, zip: _postalCodeCtrl.text, @@ -227,7 +228,7 @@ class _SettingsEditAddressViewState extends State { countryId: country.id, fileBase64: fileBase64, fileName: _selectedFile!.name, - ); + )); } } } diff --git a/lib/screens/settings_user_data/subpages/edit_name/settings_edit_name_page.dart b/lib/screens/settings_user_data/subpages/edit_name/settings_edit_name_page.dart index 1eb29118..3ff2b537 100644 --- a/lib/screens/settings_user_data/subpages/edit_name/settings_edit_name_page.dart +++ b/lib/screens/settings_user_data/subpages/edit_name/settings_edit_name_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -160,12 +161,12 @@ class _SettingsEditNameViewState extends State { if ((_formKey.currentState?.validate() ?? false) && _selectedFile != null) { final fileBase64 = await _selectedFile!.toBase64DataUri(); if (mounted) { - context.read().submitName( + unawaited(context.read().submitName( firstName: _firstNameCtrl.text, lastName: _lastNameCtrl.text, fileBase64: fileBase64, fileName: _selectedFile!.name, - ); + )); } } } diff --git a/lib/screens/verify_seed/verify_seed_page.dart b/lib/screens/verify_seed/verify_seed_page.dart index 17804a1a..b25a9ff5 100644 --- a/lib/screens/verify_seed/verify_seed_page.dart +++ b/lib/screens/verify_seed/verify_seed_page.dart @@ -39,7 +39,7 @@ class VerifySeedView extends StatelessWidget { child: BlocConsumer( listener: (context, state) async { if (state.isVerified) { - await Future.delayed(const Duration(seconds: 2)); + await Future.delayed(const Duration(seconds: 2)); if (context.mounted) { // Load the *committed* wallet (`state.committedWallet`), not // the page's draft (`id == 0`). `committedWallet` is only diff --git a/lib/screens/welcome/welcome_page.dart b/lib/screens/welcome/welcome_page.dart index 9c5ddefb..229842a7 100644 --- a/lib/screens/welcome/welcome_page.dart +++ b/lib/screens/welcome/welcome_page.dart @@ -163,7 +163,7 @@ class _WelcomePageState extends State { ); Future onBitboxPressed(BuildContext context) async { - await showModalBottomSheet( + await showModalBottomSheet( isScrollControlled: true, context: context, builder: (_) => ConnectBitboxPage( diff --git a/lib/setup/routing/router_config.dart b/lib/setup/routing/router_config.dart index 3d0b9e26..4e1bdf00 100644 --- a/lib/setup/routing/router_config.dart +++ b/lib/setup/routing/router_config.dart @@ -79,7 +79,7 @@ final GoRouter routerConfig = GoRouter( GoRoute( name: OnboardingRoutes.verifySeed, path: '/verifySeed', - builder: (_, state) => VerifySeedPage(wallet: state.extra as SoftwareWallet), + builder: (_, state) => VerifySeedPage(wallet: state.extra! as SoftwareWallet), ), GoRoute( @@ -99,7 +99,7 @@ final GoRouter routerConfig = GoRouter( name: PinRoutes.gate, path: '/pinGate', builder: (_, state) { - final params = state.extra as VerifyPinParams; + final params = state.extra! as VerifyPinParams; return VerifyPinPage( description: params.description, onAuthenticated: params.onAuthenticated, @@ -147,7 +147,7 @@ final GoRouter routerConfig = GoRouter( GoRoute( name: AppRoutes.sellBitbox, path: '/sellBitbox', - builder: (_, state) => SellBitboxPage(paymentInfo: state.extra as SellPaymentInfo), + builder: (_, state) => SellBitboxPage(paymentInfo: state.extra! as SellPaymentInfo), ), GoRoute( @@ -162,7 +162,7 @@ final GoRouter routerConfig = GoRouter( builder: (_, state) { final extra = state.extra; return LegalDocumentPage( - params: extra as LegalDocumentParams, + params: extra! as LegalDocumentParams, ); }, ), @@ -303,7 +303,7 @@ final GoRouter routerConfig = GoRouter( GoRoute( name: AppRoutes.webView, path: '/webView', - builder: (_, state) => WebViewPage(state.extra as WebViewRouteParams), + builder: (_, state) => WebViewPage(state.extra! as WebViewRouteParams), ), ], ); diff --git a/lib/widgets/buttons/app_filled_button.dart b/lib/widgets/buttons/app_filled_button.dart index 2fe2790f..77c88396 100644 --- a/lib/widgets/buttons/app_filled_button.dart +++ b/lib/widgets/buttons/app_filled_button.dart @@ -44,7 +44,7 @@ class AppFilledButton extends StatelessWidget { onPressed: onPressed, style: style, icon: Icon( - icon!, + icon, color: variant == FilledButtonVariant.secondary ? RealUnitColors.realUnitBlack : null, ), label: Text( @@ -99,7 +99,7 @@ class AppFilledButton extends StatelessWidget { foregroundColor: WidgetStateProperty.all(RealUnitColors.basic.white), iconColor: WidgetStateProperty.all(RealUnitColors.basic.white), ), - icon: Icon(icon!), + icon: Icon(icon), label: Text( label, textAlign: .center, @@ -129,7 +129,7 @@ class AppFilledButton extends StatelessWidget { foregroundColor: WidgetStateProperty.all(RealUnitColors.basic.white), iconColor: WidgetStateProperty.all(RealUnitColors.basic.white), ), - icon: Icon(icon!), + icon: Icon(icon), label: Text( label, textAlign: .center, diff --git a/lib/widgets/buttons/app_text_button.dart b/lib/widgets/buttons/app_text_button.dart index 24bf46ff..a7aa46db 100644 --- a/lib/widgets/buttons/app_text_button.dart +++ b/lib/widgets/buttons/app_text_button.dart @@ -18,7 +18,7 @@ class AppTextButton extends StatelessWidget { @override Widget build(BuildContext context) { final button = icon != null - ? TextButton.icon(onPressed: onPressed, icon: Icon(icon!), label: Text(label)) + ? TextButton.icon(onPressed: onPressed, icon: Icon(icon), label: Text(label)) : TextButton(onPressed: onPressed, child: Text(label)); if (fullWidth) return SizedBox(width: .infinity, child: button); return button; diff --git a/test/packages/config/network_mode_test.dart b/test/packages/config/network_mode_test.dart index 70e896e5..c074fa59 100644 --- a/test/packages/config/network_mode_test.dart +++ b/test/packages/config/network_mode_test.dart @@ -48,7 +48,7 @@ void main() { ); expect(resolved, isNotNull); - expect(resolved!, isNotEmpty); + expect(resolved, isNotEmpty); }); testWidgets('testnet resolves to a non-empty localized string distinct from mainnet', ( @@ -67,7 +67,7 @@ void main() { ); expect(testnetText, isNotNull); - expect(testnetText!, isNotEmpty); + expect(testnetText, isNotEmpty); // Pin the per-arm wiring — if both arms accidentally resolved to // the same string key, the user would see "Mainnet" on testnet. expect(testnetText, isNot(equals(mainnetText))); diff --git a/test/packages/service/dfx/dfx_blockchain_api_service_test.dart b/test/packages/service/dfx/dfx_blockchain_api_service_test.dart index 44397490..7e5f4c9b 100644 --- a/test/packages/service/dfx/dfx_blockchain_api_service_test.dart +++ b/test/packages/service/dfx/dfx_blockchain_api_service_test.dart @@ -66,7 +66,7 @@ void main() { expect(capturedBody!['address'], _testAddress); // chainId 1 → 'Ethereum'. expect(capturedBody!['blockchain'], 'Ethereum'); - expect(capturedBody!['assetIds'], isA()); + expect(capturedBody!['assetIds'], isA>()); expect(capturedHeaders!['Authorization'], 'Bearer jwt-abc'); expect(capturedHeaders!['Content-Type'], 'application/json'); }); @@ -78,7 +78,7 @@ void main() { Map? capturedBody; final client = MockClient((request) async { capturedBody = jsonDecode(request.body) as Map; - return http.Response(jsonEncode({'balances': []}), 200); + return http.Response(jsonEncode({'balances': []}), 200); }); await build(client).getEthBalance(_testAddress); @@ -89,7 +89,7 @@ void main() { test('returns 0.0 when the balances list is empty', () async { sessionCache.setAuthToken('jwt-abc'); final client = MockClient((_) async => http.Response( - jsonEncode({'balances': []}), + jsonEncode({'balances': []}), 200, )); diff --git a/test/packages/service/dfx/models/registration/registration_dtos_test.dart b/test/packages/service/dfx/models/registration/registration_dtos_test.dart index 7834fe35..d42f85de 100644 --- a/test/packages/service/dfx/models/registration/registration_dtos_test.dart +++ b/test/packages/service/dfx/models/registration/registration_dtos_test.dart @@ -97,7 +97,7 @@ void main() { ); expect(label, isNotNull); - expect(label!, isNotEmpty); + expect(label, isNotEmpty); }); testWidgets('corporation resolves to a non-empty label distinct from human', (tester) async { @@ -114,7 +114,7 @@ void main() { ); expect(corporation, isNotNull); - expect(corporation!, isNotEmpty); + expect(corporation, isNotEmpty); expect(corporation, isNot(equals(human))); }); }); diff --git a/test/screens/buy/buy_page_test.dart b/test/screens/buy/buy_page_test.dart index 4678f2f1..5dcb09ff 100644 --- a/test/screens/buy/buy_page_test.dart +++ b/test/screens/buy/buy_page_test.dart @@ -299,8 +299,8 @@ void main() { final amountField = find.byType(TextField).first; final resultField = find.byType(TextField).last; - TextField amount = tester.widget(amountField); - TextField result = tester.widget(resultField); + final TextField amount = tester.widget(amountField); + final TextField result = tester.widget(resultField); expect(amount.controller!.text, equals('5.00')); expect(result.controller!.text, equals('0.50')); diff --git a/test/screens/legal/cubit/legal_disclaimer_cubit_test.dart b/test/screens/legal/cubit/legal_disclaimer_cubit_test.dart index 2119642e..71d89939 100644 --- a/test/screens/legal/cubit/legal_disclaimer_cubit_test.dart +++ b/test/screens/legal/cubit/legal_disclaimer_cubit_test.dart @@ -89,7 +89,7 @@ void main() { 'previousStep at step 0 is a no-op (cannot go below zero)', build: LegalDisclaimerCubit.new, act: (cubit) => cubit.previousStep(), - expect: () => [], + expect: () => [], ); }); } diff --git a/test/screens/pin/setup_pin_cubit_test.dart b/test/screens/pin/setup_pin_cubit_test.dart index cc0256c6..03c9cc8a 100644 --- a/test/screens/pin/setup_pin_cubit_test.dart +++ b/test/screens/pin/setup_pin_cubit_test.dart @@ -58,7 +58,7 @@ void main() { build: build, seed: () => const SetupPinState(currentPin: '123456'), act: (cubit) => cubit.addDigit(7), - expect: () => [], + expect: () => [], ); blocTest( @@ -73,7 +73,7 @@ void main() { 'deleteDigit on an empty pin is a no-op', build: build, act: (cubit) => cubit.deleteDigit(), - expect: () => [], + expect: () => [], ); test('completing 6 digits in create mode switches to confirm with an empty pin', () async { diff --git a/test/screens/pin/setup_pin_page_test.dart b/test/screens/pin/setup_pin_page_test.dart index d781887e..bad19bc8 100644 --- a/test/screens/pin/setup_pin_page_test.dart +++ b/test/screens/pin/setup_pin_page_test.dart @@ -44,7 +44,7 @@ void main() { setUpAll(() { pinAuthCubit = MockPinAuthCubit(); when(() => pinAuthCubit.state).thenReturn(const PinAuthState()); - when(() => pinAuthCubit.onPinSetupComplete()).thenAnswer((_) => Future.value()); + when(() => pinAuthCubit.onPinSetupComplete()).thenAnswer((_) => Future.value()); setupDependencyInjection(); }); diff --git a/test/screens/pin/verify_pin_cubit_test.dart b/test/screens/pin/verify_pin_cubit_test.dart index 3bf86183..77ed5144 100644 --- a/test/screens/pin/verify_pin_cubit_test.dart +++ b/test/screens/pin/verify_pin_cubit_test.dart @@ -74,7 +74,7 @@ void main() { 'deleteDigit on empty pin is a no-op', build: build, act: (cubit) => cubit.deleteDigit(), - expect: () => [], + expect: () => [], ); blocTest( @@ -85,7 +85,7 @@ void main() { lockedUntil: DateTime(2030), ), act: (cubit) => cubit.addDigit(1), - expect: () => [], + expect: () => [], ); blocTest( @@ -93,7 +93,7 @@ void main() { build: build, seed: () => const VerifyPinLocked(failedAttempts: 9), act: (cubit) => cubit.addDigit(1), - expect: () => [], + expect: () => [], ); }); diff --git a/test/screens/pin/verify_pin_page_test.dart b/test/screens/pin/verify_pin_page_test.dart index 37fc6ad0..69f075ed 100644 --- a/test/screens/pin/verify_pin_page_test.dart +++ b/test/screens/pin/verify_pin_page_test.dart @@ -34,7 +34,7 @@ void main() { verifyPinCubit = MockVerifyPinCubit(); when(() => verifyPinCubit.state).thenReturn(const VerifyPinState()); - when(() => verifyPinCubit.checkBiometricAvailability()).thenAnswer((_) => Future.value()); + when(() => verifyPinCubit.checkBiometricAvailability()).thenAnswer((_) => Future.value()); }); void setupDependencyInjection() { @@ -52,7 +52,7 @@ void main() { setUpAll(() { pinAuthCubit = MockPinAuthCubit(); when(() => pinAuthCubit.state).thenReturn(const PinAuthState()); - when(() => pinAuthCubit.onPinVerified()).thenAnswer((_) => Future.value()); + when(() => pinAuthCubit.onPinVerified()).thenAnswer((_) => Future.value()); setupDependencyInjection(); }); diff --git a/test/screens/pin/widgets/pin_indicator_test.dart b/test/screens/pin/widgets/pin_indicator_test.dart index 7f1750aa..1cd4fde4 100644 --- a/test/screens/pin/widgets/pin_indicator_test.dart +++ b/test/screens/pin/widgets/pin_indicator_test.dart @@ -25,7 +25,7 @@ void main() { )); final fills = dots(tester) - .map((c) => (c.decoration as BoxDecoration).color) + .map((c) => (c.decoration! as BoxDecoration).color) .toList(); // First three filled (black), last three transparent. expect(fills.take(3), everyElement(RealUnitColors.realUnitBlack)); @@ -38,7 +38,7 @@ void main() { )); final borderColors = dots(tester) - .map((c) => (c.decoration as BoxDecoration).border!.top.color) + .map((c) => (c.decoration! as BoxDecoration).border!.top.color) .toList(); expect(borderColors, everyElement(RealUnitColors.status.red600)); @@ -51,7 +51,7 @@ void main() { )); final borderColors = dots(tester) - .map((c) => (c.decoration as BoxDecoration).border!.top.color) + .map((c) => (c.decoration! as BoxDecoration).border!.top.color) .toList(); expect(borderColors, everyElement(RealUnitColors.realUnitBlack)); @@ -63,7 +63,7 @@ void main() { )); final fills = dots(tester) - .map((c) => (c.decoration as BoxDecoration).color) + .map((c) => (c.decoration! as BoxDecoration).color) .toList(); expect(fills, everyElement(RealUnitColors.realUnitBlack)); }); diff --git a/test/screens/welcome/widgets/welcome_card_test.dart b/test/screens/welcome/widgets/welcome_card_test.dart index a3f48455..50d83142 100644 --- a/test/screens/welcome/widgets/welcome_card_test.dart +++ b/test/screens/welcome/widgets/welcome_card_test.dart @@ -14,7 +14,7 @@ void main() { String? description; VoidCallback? onPressed; Widget? trailing; - MockFunction functions = MockFunction(); + final MockFunction functions = MockFunction(); setUp(() { title = 'Welcome'; diff --git a/test/widgets/text_substring_highlighting_test.dart b/test/widgets/text_substring_highlighting_test.dart index 94e2aed7..abd4ed9e 100644 --- a/test/widgets/text_substring_highlighting_test.dart +++ b/test/widgets/text_substring_highlighting_test.dart @@ -117,7 +117,7 @@ void main() { // Fire the recognizer manually since hit-testing a TextSpan in a widget // test is fiddly — the gestureRecognizer itself is what would receive // the gesture in production. - (highlightedSpan.recognizer as TapGestureRecognizer).onTap!(); + (highlightedSpan.recognizer! as TapGestureRecognizer).onTap!(); expect(taps, 1); }); From 64382b4e957115ac33188aa9899cc0cd897055ec Mon Sep 17 00:00:00 2001 From: joshuakrueger-dfx Date: Thu, 4 Jun 2026 08:51:32 +0200 Subject: [PATCH 2/2] ci: drop --fatal-infos from analyze, keep --fatal-warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The targeted bug classes (unguarded casts, unawaited futures, dynamic calls) already fail under the new strict-* modes and warning-level lints. --fatal-infos would hard-red every PR on future SDK/lint bumps that add new info-level lints — the noisiest tier — for no extra bug coverage. --- .github/workflows/pull-request.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 2626ec07..e65f82ed 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -70,7 +70,7 @@ jobs: - run: dart run tool/generate_localization.dart - run: dart run tool/generate_release_info.dart - run: flutter pub run build_runner build - - run: flutter analyze --fatal-infos --fatal-warnings + - run: flutter analyze --fatal-warnings # Excludes the `golden` tag: visual-regression tests live under # `test/goldens/` and are validated on the dfx01 self-hosted runner # in the parallel `golden-tests` job (Hardware-Determinismus, see