on other Page CustomListTile usage - flutter

I am trying to use CustomListTile Widget for format my JSON file on other Page.
This structure uses Future Builder and I can't see any error
child: FutureBuilder<Articles>(
future: _futureArticles,
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
final articles = snapshot.data?.data;
return ListView.builder(
itemCount: articles!.length,
itemBuilder: (BuildContext context, int index) =>
customListTile(articles[index], context),
);
} else if (snapshot.hasError) {
return NewsError(
errorMessage: '${snapshot.hasError}',
);
} else {
return const NewsLoading(
text: 'Loading...',
);
}
},
),
on the other Page I have a CustomListTile Widget. I have imported the FutureBuilder page already
Widget customListTile(index article, BuildContext context) {
return InkWell(
onTap: () {
Icons.message;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => article(
index: article,
)));
},
child: Container(
margin: EdgeInsets.all(12.0),
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3.0,
),
]),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 200.0,
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(article[index].imageUrl!),
fit: BoxFit.cover),
borderRadius: BorderRadius.circular(12.0),
),
),
SizedBox(
height: 8.0,
),
Container(
padding: EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(30.0),
),
child: Text(
article.category.name,
style: TextStyle(
color: Colors.white,
),
),
),
SizedBox(
height: 8.0,
),
Text(
article.title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
)
],
),
),
);
}
but VS Code highlight index and this is not working
Widget customListTile(index article, BuildContext context) {
Where I am doing a mistake?

I guess index(index article) is no type. As far as I can see you need the index for Lists, to tell the app which "item" of the list it should take. If you want to retrieve an item of a list, you have to call the name of the list and then in []"the number" of the place, where the item is in the list. The type of "number" you need is of type int. So your argument index, should be of type int.
Try this:
Widget customListTile(int index, BuildContext context) {

Related

Flutter future builder to list

I have some troubles in understanding how future builder works in flutter. I want to pass a list of String from my future call and I want to display them in a SingleChildScrollView.
The problem is that when I access the snapshot.data I can not access the element of the list. Because in my SingleChildScrollView I have container and in each container I want to display one String from the list.
This is the Future getData method with which I retrieve the data.
Future<List<String>> getData () async {
List<String> data = [];
data.add("A");
data.add("B");
data.add("C");
// DEBUG
await Future.delayed(const Duration(seconds: 2), (){});
return data;
}
And this is my future builder in which I want to display the data in each container. In the loading I added a shimmer effect.
FutureBuilder(
builder: (context, snapshot) {
List<Widget> children;
if (snapshot.hasData) {
children = <Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
],
),
),
];
} else if (snapshot.hasError) {
children = <Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
)
];
} else {
children = <Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Shimmer.fromColors(
baseColor: Colors.grey.shade200,
highlightColor: Colors.grey.shade300,
child: Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
),
Shimmer.fromColors(
baseColor: Colors.grey.shade200,
highlightColor: Colors.grey.shade300,
child: Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
),
Shimmer.fromColors(
baseColor: Colors.grey.shade200,
highlightColor: Colors.grey.shade300,
child: Container(
margin: EdgeInsets.only(left: 5.w),
width: 40.w,
height: 20.h,
decoration: BoxDecoration(
color: green400,
borderRadius: BorderRadius.all(Radius.circular(5.w)),
),
),
),
],
),
),
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
);
},
future: getData(),
),
So in this way I can I access the elements of my list of Strings?
As #piskink mentioned, using ListView.builder is more efficient.
body: FutureBuilder<List<String>?>(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Text(snapshot.data?[index] ?? "got null");
},
);
}
/// handles others as you did on question
else {
return CircularProgressIndicator();
}
},
If you still wish to use SingleChildScrollView, you can generate items like
return Column(
children: List.generate(
snapshot.data!.length,
(index) => Text(snapshot.data?[index] ?? "got null"),
),
);
Check more about async-await and Future.
When you declare a FutureBuilder you have also to pass it it's data type. In this case it will be:
FutureBuilder<List<String>>(
future: getData(),
builder: (context,snapshot){
return ...;
}
)
If you don't declare its datatype, your snapshot will be considered as an AsyncDataSnapshot<dynamic> instead of AsyncDataSnapshot<List<String>>.
This is a full example for FutureBuilder with Null Safty
if you got these errors:
( The getter 'length' isn't defined for the type 'Object'. )
( The operator '[]' isn't defined for the type 'Object'. )
This is the Fix ↓↓↓↓↓
Fix & Important Note:
You have to select datatype of the Future Data that will be received.
Right: FutureBuilder<List>()
Wrong: FutureBuilder()
Full Simple Example:
import 'package:flutter/material.dart';
class FutureExample extends StatelessWidget {
const FutureExample({Key? key}) : super(key: key);
Future<List<String>> getData() async{
await Future.delayed(
const Duration(seconds:2)
);
return ["I'm Ramy","I'm Yasser", "I'm Ahmed", "I'm Yossif",];
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List<String>>(
future: getData(),
builder: (context, snapshot) {
return snapshot.connectionState == ConnectionState.waiting
? const CircularProgressIndicator()
: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: List.generate(snapshot.data!.length,
(index) {
return Text(snapshot.data?[index] ?? "null") ;
},
),
);
},
),
),
);
}
}

