Search code examples
firebaseflutterfirebase-realtime-database

Why is the Firebase Realtime Database's data appearing as null in the console?


I am trying to create a simple app that adds posts to a firebase realtime database and shows them on a listview in flutter, but when I "post" it to the database, on the firebase console, the data value constantly appears as null. does anyone know what is wrong?

My Screenshot -

Here is a bit of my code -

post.dart

class Post {
  Image coverImg;
  File audioFile;
  String body;
  String author;
  Set usersLiked = {};
  DatabaseReference _id;

  Post(this.body, this.author);

  void likePost(User user) {
    if (this.usersLiked.contains(user.uid)) {
      this.usersLiked.remove(user.uid);
    } else {
      this.usersLiked.add(user.uid);
    }
  }

  void update() {
    updatePost(this, this._id);
  }

  void setId(DatabaseReference id) {
    this._id = id;
  }

  Map<String, dynamic> toJson() {
    return {
      'body': this.body,
      'author': this.author,
      'usersLiked': this.usersLiked.toList()
    };
  }
}

Post createPost(record) {
  Map<String, dynamic> attributes = {
    'author': '',
    'usersLiked': [],
    'body': ''
  };

  record.forEach((key, value) => {attributes[key] = value});

  Post post = new Post(attributes['body'], attributes['author']);
  post.usersLiked = new Set.from(attributes['usersLiked']);
  return post;
}

class PostList extends StatefulWidget {
  final List<Post> listItems;
  final User user;
  PostList(this.listItems, this.user);

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

class _PostListState extends State<PostList> {
  void like(Function callBack) {
    this.setState(() {
      callBack();
    });
  }

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

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: this.widget.listItems.length,
      itemBuilder: (context, index) {
        var post = this.widget.listItems[index];

        return Card(
            elevation: 7.0,
            color: Colors.white,
            child: Column(
              children: [
                Row(
                  children: [
                    IconButton(
                      icon: Icon(
                        Icons.play_circle_outline,
                        color: Colors.black,
                        size: 30,
                      ),
                      onPressed: () {},
                    ),
                    Expanded(
                      child: ListTile(
                        title: Text(post.body,
                            style: TextStyle(
                                fontWeight: FontWeight.w600,
                                color: Colors.black)),
                        subtitle: Text(post.author,
                            style: TextStyle(color: Colors.black)),
                      ),
                    ),
                    Expanded(
                        child: Row(
                      mainAxisAlignment: MainAxisAlignment.end,
                      children: [
                        Text(post.usersLiked.length.toString(),
                            style: TextStyle(
                                color: Colors.black,
                                fontWeight: FontWeight.w600)),
                        IconButton(
                          icon: Icon(
                            Icons.thumb_up,
                            color: post.usersLiked.contains(widget.user.uid)
                                ? Colors.blue
                                : Colors.black,
                          ),
                          onPressed: () =>
                              this.like(() => post.likePost(widget.user)),
                        )
                      ],
                    ))
                  ],
                )
              ],
            ));
      },
    );
  }
}

database.dart

final databaseReference = FirebaseDatabase.instance.reference();

DatabaseReference savePost(Post post) {
  var id = databaseReference.child('posts/').push();
  id.set(post.toJson());
  return id;
}

void updatePost(Post post, DatabaseReference id) {
  id.update(post.toJson());
}

Future<List<Post>> getAllPosts() async {
  DataSnapshot dataSnapshot = await databaseReference.child('posts/').once();
  List<Post> posts = [];
  if (dataSnapshot.value != null) {
    dataSnapshot.value.forEach((key, value) {
      Post post = createPost(value);
      post.setId(databaseReference.child('posts/' + key));
      posts.add(post);
    });
  }
  return posts;
}

home.dart

class UserInfoScreen extends StatefulWidget {
  const UserInfoScreen({Key key, User user})
      : _user = user,
        super(key: key);

  final User _user;

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

class _UserInfoScreenState extends State<UserInfoScreen> {
  User _user;
  bool _isSigningOut = false;
  List<Post> posts = [];

  void newPost(String text) {
    var post = new Post(text, widget._user.displayName);
    post.setId(savePost(post));
    this.setState(() {
      posts.add(post);
    });
  }

  void updatePosts() {
    getAllPosts().then((posts) => {
          this.setState(() {
            this.posts = posts;
          })
        });
  }

  Route _routeToSignInScreen() {
    return PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => SignInScreen(),
      transitionsBuilder: (context, animation, secondaryAnimation, child) {
        var begin = Offset(-1.0, 0.0);
        var end = Offset.zero;
        var curve = Curves.ease;

        var tween =
            Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

        return SlideTransition(
          position: animation.drive(tween),
          child: child,
        );
      },
    );
  }

