Hello I am a self taught Flutter dev coding my first app. I am building a basic quotes application in Flutter which utilizes Firebase for auth and FireStore as database. Here is the FireStore noSQL schema:
There's a Stories collection which is read only data that provides image links & text as shown below. Note the document ID is auto-generated in the Stories Collection:
Then there's a Users collection that stores user data such as Username, email, account creation dateTime, and a likes array [to potentially store liked stories]. In this collection, the document's ID is UID (Unique Id) of the authenticated (logged in) user.
So here's the user story that I am chasing and my approach toward it:
Whenever a user taps on the favorite icon below, the like has to be saved in to the users collection where User's uid is the document ID and store the liked story in the likes array.
Simulator Screenshot of UI before like
Then utilize an if statement saying, if the authenticated user has stories in the likes array turn the outlined white heart into a red one like this:
Simulator Screenshot of UI after like
However, there's a bug in my code which turns all the Stories' hearts red at once whenever a user taps the favorite icon. Can somebody please help? Here's a snippet of the code:
class FirestoreSlideshowState extends State<FirestoreSlideshow> {
static const likedKey = 'liked_key';
bool liked;
final PageController ctrl = PageController(viewportFraction: 0.8);
final Firestore db = Firestore.instance;
Stream slides;
String activeTag = 'favs';
// Keep track of current page to avoid unnecessary renders
int currentPage = 0;
@override
void initState() {
super.initState();
_restorePersistedPreference();
_queryDb();
// Set state when page changes
ctrl.addListener(() {
int next = ctrl.page.round();
if (currentPage != next) {
setState(() {
currentPage = next;
});
}
});
}
void _restorePersistedPreference() async {
var preferences = await SharedPreferences.getInstance();
var liked = preferences.getBool(likedKey) ?? false;
setState(() {
this.liked = liked;
});
}
void _persistPreference() async {
setState(() {
liked = !liked;
});
var preferences = await SharedPreferences.getInstance();
preferences.setBool(likedKey, liked);
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: slides,
initialData: [],
builder: (context, AsyncSnapshot snap) {
List slideList = snap.data.toList();
return PageView.builder(
controller: ctrl,
itemCount: slideList.length,
itemBuilder: (context, int currentIdx) {
if (slideList.length >= currentIdx) {
// Active page
bool active = currentIdx == currentPage;
return _buildStoryPage(slideList[currentIdx], active);
}
});
});
}
Stream _queryDb({String tag = 'favs'}) {
// Make a Query
Query query = db.collection('Stories').where('tags', arrayContains: tag);
// Map the documents to the data payload
slides =
query.snapshots().map((list) => list.documents.map((doc) => doc.data));
// Update the active tag
setState(() {
activeTag = tag;
});
}
_buildStoryPage(Map data, bool active) {
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
// Animated Properties
final double blur = active ? 20 : 0;
final double offset = active ? 20 : 0;
final double top = active ? 75 : 150;
return AnimatedContainer(
duration: Duration(milliseconds: 500),
curve: Curves.easeOutQuint,
width: _width / 2,
height: _height,
margin: EdgeInsets.only(top: top, bottom: 20, right: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(data['img']),
),
boxShadow: [
BoxShadow(
color: Colors.black87,
blurRadius: blur,
offset: Offset(offset, offset))
]),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Text(data['quote'],
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontFamily: "Typewriter")),
),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(data['author'],
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontFamily: "Typewriter")),
),
SizedBox(height: 20),
Row(mainAxisAlignment: MainAxisAlignment.end, children: [
IconButton(
icon: Icon(liked ? Icons.favorite : Icons.favorite_border,
color: liked ? Colors.red : Colors.grey[50]),
onPressed: (){
_persistPreference();
} ),
IconButton(
icon: Icon(
Icons.share,
color: Colors.white,
),
onPressed: () {
share();
})
])
],
));
}
}