Flutter State won't update

I'm trying to create a listview of cards whose images get displayed in the listview only if the card is selected. The selection widget is the PopupSubscription() where I'm choosing which cards (SubscriptionCard) to display by setting the bool of that particular image to be true. But when the selections are applied, the selected images don't appear immediately even after doing setState().
However, they do appear when I switch tabs and return the screen again. What should I do in order to change the state of an object that's not in my current state class? I tried using Provider but it left me confused as to what I'm supposed to do.
This is the SubscriptionCard where the bool is set on tapping it:
return InkWell(
radius: 1.0,
borderRadius: BorderRadius.circular(8.0),
highlightColor: buttonBackground,
onTap: () {
setState(() {
widget.currentSubscription.selected = !widget.currentSubscription.selected;
});
},
child: Card(
elevation: 1.0,
borderOnForeground: true,
shape: widget.currentSubscription.selected ? RoundedRectangleBorder(
borderRadius: BorderRadius.circular(3.0),
side: BorderSide(color: buttonBackground, width: 2.0),
) : ContinuousRectangleBorder(),
color: bgDarkColor,
child: SizedBox(
width: SizeConfig.blockSizeHorizontal * 30,
child: Stack(
alignment: Alignment.topRight,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Image.asset(this.widget.currentSubscription.logo, height: 35.0,),
Text(
' ${this.widget.currentSubscription.name}',
style: TextStyle(fontFamily: 'Muli', fontSize: 16.0)
),
],
),
widget.currentSubscription.selected ? Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: buttonBackground,
),
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Icon(
Icons.check,
size: 10.0,
color: Colors.white,
),
),
) : Container()
],
),
),
),
);
This is the ListView where the selected cards' images are rendered:
Container(
height: 50,
width: 350,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return PopupSubscription();
}
);
},
icon: Icon(Icons.add_box_rounded, size: 30.0,),
),
StatefulBuilder(
builder: (context, setState) {
return ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: subscriptionsList.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 5.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(25.0),
child: Image.asset(
subscriptionsList[index].selected ? subscriptionsList[index].logo
: "assets/elements/streaming-services/netflix.jpeg",
),
),
);
},
);
}
),
],
)
),
Based on your current code, I'm guessing you've added the currentSubscription variable as final in the StatefulWidget above the actual State, like:
class MyClass extends StatefulWidget{
final currentSubscription;
// Rest of the code
}
class _MyClassState extends State<MyClass> {
// Your ListView Code and other stuff.
}
This wont work when you want to change the state onTap, I recommend making the variable you use in setState within the _MyClassState class and use setState in that. Something like:
class _MyClassState extends State<MyClass> {
bool _isSelected = false;
// And in your onTap method, something like:
setState(() {
_isSelected = !_isSelected;
});
}

Pass document content from one screen to another in flutter firestore

