# #firebase #flutter #asynchronous #google-cloud-firestore #flutter-web
Вопрос:
Я держу счетчик в своей базе огня, который содержит общее количество голосов за картинку. При нажатии кнопки «Голосовать» база данных должна обновить счетчик указанного счетчика на 1, что она и делает. Однако обновление не отображается на экране приложения. Например, если изображение имеет 8 голосов, и кнопка нажата для повышения, на экране по-прежнему будет отображаться 8 голосов, но в базе данных теперь будет 9 голосов. При горячем обновлении значение меняется. Как я могу сделать так, чтобы и то, и другое происходило асинхронно? Я попытался поиграть с ним, и всегда либо он обновляет базу данных, а экран остается неизменным, либо экран меняется, а база данных-нет.
Для функций ниже они ведут себя так, как ожидалось, но только не асинхронно на экране.
Соответствующая функция, которая увеличивает количество подписчиков в базе данных:
// likedposts is a list of posts that have already been liked and is initalised earlier
// even if I remove the if statement here, the behaviour is the same
void incrementFollowers(int index) async {
if (!likedposts.contains(posts[index])) {
likedposts.add(posts[index]);
addLikedPost();
FirebaseFirestore.instance
.collection('uploads')
.doc(usernames[index])
.collection('images')
.where('caption', isEqualTo: captions[index])
.get()
.then((querySnapshot) {
querySnapshot.docs.forEach((result) async {
FirebaseFirestore.instance
.collection('uploads')
.doc(usernames[index])
.collection('images')
.doc(result.id)
.update({'upvotes': upvotes[index] 1,});
setState(() {
getUpvotes(index);
});
});
});
}
}
Функция, отображающая голоса:
getUpvotes(int index) {
return RichText(
text: TextSpan(
style:
TextStyle(color: Colors.black, fontSize: 20.0),
children: <TextSpan>[
TextSpan(
text: upvotes[index].toString() ' upvotes',
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()
..onTap = () {
print(
'This will take to upvoters of the photo');
}),
]));
}
Виджет, который отображает все в моем приложении (чтобы найти, где я вызываю кнопку incrementFollowers, просто нажмите ctrl F для incrementFollowers, и вы найдете его):
Widget _getPost() {
Size size = MediaQuery.of(context).size;
if (url!= null) {
return new ListView.builder(
itemCount: images.length,
itemBuilder: (BuildContext context, int userIndex) {
return Container(
child: Column(
children: <Widget>[
Container(
//Includes dp username report flag
margin: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(right: 8),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfile(usernames[userIndex])
),
);
},
child: CircleAvatar(
backgroundImage: displayPic[1],
))),
RichText(
text: TextSpan(children: <TextSpan>[
TextSpan(
text: usernames[userIndex],
style: TextStyle(
color: Colors.black, fontSize: 15.0),
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfile(usernames[userIndex])
),
);
})
]),
)
],
),
IconButton(
icon: Image.asset('assets/pictures/ICON_flag.png'),
iconSize: 25,
onPressed: () {
reportUser(userIndex, context);
},
),
],
),
),
Stack(children: <Widget>[
Container(
//the post picture
child: GestureDetector(
//This is to handle the tagged users raised button
onTap: () {
if (isVisible == false)
setState(() {
isVisible = true;
});
else
setState(() {
isVisible = false;
});
},
),
height: size.height * 0.5,
width: returnWidth(),
padding: EdgeInsets.only(
left: 16,
right: 16,
top: 0,
bottom: 24,
),
// constraints: BoxConstraints(maxHeight: 50),
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill, image: NetworkImage(images[userIndex])),
)
),
Positioned(
top: 25,
left: 50,
child: returnTaggedUsers(userIndex),)
]),
Row(
mainAxisAlignment: returnAlignment(),
// upvote downvote comment send save icons
children: <Widget>[
Container(
color: upVoted ? Colors.blue : Colors.white,
margin: EdgeInsets.only(right: 8),
child: IconButton(
icon: Image.asset('assets/pictures/ICON_upvote.png'),
iconSize: 25,
onPressed: () async {
setState(() {
incrementFollowers(userIndex);
});
getUpvotes(userIndex);
},
)
),
Container(
color: downVoted ? Colors.blue : Colors.white,
margin: EdgeInsets.only(right: 8),
child: IconButton(
icon: Image.asset('assets/pictures/ICON_downvote.png'),
iconSize: 25,
onPressed: () {
setState(() {
downVoted = true;
upVoted = false;
});
},
)),
Container(
margin: EdgeInsets.only(right: 8),
child: IconButton(
icon: Image.asset('assets/pictures/ICON_comment.png'),
iconSize: 25,
onPressed: () {
commentPopUp(userIndex, context);
},
)),
Container(
margin: EdgeInsets.only(right: 8),
child: IconButton(
icon: Image.asset('assets/pictures/ICON-send.png'),
iconSize: 25,
onPressed: () {
print(
'This will let a user send the post to another user');
},
)),
Container(
margin: EdgeInsets.only(right: 8),
child: IconButton(
icon: Image.asset('assets/pictures/ICON_save.png'),
iconSize: 25,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ReportPanel()
),
);
},
)),
],
),
Column(
mainAxisAlignment: returnAlignment(),
//This column contains username, upload description and total upvotes
children: <Widget>[
Container(
//The person who posted along with photo description
alignment: returnCommentAlignment(),
margin: EdgeInsets.only(left: 10, right: 10),
child: RichText(
text: TextSpan(
style:
TextStyle(color: Colors.black, fontSize: 20.0),
children: <TextSpan>[
TextSpan(
text: usernames[userIndex] ': ',
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfile(usernames[userIndex])
),
);
}),
TextSpan(text: captions[userIndex]),
])),
),
Container(
//The total upvotes of post
alignment: returnCommentAlignment(),
margin: EdgeInsets.only(left: 10, right: 10),
child: getUpvotes(userIndex),
)
],
),
Column(
mainAxisAlignment: returnAlignment(),
//This column contains username and comment of commenters
children: <Widget>[
Container(
//First comment
alignment: returnCommentAlignment(),
margin: EdgeInsets.only(left: 10, right: 10),
child: RichText(
text: TextSpan(
style:
TextStyle(color: Colors.black, fontSize: 20.0),
children: <TextSpan>[
TextSpan(
text:
'HarperEvans1: ', //will be a username from firebase
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()
..onTap = () {
print(
'This will take to profile of that person');
}),
TextSpan(text: 'Nice photo!'),
])),
),
Container(
//Second comment
alignment: returnCommentAlignment(),
margin: EdgeInsets.only(left: 10, right: 10),
child: RichText(
text: TextSpan(
style:
TextStyle(color: Colors.black, fontSize: 20.0),
children: <TextSpan>[
TextSpan(
text:
'trevorwilkinson: ', //will be a username from firebase
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()
..onTap = () {
print(
'This will take to profile of that person');
}),
TextSpan(
text:
'Panda Panda Panda Panda Panda Panda Panda Panda Panda Panda Panda Panda Panda Panda'),
])),
),
Container(
//view more comments
alignment: returnCommentAlignment(),
margin: EdgeInsets.only(left: 10, right: 10),
child: RichText(
text: TextSpan(
style:
TextStyle(color: Colors.grey, fontSize: 20.0),
children: <TextSpan>[
TextSpan(
text:
'view more comments', //will take to the comments
style: TextStyle(color: Colors.grey),
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CommentPage(posts[userIndex], usernames[userIndex])
),
);
}),
])),
)
],
)
],
));
});
}
}
Спасибо!
Ответ №1:
В настоящее время у вас нет ничего, что могло бы вызвать перестройку с Firebase. Вам нужно вернуть FutureBuilder
или StreamBuilder
в вашей getUpvotes
функции. Это будет получать уведомления об изменениях в облаке и инициировать повторную сборку.
Вот кое-что, с чего вы можете начать. Верните это вместо этого в свой getUpvotes
метод и заполните потоковую часть StreamBuilder
StreamBuilder(
stream: Firestore.instance.collection...// finish this part to get your snapshot of total upvotes from your collection,
builder: (context, snapshot) {
if(snapshot.hasData) {
return RichText(
text: TextSpan(
style: TextStyle(color: Colors.black, fontSize: 20.0),
children: <TextSpan>[
TextSpan(
text: upvotes[index].toString() ' upvotes',
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()
..onTap = () {
print('This will take to upvoters of the photo');
}),
],
),
);
}
else {
// handle no data
}
},
);
Комментарии:
1. Привет, спасибо за ваш комментарий. Поэтому я попытался сделать то, что вы сказали, однако поведение по-прежнему остается прежним; база данных обновляется, но экран не обновляется до тех пор, пока я не обновлюсь или не перейду на другую страницу и не вернусь на нее. Это то, что я добавил в поток: поток: FirebaseFirestore.экземпляр.коллекция («загрузки»). doc(имена пользователей[индекс]).коллекция («изображения»). снимки (), Но он по-прежнему не обновляет виджет асинхронно. Я сделал что-то не так? Заранее спасибо
2. И только что заметил еще кое-какое странное поведение. Если вы посмотрите на мою функцию incrementFollowers выше, вы увидите строку:
.update({'upvotes': upvotes[index] 1,});
. Это означает, что база данных обновляется, но виджет этого не делает. Но если я это сделаю:.update({'upvotes': upvotes[index] ,});
, виджет будет обновляться правильно, а база данных-нет. Не знаю, почему это так3. Поиграл с твоим решением, и я его получил. Большое спасибо!