Flutter: Select Card inside GridView - flutter

I have an app which displays data in Cards inside GridView like so:
GridView.count(
crossAxisCount: 2,
children: <Widget>[
_customCard(
imageUrl: image1, item: item1, price: price1, count: count1
),
_customCard(
imageUrl: image2, item: item2, price: price2, count: count2
),
_customCard(
imageUrl: image3, item: item3, price: price3, count: count3
),
_customCard(
imageUrl: image4, item: item4, price: price4, count: count4
),
],
),
I want to get the specified card name when it is pressed depending on its index inside a List as we do in ListView.
class item {
final String name;
final int count;
final String imageUrl;
final double price;
item({this.name, this.imageUrl, this.count, this.price});
}
List<item> tops = [
new item(
imageUrl: "tshirt.png",
name: "T-shirt",
count: 0,
price: 0.50
),
new item(
imageUrl: "shirt.png",
name: "Shirt",
count: 0,
price: 0.80
),
Is there a way to do so with GridView or shall I try something else?

You can use Gridview.builder and use its index to get the related object from list:
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
physics: ScrollPhysics(),
itemCount: _customCards.length,
itemBuilder: (BuildContext context, int index){
return Inkwell(child:_customCards(
imageUrl: image[index], item: item[index], price: price[index], count: count[index]),
onTap(){
print(tops[index]);
}
),
}
);*

Related

Flutter Grouped ListView: How to sort by Date?

