1

我正在尝试将 BLoC 模式与 RxDart 一起使用,并且我想通过 StreamBuilder 从列表中获取一个事件。

我的问题是,当我打开我的详细信息屏幕时,像 1 秒我收到以下错误消息:The method 'firstWhere' was called on null.

有没有办法在不添加加载进度的情况下等待获取数据,因为我想将 HeroAnimation 保留在我的图像上?

class _EventsDetailsScreenState extends State<EventsDetailsScreen> {
  @override
  Widget build(BuildContext context) {
    final eventBloc = BlocProvider.of<EventsBloc>(context);
    //Event event = eventBloc.events.firstWhere((elmt) => elmt.id == widget.id, orElse:() => null);
    return StreamBuilder(
      stream : eventBloc.allEvents,
      builder: (context, snapshot){
        final Event event = snapshot.data.firstWhere((elmt) => elmt.id == widget.id, orElse:() => null);
        return Scaffold(
          body: Stack(
            children: <Widget>[
              Column(children: <Widget>[
                Expanded(
                    child: ListView(
                  shrinkWrap: true,
                  children: <Widget>[
                    _buildEventImage(context, event),
                    (event.description != null && event.description.trim().isNotEmpty) ? _buildDescriptionSection(context, event) : Container(),
                  ],
                ))
              ]),
              new Positioned(
                //Place it at the top, and not use the entire screen
                top: 0.0,
                left: 0.0,
                right: 0.0,
                child: AppBar(
                  actions: <Widget>[
                    IconButton(icon: Icon(Icons.create), onPressed: () async => print('Edit')),
                  ],
                  backgroundColor: Colors.transparent, //No more green
                  elevation: 0.0, //Shadow gone
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  Widget _buildEventImage(BuildContext context, Event event) {
    return Hero(
      tag: 'eventImg${event.id}',
      child: Container(
          decoration: BoxDecoration(
            image: DecorationImage(
                image: AssetImage("images/croatia.jpg"),
                alignment: Alignment.topCenter,
                fit: BoxFit.fill),
          ),
          padding: EdgeInsets.only(left: 40.0, bottom: 250.0),
          alignment: Alignment.bottomLeft,
          height: MediaQuery.of(context).size.height - 30.0,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Container(
                child: Material(
                    color: Colors.transparent,
                    child: Text(
                      event.name,
                      style: eventNameTextStyle,
                    )),
              ),
              Container(
                  child: Material(
                      color: Colors.transparent,
                      child: Text(
                          "In ${event.date.difference(DateTime.now()).inDays} days",
                          style: eventDateTextStyle)))
            ],
          )),
    );
  }
4

2 回答 2

1

我希望你已经解决了这个问题,但是我在 Flutter 中使用 BLoC Pattern 时遇到了同样的问题。问题在于流是异步的,而构建方法是同步的。

  • 这是来自官方文档

可以通过指定 initialData 来控制初始快照数据。这应该用于确保第一帧具有预期值,因为总是会在流侦听器有机会处理之前调用构建器。 StreamBuilder 类

因此,为了避免这种行为,您必须在 StreamBuilder中使用 initialData 属性。

要将它与 BLoC 一起使用,您可以在 BLoC 中公开一个简单的getter,它可以获取当前状态的同步值,作为任何新订阅者的初始数据。

于 2020-07-12T17:58:54.173 回答
0

您可以添加一条if语句来检查快照是否有数据,这种方式只有在快照中有数据firstWhere时才会调用函数:

 class _EventsDetailsScreenState extends State<EventsDetailsScreen> {
 @override
 Widget build(BuildContext context) {
  final eventBloc = BlocProvider.of<EventsBloc>(context);
  //Event event = eventBloc.events.firstWhere((elmt) => elmt.id == widget.id, orElse:() => null);
  return StreamBuilder(
  stream : eventBloc.allEvents,
  builder: (context, snapshot){
   if(snapshot.hasData){
     return Scaffold(
      body: Stack(
        children: <Widget>[
          Column(children: <Widget>[
            Expanded(
                child: ListView(
              shrinkWrap: true,
              children: <Widget>[
                _buildEventImage(context, event),
                (event.description != null && event.description.trim().isNotEmpty) ? _buildDescriptionSection(context, event) : Container(),
              ],
            ))
          ]),
          new Positioned(
            //Place it at the top, and not use the entire screen
            top: 0.0,
            left: 0.0,
            right: 0.0,
            child: AppBar(
              actions: <Widget>[
                IconButton(icon: Icon(Icons.create), onPressed: () async => print('Edit')),
              ],
              backgroundColor: Colors.transparent, //No more green
              elevation: 0.0, //Shadow gone
            ),
          ),
        ],
      ),
    );
  }else{
    return Scaffold();
  }
});
}
于 2019-04-28T13:10:51.640 回答