Related
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?
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
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 dstabase, 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.
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?
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
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 dstabase, 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.
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?
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
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 dstabase, 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.
Running my App on Android and iOS I get the Error shown below. What I want to do, give every Data that get's added a Author, the Author should be the Mail Adress the User Registers with, so by Inviting other Users get Access to this Data.
Another Problem is that in Line 302 the String "user" is unused.
Do i really need to use this String?
Error
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building FutureBuilder<FirebaseUser>(dirty, state: _FutureBuilderState<FirebaseUser>#7f7ed):
The getter 'iterator' was called on null.
Receiver: null
Tried calling: iterator
The relevant error-causing widget was
FutureBuilder<FirebaseUser>
lib/…/MealPlan/mealTile.dart:92
When the exception was thrown, this was the stack
Code
import 'package:flutter/material.dart';
import 'package:mealapp/models/Widgets/whenAndWhatToEat.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:intl/intl.dart';
import 'package:mealapp/models/global.dart';
import 'package:status_alert/status_alert.dart';
import 'package:firebase_auth/firebase_auth.dart';
class MealTile extends StatefulWidget {
final MealsAndWhen mealsAndWhen;
MealTile({this.mealsAndWhen});
#override
MealTileState createState() {
return MealTileState();
}
}
class MealTileState extends State<MealTile> {
String id;
final db = Firestore.instance;
String mail;
List<String> authors = [];
DateTime selectedDate = DateTime.now();
Future pickDate() async {
DateTime datepick = await showDatePicker(
context: context,
initialDate: new DateTime.now(),
firstDate: new DateTime.now().add(Duration(days: 0)),
lastDate: new DateTime.now().add(Duration(days: 365)));
if (datepick != null)
setState(() {
selectedDate = datepick;
});
}
Future<String> inputData() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user != null ? user.uid : null;
}
Future<String> inputDataMail() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user != null ? user.email : null;
}
String userId;
void _getUserId() {
inputData().then((value) => setState(() {
userId = value;
}));
}
String currentMail;
void _getMail(doc) {
inputDataMail().then((value) => setState(() {
currentMail = value;
}));
}
/*void _getAuthors(DocumentSnapshot doc) async {
authors = [];
//if (await FirebaseAuth.instance.currentUser() != null) {
authors = List.from(doc.data['Authors']);
print(doc.data['authors']);
//authors.insert(0, currentMail);
//}
}*/
Widget buildItem(DocumentSnapshot doc) {
DateTime now = doc.data['Date'].toDate();
DateFormat formatter = DateFormat('dd-MM-yyyy');
String formatted = formatter.format(now);
_getUserId();
_getMail(doc);
if (doc.data['Authors'] != null) {
//_getAuthors(doc);
//print('Current mail: ' + currentMail + authors.toString() + doc.data['Author'] + doc.data['Meal']);
}
if (now.day == DateTime.now().day) { // If the Date of the meal is today
deleteData(doc, false); // Delete it!
}
// You could also change ".day" to ".hour".
// Example: if (now.day == DateTime.now().day && now.hour == DateTime.hour())
// So, if a meal is set for 2PM, it will delete at 2PM
return FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.hasData && snapshot != null) {
return Container(
margin: const EdgeInsets.all(8.0),
child: currentMail == doc.data['Author'] || // If the current mail is the author
List.from(doc.data['Authors']).contains(currentMail) // Or if the current mail is part of the authors
? Column( // then if true, show a Column
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Meal:',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
Text(
'${doc.data['Meal']}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
SizedBox(height: 20),
Text(
'When:',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
onPressed: () => updateData(doc),
color: lightBlueColor,
icon: Icon(Icons.calendar_today,
color: Colors.white),
tooltip: 'Update Date',
),
Text(
formatted,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
SizedBox(width: 8),
FlatButton(
color: Colors.red,
onPressed: () => deleteData(doc, true),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadiusDirectional.circular(12)),
child: Row(children: <Widget>[
Text('Delete',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white)),
Icon(Icons.delete_forever, color: Colors.white),
]),
),
SizedBox(width: 8),
FlatButton(
color: Colors.blue,
onPressed: () => [
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
child: invite(doc),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12)),
),
);
})
],
shape: RoundedRectangleBorder(
borderRadius:
BorderRadiusDirectional.circular(12)),
child: Row(children: <Widget>[
Text('Invite',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white)),
Icon(Icons.share, color: Colors.white),
]),
),
],
),
],
)
: Text(''), // if false, show an empty text widget
decoration: BoxDecoration(
color: lightBlueColor,
borderRadius: BorderRadius.all(Radius.circular(12)),
),
);
}
else {
return CircularProgressIndicator();
}
/*Navigator.pop(context);
return HomePage();*/
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: darkGreyColor,
body: ListView(
padding: EdgeInsets.only(top: 220),
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: db
.collection('mealList')
.orderBy('Date', descending: false) // Order by Date, not descending
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents
.map((doc) => buildItem(doc))
.toList());
} else {
return Container();
}
},
),
],
),
);
}
/*share(BuildContext context, DocumentSnapshot doc) {
final RenderBox box = context.findRenderObject();
final dynamic date = timeago.format(doc['Date'].toDate());
Share.share(
"${doc['Meal']} - $date",
subject: doc['Meal'],
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size,
);
}*/
Widget invite(DocumentSnapshot doc) {
final _formKey = GlobalKey<FormState>();
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Center(
child: Text(
"Invite someone by mail",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
)),
SizedBox(
height: 24,
),
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
labelText: 'Enter the email address'),
validator: (value) {
if (value.isEmpty) {
return 'Please enter an email address';
}
return null;
},
onSaved: (value) => mail = value,
),
FlatButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
saveInviteToFirestore(doc, mail);
}
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Text("Save"),
color: redColor,
textColor: Colors.white,
),
]),
),
);
}
Future<String> getCurrentUser() async {
return await FirebaseAuth.instance.currentUser().then((value) => value.uid);
}
void saveInviteToFirestore(DocumentSnapshot doc, String email) async {
final String user = await getCurrentUser();
var list = List<String>();
list.add(email);
Firestore.instance
.collection('mealList')
.document(doc.documentID)
.updateData({"Authors": FieldValue.arrayUnion(list)});
//setState(() => id = doc.documentID);
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Added',
subtitle: 'You have Added your and the Date to your List',
configuration: IconConfiguration(icon: Icons.done),
);
//Navigator.pop(context);
}
void deleteData(DocumentSnapshot doc, bool showMessage) async {
await db.collection('mealList').document(doc.documentID).delete();
setState(() => id = null);
if (showMessage) {
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Deleted',
subtitle: 'You have Deleted your Meal',
configuration: IconConfiguration(icon: Icons.delete),
);
}
}
void updateData(DocumentSnapshot doc) async {
await pickDate();
await db
.collection('mealList')
.document(doc.documentID)
.updateData({'Date': selectedDate});
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Updated',
subtitle: 'You have updated your Meal Date',
configuration: IconConfiguration(icon: Icons.done),
);
}
}
Currently i develop a Meal and Shopping App. In this App you can Add what you want to Eat next and have the secound Tab, Shopping where you can Add your Items you want to buy next. Created is that a User can invite another User to edit together the List.
I get the Error shown below. I can't figure out how to return the Container. At the void saveInviteToFirestore the user is not used do I need that it used?
Code
import 'package:flutter/material.dart';
import 'package:mealapp/models/Widgets/whenAndWhatToEat.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:intl/intl.dart';
import 'package:mealapp/models/global.dart';
import 'package:status_alert/status_alert.dart';
import 'package:firebase_auth/firebase_auth.dart';
class MealTile extends StatefulWidget {
final MealsAndWhen mealsAndWhen;
MealTile({this.mealsAndWhen});
#override
MealTileState createState() {
return MealTileState();
}
}
class MealTileState extends State<MealTile> {
String id;
final db = Firestore.instance;
String mail;
List<String> authors = [];
DateTime selectedDate = DateTime.now();
Future pickDate() async {
DateTime datepick = await showDatePicker(
context: context,
initialDate: new DateTime.now(),
firstDate: new DateTime.now().add(Duration(days: -0)),
lastDate: new DateTime.now().add(Duration(days: 365)));
if (datepick != null)
setState(() {
selectedDate = datepick;
});
}
Future<String> inputData() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user != null ? user.uid : null;
}
Future<String> inputDataMail() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user != null ? user.email : null;
}
String userId;
void _getUserId() {
inputData().then((value) => setState(() {
userId = value;
}));
}
String currentMail;
void _getMail(doc) {
inputDataMail().then((value) => setState(() {
currentMail = value;
}));
}
/*void _getAuthors(DocumentSnapshot doc) async {
authors = [];
//if (await FirebaseAuth.instance.currentUser() != null) {
authors = List.from(doc.data['Authors']);
print(doc.data['authors']);
//authors.insert(0, currentMail);
//}
}*/
Widget buildItem(DocumentSnapshot doc) {
DateTime now = doc.data['Date'].toDate();
DateFormat formatter = DateFormat('dd-MM-yyyy');
String formatted = formatter.format(now);
_getUserId();
_getMail(doc);
if (doc.data['Authors'] != null) {
//_getAuthors(doc);
//print('Current mail: ' + currentMail + authors.toString() + doc.data['Author'] + doc.data['Meal']);
}
if (now.day == DateTime.now().day) { // If the Date of the meal is today
deleteData(doc, false); // Delete it!
}
// You could also change ".day" to ".hour".
// Example: if (now.day == DateTime.now().day && now.hour == DateTime.hour())
// So, if a meal is set for 2PM, it will delete at 2PM
return FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.hasData && snapshot != null) {
return Container(
margin: const EdgeInsets.all(8.0),
child: currentMail == doc.data['Author'] || // If the current mail is the author
List.from(doc.data['Authors']).contains(currentMail) // Or if the current mail is part of the authors
? Column( // then if true, show a Column
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Meal:',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
Text(
'${doc.data['Meal']}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
SizedBox(height: 20),
Text(
'When:',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
onPressed: () => updateData(doc),
color: lightBlueColor,
icon: Icon(Icons.calendar_today,
color: Colors.white),
tooltip: 'Update Date',
),
Text(
formatted,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
textAlign: TextAlign.center,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
SizedBox(width: 8),
FlatButton(
color: Colors.red,
onPressed: () => deleteData(doc, true),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadiusDirectional.circular(12)),
child: Row(children: <Widget>[
Text('Delete',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white)),
Icon(Icons.delete_forever, color: Colors.white),
]),
),
SizedBox(width: 8),
FlatButton(
color: Colors.blue,
onPressed: () => [
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
child: invite(doc),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12)),
),
);
})
],
shape: RoundedRectangleBorder(
borderRadius:
BorderRadiusDirectional.circular(12)),
child: Row(children: <Widget>[
Text('Invite',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white)),
Icon(Icons.share, color: Colors.white),
]),
),
],
),
],
)
: Text(''), // if false, show an empty text widget
decoration: BoxDecoration(
color: lightBlueColor,
borderRadius: BorderRadius.all(Radius.circular(12)),
),
);
}
/*Navigator.pop(context);
return HomePage();*/
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: darkGreyColor,
body: ListView(
padding: EdgeInsets.only(top: 220),
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: db
.collection('mealList')
.orderBy('Date', descending: false) // Order by Date, not descending
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents
.map((doc) => buildItem(doc))
.toList());
} else {
return Container();
}
},
),
],
),
);
}
/*share(BuildContext context, DocumentSnapshot doc) {
final RenderBox box = context.findRenderObject();
final dynamic date = timeago.format(doc['Date'].toDate());
Share.share(
"${doc['Meal']} - $date",
subject: doc['Meal'],
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size,
);
}*/
Widget invite(DocumentSnapshot doc) {
final _formKey = GlobalKey<FormState>();
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Center(
child: Text(
"Invite someone by mail",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
)),
SizedBox(
height: 24,
),
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
labelText: 'Enter the email address'),
validator: (value) {
if (value.isEmpty) {
return 'Please enter an email address';
}
return null;
},
onSaved: (value) => mail = value,
),
FlatButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
saveInviteToFirestore(doc, mail);
}
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Text("Save"),
color: redColor,
textColor: Colors.white,
),
]),
),
);
}
Future<String> getCurrentUser() async {
return await FirebaseAuth.instance.currentUser().then((value) => value.uid);
}
void saveInviteToFirestore(DocumentSnapshot doc, String email) async {
final String user = await getCurrentUser();
var list = List<String>();
list.add(email);
Firestore.instance
.collection('mealList')
.document(doc.documentID)
.updateData({"Authors": FieldValue.arrayUnion(list)});
//setState(() => id = doc.documentID);
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Added',
subtitle: 'You have Added your and the Date to your List',
configuration: IconConfiguration(icon: Icons.done),
);
//Navigator.pop(context);
}
void deleteData(DocumentSnapshot doc, bool showMessage) async {
await db.collection('mealList').document(doc.documentID).delete();
setState(() => id = null);
if (showMessage) {
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Deleted',
subtitle: 'You have Deleted your Meal',
configuration: IconConfiguration(icon: Icons.delete),
);
}
}
void updateData(DocumentSnapshot doc) async {
await pickDate();
await db
.collection('mealList')
.document(doc.documentID)
.updateData({'Date': selectedDate});
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Updated',
subtitle: 'You have updated your Meal Date',
configuration: IconConfiguration(icon: Icons.done),
);
}
}
Error
The following assertion was thrown building FutureBuilder<FirebaseUser>(dirty, state: _FutureBuilderState<FirebaseUser>#a4504):
A build function returned null.
The offending widget is: FutureBuilder<FirebaseUser>
Build functions must never return null.
To return an empty space that causes the building widget to fill available room, return "Container()". To return an empty space that takes as little room as possible, return "Container(width: 0.0, height: 0.0)".
The relevant error-causing widget was
FutureBuilder<FirebaseUser>
lib/…/MealPlan/mealTile.dart:92
When the exception was thrown, this was the stack
#0 debugWidgetBuilderValue.<anonymous closure>
package:flutter/…/widgets/debug.dart:276
In your FutureBuilder you are not returning anything when the Future hasn't completed yet. A widget always needs to be returned whether there is data or not.
Example fix for your code:
return FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.hasData && snapshot != null) {
return Container(
...
);
}
//ADDED ELSE BLOCK
else {
return Container();
}
}
);
Or as #stacker suggested, you can return a CircularProgressIndicator().