使用带有无状态小部件的 bloc 并在屏幕之间导航时如何维护 rxdart 广播流侦听器?

问题描述 投票:0回答:1

我正在学习 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 页面时触发重新订阅流的推荐方法是什么?

提前致谢!

flutter stream bloc flutter-bloc rxdart
1个回答
0
投票

目前还不清楚如何吸收流以及何时在块中调用 _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();
      }
    }
...

您应该从块中的存储库监听流,并通过对流中的每个事件发出状态来做出反应。

© www.soinside.com 2019 - 2024. All rights reserved.