【问题标题】:Flutter StreamBuilder Called Twice When InitializedFlutter StreamBuilder 初始化时调用了两次
【发布时间】:2019-12-25 00:11:24
【问题描述】:

StreamBuilder 是否总是被调用两次?一次用于初始数据,然后一次用于输入流?

初始化下面的 StreamBuilder 表明 build 方法被调用了两次。第二次调用是在第一次调用之后 0.4 秒。

流:构建 1566239814897

流:构建 1566239815284

import 'dart:async';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:nocd/utils/bloc_provider.dart';

void main() =>
    runApp(BlocProvider<MyAppBloc>(bloc: MyAppBloc(), child: MyApp()));

class MyAppBloc extends BlocBase {
  String _page = window.defaultRouteName ?? "";

  /// Stream for [getPage].
  StreamController<String> pageController = StreamController<String>();

  /// Observable navigation route value.
  Stream get getPage => pageController.stream;

  MyAppBloc() {}

  @override
  void dispose() {
    pageController.close();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyAppBloc myAppBloc = BlocProvider.of<MyAppBloc>(context);
    return StreamBuilder(
      stream: myAppBloc.getPage,
      initialData: "Build",
      builder: (context, snapshot) {
        print("Stream: " +
            snapshot.data +
            DateTime.now().millisecondsSinceEpoch.toString());
        return Container();
      },
    );
  }
}

为什么 StreamBuilder 会被调用两次?

【问题讨论】:

    标签: flutter dart stream-builder


    【解决方案1】:

    如上所述,您只需将代码放入 Connection.Active 状态。见下文:

    StreamBuilder<QuerySnapshot>(
                  stream: historicModel.query.snapshots(),
                  builder: (context, stream){
    
                    if (stream.connectionState == ConnectionState.waiting) {
                      return Center(child: CircularProgressIndicator());
                    } else if (stream.hasError) {
                      return Center(child: Text(stream.error.toString()));
                    } else if(stream.connectionState == ConnectionState.active){
                       //place your code here. It will prevent double data call.
    
                    }
    

    【讨论】:

      【解决方案2】:

      StreamBuilder 在初始化时会进行两次构建调用,一次用于初始数据,第二次用于流数据。

      流不保证它们会立即发送数据,因此需要初始数据值。将 null 传递给 initialData 会引发 InvalidArgument 异常。

      即使传递的流为空,StreamBuilders 也将始终构建两次。

      更新:

      即使提供了initalData,也可以在此 Flutter 问题线程中找到 StreamBuilders 多次构建的详细技术解释:https://github.com/flutter/flutter/issues/16465

      广播流不可能有初始状态。您在添加数据时订阅了或您错过了它。在异步单订阅流中,任何添加的监听调用都不会被调用,直到下一个微任务或下一个事件循环(不记得,可能取决于),但无论如何都无法将数据从在当前帧上流。 - 乔纳威廉姆斯

      【讨论】:

      • StreamBuilder 不打电话,不。添加侦听器时,您的流会发出事件。
      • 发出了两个事件。第一个事件由 initialData 发出。附加 Stream 时,StreamBuilder 会发出第二个事件。第二个事件不依赖于流中的事件,因为即使流为空,也会发出第二个构建请求。如果 Stream 中有事件,则进行三个构建调用。
      • 我已将我的答案标记为正确,因为这是 StreamBuilder 所展示的确切行为。如果推理不正确,请纠正我。
      • @RayLi 我认为你是对的,经过很长时间我一直在寻找为什么 streambuilder 的构建方法被调用 2 次,我害怕调用我的 firebase API 两次但它调用了一次但是streambuilder的build方法被调用了2次。
      • @M.ArslanKhan 很高兴能够提供帮助!我也很困惑,担心重复的数据请求。重要的区别是 StreamBuilder 的 build 方法被调用了两次,而 Stream 只发出一次。
      【解决方案3】:

      Streambuilder 将被调用 2 次,第一次用于 Initial,第二次用于流。并且数据仅在状态为 ConnectionState.active. 时更改。 kinldy 请参阅官方文档示例。

          StreamBuilder<int>(
        //stream:fire, // a Stream<int> or null
        builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
          if (snapshot.hasError) return Text('Error: ${snapshot.error}');
          switch (snapshot.connectionState) {
            case ConnectionState.none:
              return Text('Select lot');
            case ConnectionState.waiting:
              return Text('Awaiting bids...');
            case ConnectionState.active:
              return Text('\$${snapshot.data}');
            case ConnectionState.done:
              return Text('\$${snapshot.data} (closed)');
          }
          return null; // unreachable
        },
      );
      

      StreamBuilder documentation

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

      initialData

      提供这个值(大概是在创建 Stream 时以某种方式同步获得的)确保第一帧将显示有用的数据。否则,将使用 null 值构建第一帧,而不管流上是否有可用值:由于流是异步的,因此在初始构建之前无法从流中获取任何事件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-10-20
        • 2017-06-21
        • 2015-03-06
        • 1970-01-01
        • 2019-12-26
        • 2012-02-16
        • 2019-11-01
        相关资源
        最近更新 更多