Wazza
Wazza

Reputation: 1885

data = null on snapshot in stream builder

Im trying to connect my viewmodel to my screen using streams for the first time but im struggling to grasp the concept completely.

My Screen:

class LoginAndSignupScreen extends StatefulWidget {
  LoginAndSignupScreen({@required this.viewModel});

  final LoginAndSignupScreenViewModelType viewModel;

  @override
  State<StatefulWidget> createState() => new _LoginAndSignupScreenState();
}

class _LoginAndSignupScreenState extends State<LoginAndSignupScreen> {
  Widget showErrorMessage() {
    return StreamBuilder<String>(
      initialData: "",
      stream: widget.viewModel.errorText,
      builder: (context, snapshot) {
          return new Text(
            snapshot.data,
            style: TextStyle(
                fontSize: 13.0,
                color: Colors.red,
                height: 1.0,
                fontWeight: FontWeight.w300
            ),
          );
        }
    );
  }

My view model:

abstract class LoginAndSignupScreenViewModelType {
  Stream<String> get errorText;

  void signIn(String email, String password);
  void signUp(String email, String password, String firstName, String lastName);
}

class LoginAndSignupScreenViewModel implements LoginAndSignupScreenViewModelType {
  LoginAndSignupScreenViewModel({@required this.authenticationService,
                                 @required this.cloudStoreService});

  final AuthenticationServiceType authenticationService;
  final CloudStoreServiceType cloudStoreService;

  final errorController = StreamController<String>.broadcast();

  @override
  Stream<String> get errorText => errorController.stream;

  @override
  void signIn(String email, String password) async {
    try {
      String userId = await authenticationService.signIn(email, password);
      User user = await cloudStoreService.fetchUserWithId(userId)
        .whenComplete(loginCallback);
      print('Signed in: ${user.firstName} ${user.lastName}');
    } catch (error) {
      errorController.add(error);
    }
  }

  @override 
  void signUp(String email, String password, String firstName, String lastName) async {
    try {
      String userId = await authenticationService.signUp(email, password);
      User user = new User(userId, firstName, lastName);
      await cloudStoreService.createUser(user)
        .then(showHomeScreenIfValidUser);
      print('Signed up user: ${user.id}');
    } catch (error) {
        errorController.add(error);
    }
  }
}

When I do this and run I get an error saying that snapshot.data = null which I understand. My issue is I want there to be no widget if there is no error string.

Can anyone help?

Upvotes: 0

Views: 857

Answers (1)

Frank Treacy
Frank Treacy

Reputation: 3716

To answer your question, different widgets can be returned depending on the value of snapshot.data:

stream: widget.viewModel.errorText,
builder: (context, snapshot) {
  if (snapshot.hasData) {
    return Text(
      snapshot.data,
      style: TextStyle(
          fontSize: 13.0,
          color: Colors.red,
          height: 1.0,
          fontWeight: FontWeight.w300
      ),
    );
  }

  // returns an invisible widget
  return SizedBox.shrink();

}

That aside, did you know that you can have a Stream that yields both data and errors? I wonder why you are using callbacks instead of just one stream for everything, where snapshot.data has data, and snapshot.error has errors.

Upvotes: 1

Related Questions