diff --git a/lib/screens/dashboard/bloc/pending_transactions_cubit.dart b/lib/screens/dashboard/bloc/pending_transactions_cubit.dart index 7db47c80..c34fc8b5 100644 --- a/lib/screens/dashboard/bloc/pending_transactions_cubit.dart +++ b/lib/screens/dashboard/bloc/pending_transactions_cubit.dart @@ -14,9 +14,14 @@ class PendingTransactionsCubit extends Cubit> { Future _loadPendingTransactions() async { try { final transactions = await _transactionHistoryService.fetchPendingTransactions(); + // The fetch is started in the constructor and not awaited, so the cubit + // can be closed (page popped) before it resolves. Guard the emit to avoid + // a StateError after close (issue #657 P3 #16). + if (isClosed) return; emit(transactions); } catch (e) { developer.log('Failed to load pending transactions: $e', name: '$PendingTransactionsCubit'); + if (isClosed) return; emit([]); } } diff --git a/test/screens/dashboard/pending_transactions_cubit_test.dart b/test/screens/dashboard/pending_transactions_cubit_test.dart index 2564da29..dc9e61c9 100644 --- a/test/screens/dashboard/pending_transactions_cubit_test.dart +++ b/test/screens/dashboard/pending_transactions_cubit_test.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:realunit_wallet/packages/service/dfx/models/transactions/dto/transactions_dto.dart'; @@ -36,6 +38,25 @@ void main() { expect(cubit.state, [tx1, tx2]); }); + test('does not emit (no StateError) if closed before the fetch resolves ' + '(issue #657 P3 #16 regression)', () async { + final completer = Completer>(); + when(() => service.fetchPendingTransactions()) + .thenAnswer((_) => completer.future); + + final cubit = PendingTransactionsCubit(service); + // Close while the constructor-started fetch is still in flight. + await cubit.close(); + + // Resolving now would, without the isClosed guard, emit after close and + // throw 'Cannot emit new states after calling close'. + completer.complete([_StubTx()]); + await Future.delayed(Duration.zero); + + // No exception escaped; the closed cubit kept its last state. + expect(cubit.isClosed, isTrue); + }); + test('falls back to an empty list when the service throws', () async { when(() => service.fetchPendingTransactions()) .thenAnswer((_) async => throw Exception('network'));