In the code below, I am trying to pass data from the home screen to the detail screen when user clicks on any of the product listed on the home screen. I seem to be having difficulty here.
Similar Question but does not solve my issue
Excerpt of the code at the home screen.
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("name")
.limit(12)
.orderBy("Published Date", descending: true)
.snapshots(),
builder: (context, snapshot){
if (!snapshot.hasData) {
return Center(
child: spinkit,
);
}
return GridView.builder(
physics: ScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10,
),
itemBuilder: (BuildContext context, int index){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: spinkit,
);
}
print("${snapshot.data.documents[index].get('Product Title')}");
return GestureDetector(
onTap: (){
// ===> SEND USER TO THE DETAILS SCREEN <===
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProductDetailsScreen()),
);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Stack(
children: [
Container(
height: 150,
width: 150,
child: Image.network(
snapshot.data.documents[index].get('image') ?? spinkit,
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
),
Positioned(
left: 0,
bottom: 0,
child: Container(
height: 20,
width: 150,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black38,
Colors.black38,
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
)),
),
),
Positioned(
left: 4,
bottom: 5,
child: Text(
snapshot.data.documents[index].get('name') ?? "Product Name",
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Palette.whiteColor,
fontSize: 11,
fontWeight: FontWeight.bold),
),
)
],
),
),
);
}
);
},
)
And excerpt of the code at the Details Screen. what you see at the detail screen is data that I have populated manually. it is not coming from firestore.
class ProductDetailsScreen extends StatefulWidget {
#override
_ProductDetailsScreenState createState() => _ProductDetailsScreenState();
}
class _ProductDetailsScreenState extends State<ProductDetailsScreen> {
final productDb = FirebaseFirestore.instance.collection("name");
User user = FirebaseAuth.instance.currentUser;
final spinkit = SpinKitHourGlass(
color: Colors.white,
size: 50.0,
);
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onBackPressed,
child: Scaffold(
body: SafeArea(
child: Padding(
padding: EdgeInsets.only(left: 15, right: 15, top: 3, bottom: 10),
child: FutureBuilder(
future: productDb.get(),
builder: (context, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: spinkit,
);
}
return Container(
child: ListView(
children: [
Column(
children: [
Center(
child: Container(
width: 350,
child: Card(
elevation: 5,
child: Container(
padding: EdgeInsets.all(8),
child: Container(
height: 220,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage("asset/images/headphone.jpg",)
)
),
),
),
),
),
),
SizedBox(height: 10,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Item Name",
style: TextStyle(
color: Palette.blackColor,
fontSize: 18,
fontWeight: FontWeight.w300
),),
Text("Item Price",
style: TextStyle(
color: Palette.blackColor,
fontSize: 18,
fontWeight: FontWeight.w300
),),
],
),
SizedBox(height: 10,),
Text("Item Description",
style: TextStyle(
color: Palette.blackColor,
fontSize: 18,
fontWeight: FontWeight.w300
),),
Container(
height: 1,
width: 100,
color: Colors.black12,
),
SizedBox(height: 10,),
Container(
height: 140,
width: 350,
child: SingleChildScrollView(
child: Wrap(
children: [
Text(
"What is Lorem Ipsum Lorem Ipsum is simply dummy "
"text of the printing and typesetting industry"
" Lorem Ipsum has been the industry's standard"
" dummy text ever since the 1500s when an "
"unknown printer took a galley of type and "
"scrambled it to make a type specimen book "
"it has?",
textAlign: TextAlign.justify,
style: TextStyle(
color: Palette.blackColor,
fontSize: 16,
),),
]
),
),
),
],
),
],
),
);
},
),
)),
),
);
}
}
In your GestureDetector onTap
// ===> SEND USER TO THE DETAILS SCREEN WITH DOC<===
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProductDetailsScreen(snapshot.data.documents[index])),
);
Then
class ProductDetailsScreen extends StatefulWidget {
ProductDetailsScreen(this.doc);
QueryDocumentSnapshot doc;
#override
_ProductDetailsScreenState createState() => _ProductDetailsScreenState();
}
Access in _ProductDetailsScreenState as widget.doc.
The document data will be found at widget.doc.data as a Map<String, dynamic>.

The operator '[]' isn't defined for the type 'Type'. Try defining the operator '[]'

