skip to Main Content

I’m currently writting a code to fetch a firebase data in initState.
But no matter what I do it does’t return or does’t store data so I get following error.
I’m getting error at code Text(userName["username"].toString())

Exception has occurred. NoSuchMethodError (NoSuchMethodError: The
method ‘[]’ was called on null. Receiver: null Tried calling:
)

Please teach me how to make it work.
Since I’m using setState inside of by code I would not like to use futurebuilder nor streambuilder to perform it.

  class _reviewWriteHomeState extends State<reviewWriteHome> {
  var user = FirebaseAuth.instance.currentUser;
  var userName;
  String? userIcon;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    Future(() async {
      fetchData();
      await Future.delayed(Duration(seconds: 5));
    });
  }

  Future fetchData() async {
    final userSnap = await FirebaseFirestore.instance.collection("user").doc("user").get();
    final pos = (userSnap.data() as Map<String, dynamic>);
    setState(() {
      userName = pos;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
          child: Center(
        child: Column(
          children: [
            const SizedBox(height: 30),
            Text(userName["username"].toString()),
            const SizedBox(height: 10),
            GestureDetector(
              child: Image.asset('assets/image.png'),
              onTap: () {
                Navigator.push(context, MaterialPageRoute(builder: (context) => const newPage()));
              },
            )
          ],
        ),
      )),
    );
  }
}

3

Answers


  1. It is not advised to use future in initState instead use the FutureBuilder

    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        // Future(() async { // Remove these
         // fetchData();
         // await Future.delayed(Duration(seconds: 5));
        // });
      }
     @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
              child: FutureBuilder<DocumentSnapshot<Map<String, dynamic>>>(
            future: FirebaseFirestore.instance.collection("user").doc("user").get(), // Call your future function directly here
            builder: (_, snapshot) {
              if (snapshot.hasError) return Text('Error = ${snapshot.error}');
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const Text("Loading");
              }
              Map<String, dynamic> data = snapshot.data!.data()!;
              return Text(data['username']); 
            },
          )),
        );
      }
    

    Update:

      @override
      void initState() {
        FirebaseFirestore.instance.collection("user").doc("user").get()
            .then((value) {
          setState(() {
            name = value.data()?['username'];
          });
        });
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
              child:
                  name != null ? Text(name!) : const CircularProgressIndicator())
    
        );
      }
    }
    
    Login or Signup to reply.
  2. using future in initState() is not a best idea, you can use addPostFrameCallback inside initState, or use it before returning widget tree to the build method
    and you don’t really have to use the future builder.

    here is an example

    WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
          Future(() {
          fetchData();
          await Future.delayed(Duration(seconds: 5));
        });
        });
    

    or You can use FutureBuilder. refer BouncyBits answer for that.

    Login or Signup to reply.
  3. You declared userName as:

    var userName;
    

    Since you don’t specify a default value, that means that its initial value is null. Since you only set the vale of userName when you get data from Firestore (many seconds later), the UI will be rendered while userName is still null. And in the build method you then do:

    Text(userName["username"].toString())
    

    Since username is null initially, you’re trying to call ["username"] on it, which isn’t valid. And that’s exactly what the error message tells you.

    A simple way to solve this is to show a loading indicator while the user name is being loaded:

    userName != null? Text(userName["username"].toString()) : const CircularProgressIndicator()
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search