0

When I try to use provider in another component, it says the value that is to be printed hasn't been initialized yet. But, when sign up function is called, if I were to print the username value of the class it is reflected in the console. How can I carry over the initialized values to other components with provider ?

import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

class UserSchema extends ChangeNotifier {


late final String email;
  late final String password;
  late final String username;

  final FirebaseAuth _auth = FirebaseAuth.instance;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  Future<String> userSignUp({
    required String email,
    required String password,
    required String username,
  }) async {
    String res = 'not successful';

try {
  if (email.isNotEmpty && password.isNotEmpty && username.isNotEmpty) {
    await _auth.createUserWithEmailAndPassword(
        email: email, password: password);
    await _firestore
        .collection('users')
        .doc(username)
        .set({'username': username, 'email': email, 'password': password});

    res = 'success';
    this.email = email;
    this.username = username;
    this.password = password;
    notifyListeners();

    print(this.username);
  }
} catch (e) {
  print(e);
}
notifyListeners();
return res;


}

  Future<String> userSignIn(
      {required String email, required String password}) async {
    String res = 'sign in not successful';
    try {
      await _auth.signInWithEmailAndPassword(email: email, password: password);
      res = 'success';
    } catch (e) {
      print(e);
    }
    return res;
  }
}

// when calling provider in another component

import 'package:flutter/material.dart';
import 'package:instagram_clone/firebase/userSchema.dart';
import 'package:instagram_clone/screens/authScreens/loginScreen.dart';
import 'package:ionicons/ionicons.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:provider/provider.dart';

class FeedScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: GestureDetector(
          onTap: () {
            FirebaseAuth.instance.signOut();
            Navigator.pushReplacementNamed(context, LoginScreen.pageName);
          },
          child: Hero(
            tag: 'writtenLogo',
            child: Image.asset(
              'assets/images/writtenLogo.png',
              height: 130,
              width: 130,
            ),
          ),
        ),
        actions: [
          Icon(
            Ionicons.chatbox_outline,
            size: 30,
            color: Colors.white,
          ),
          SizedBox(width: 10)
        ],
      ),
      body: Center(
        child: Container(
          child: Text(Provider.of<UserSchema>(context).username),
        ),
      ),
    );
  }
}

1 Answers1

0

Most probably the Center -> Container -> Text is being built before the userSignUp finishes. A good approach to deal with futures is to use FutureBuilder.

The usual flow of FutureBuilder state is as follows (from the docs):

  1. none, maybe with some initial data.
  2. waiting, indicating that the asynchronous operation has begun, typically with the data being null.
  3. active, with data being non-null, and possibly changing over time.
  4. done, with data being non-null.

So, to fix the code pass the future returned from userSignUp to the FeedScreen widget. Something like below:

class FeedScreen extends StatelessWidget {
  final Future<String> userSignUpFuture;
  const FeedScreen({Key? key, required this.userSignUpFuture}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: GestureDetector(
          onTap: () {
            FirebaseAuth.instance.signOut();
            Navigator.pushReplacementNamed(context, LoginScreen.pageName);
          },
          child: Hero(
            tag: 'writtenLogo',
            child: Image.asset(
              'assets/images/writtenLogo.png',
              height: 130,
              width: 130,
            ),
          ),
        ),
        actions: [
          Icon(
            Ionicons.chatbox_outline,
            size: 30,
            color: Colors.white,
          ),
          SizedBox(width: 10)
        ],
      ),
      body: Center(
        child: Container(
          child: FutureBuilder<String>(
            future: userSignUpFuture,
            builder: (_, snapshot) {
              if (snapshot.hasError) {
                return const Text('Error');
              }

              if (snapshot.data != null) {
                return const Text('Loading...');
              }

              return Text(Provider.of<UserSchema>(context).username);
            },
          ),
        ),
      ),
    );
  }
}
lepsch
  • 8,927
  • 5
  • 24
  • 44
  • Thank you ! This method worked when I set the future constructor of Future Builder as the future return. Any method you'd suggest so that I just have to return the future once as compared to the multiple reads that FutureBuilder calls taking a toll on the backend. – Prasaanth Vallabhan Jul 15 '22 at 08:49