pass_existing_stream_to_stream_builder
v0.8.0 Warning Widget Best Practices
This rule flags a StreamBuilder whose stream: argument creates a new Stream inline — a method call, a Stream constructor, or an immediately invoked generator.
Why use this rule
Section titled “Why use this rule”Every build() re-evaluates the stream: argument. When that argument creates a stream, the StreamBuilder cancels its old subscription and opens a new one on each rebuild.
For a single-subscription stream this is worse than the FutureBuilder equivalent: events buffered before the resubscription are lost outright, the snapshot resets to its initial state, and the discarded subscription may keep its source alive. With a socket or a database watcher, this means reconnecting on every frame.
Create the stream once and pass the same instance — a field assigned in initState, or a value held by your state manager.
See also: StreamBuilder API docs
Widget build(BuildContext context) { return StreamBuilder<int>( // A new subscription on every rebuild — drops events stream: repository.watchCounter(), builder: (context, snapshot) => Text('${snapshot.data}'), );}class _MyWidgetState extends State<MyWidget> { late final Stream<int> _counter;
@override void initState() { super.initState(); // Subscribed once, kept across rebuilds _counter = repository.watchCounter(); }
@override Widget build(BuildContext context) { return StreamBuilder<int>( stream: _counter, builder: (context, snapshot) => Text('${snapshot.data}'), ); }}Known limitations
Section titled “Known limitations”The rule reports only expressions that certainly allocate: constructor calls, method invocations, and invoked closures. A bare identifier, a property access, or anything it cannot resolve is treated as an existing instance and left alone. A getter that creates a new stream on each access will therefore not be flagged.
Configuration
Section titled “Configuration”This rule is in the recommended preset, so it is on with
preset: recommended or preset: opinionated. Add it to preset: core with
pass_existing_stream_to_stream_builder: true.
To turn it off:
rules: pass_existing_stream_to_stream_builder: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”pass_existing_future_to_future_builder— Don’t create a new Future inline inside FutureBuilder.always_pass_global_key— Don’t create a GlobalKey inside build.avoid_conditional_hooks— Never call hooks inside conditionals, loops, or ternaries.avoid_deep_widget_nesting— Keep a widget tree within a nesting budget.