CuriousCoder
CuriousCoder

Reputation: 262

InitState() not finishing before Build method is called

I'm having a problem in my home_screen.dart file. I have a method called pullUserData() that is called in initState() but before pullUserData() is completely finished, the build method in home_screen.dart begins. This results in null values (auth and friendsList) being sent to NavDrawer() and FriendsFeed() in the build method of home_screen.dart.

How can I prevent NavDrawer() and FriendsFeed() from being called in the build method before initState() is completely finished? Should I use FutureBuilder?

User_data.dart handles gets the values for auth and friendsList.

home_screen.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:mood/components/friends_feed.dart';
import 'package:mood/components/nav_drawer.dart';
import 'package:mood/services/user_data.dart';

class LandingScreen extends StatefulWidget {
  static const String id = 'landing_screen';

  @override
  _LandingScreenState createState() => _LandingScreenState();
}

class _LandingScreenState extends State<LandingScreen> {
  FirebaseAuth auth;
  List<dynamic> friendsList;

  @override
  void initState() {
    super.initState();
    pullUserData();
  }

  Future<void> pullUserData() async {
    UserData userData = UserData();
    await userData.getUserData();
    auth = userData.auth;
    friendsList = userData.friendsList;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text('Mood'),
        centerTitle: true,
      ),
      drawer: NavDrawer(auth),
      body: FriendsFeed(friendsList),
    );
  }
}

user_data.dart

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

class UserData {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  User _currentUser;
  String _currentUserUID;
  List<dynamic> _friendsList;

  FirebaseAuth get auth => _auth;
  User get currentUser => _currentUser;
  String get currentUserUID => _currentUserUID;
  List<dynamic> get friendsList => _friendsList;

  Future<void> getUserData() async {
    getCurrentUser();
    getCurrentUserUID();
    await getFriendsList();
  }

  void getCurrentUser() {
    _currentUser = _auth.currentUser;
  }

  void getCurrentUserUID() {
    _currentUserUID = _auth.currentUser.uid;
  }

  Future<void> getFriendsList() async {
    await FirebaseFirestore.instance
        .collection("Users")
        .doc(_currentUserUID)
        .get()
        .then((value) {
      _friendsList = value.data()["friends"];
    });
  }
}

Upvotes: 0

Views: 1081

Answers (1)

Ady Hnat
Ady Hnat

Reputation: 73

There are couple of problems in your code but it will work.

Firstly, if you want to set value of your friendslist during build, you have to use setState like this:

setState(() {
  friendsList = userData.friendsList;
});

And if you want to wait until pullUserData() finish, you are looking for something called splash screen, but in your problem, you are waiting only for body to be build so I will recommend to use progress indicator in your scaffold like this:

return Scaffold(
   appBar: Bars.menuAppBar('Devices'),
   drawer: DrawerMenu(),
   backgroundColor: ColorThemes.backgroundColor,
   body: _loading
       ? Center(
           child: CircularProgressIndicator(
           valueColor: new AlwaysStoppedAnimation<Color>(
               Colors.blue), //choose your own color
         ))
       : FriendsFeed(friendsList)
);

You can see that I used _loading variable. You will have to define it before your initState() like

bool _loading = true;

Then after you set your friendsList inside of your pullUserData() function, change _loading to false inside of setState just like this:

setState(() {
  friendsList = userData.friendsList;
  _loading = false;
});

Upvotes: 1

Related Questions