I have a recipe app. Users can save recipes in a weekly planner (using Hive). The date is saved in this format: 2022-09-08 00:00:00.000 (= user wants to cook a recipe on September 8th).
On my planner page I want to display the scheduled recipes. I am using Grouped Listview for that to group my recipes by date. I also need to be able to sort the listed recipes (the recipe a user wants to cook tomorrow should be on top of the list and after that comes the day after tomorrow and so on).
When I use the date in the format 2022-09-08 00:00:00.000 I am able to group the recipes and bring them in the correct order. But I don't like the format. I rather want to display (Thursday, 8. September). So I tried the following:
return Column(
children: [
SizedBox(height: 24),
Expanded(
child: GroupedListView<dynamic,String>(
elements: jsonList,
groupBy: (element) {
element['plannerdate'];
String date = DateFormat('dd. MMMM, EEEE').format(element['plannerdate']);
return date;
},
groupSeparatorBuilder: (value) => Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
color: Colors.white,
child: Text(value),
),
itemBuilder: (context, element) => Card(
child: Row(
children: <Widget>[
CircleAvatar(
radius: 70,
backgroundImage: NetworkImage(element['url'],
),
),
But with that my date gets converted to a string. The format looks nice, but this way the order is messed up. Recipes from 31 August are at the end of the list while recipes from 2 September are on top. Can someone help?
Edit:
My recipe class is:
//Recipe Model class for Hive Recipes Favorite Box
import 'package:hive/hive.dart';
part 'recipe_data_model.g.dart';
#HiveType(typeId: 0)
class RecipeModelData extends HiveObject {
#HiveField(0)
String? id;
#HiveField(1)
String? title;
#HiveField(2)
String? url;
#HiveField(3)
String? price;
#HiveField(4)
int? servings;
#HiveField(5)
String? calories;
#HiveField(6)
String? carbs;
#HiveField(7)
String? protein;
#HiveField(8)
String? fat;
#HiveField(9)
List? ingredients;
#HiveField(10)
List? instructions;
#HiveField(11)
DateTime? plannerdate;
Map<String, dynamic> toJson() =>
{
'id': id,
'title': title,
'url': url,
'price': price,
'servings': servings,
'calories': calories,
'carbs': carbs,
'protein': protein,
'fat': fat,
'ingredients': ingredients,
'instructions': instructions,
'plannerdate': plannerdate,
};
}
And the jsonList with all the recipe details is:
flutter: jsonList: [{id: 1, title: Bananenbrott, url: https://firebasestorage.xyz, price: 0,77, servings: 1, calories: 234, carbs: 12, protein: 34, fat: 1, ingredients: [2 Bananen, 30 g Mehl, 2 Eier], instructions: [1. Zunächst das Ei mit den zerdrückten Bananen in einer Schüssel vermischen., 2. Dann das Mehl sieben und hinzufügen., 3. Alles umrühren und fertig], plannerdate: 2022-08-31 00:00:00.000}, {id: 2, title: Gemüsecurry, url: https://firebasestorage.xys, price: 1,20, servings: 1, calories: 450, carbs: 42, protein: 22, fat: 9, ingredients: [2 Dosen Kokosmilch, 400 ml Wasser, 1 Karotte], instructions: [1. Das Wasser zum Kochen bringen und dann mit der Kokosmilch vermischen., 2. Die Karotte schälen und dann mit dazu geben, 3. Alles miteinander gut kochen und verspeisen.]<…>
You could display the formatted date in the groupSeparatorBuilder instead of in groupBy.
Example
return Column(
children: [
SizedBox(height: 24),
Expanded(
child: GroupedListView<dynamic,DateTime>(
elements: jsonList,
groupBy: (element) => element['plannerdate'],
groupSeparatorBuilder: (value) {
String date = DateFormat('dd. MMMM, EEEE').format(value);
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
color: Colors.white,
child: Text(date),
)},
itemBuilder: (context, element) => Card(
child: Row(
children: <Widget>[
CircleAvatar(
radius: 70,
backgroundImage: NetworkImage(element['url'],
),
),

FLUTTER: " Undefined name 'manSoMi' "

I'm trying to call a final variable name "manSoMi" in AssetImage Widget but it always gets an error. I've tried many solutions to solve an error but it always says "Undefined name 'manSoMi'
import 'package:flutter/material.dart';
import 'package:zeus_app/models/Somiproduct.dart';
class DetailProductCart extends StatefulWidget {
final SoMiNam manSoMi;
const DetailProductCart({super.key, required this.manSoMi});
#override
State<DetailProductCart> createState() => _DetailProductCartState();
}
class _DetailProductCartState extends State<DetailProductCart> {
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.white,
body: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
Container(
width: size.width,
height: 500,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(manSoMi.image),
fit: BoxFit.cover)),
),
],
)
);
}
}
This is a Product class that i want to call it in AssetImage Widget by using the index name "manSoMi.image"
class SoMiNam {
final String image, title, addColors, price;
final int id;
SoMiNam(
{required this.image,
required this.title,
required this.addColors,
required this.price,
required this.id});
}
List<SoMiNam> aoSoMi = [
SoMiNam(
title: 'Sunflower Polo Tee',
price: '399.000 đ',
image: 'assets/images/v1.jpeg',
addColors: '+3 màu',
id: 1),
SoMiNam(
title: 'Bei Linen Shirt',
price: '429.000 đ',
image: 'assets/images/v6.jpeg',
addColors: '+3 màu',
id: 2),
SoMiNam(
title: 'GodFather Shirt',
price: '449.000 đ',
image: 'assets/images/v8.jpeg',
addColors: '+4 màu',
id: 3),
SoMiNam(
title: 'Felix Shirt',
price: '449.000 đ',
image: 'assets/images/FelixShirt.JPG',
addColors: '+2 màu',
id: 4),
SoMiNam(
title: 'Hemi Shirt',
price: '419.000 đ',
image: 'assets/images/HemiShirt.JPG',
addColors: '+1 màu',
id: 5),
SoMiNam(
title: 'Lala Cuban Shirt',
price: '449.000 đ',
image: 'assets/images/LalacubanShirt.JPG',
addColors: '+2 màu',
id: 6),
];
This is an error
You should call widget.manSoMi in order to access your variable in state full widget.
You need to use widget:
widget.manSoMi.image;
To access varieble manSoMi of DetailProductCart in _DetailProductCartState class.

How to get the index value whilst not getting duplicates within ListView.builder?

I'm trying to show the week's budget spending & timeline only when the user has put in the spending information.
This is the expected result and all seems to be working nicely until I add in more than just one spending in each week.
Here's what happens:
The problem that I understand is that the ListView.builder gets the date that is between "Initial" and "End" and builds the widgets. So because there's 2 spending between those dates, then it builds 2 of those widgets. The problem is I just can't seem to figure out a way to show them without duplication.
Here's my code:
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
controller: ScrollController(),
itemCount: snapshot.data!.length,
padding: const EdgeInsets.only(bottom: 8),
itemBuilder: (context, index) {
final spending = snapshot.data![index];
DateTime spendingDate = DateTime.parse(spending.date);
var initial =
DateTime(initialDate.year, initialDate.month, initialDate.day - 1);
var end = DateTime(endDate.year, endDate.month, endDate.day + 1);
return spendingDate.isAfter(initial) && spendingDate.isBefore(end)
? Column(
children: [
WeekDivider(label: label, dateEstimation: dateEstimation),
WeeklySpendingStream(
color: color,
snapshot: snapshot,
initialDate: initialDate,
endDate: endDate,
),
],
)
: const SizedBox();
},
);
}
WeeklySpendingStreamCode:
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
controller: ScrollController(),
itemCount: snapshot.data!.length,
padding: const EdgeInsets.only(bottom: 8),
itemBuilder: (context, index) {
final spending = snapshot.data![index];
DateTime spendingDate = DateTime.parse(spending.date);
var initial =
DateTime(initialDate.year, initialDate.month, initialDate.day - 1);
var end = DateTime(endDate.year, endDate.month, endDate.day + 1);
if (spendingDate.isAfter(initial) && spendingDate.isBefore(end)) {
return SwipeActionCell(
editModeOffset: 0,
fullSwipeFactor: 0.50,
key: ObjectKey(snapshot.data![index]),
trailingActions: [
SwipeAction(
performsFirstActionWithFullSwipe: true,
color: Colors.transparent,
content: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.red,
),
child: getIconButton(Colors.red, IconlyBold.delete)),
onTap: (handler) async {
handler(true);
await Future.delayed(const Duration(milliseconds: 100));
snapshot.data!.removeAt(index);
SpendingDatabaseHelper.instance.removeMethod(spending.id!);
},
),
],
child: SpendingCard(
beneficiary: spending.beneficiary,
budgetSpent: currency.format(int.parse(spending.budgetSpent)),
date: DateFormat("dd-MM-yyyy")
.format(DateTime.parse(spending.date)),
colorValue: color,
),
);
} else {
return const SizedBox();
}
},
);
}
The output of snapshot.data:
[
{id: 8, budgetName: 🍣 Food & Beverage, beneficiary: ddd, budgetSpent: 1, date: 2022-02-21},
{id: 7, budgetName: 🍣 Food & Beverage, beneficiary: dfgvsd, budgetSpent: 1, date: 2022-02-14},
{id: 4, budgetName: 🍣 Food & Beverage, beneficiary: ddd, budgetSpent: 1, date: 2022-02-11},
{id: 10, budgetName: 🍣 Food & Beverage, beneficiary: ddd, budgetSpent: 1, date: 2022-02-11},
{id: 5, budgetName: 🍣 Food & Beverage, beneficiary: asxasd, budgetSpent: 1, date: 2022-02-06}
]
Would really appreciate any suggestions/ideas of how this can be resolved.
Thanks in advance!
Nevermind, I somehow found the solution by getting the data using ".where" and checking whether it is empty or not. It worked nicely.
var initial = DateTime(initialDate.year, initialDate.month, initialDate.day - 1);
var end = DateTime(endDate.year, endDate.month, endDate.day + 1);
final spending = snapshot.data!.where((s) =>
DateTime.parse(s.date).isAfter(initial) &&
DateTime.parse(s.date).isBefore(end));
return spending.isNotEmpty ||
DateTime.now().isAfter(initial) && DateTime.now().isBefore(end)
? spending.isEmpty
? Column(
children: [
WeekDivider(label: label, dateEstimation: dateEstimation),
const Padding(
padding: EdgeInsets.only(top: 8, bottom: 16),
child: Text(
'No spending this week',
style: kCaption,
),
),
],
)
: ListView(
shrinkWrap: true,
controller: ScrollController(),
padding: const EdgeInsets.only(bottom: 8),
children: [
Column(
children: [
WeekDivider(label: label, dateEstimation: dateEstimation),
WeeklySpendingStream(
color: color,
snapshot: snapshot,
initialDate: initialDate,
endDate: endDate,
),
],
)
],
)
: const SizedBox();
}
I think your problem raised because of you just extract the database file without filtering so do this use .toSet() or assign the extracted data to a variable as a map or Set and use .toSet().

SetState does not rebuild ListView.builder

I am trying to create a comment page. The list of comments are listed using ListView.builder. And when the user enter a comment, it will rebuild the list again to include the newly added comment. But somehow the list is not rebuild and i'm getting this message in terminal:
Changing the content within the the composing region may cause the
input method to behave strangely, and is therefore discouraged. See
https://github.com/flut ter/flutter/issues/78827 for more details
The newly added comment only shows when i close the comment page and reopen it again. Please help me, as i am not sure what is the issue and how to fix it.
Comment Page:
import 'package:flutter/material.dart';
import '../model/model_comment.dart';
class CommentsPage extends StatefulWidget {
#override
_CommentsPageState createState() => _CommentsPageState();
}
class _CommentsPageState extends State<CommentsPage> {
ValueNotifier<int> _counter = ValueNotifier<int>(0);
TextEditingController _controllerComment = TextEditingController();
bool _hasComment = false;
#override
void dispose() {
_controllerComment.dispose();
super.dispose();
}
_commentOnSend() {
setState(() {
var value = CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/women/34.jpg",
name: "Laurent Oslo",
dateTime: "30 Dec 20 08:00",
comment: _controllerComment.text,
);
CommentModel.dummyData.insert(0, value);
});
_controllerComment.clear();
FocusScope.of(context).unfocus();
}
Widget _listView = ListView.builder(
itemCount: CommentModel.dummyData.length,
itemBuilder: (context, index) {
CommentModel _model = CommentModel.dummyData[index];
return Column(
children: <Widget>[
Divider(
height: 12.0,
),
Container(
padding: EdgeInsets.fromLTRB(3.0, 3.0, 3.0, 3.0),
child: ListTile(
leading: CircleAvatar(
radius: 24.0,
backgroundImage: NetworkImage(_model.avatarUrl),
),
title: Text(_model.name),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_model.comment),
Text(_model.dateTime),
],
),
),
),
],
);
},
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Comments"),
titleSpacing: 40.0,
),
body: Container(
child: Column(
children: [
Expanded(child: _listView),
Divider(
height: 1.0,
),
ListTile(
leading: Container(
height: 40.0,
width: 40.0,
decoration: BoxDecoration(
color: Colors.deepPurple,
borderRadius: BorderRadius.all(Radius.circular(50.0))),
child: CircleAvatar(
radius: 50.0,
backgroundImage: NetworkImage(
"https://randomuser.me/api/portraits/men/83.jpg")),
),
title: TextField(
decoration: (InputDecoration(
hintText: "Add Comment"
)),
minLines: 1,
maxLines: 5,
controller: _controllerComment,
onChanged: (val) {
setState(() {
_counter.value += 1;
if (val.isNotEmpty) {
_hasComment = true;
} else {
_hasComment = false;
}
});
}),
trailing: ValueListenableBuilder(
valueListenable: _counter,
builder: (BuildContext context, int value, Widget? child) {
return IconButton(
onPressed: _hasComment
? () {
_commentOnSend();
}
: null,
icon: Icon(Icons.send_sharp,
color: _hasComment ? Colors.deepPurple : null),
);
},
),
),
],
),
),
);
}
}
Comment Model Class:
class CommentModel {
final String avatarUrl;
final String name;
final String dateTime;
final String comment;
CommentModel(
{required this.avatarUrl,
required this.name,
required this.dateTime,
required this.comment});
static List<CommentModel> dummyData = [
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/women/34.jpg",
name: "Laurent Oslo",
dateTime: "30 Dec 20 08:00",
comment:
"There is a reason why I implemented it like this. In a comment section, the same comment widget can appear multiple times. So, the keys assigned to each widget needs to be different. Otherwise I won’t be able to refer to a specific widget later on",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/women/49.jpg",
name: "Tracy Wilbur",
dateTime: "01 Oct 20 17:00",
comment: "First Comment!",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/women/23.jpg",
name: "Michael Scott",
dateTime: "30 Sept 20 06:00",
comment:
"The idea is simple. Use the prefix with something else to make the key unique. In this case, I’ve used the index value to make them unique. I used the keys in line 25, 51, 56, and 60. See how I’ve done it in these lines.",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/men/45.jpg",
name: "Williams John",
dateTime: "17 Sept 20 02:00",
comment: "Join!",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/women/77.jpg",
name: "Claire Rach",
dateTime: "15 Aug 20 19:00",
comment:
"I want the comment section to be hidden away. A user can view comments by tapping to expand a widget. Meaning, the comment section should be collapsible. It will toggle between expanded and collapsed mode when being tapped.",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/men/81.jpg",
name: "Joe Panama",
dateTime: "05 Jul 20 03:00",
comment:
"A comment will have 3 data values which are commenting user details, time of comment posting and the actual text of the comment. I’ve created a “CommentModel” class to create this model.",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/men/83.jpg",
name: "Mark Hamill",
dateTime: "09 Jun 20 15:00",
comment:
"Because comments are part of a post, “PostModel” needs to have a list of comment data. So I’ve modified “PostModel” to have a list of “CommentModel” objects. Refer to the code changes to see what I’ve done.",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/men/85.jpg",
name: "Williams Dafoe",
dateTime: "25 May 20 20:00",
comment:
"Notice lines 18 to 29. I’ve used the “ExpansionTile” widget to create a collapsible list of comments. Each comment is a “_SingleComment” widget implemented in lines 34 to 67.",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/men/98.jpg",
name: "Phillips Mach",
dateTime: "01 Apr 20 17:00",
comment:
"New to app development and flutter in general(high schooler). Can I use this template? Do I have to give credit or can I just use it? At the very least, can I see the source code so I can learn from it?",
),
CommentModel(
avatarUrl: "https://randomuser.me/api/portraits/men/12.jpg",
name: "Joe Snowden",
dateTime: "04 Mar 20 16:00",
comment: "PM ME!",
),
];
}
This is because you are putting your Listview.builder in a state variable which is expected behavior since state variables do not get reinitialized in rebuilds.
If you want to refactor you can create a new function to return it:
ListView getList(){
return ListView.builder(
itemCount: CommentModel.dummyData.length,
itemBuilder: (context, index) {
CommentModel _model = CommentModel.dummyData[index];
return Column(
children: <Widget>[
Divider(
height: 12.0,
),
Container(
padding: EdgeInsets.fromLTRB(3.0, 3.0, 3.0, 3.0),
child: ListTile(
leading: CircleAvatar(
radius: 24.0,
backgroundImage: NetworkImage(_model.avatarUrl),
),
title: Text(_model.name),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_model.comment),
Text(_model.dateTime),
],
),
),
),
],
);
},
);
}

