0

I’m building an app, so far everything works. Until I click on a button that calls this Staful Widget:

class ToDo1 extends StatefulWidget {
  @override
  _ToDo1State createState() => _ToDo1State();
}

class _ToDo1State extends State<ToDo1> {

  var User;
  late DatabaseService database;

  Future<void> connectToFirebase() async{
    await Firebase.initializeApp();
    final FirebaseAuth auth = FirebaseAuth.instance;
    UserCredential result = await FirebaseAuth.instance.signInAnonymously();
    User = result.user;
    database = DatabaseService(User.uid);

    if (!(await database.checkIfUserExists())) {
      database.setTodo('To-Do anlegen', false);
    }
  }

  void toggleDone(String key, bool value) {
    database.setTodo(key, !value);
    }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Center(
            child: Text(
          'Stufe 1',
          style: TextStyle(
              fontStyle: FontStyle.italic,
              decoration: TextDecoration.underline),
        )),
        backgroundColor: Color.fromRGBO(35, 112, 192, 1),
      ),
      body: FutureBuilder(
        future: connectToFirebase(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          } else {
            return StreamBuilder<DocumentSnapshot> (
              stream: database.getTodos(),
              builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
                if(!snapshot.hasData) {
                  return CircularProgressIndicator();
                } else {
                  Map<String, dynamic> items = snapshot.data!.data as Map<String, dynamic>;
                  return ListView.separated(
                      separatorBuilder: (BuildContext context, int index) {
                        return SizedBox(
                          height: 10,
                        );
                      },
                      padding: EdgeInsets.all(10),
                      itemCount: items.length,
                      itemBuilder: (BuildContext, i) {
                        String key = items.keys.elementAt(i);
                        return ToDoItem(
                          key,
                          items[key]!,
                              () => toggleDone(key, items[key]),
                        );
                      });
                }
              }
            );
          }
            },
      )
    );
  }
}

Then I am confronted with the following error:

The following LateError was thrown building FutureBuilder<void>(dirty, state: _FutureBuilderState<void>#bc115):
LateInitializationError: Field 'database' has not been initialized.

This is the class that interacts with the firebase:

class DatabaseService {
  final String userID;
  DatabaseService(this.userID);

  final CollectionReference userTodos =
      FirebaseFirestore.instance.collection('userTodos');

  Future setTodo(String item, bool value) async {
    return await userTodos.doc(userID).set(
      {item:value}, SetOptions(merge: true));
  }

    Future deleteTodo(String key) async {
      return await userTodos.doc(userID).update(
        {key: FieldValue.delete(),}
      );
    }

    Future checkIfUserExists() async {
    if((await userTodos.doc(userID).get()).exists) {
      return true;
    }
      else {
        return false;
      }
    }

  Stream<DocumentSnapshot> getTodos() {
    return userTodos.doc(userID).snapshots();
  }
}

I hope I have provided all the necessary data so that the problem can be solved. If not, just write it to me and I will try to send you the material you need.

Lars
  • 65
  • 1
  • 7

2 Answers2

0

Let try changing this code from

          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          } else {

to

          if (snapshot.connectionState != ConnectionState.done) {
            return Center(child: CircularProgressIndicator());
          } else {

In async.dart file you can see:

/// The state of connection to an asynchronous computation.
///
/// The usual flow of state is as follows:
///
/// 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 possible changing over time.
/// 4. [done], with data being non-null.
///
/// See also:
///
///  * [AsyncSnapshot], which augments a connection state with information
///    received from the asynchronous computation.
enum ConnectionState {
  /// Not currently connected to any asynchronous computation.
  ///
  /// For example, a [FutureBuilder] whose [FutureBuilder.future] is null.
  none,

  /// Connected to an asynchronous computation and awaiting interaction.
  waiting,

  /// Connected to an active asynchronous computation.
  ///
  /// For example, a [Stream] that has returned at least one value, but is not
  /// yet done.
  active,

  /// Connected to a terminated asynchronous computation.
  done,
}

You will miss ConnectionState.none and ConnectionState.active if you just compare with ConnectionState.waiting, so the Future isn't completed when you call .todos() in your stream and it will cause the issue.

Lam Thanh Nhan
  • 486
  • 4
  • 5
  • That totally makes sence! I've changed it, but I'm still getting the same error. Any idea what I can do now? – Lars Sep 08 '21 at 09:57
0

I suggest you to check this answer in which they have the exact same issue as you do:

Future Builders are built even before getting the data. So, you should check whether it has data.

In that answer they suggest to use FutureBuilder in the following:

FutureBuilder<Position>(
            future: getInitialPosition(),
            builder: (context, snapshot) {
                if (snapshot.hasData) {
                return Map(snapshot.data);
              }else{
                return CircularProgressIndicator(); 
                //Display loading, you may adapt this widget to your interface or use some state management solution
              }
            }
        )
drauedo
  • 641
  • 4
  • 17