I have a home screen where at the top I declared a listview, with scrolldirection taking information from a list.dart file. This horizontal scrolling screen brings me 5 images and a text in each of them. I would like to insert an onpress directing to other screens according to the information passed in this list. Example: Chat, direct to chat.screen.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 4 / 7,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xff40dedf), Color(0xff0fb2ea)],
),
),
),
Positioned(
top: 100,
left: 20,
child: Container(
height: 100,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categoryData.length,
itemBuilder: (context, index) {
bool isSelected = true;
if (index == 0) {
isSelected = true;
}
Navigator.push<dynamic>(
context,
MaterialPageRoute<dynamic>(
builder: (BuildContext
context) =>
HomeList[index].navigateScreen,
),
);
return Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: 65,
height: 65,
decoration: BoxDecoration(
color: isSelected
? Colors.transparent
: Colors.transparent,
borderRadius:
BorderRadius.circular(16),
border: Border.all(
color: Colors.white,
width: 1,
),
boxShadow: isSelected
? [
BoxShadow(
color: Color(0x14000000),
blurRadius: 10)
]
: null),
child: Center(
child: Image.asset(categoryData[index].imageUrl),
),
),
SizedBox(
height: 10,
),
Text(
categoryData[index].name,
style: TextStyle(color: Colors.white, fontSize: 15),
),
],
),
SizedBox(
width: 20,
)
],
);
},
),
),
),
Homelist
import 'package:flutter/material.dart';
import 'package:projeto/pages/chat.screen.dart';
class HomeList {
HomeList({
this.navigateScreen,
this.imagePath = '',
});
Widget navigateScreen;
String imagePath;
static List<HomeList> homeList = [
HomeList(
imagePath: 'assets/page1/usuario.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/entregas.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/msg.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/configurações.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/sair.png',
navigateScreen: ChatScreen(),
),
];
}
from what i understand, you want to transform the data in your HomeList file to a listview where clicking on one of its items takes you to its related page, you could use a ListView.builder with an itemBuilder and itemCount, the code below shows how you can achieve a listView where items are images with text in them and an onTap function:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return GestureDetector(
child: Stack(
children: <Widget>[
Image.asset(
homeList[index].imagePath,
),
Positioned(child: Text())
],
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => homeList[index].navigateScreen),
),
);
},
itemCount: homeList.length,
),
);
}
}

Flutter: Creating GridView From Firebase Realtime DB items

Currently, in my app's homescreen I have a firebase_animated_list widget which creates a listView. However, I want to show these items in a GridView because it'll look much better.
Here's my code snippet.👇🏼
body: Column(
children: <Widget>[
Flexible(
child: FirebaseAnimatedList(
query: firebaseRef,
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return InkWell(
child: ListTile(
contentPadding: EdgeInsets.all(7),
title: Text(mynode[index].key),
leading: CircleAvatar(
radius: 30,
child: FittedBox(
child: Text(mynode[index].id),
),
),
trailing: Icon(Icons.play_arrow),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DemoDb(
id: othernode[index].id,
),
),
),
),
);
},
),
),
],
),
This perfectly creates a list of items but how should I change it to a GridView? I tried using GridView.count in place of ListTile widget. but because it was nested inside the firebase_animated_list , Each grid is layed out inside this animated list.
Is there any plugins or libraries that can help me in this? perhaps a code snippet Or if someone can suggest me any better approach to achieve this, it would mean world to me.
Thank You.
try Using StreamBuilder to load the items in a map and then use a Gridview and then you can populate the Gridview with your data.
have a look at the following Example:
Here we have a model of the data that we're trying to pull from firebase.
class Product {
String key;
String cardid;
String cardname;
String cardimage;
int cardprice;
String carddiscription;
Product(this.key,this.cardid,this.cardname,this.cardimage,this.cardprice,this.carddiscription);
}
and here is how to implement a GridView inside a StreamBuilder and populate it with the data:
return StreamBuilder(
stream: FirebaseDatabase.instance
.reference()
.child("Products")
.onValue,
builder: (BuildContext context, AsyncSnapshot<Event> snapshot) {
if (snapshot.hasData) {
Map<dynamic, dynamic> map = snapshot.data.snapshot.value;
products.clear();
map.forEach((dynamic, v) =>
products.add( new Product(v["key"],v["cardid"] , v["cardname"],v["cardimage"] ,v["cardprice"], v["carddiscription"]))
);
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
itemCount: products.length,
padding: EdgeInsets.all(2.0),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: (){
},
child: Padding(
padding: EdgeInsets.all(5),
child: Container(
width: (screenWidth(context)/2)-15,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(15.0)),
image: DecorationImage(
image: NetworkImage(products[index].cardimage),
fit: BoxFit.cover,
),
),
child: Padding(
padding: EdgeInsets.all(0),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(15.0)),
gradient: new LinearGradient(
colors: [
Colors.black,
const Color(0x19000000),
],
begin: const FractionalOffset(0.0, 1.0),
end: const FractionalOffset(0.0, 0.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
products[index].cardname,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500,color: Colors.white),
),
Text('Rs. ${products[index].cardprice}'
,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w200,color: Colors.white),
),
],
),
),
),
), /* add child content here */
),
),
);
},
);
} else {
return CircularProgressIndicator();
}
}),