Flutter : Display Values inside GridView depending on Selected Category

I have an app that displays _customCard which is customized card inside GridView. I want to control the contents of the cards inside GridView so it could be changed according to selected value SelectedCategory of 6 different categories.
Here is where I control SelectedCategory:
_favouritePressed(int index){
setState(() {
selectedIndex = index;
});
}
Here is the code of GridView:
GridView.count(
crossAxisCount: 2,
children: <Widget>[
_customCard(
imageUrl: "assassin.png", item: "game", price: "\$50", count: 1
),
_customCard(
imageUrl: "sedan.png", item: "car", price: "\$25", count: 4
),
_customCard(
imageUrl: "blouse.png", item: "T-shirt", price: "\$20", count: 2
),
_customCard(
imageUrl: "toy.png", item: "kids", price: "\$2", count: 3
),
],
),
I am not sure I understand your question but I will take a shot at the solution:
GridView.count(
crossAxisCount: 2,
children: <Widget>[
if (selectedIndex==0)
_customCard(
imageUrl: "assassin.png", item: "game", price: "\$50", count: 1
),
if (selectedIndex==1)
_customCard(
imageUrl: "sedan.png", item: "car", price: "\$25", count: 4
),
if (selectedIndex==2)
_customCard(
imageUrl: "blouse.png", item: "T-shirt", price: "\$20", count: 2
),
if (selectedIndex==3)
_customCard(
imageUrl: "toy.png", item: "kids", price: "\$2", count: 3
),
],
),
Sorry if I misunderstood your question.