Cant Figure Out How To display Only the post details for the post I have clicked on - firebase

So I have this code here and I would like to display post details from firebase for the post which I have clicked on, but instead, it lists post details for every single post in the database one after another.
Can anyone help me figure out how I can make it so that when A post is clicked, details will show for only the post which was clicked, and not for all of the posts? Any help would be greatly appreciated, thank you.
The Info I would like to display on the post is
postTitle
postDesc
postAuthor
Here is what the firebase looks like
Code Here:
import 'package:tennis_event_app/services/crud.dart';
import 'package:tennis_event_app/views/create_blog.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
CrudMethods crudMethods = new CrudMethods();
QuerySnapshot? blogSnapshot;
#override
void initState() {
crudMethods.getData()?.then((result) {
blogSnapshot = result;
setState(() {});
});
super.initState();
}
Widget blogsList() {
return Container(
child: ListView.builder(
padding: EdgeInsets.only(top: 24),
itemCount: blogSnapshot!.docs.length,
itemBuilder: (context, index) {
return BlogTile(
author: blogSnapshot!.docs[index].get('author'),
title: blogSnapshot!.docs[index].get('title'),
desc: blogSnapshot!.docs[index].get('desc'),
imgUrl: blogSnapshot!.docs[index].get('imgUrl'),
);
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Flutter",
style: TextStyle(fontSize: 22),
),
Text(
"Blog",
style: TextStyle(fontSize: 22, color: Colors.blue),
)
],
),
backgroundColor: Colors.transparent,
elevation: 0.0,
),
body: Container(
child: blogSnapshot != null
? blogsList()
: Container(
child: Center(
child: CircularProgressIndicator(),
))),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => CreateBlog()));
},
),
);
}
}
class BlogTile extends StatelessWidget {
final String imgUrl, title, desc, author;
BlogTile(
{required this.author,
required this.desc,
required this.imgUrl,
required this.title});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 16, right: 16, left: 16),
child: Stack(
children: <Widget>[
Container(
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8)),
child: Image.network(
imgUrl,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
height: 170,
),
),
),
Container(
height: 170,
decoration: BoxDecoration(
color: Colors.black45.withOpacity(0.3),
borderRadius: BorderRadius.circular(6)),
),
Container(
height: 170,
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
title,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w500),
),
SizedBox(height: 4),
Text(
'$desc',
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w400),
),
SizedBox(
height: 4,
),
Text(author),
],
)),
Container(
child: SizedBox(
height: 170,
width: MediaQuery.of(context).size.width,
child: TextButton(
style: TextButton.styleFrom(
textStyle: const TextStyle(fontSize: 20),
),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailPage()));
},
child: const Text(''),
),
),
),
],
),
);
}
}
class DetailPage extends StatefulWidget {
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
CrudMethods crudMethods = new CrudMethods();
QuerySnapshot? blogSnapshot;
#override
void initState() {
crudMethods.getData()?.then((result) {
blogSnapshot = result;
setState(() {});
});
super.initState();
}
Widget blogsList2() {
return Container(
child: ListView.builder(
padding: EdgeInsets.only(top: 24),
itemCount: blogSnapshot!.docs.length,
itemBuilder: (context, index) {
return PageContent(
postAuthor: blogSnapshot!.docs[index].get('postAuthor'),
postTitle: blogSnapshot!.docs[index].get('postTitle'),
postDesc: blogSnapshot!.docs[index].get('postDesc'),
imgUrl: blogSnapshot!.docs[index].get('imgUrl'),
);
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Flutter",
style: TextStyle(fontSize: 22),
),
Text(
"Blog",
style: TextStyle(fontSize: 22, color: Colors.blue),
)
],
),
backgroundColor: Colors.transparent,
elevation: 0.0,
),
body: Container(
child: blogSnapshot != null
? blogsList2()
: Container(
child: Center(
child: CircularProgressIndicator(),
))),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => CreateBlog()));
},
),
);
}
}
class PageContent extends StatelessWidget {
final String imgUrl, postTitle, postDesc, postAuthor;
PageContent(
{required this.postAuthor,
required this.postDesc,
required this.imgUrl,
required this.postTitle});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 16, right: 16, left: 16),
child: Card(
child: ListTile(
title: Text(
postTitle,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w500),
),
subtitle: Text(
'$postDesc',
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w400),
),
)
)
);
}
}
I also reference crud.dart in that code, so incase you need it, here it is:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:collection';
class CrudMethods {
Future<void> addData(blogData) async {
print(blogData);
FirebaseFirestore.instance
.collection("blogs")
.add(blogData)
.then((value) => print(value))
.catchError((e) {
print(e);
});
}
getData() async {
return await FirebaseFirestore.instance
.collection("blogs")
.orderBy("ts", descending: true)
.get();
}
}
Thank you again for any help!