  @override
  void initState() {
    _user = widget._user;

    // HOME PAGE

    pageList.add(Scaffold(
      backgroundColor: Colors.white,
      body: Column(children: [
        Expanded(
          child: PostList(this.posts, _user),
        ),
      ]),
    ));

    pageList.add(Test1());

    pageList.add(Scaffold(
      backgroundColor: Colors.white,
      body: Column(
        children: [
          TextInputWidget(this.newPost, "Title:"),
        ],
      ),
    ));

    pageList.add(Test3());

    // ACCOUNT PAGE

    pageList.add(
      Scaffold(
        backgroundColor: Colors.white,
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Row(),
            _user.photoURL != null
                ? ClipOval(
                    child: Material(
                      color: Colors.grey.withOpacity(0.3),
                      child: Image.network(
                        _user.photoURL,
                        fit: BoxFit.fitHeight,
                      ),
                    ),
                  )
                : ClipOval(
                    child: Material(
                      color: Colors.grey.withOpacity(0.3),
                      child: Padding(
                        padding: const EdgeInsets.all(16.0),
                        child: Icon(
                          Icons.person,
                          size: 60,
                          color: Colors.black,
                        ),
                      ),
                    ),
                  ),
            SizedBox(height: 16.0),
            Text(
              '${_user.displayName}',
              style: TextStyle(
                  color: Colors.black,
                  fontSize: 26,
                  fontWeight: FontWeight.w700),
            ),
            SizedBox(height: 8.0),
            Text(
              ' ${_user.email} ',
              style: TextStyle(
                color: Colors.black,
                fontSize: 20,
                letterSpacing: 0.5,
              ),
            ),
            SizedBox(height: 60.0),
            _isSigningOut
                ? CircularProgressIndicator(
                    valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                  )
                : ElevatedButton(
                    style: ButtonStyle(
                      backgroundColor: MaterialStateProperty.all(
                        Colors.redAccent,
                      ),
                      shape: MaterialStateProperty.all(
                        RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(10),
                        ),
                      ),
                    ),
                    onPressed: () async {
                      setState(() {
                        _isSigningOut = true;
                      });
                      await Authentication.signOut(context: context);
                      setState(() {
                        _isSigningOut = false;
                      });
                      Navigator.of(context)
                          .pushReplacement(_routeToSignInScreen());
                    },
                    child: Padding(
                      padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
                      child: Text(
                        'Logout',
                        style: TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.w500,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
          ],
        ),
      ),
    );

    super.initState();
    updatePosts();
  }

  @override
  List<Widget> pageList = List<Widget>();
  int _currentIndex = 0;

  void onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0.0,
        backgroundColor: Colors.white,
        title: Text(
          'SoundFX',
          style: TextStyle(color: Colors.black),
        ),
        centerTitle: true,
      ),
      body: IndexedStack(
        index: _currentIndex,
        children: pageList,
      ),
      bottomNavigationBar: BottomNavigationBar(
        unselectedItemColor: Colors.grey,
        selectedItemColor: Colors.blue,
        backgroundColor: Colors.white,
        elevation: 19.0,
        onTap: onTabTapped,
        currentIndex: _currentIndex,
        type: BottomNavigationBarType.fixed,
        items: [
          BottomNavigationBarItem(
              icon: new Icon(Icons.home),
              title: Container(
                height: 1.0,
              )),
          BottomNavigationBarItem(
              icon: new Icon(Icons.search),
              title: Container(
                height: 1.0,
              )),
          BottomNavigationBarItem(
              icon: new Icon(
                Icons.add_circle,
                size: 40,
              ),
              title: Container(
                height: 1.0,
              )),
          BottomNavigationBarItem(
              icon: new Icon(Icons.videocam),
              title: Container(
                height: 1.0,
              )),
          BottomNavigationBarItem(
              icon: Icon(Icons.account_circle),
              title: Container(
                height: 1.0,
              )),
        ],
      ),
    );
  }
}




class Test1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
    );
  }
}

class Test2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue,
    );
  }
}

class Test3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.yellow,
    );
  }
}

text-input.dart

class TextInputWidget extends StatefulWidget {
  final Function(String) callback;
  String label;

  TextInputWidget(this.callback, this.label);

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

class _TextInputWidgetState extends State<TextInputWidget> {
  final controller = TextEditingController();

  @override
  void dispose() {
    super.dispose();

    controller.dispose();
  }

  void click() {
    widget.callback(controller.text);
    FocusScope.of(context).unfocus();
    controller.clear();
  }

  void pickFiles() async {
    FilePickerResult result =
        await FilePicker.platform.pickFiles(type: FileType.any);

    File file = File(result.files.first.path);
    print(file);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 400,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Expanded(
            child: Container(
                padding: EdgeInsets.all(20.0),
                child: TextField(
                    controller: this.controller,
                    decoration: InputDecoration(
                      labelText: this.widget.label,
                    ))),
          ),
          Container(
            child: ElevatedButton(onPressed: this.click, child: Text('Post')),
          ),
          Container(
            child:
                ElevatedButton(onPressed: this.pickFiles, child: Text('test')),
          )
        ],
      ),
    );
  }
}

PS: Also, note that some of the functions and classes are unused or basically useless, and some of the names are very stupid, but please do not comment on those if they are not related to the question, as those are just for some tests for features for me to add into the application.

Also, note that i followed this tutorial - https://www.youtube.com/playlist?list=PLzMcBGfZo4-knQWGK2IC49Q_5AnQrFpzv


Solution

  • firebaser here

    Most likely the Firebase SDK is unable to read the URL of the database from its config file, because your database is hosted outside of the default (US) region.

    If that is indeed the cause, you should specify the database URL in your code. By specifying it in your code, the SDK should able to connect to the database instance no matter what region it is hosted in.

    This mean you should get your database with

    FirebaseDatabase("your database URL").reference()
    // Or
    FirebaseDatabase(databaseURL: "your database URL").reference()
    

    instead of

    FirebaseDatabase.instance.reference()
    

    This is documented here:

    To get a reference to a database other than a us-central1 default database, you must pass the database URL to getInstance() (or Kotlin+KTX database()) . For a us-central1 default database, you can call getInstance() (or database) without arguments.

    So while this is documented, it is incredibly easy to overlook. I'm checking with our team if there's something we can do to make this configuration problem more obvious.