Flutter StreamBuilder 在初始化时调用了两次

Ray*_* Li 6 dart flutter stream-builder

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

初始化以下 StreamBuilder 显示构建方法被调用了两次。第二个调用是在第一个调用之后的 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();
      },
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

为什么 StreamBuilder 会被调用两次?

M.A*_*han 8

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

    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
  },
);
Run Code Online (Sandbox Code Playgroud)

StreamBuilder 文档

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

初始数据

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


Ray*_* Li 5

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

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

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

更新:

initalData在这个 Flutter 问题线程中可以找到关于为什么 StreamBuilders 多次构建多次的详细技术解释可以在这个 Flutter 问题线程中找到:https : //github.com/flutter/flutter/issues/16465

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

  • StreamBuilder 不会调用,不。当添加侦听器时,您的流会发出事件。 (3认同)
  • @RayLi我认为你是对的,经过很长一段时间,我一直在寻找为什么streambuilder的构建方法被调用2次,我害怕调用我的firebase API两次,但它调用了一次,但streambuilder的构建方法被调用了2次。 (2认同)

Thi*_*lva 5

如上所述,您只需将代码放入 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.

                }
Run Code Online (Sandbox Code Playgroud)