First I would recommend to modelize your data in an object for exemple a class Article that is easier to serialize and manipulate.
Then instead of requesting another time the database you should save your data in a List<Article> for example then you only update this list on refresh from your main page. That way you don'y manipulate a QuerySnapshot or Future but just your list of objects.
Finally and to answer your question, you could simply pass the clicked item Article to your details page and only display its content. Because here, you have the same construction as your main page with the same request that is resent.
Usually you can build your route like that (adding a parameter to your details with the index you clicked on for example)
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailPage(article: _data[i])));
},
Here is an article on serialization from Flutter docs, it shows how to build your model with the toMap, toJson and fromJson methods.

Related

I want to Navigate to another page and parse with firestore data but how do I do that when I am using MaterialPageRoute?

I want to navigate from Products Page to a ProductDetail page but I am using data directly from firebase firestore. What do I put in the MaterialPageRoute as the arguments? I am also using StreamBuilder to get data snapshot from firebase.
Below is the code. This is the products page.
class PopularRecipes extends StatefulWidget {
const PopularRecipes({Key key}) : super(key: key);
#override
_PopularRecipesState createState() => _PopularRecipesState();
}
class _PopularRecipesState extends State<PopularRecipes> {
final Stream _productStream =
FirebaseFirestore.instance.collection('products').snapshots();
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 20),
child: CustomText(
text: 'Popular Recipes',
size: 20,
),
),
Container(
height: 500,
child: StreamBuilder(
stream: _productStream,
builder: (context, snapshot) {
if (snapshot.data == null)
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.teal,
),
);
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) =>
_popularWidget(context, snapshot.data.docs[index]));
}),
),
],
);
}
Widget _popularWidget(BuildContext context, DocumentSnapshot document) {
return Stack(
children: [
Container(
margin: EdgeInsets.fromLTRB(40, 5, 20, 5),
height: 160,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(20)),
child: Padding(
padding: const EdgeInsets.fromLTRB(100, 20, 10, 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (_) => DetailsScreen())),
child: Container(
width: 160,
child: Text(
document['name'],
style: TextStyle(
fontSize: 19.0, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
),
),
Column(
children: [
CustomText(
text: 'Ush.',
size: 14,
color: Colors.grey,
),
CustomText(
text: document['price'],
),
],
)
],
),
RatingBarIndicator(
rating: document['rating'].toDouble(),
itemBuilder: (context, index) => Icon(
Icons.star,
color: Colors.amber.shade200,
),
itemCount: 5,
itemSize: 15.0,
unratedColor: Colors.grey.shade300,
direction: Axis.horizontal,
),
SizedBox(
height: 10,
),
Container(
height: 40,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Row(
children: [
for (var tag in document['tags'])
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: Colors.grey.shade200,
),
margin: EdgeInsets.only(right: 4),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6),
child: Text(
tag.toString(),
style: TextStyle(
fontSize: 14,
),
textAlign: TextAlign.center,
),
),
)
],
),
],
),
),
],
),
),
),
Positioned(
top: 10,
left: 20,
bottom: 10,
child: GestureDetector(
onTap: () => Navigator.push(
context, MaterialPageRoute(builder: (_) => DetailsScreen())),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.network(
document['image'],
width: 110,
fit: BoxFit.cover,
),
),
),
)
],
);
}
}
This is the DetailsScreen page I want to go to when I click a specific product card.
class DetailsScreen extends StatefulWidget {
const DetailsScreen({Key key}) : super(key: key);
#override
_DetailsScreenState createState() => _DetailsScreenState();
}
class _DetailsScreenState extends State<DetailsScreen> {
#override
Widget build(BuildContext context) {
return Text(document['name']);
}
}
How do I pass data from the previous page to the details page with MaterialPageRoute when working with firebase data? Thank you.
In your DetailsScreen page add the product variable
class DetailsScreen extends StatefulWidget {
final DocumentSnapshot product;
const DetailsScreen({Key key, #required this.product}) : super(key: key);
#override
_DetailsScreenState createState() => _DetailsScreenState();
}
class _DetailsScreenState extends State<DetailsScreen> {
#override
Widget build(BuildContext context) {
return Text(widget.product['name']);
}
}
And inside the onTap:
GestureDetector(
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (_) => DetailsScreen(document))), //pass the product
You can try with this one.
In your DetailsScreen page add the product variable, for example:
class DetailsScreen extends StatefulWidget { final DocumentSnapshot product; const DetailsScreen({Key key, #required this.product}) : super(key: key); #override _DetailsScreenState createState() => _DetailsScreenState(); } class _DetailsScreenState extends State<DetailsScreen> { #override Widget build(BuildContext context) { return Text(widget.product['name']); } }
And inside the onTap:
GestureDetector( onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => DetailsScreen(document))), //pass the product
You can also validate in google documentation.

How can I make that the on ttap button only be pressed one time? - Flutter

I have an issue with flutter, and the isComplet bool are not working well. When I press it twice in the app, it make the task as may times as you tap on there. How can I solve it? This is the entire code. The todos list is a list of tasks to do in a task app, but it works fine:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:mrplan/loading.dart';
import 'package:mrplan/services/database_services.dart';
import 'package:mrplan/src/models/todo.dart';
import 'package:mrplan/src/pages/graficas_circulares_page.dart';
import 'package:mrplan/src/widgets/appbar.dart';
bool isComplet = false;
TextEditingController todoTitleController = TextEditingController();
class Todolist extends StatefulWidget {
#override
_TodolistState createState() => _TodolistState();
}
class _TodolistState extends State<Todolist> {
TextEditingController todoTitleController = TextEditingController();
#override
Widget build(BuildContext context) {
setState(() {
});
return Scaffold(
body: SafeArea(
child: StreamBuilder<List<Todo>>(
stream: DatabaseService().listTodos(),
builder: (context, snapshot) {
if(!snapshot.hasData){
return Loading();
}
List<Todo> todos = snapshot.data;
if(todos.length == 0){
return Center(child: Text('No hay tareas agregadas!'));
}
return Padding(
padding:EdgeInsets.all(0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 20),
Expanded(
child: ListView.separated(
separatorBuilder: (context, index)=> Divider(color: Colors.white),
shrinkWrap: true,
itemCount: todos.length,
itemBuilder: (context, index){
return Dismissible(
key: UniqueKey(),
background: Container(padding: EdgeInsets.only(left: 20),
alignment: Alignment.centerLeft,
child: Icon(Icons.delete),
color: Colors.red),
onDismissed: (direction) async {
await DatabaseService().removeTodo(todos[index].uid);
quitar();
},
child: StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance.collection('Todos').doc().snapshots(),
builder: (context, snapshot) {
var todoss = snapshot.data;
return ListTile(
onTap: (){
isComplet = !isComplet;
if (Todo().isComplet = true)
{DatabaseService().completTask(todos[index].uid);}
if (Todo().isComplet = false)
{DatabaseService().uncompletTask(todos[index].uid);}},
leading: Container(
padding: EdgeInsets.all(2),
height: 30,
width: 30,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
shape: BoxShape.circle
),
child: todos[index].isComplet
? Icon(
Icons.check,
color: Colors.white,)
: Container(),
),
title: Text(
todos[index].title,
style: todos[index].isComplet
? TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
decoration: TextDecoration.lineThrough,
decorationColor: Colors.red,
)
: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
decoration: null
)
),
);
}
),
);
}
),
)
],
),
);
}
)
),
);
}
}
class Appbar extends StatefulWidget {
#override
_AppbarState createState() => _AppbarState();
}
class _AppbarState extends State<Appbar> {
#override
Widget build(BuildContext context) {
bool isbuttondisabled = false;
setState(() {
});
return StreamBuilder<Object>(
stream: null,
builder: (context, snapshot) {
return Container(
alignment: Alignment.bottomCenter,
child: BottomAppBar(
elevation: 0,
child: Container(
height: MediaQuery.of(context).size.height * 0.05,
width: MediaQuery.of(context).size.width,
child: Row(
children: [
Container(
decoration: BoxDecoration(
color:Colors.grey[100],
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(5.0),
)
),
height: MediaQuery.of(context).size.height,
padding: EdgeInsets.symmetric(horizontal: 10.0),
width: MediaQuery.of(context).size.width * 0.835,
child: Center(
child: TextFormField(
controller: todoTitleController,
autofocus: true,
decoration: InputDecoration(
hintStyle: TextStyle(color: Colors.blue[800]),
hintText: "Inserte una tarea...",
border: InputBorder.none
),
),
),
),
TextButton(
child: Container(
height: MediaQuery.of(context).size.height,
child: Center(child: Icon(Icons.add))),
style: TextButton.styleFrom(
primary: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: const Radius.circular(10.0),
)
),
backgroundColor: Theme.of(context).primaryColor
),
onPressed: isbuttondisabled ? null : ()async {
if(todoTitleController.text.isNotEmpty){
await DatabaseService().createNewTodo(todoTitleController.text.trim());
FocusScope.of(context).unfocus();
anadir();
todoTitleController.clear();
isComplet = !isComplet;
}
}
)
],
),
)
)
);
}
);
}
}
The relevant code where I am stucked is this:
onTap: (){
isComplet = !isComplet;
if (Todo().isComplet = true)
{DatabaseService().completTask(todos[index].uid);}
if (Todo().isComplet = false)
{DatabaseService().uncompletTask(todos[index].uid);}},
Thanks a lot!
It would be better if you could refactor the code a little bit. But heres the problem
remove this code inside the build method
bool isbuttondisabled = false;
setState(() {
});
add bool isbuttondisabled = false;
outside the build method.
where ever your button is,
in the onPressed parameter
setState(() {
isbuttondisabled=true;
//Todo add your code
});```

No such method error while calling a function

I retrieved products data from Firebase for my E-commerce homepage through future builder. I've created another screen that displays more information on a product when the user clicks on a product from the E-commerce homepage.To navigate to more information screen i created a function called showProduct and called it with a button, but when the button id pressed it shows
NoSuchMethodError: The method '[]' was called on null.Receiver: null Tried calling .
The showProduct function ] was also used on the vendor profile page where the products shown are shown in gridview and when clicked on any product from the vendors's profile.The showProduct function works and open more information screen.
E-commerce Homepage:
c
lass Shop extends StatefulWidget {
final Prod products;
final User currentUser;
final String prodId;
final String onwerId;
Shop({ this.currentUser,
this.prodId,
this.products,
this.onwerId});
#override
_ShopState createState() => _ShopState( prodId: this.prodId,products: this.products,ownerId:this.onwerId);
}
class _ShopState extends State<Shop> {
String postOrientation = "grid";
String shopOrientation = "grid";
bool isFollowing = false;
bool isLoading = false;
String uid="";
String prodId;
String ownerId;
Prod products;
_ShopState({
this.prodId, this.products,this.ownerId,
});
#override
void initState() {
super.initState();
showProduct;
}
Future getProducts()async {
var firestore = Firestore.instance;
QuerySnapshot snap = await firestore.collectionGroup("userProducts").getDocuments();
return snap.documents;
}
Future<Null>getRefresh()async{
await Future.delayed(Duration (seconds : 3));
setState(() {
getProducts();
});
}
showProduct(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductScreen(
prodId: prodId,
userId: ownerId,
),
),
);
}
#override
Widget build(BuildContext context) {
return
Scaffold(
appBar: AppBar(backgroundColor: kSecondaryColor,
title: Text( 'Shop',
style: TextStyle(
fontFamily :"MajorMonoDisplay",
fontSize: 35.0 ,
color: Colors.white),),
iconTheme: new IconThemeData(color: kSecondaryColor),
),
backgroundColor: kPrimaryColor,
body:FutureBuilder(
future: getProducts(),
builder: (context,snapshot)
{
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: circularProgress(),);
} else {
return
RefreshIndicator(
onRefresh: getRefresh,
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
var ourdata = snapshot.data[index];
return
Container(
height: 500,
margin: EdgeInsets.only(
top: 1.0, left: 10.0, right: 10.0, bottom: 1.0),
child: Column( children: <Widget>[
SizedBox( height:1.0,),
Stack(
children: <Widget>[
Container(
child:Row(
children: <Widget>[
Expanded(
child: Container(
height: 400.0,
child: ClipRRect(borderRadius: BorderRadius.circular(20.0),
child: cachedNetworkImage(ourdata.data['shopmediaUrl'],
),
),
),
),
],
),
),
Expanded(
child: Positioned(
bottom: 10,
left: 10,
child: Container(
height: 40,
width: 40,
child: ClipRRect(
borderRadius: BorderRadius.circular(40.0),
child: Image.network(ourdata.data['photoUrl'],)),
),
),
),
Expanded(
child: Positioned(
bottom: 20,
left: 60,
child: Container(
child: Text(ourdata.data['username'],style: TextStyle(color: Colors.white,fontWeight:FontWeight.bold),),
),
),
),
Container(
alignment: Alignment.bottomRight,
child: GFButton(
onPressed: () => showProduct(context) ,
text: "More",
icon: Icon(Icons.card_travel),
shape: GFButtonShape.pills,
),
),
],
),
Row(
children: <Widget>[
Container(
child: Text(ourdata.data['productname'],style: TextStyle(color: kText,fontSize: 30.0,fontWeight:FontWeight.bold),),
),
],
),
Row(
children: <Widget>[
Container( child: Text('₹',style: TextStyle(color: kText,)),
),
Container(
child: Text(ourdata.data['price'],style: TextStyle(color: kText,fontSize: 20.0,fontWeight:FontWeight.bold),),
),
],
),
Divider(color: kGrey,),
],
),
);
}
)
);
}
}
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.black38,
onPressed: ()
async{ Navigator.push(context, MaterialPageRoute(builder: (context) =>Uploadecom(currentUser: currentUser, )));
},
child: Icon(Icons.add_box),
),
);
}
}
This is the gridview on the vendor's profile.
class ProductTile extends StatelessWidget {
final Prod products;
ProductTile(this.products);
showProduct(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductScreen(
prodId: products.prodId,
userId: products.ownerId,
),
),
);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => showProduct(context),
child: cachedNetworkImage(products.shopmediaUrl),
);
}
}
If you need more codes please ask me.

The following RangeError was thrown building Builder(dirty):

Context:
I'm trying to create a Dynamic Slider with Flutter, it'll get Firebase url's for the images that form a comic and put it into a CarouselSlider, I treated null and empty return's but it keep's displaying errors from invalid index in my app.
Error:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following RangeError was thrown building Builder(dirty):
RangeError (index): Invalid value: Valid value range is empty: 0
The relevant error-causing widget was:
Builder file:///Users/murilopazzini/Documents/Projs/SchoolsOut_Dev/schools_out/lib/components/hqSlider.dart:55:24
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:149:60)
#1 _hqSliderState.build.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:schools_out/components/hqSlider.dart:72:61)
#2 Builder.build (package:flutter/src/widgets/basic.dart:6757:41)
#3 StatelessElement.build (package:flutter/src/widgets/framework.dart:4291:28)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4223:15)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
Home.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:schools_out/pages/comicsPage.dart';
import 'package:schools_out/components/hqSlider.dart';
import 'package:schools_out/entities/comics.dart';
import 'package:schools_out/entities/comicsPage.dart';
class Homepage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage>
with SingleTickerProviderStateMixin {
Animation animation;
AnimationController animationController;
#override
void initState() {
// super.initState();
animationController =
new AnimationController(duration: Duration(seconds: 200), vsync: this);
animation =
IntTween(begin: 0, end: photos.length - 1).animate(animationController)
..addListener(() {
setState(() {
index = animation.value;
});
});
animationController.repeat(period: Duration(seconds: 20));
}
int index = 0;
List<String> photos = [
'https://i.ibb.co/kc1mjTW/abduc-a-o-55-1.png',
'https://i.ibb.co/nmFdgs8/abduc-a-o-55-2.jpg'
];
#override
void dispose() {
// TODO: implement dispose
super.dispose();
animationController.dispose();
}
#override
Widget build(BuildContext context) {
ComicsList(Comics clist) => InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ComicsReadingPage(
comics: clist,
)),
);
},
child: PopularHq(
image: clist.pages[0].image,
name: clist.name,
));
bestm(BMovies movie) => HqWidget(
image: movie.Image,
);
final comics_scroll = Container(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
child: Row(
children: comicsList.map((cl) => ComicsList(cl)).toList(),
),
),
);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
iconTheme: new IconThemeData(color: Colors.blueGrey[600]),
centerTitle: true,
title: Text(
"Schools Out",
style: TextStyle(color: Colors.blueGrey[600], fontSize: 28),
),
backgroundColor: Colors.white,
// leading:Icon(Icons.notifications,color: Colors.red,) ,
// toolbarOpacity: 0,
),
drawer: new Drawer(
child: ListView(
children: <Widget>[
new UserAccountsDrawerHeader(
accountName: new Text('Test User'),
accountEmail: new Text('testemail#test.com'),
currentAccountPicture: new CircleAvatar(
backgroundImage: new NetworkImage('http://i.pravatar.cc/300'),
),
),
new ListTile(
title: new Text('Test Navigation'),
onTap: () {
Navigator.of(context).pop();
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) => new Homepage()));
},
),
],
),
),
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: <Widget>[
Container(
color: Colors.white,
child: Column(
children: <Widget>[
ImageData(photos[index]),
// TopMovies()
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"HQ's",
style: TextStyle(
fontWeight: FontWeight.w900,
fontSize: 16,
color: Colors.blueGrey),
),
SizedBox(
width: 180,
),
],
),
),
hqSlider(),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Tirinhas",
style: TextStyle(
fontWeight: FontWeight.w900,
fontSize: 16,
color: Colors.blueGrey),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.65,
),
Text(
"See All",
style: TextStyle(
fontWeight: FontWeight.w900,
fontSize: 16,
color: Colors.blueGrey),
)
],
),
),
comics_scroll
],
),
));
}
}
class ImageData extends StatelessWidget {
String image;
ImageData(this.image);
#override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
height: 180,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(image),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(8.0)),
child: Align(
alignment: Alignment.bottomCenter,
),
);
}
}
class HqWidget extends StatelessWidget {
final String image;
HqWidget({Key key, this.image});
#override
Widget build(BuildContext context) {
// TODO: implement build
return Padding(
padding: const EdgeInsets.all(8.0),
child: ClipOval(
child: Container(
height: 60,
width: 60,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(image),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
BoxShadow(
offset: Offset(0.5, 1.0),
blurRadius: 5,
color: Colors.white)
]),
),
),
);
}
}
////////////////////////////////////////////
class PopularHq extends StatelessWidget {
String image, name;
PopularHq({this.image, this.name});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
child: Column(
children: <Widget>[
Container(
height: 140,
width: 100,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(image),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(6.0),
boxShadow: [
BoxShadow(
offset: Offset(0.5, 1.0),
blurRadius: 5,
color: Colors.white)
]),
),
Container(
//width: 100,
//height: 40,
child: Text(
name,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Colors.white),
),
),
],
),
),
);
}
}
////////////////////////////////////////////
final List<Comics> comicsList = [
Comics('Teste', 1, [
ComicsPage('https://i.ibb.co/nmFdgs8/abduc-a-o-55-2.jpg', 1),
ComicsPage('https://i.ibb.co/kc1mjTW/abduc-a-o-55-1.png', 2),
]),
Comics('Teste 2', 2, [
ComicsPage('https://i.ibb.co/nmFdgs8/abduc-a-o-55-2.jpg', 1),
ComicsPage('https://i.ibb.co/kc1mjTW/abduc-a-o-55-1.png', 2),
]),
Comics('Teste 3', 3, [
ComicsPage('https://i.ibb.co/nmFdgs8/abduc-a-o-55-2.jpg', 1),
ComicsPage('https://i.ibb.co/kc1mjTW/abduc-a-o-55-1.png', 2),
]),
];
class BMovies {
String Image;
int Boxc;
BMovies(this.Image);
}
hqSlider.dart (error source)
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:schools_out/pages/comicsPage.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:schools_out/entities/comicsPage.dart';
import 'package:schools_out/entities/comics.dart';
import 'dart:async';
class hqSlider extends StatefulWidget {
#override
_hqSliderState createState() => _hqSliderState();
}
class _hqSliderState extends State<hqSlider> {
Future getComics() async {
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore.collection("comics").getDocuments();
return qn.documents;
}
#override
Widget build(BuildContext context) {
List<Comics> hqList = new List<Comics>();
return FutureBuilder(
future: getComics(),
builder: (_, snapshot) {
if (snapshot.data == null) {
return Center(
child: Text("Loading ..."),
);
} else if(snapshot.data.length > 0){
hqList.clear();
List<ComicsPage> pagesForThisHq = new List<ComicsPage>();
snapshot.data.forEach((element) {
element.data['pages'].forEach((page) {
pagesForThisHq.add(ComicsPage(page['image'].toString(), page['page']));
});
hqList.add(
Comics(element.data['name'], element.data['edition'], pagesForThisHq));
pagesForThisHq.clear();
});
return CarouselSlider(
autoPlay: true,
viewportFraction: 0.9,
aspectRatio: 2.4,
enlargeCenterPage: false,
items: hqList.map((hq) {
return Builder(
builder: (BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: GestureDetector(onTap: () {
Navigator.push<Widget>(
context,
MaterialPageRoute(
builder: (context) => ComicsReadingPage(
comics: hq,
),
),
);
}),
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(hq.pages[0].image),
fit: BoxFit.cover),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
offset: Offset(0.2, 1.0),
blurRadius: 2,
color: Colors.grey)
]),
),
);
},
);
//////
}).toList());
} else {
return Center(
child: Text("No Content ..."),
);
}
},
);
}
}
classes Comics.dart and ComicsPage.dart
import 'package:schools_out/entities/comicsPage.dart';
class Comics {
List<ComicsPage> pages;
String name;
int edition;
Comics(this.name, this.edition, this.pages);
}
class ComicsPage {
String image;
int page;
ComicsPage(this.image, this.page);
}
Turns out the error above can be easily solved by running flutter clean
There's a second error on this code thought, it was an index error caused by this line of code in the hqSlider.dart:
pagesForThisHq.clear();
Like JS, Flutter stores reference of the data, instead of a copy, so when I cleared this array, it's copy bound to hqList was affected too, causing the index error.

Flutter Passing Document ID in Firestore Query

I am trying to figure out how to do this.
If I pass the document ID hardcoded it works. But Since I have multiple pages, I am trying to figure out how to pass it and then query it..
My main page has the following.
import 'package:flutter/material.dart';
import 'package:onlytag2/pages/category.dart';
import 'package:onlytag2/widget/maincard.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
StreamSubscription<QuerySnapshot> subscription;
List<DocumentSnapshot> snapshot;
Query collectionReference = Firestore.instance.collection("mainCategories").orderBy("title");
void initState() {
subscription = collectionReference.snapshots().listen((datasnapshot) {
setState(() {
snapshot = datasnapshot.documents;
});
});
super.initState();
}
#override
void dispose() {
subscription.cancel(); //Streams must be closed when not needed
super.dispose();
}
passData(DocumentSnapshot snap, String cat, String title) {
print(cat);
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Category(snapshot: snap, category: cat, title: title,)));
}
#override
Widget build(BuildContext context) {
if (snapshot == null) return Center(
child: Container(
color: Colors.black,
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.black,
constraints: BoxConstraints(
maxHeight: 300.0,
maxWidth: 200.0,
minWidth: 150.0,
minHeight: 150.0
),
child: CircularProgressIndicator(),
),
),
);
return Scaffold(
backgroundColor: Color(0xff0E0E0F),
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.black,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"#",
style: TextStyle(
fontSize: 25, color: Color(0xffff9900), fontFamily: 'Dokyo'),
),
Text(
"onlytags",
style: TextStyle(color: Colors.white, fontFamily: 'Dokyo'),
)
],
),
),
body: Column(
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: snapshot.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () => passData(snapshot[index], snapshot[index].documentID.toString(), snapshot[index].data["title"] ),
child: MainCard(
title: snapshot[index].data["title"],
subtitle: snapshot[index].data["subTitle"],
image: snapshot[index].data["image"],
),
);
}),
),
],
),
),
],
),
);
}
}
The category widget is where I am having the problem.
Comment Listed where I am having the problem.
import 'package:flutter/material.dart';
import 'package:onlytag2/widget/sub.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
class Category extends StatefulWidget {
DocumentSnapshot snapshot;
final String category;
final String title;
Category({this.snapshot, this.category, this.title});
#override
_CategoryState createState() => _CategoryState();
}
class _CategoryState extends State<Category>{
StreamSubscription<QuerySnapshot> subscription;
List<DocumentSnapshot> snapshot;
//How do I get the category passed properly? I know it is the Document ID, but since it changes based on what button is pressed before,
//I cant figure out how to pass it..
Query collectionReference = Firestore.instance.collection("mainCategories").document(widget.category).collection("subCategories").orderBy("title");
void initState() {
subscription = collectionReference.snapshots().listen((datasnapshot) {
setState(() {
snapshot = datasnapshot.documents;
});
});
super.initState();
}
#override
void dispose() {
subscription.cancel(); //Streams must be closed when not needed
super.dispose();
}
#override
Widget build(BuildContext context) {
if (snapshot == null) return Center(
child: Container(
color: Colors.black,
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.black,
constraints: BoxConstraints(
maxHeight: 300.0,
maxWidth: 200.0,
minWidth: 150.0,
minHeight: 150.0
),
child: CircularProgressIndicator(),
),
),
);
return Scaffold(
backgroundColor: Color(0xff0E0E0F),
appBar: AppBar(
iconTheme: IconThemeData(
color: Color(0xffff9900),
),
centerTitle: true,
backgroundColor: Colors.black,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
"#", style: TextStyle(fontSize: 25, color: Color(0xffff9900), fontFamily: 'Dokyo'),
),
Text(widget.title.toLowerCase(), style: TextStyle(color: Colors.white, fontFamily: 'Dokyo'),)
],
),
),
body: Column(
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: snapshot.length,
itemBuilder: (context, index) {
return Sub(
title: snapshot[index].data["title"],
subtitle: snapshot[index].data["subTitle"],
image: snapshot[index].data["image"],
);
}),
),
Padding(padding: EdgeInsets.fromLTRB(0, 0, 0, 15),)
],
),
),
],
),
);
}
}
I have realised that the code looks shady on the comment so let me make an answer.
What you are trying to do is currently not supported in flutter as can be seen at these GitHub issues 1 and 2.
Change your code to,
class _CategoryState extends State<Category>{
StreamSubscription<QuerySnapshot> subscription;
List<DocumentSnapshot> snapshot;
Query collectionReference;
void initState() {
collectionReference = Firestore.instance.collection("mainCategories").document(widget.category).collection("subCategories").orderBy("title");
subscription = collectionReference.snapshots().listen((datasnapshot) {
setState(() {
snapshot = datasnapshot.documents;
});
});
super.initState();
}
...
Hope this helps.

Resources