我正在学习 flutter 和 flutter_bloc:8.1.6。我正在开发一个简单的应用程序,用户可以在其中添加或编辑其交易,并在添加或更新新交易时查看交易列表。
在幕后,我的存储库使用 rxDart Behaviour 主题公开数据流,如下所示:
class TransactionRepository {
final LocalDB _database;
final BehaviorSubject<List<Transaction>> _transactionsController =
BehaviorSubject.seeded([]);
TransactionRepository({required LocalDB database}) : _database = database {
_init();
}
Future<void> _init() async {
try {
final transactions = await _getAllTransactions();
_transactionsController.add(transactions);
} catch (e) {
rethrow;
}
}
Stream<List<Transaction>> streamTransactions() {
return _transactionsController.asBroadcastStream();
}
/// Save a transaction
Future<void> createTransaction(NewTransaction transaction) async {
try {
await _database
.into(_database.transactionTable)
.insert(TransactionTableCompanion(
//... fields
));
final transactions = await _getAllTransactions();
_transactionsController.add(transactions);
} catch (e) {
rethrow;
}
}
}
通过 bloc,我正在吸收这种流,如下所示:
...
Future<void> _loadTransactions(
TransactionsEvent event, Emitter<TransactionsState> emit) async {
try {
emit(state.copyWith(transactionStatus: TransactionsStatus.fetching));
await emit.forEach(
_transactionRepository.streamTransactions(),
onData: (transactions) {
if (transactions.isEmpty) {
return state.copyWith(
transactionStatus: TransactionsStatus.initial,
transactions: [],
);
}
return state.copyWith(
transactionStatus: TransactionsStatus.fetchedSuccessfully,
transactions: transactions,
);
},
onError: (e, s) {
return state.copyWith(
transactionStatus: TransactionsStatus.fetchingFailed,
message: 'Error loading transaction(s)',
);
},
);
} catch (e) {
add(const TransactionsEvent.error('Error loading transaction(s)'));
}
}
...
这个区块与我的
main.dart
StatelessWidget 挂钩,例如:
...
return CupertinoApp(
title: appTitle,
home: SafeArea(
child: RepositoryProvider(
create: (BuildContext context) {
final database = context.read<DatabaseCubit>();
return TransactionRepository(
database: database.state,
);
},
child: BlocProvider(
create: (BuildContext context) => TransactionsBloc(
transactionRepository: context.read<TransactionRepository>(), // stream is subscribed here
)..add(const TransactionsEvent.loadAll()),
child: const HomeScreen(title: appTitle),
),
),
),
);
...
我的
HomeScreen
StatelessWidget 看起来像:
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
leading: const Icon(CupertinoIcons.line_horizontal_3),
trailing: IconButton(
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) => const AddNewTransaction(),
),
);
},
icon: const Icon(CupertinoIcons.add),
),
),
child: const TransactionsList(),
);
}
TransactionList StatelessWidget,最终显示列表:
BlocBuilder<TransactionsBloc, TransactionsState>(
builder: (BuildContext context, TransactionsState state) {
switch (state.transactionStatus) {
case TransactionsStatus.initial:
return TransactionListItem(
transaction: Transaction.nullTransaction);
case TransactionsStatus.fetching:
return const Center(
child: CupertinoActivityIndicator(),
);
case TransactionsStatus.fetchedSuccessfully:
return GroupedTransactionsListView(
transactions:
state.transactions ?? [Transaction.nullTransaction],
// transactions: state.transactions ?? [],
);
case TransactionsStatus.fetchingFailed:
return Center(
child: Text(state.message ?? 'Error loading transaction(s)'),
);
default:
return const Center(
child: Text('Something went wrong! :('),
);
}
},
),
现在发生的情况是,当应用程序启动时,流订阅成功,我可以看到交易列表中的所有数据。
用户导航至
AddTransaction
页面(参见主屏幕 -> 导航)并添加新交易。当他们单击“保存”时,一个新条目将添加到数据库中,保存成功后,用户将导航回来(使用 Navigation.pop
)。
现在,当用户返回此 TransactionList 页面时,列表不会更新。
我调试了流程,据我了解,流列表器 (
_loadTransactions
) 已关闭。
所以我想了解和学习是否以及如何保持流不被关闭?
根据我目前收集到的信息,一种方法是切换到 StatefulWidget,这没问题,但我想首先探索有关 StatelessWidget 的新想法。
如果不可能,当用户导航回 TransactionList 页面时触发重新订阅流的推荐方法是什么?
提前致谢!
目前还不清楚如何吸收流以及何时在块中调用 _loadTransactions() 。但你可以在这里做什么:
class TransactionsBloc extends Bloc< TransactionsEvent, TransactionsState> {
final TransactionRepository _repository
final StreamSubscription _subscription;
Bloc1(TransactionRepository repository) :
_repository = repository,
super(TransactionsState()) {
...
_subscription = repository.streamTransactions().listen(
onData: (transactions) {
if (transactions.isEmpty) {
return state.copyWith(
transactionStatus: TransactionsStatus.initial,
transactions: [],
);
}
return state.copyWith(
transactionStatus: TransactionsStatus.fetchedSuccessfully,
transactions: transactions,
);
},
onError: (e, s) {
return state.copyWith(
transactionStatus: TransactionsStatus.fetchingFailed,
message: 'Error loading transaction(s)',
);
},
);
}
@override
Future<void> close() {
_subscription?.cancel();
return super.close();
}
}
...
您应该从块中的存储库监听流,并通过对流中的每个事件发出状态来做出反应。