0

I am trying to make an app where I need a nested stream builder. The Stream builder looks something like this. but the widgets are built before the last stream is loaded so I get error calling getter null,

StreamBuilder(
  stream: some_stream,
  builder: (context, data){
      return StreamBuilder(
         stream: some_stream,
         builder: (context, data){
             return StreamBuilder(
                stream: some_stream,
                builder: (context, data){

                    return someWidget;

              }

            );
          }
        );
       }
    );
1

2 Answers 2

1

It is entirely possible that StreamBuilder.builder will be called while the Stream has no data, hasn't connected to the Stream yet or the value is null.

It is up to you to make sure that you handle all these cases.

To make sure that the initial value is never null, you can set inialData.

Future<String> someFutureString = Future.value('initial data seeded');

new StreamBuilder<String>(
  initialData: await someFutureString,
  builder: (ctx, snapshot) { /* ... */ }
);

This is bad practice though. It is better to build such builder that consider the state of the snapshot it works with. Building the widget tree should be quick. Imagine having to wait 3 seconds to the initialData. Your widget tree building will be blocked by at the first await.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

wrapInMaterialApp(Widget widget) => MaterialApp(
      home: widget,
    );

main() {
  testWidgets('can await initial data', (WidgetTester tester) async {
    final initialData = Future<String>.value('initial value');
    final stream = Stream.fromIterable(['first']);
    final sb = StreamBuilder<String>(
      initialData: await initialData,
      builder: (ctx, snapshot) {
        return Text('${snapshot.data}');
      },
    );
    await tester.pumpWidget(wrapInMaterialApp(sb));
    // Verify that initial data is present
    expect(find.text('initial value'), findsOneWidget);
  });

  testWidgets('can return subtree if there is data', (WidgetTester tester) async {
    final stream = Stream.fromIterable(['first']);
    final sb = StreamBuilder<String>(
      stream: stream,
      builder: (ctx, snapshot) {
        if (snapshot.hasData) {
          return Text('${snapshot.data}');
        } else
          return Container();
      },
    );
    var wrappedWidget = wrapInMaterialApp(sb);
    await tester.pumpWidget(wrappedWidget);

    expect(find.byType(Container), findsOneWidget);
    expect(find.text('first'), findsNothing);

    await tester.pump();

    expect(find.byType(Container), findsNothing);
    expect(find.text('first'), findsOneWidget);
  });
}

Other things that can help you determine what Widget your builder should return is ConnectionState, accessible through snapshot.connectionState.

Cheers!

Sign up to request clarification or add additional context in comments.

Comments

0

You should always approach the structure for the StreamBuilder like,

StreamBuilder(
  stream: some_stream,
  builder: (context, data){
      return StreamBuilder(
         stream: some_stream2,
         builder: (context, data){
             if(data.hasError) {
               return Text("Error Occured!!");
             } else if(data.hasData) {
               return StreamBuilder(
                      stream: some_stream,
                      builder: (context, data){
                         if (data.hasError){
                            return Text("Error Occured!!");
                         } else if (data.hasData) {
                            return someWidget;
                         }else {
                            return CircularProgressIndicator();
                         }
                      }
               );
             } else {
               return CircularProgressIndicator();
             }
          }
        );
       }
    );

This will save you from errors for most of the time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.