flutter List view with cards - flutter

I am a beginner in Flutter programming, and I am in the learning phase. I am trying to create only the UI of a list using dummy data for the item which can be bought very frequently by the customer. for example a customer has bought pencils very often, and a pen not so often, so the pencils will be on the top of the list and pen will be below the pencils and so on...! Below is the image which I wanted to create
waiting for your suggestions. thanks
in short frequently bought items are on the top of the list.

List dataItems = [
{"product": "pencil", "frequency" :4},
{"product": "pencil2", "frequency" :4},
{"product": "pencil4", "frequency" :4}
];
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(children: const[
Text("No"),
SizedBox(width: 16),
Text("Product"),
SizedBox(width: 16),
Text("Frequency"),
]),
Expanded(
child: ListView.builder(
itemCount: 2,
itemBuilder: (ctx, index){
return Row(children: [
Text(index.toString()),
SizedBox(width: 16),
Text(dataItems[index]["product"]),
SizedBox(width: 16),
Text(dataItems[index]["frequency"].toString()),
Spacer(),
MaterialButton(onPressed: (){}, child: Text("Deliver"), ),
MaterialButton(onPressed: (){}, child: Text("Self Pickup"), )
]);
}
))
],
),
You can use an implement like this, try using row methods in order to divide items inside the row.

i hope it little help
class DemoWork extends StatefulWidget {
const DemoWork({Key? key}) : super(key: key);
#override
State<DemoWork> createState() => _DemoWorkState();
}
class _DemoWorkState extends State<DemoWork> {
List product=[
{'product':'pencil', 'frequency': 10}, {'product':'pen','frequency':24}, {'product':'notebook','frequency':12}, {'product':'markers','frequency':2}, {'product':'erasers','frequency':21}
];
Iterable<dynamic>? data;
#override
void initState() {
// TODO: implement initState
super.initState();
product.sort((a, b) {
return a['frequency'].compareTo(b['frequency']);
});
//output is {product: pen, frequency: 24}, {product: erasers, frequency: 21}, {product: notebook, frequency: 12}, {product: pencil, frequency: 10}, {product: markers, frequency: 2}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 5,
shrinkWrap: true,
reverse: true,// because list show low to high so reverse use to show high to low value and shrink wrap adjust its size use as you want to show and adjust it
itemBuilder: (context, index) {
return ListTile(
leading:Text(index.toString()) ,
title: Text(product[index]['product']),
trailing: Text(product[index]['frequency'].toString()),
);
}),
);
}
}

You can counting time user buying stuffs and use it to sort the list. Of course u need to build your widget for yourself, i will only suggest the logic.
Example
void main() {
var stuffs = ['ball', 'pen', 'pencil', 'glass']; // your stuff here
var history = ['pencil', 'pencil', 'glass', 'pen', 'glass', 'pencil']; // your buy history here, you can add more if you want
print('stuff will dispaly in order: $stuffs');
stuffs.sort((a,b) => history.timesOf(b).compareTo(history.timesOf(a))); // Function that sort the list by 'buy times' of user store in `history` variable
print('stuff will dispaly in order when using history: $stuffs');
}
extension HistoryCheck on List<String> {
int timesOf(String name) => where((_) => _ == name).length;
}
// ===== Result =====
stuff will dispaly in order: [ball, pen, pencil, glass]
stuff will dispaly in order when using history: [pencil, glass, pen, ball]

Suppose you have the following product model and list of products:
// Product Model
class Product {
final int serialNo;
final String name;
final int frequency;
const Product(this.serialNo, this.name, this.frequency);
}
// List of products
List<Product> data = [
Product(1, 'Pencil', 35),
Product(2, 'Pen', 30),
Product(3, 'Notebook', 25),
];
Just before showing this list of products in a list view, you can sort it based on the frequency as shown below:
data.sort((a, b) => b.frequency.compareTo(a.frequency));

Related

How to give gradient ListView.builder items?

I have a listview.builder in my code and I have multiple widgets inside it. The problem is, that I have a list of numbers and I'm trying to make the gradient background of specific numbers. I only know starting index and length of the gradient background. But ListView.builder childrens building lazily. For example, I'm creating gradient numbers from 10th to 20th but it's not drawing if starting point is not showing in screen.
How can I show all colored/gradient numbers inside of ListView.builder? I don't want to use SingleChildScrollView and Column/Row, that may cause an performance issue. Is there any way to draw gradient items even if starting point not showing in screen?
Dartpad Example
I have data like that
final items = [
for (var index = 1; index <= 30; index++)
if (index == 10)
DayType(
value: index,
type: GradientType.gradient,
)
else
DayType(
value: index,
type: GradientType.common,
)
];
and I'm trying to show inside of listView.
ListView.builder(
controller: _scrollController,
itemExtent: 40,
padding: EdgeInsets.symmetric(
horizontal: (width - 40) / 2,
),
itemCount: items.length,
physics: _SnapScrollPhysics(
itemSize: 40,
onItemPositionUpdate: (index) {},
),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
final item = items;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 2,
),
child: GestureDetector(
onTap: () => _scrollToPosition(index),
child: Column(
children: [
DayIndicator(
day: item[index],
),
],
),
),
);
},
),
class DayIndicator extends StatelessWidget {
const DayIndicator({
Key? key,
required this.day,
}) : super(key: key);
final DayType day;
#override
Widget build(BuildContext context) {
if (day.type == GradientType.common) {
return _CommonIndicator(
day: day.value,
);
} else if (day.type == GradientType.gradient) {
return _GradientIndicator(
day: day.value,
length: 8,
);
} else {
throw Exception('Unknown type: ${day.type}');
}
}
}

How to remake a request when the page reload Flutter / GetX

I'm making a flutter App for a project in my School and I have a problem. I have a page where there are widgets representing categories of articles and when a category is clicked a page with articles from that category is displayed. The problem is that once a category is called, the articles in that category remain the same despite the category change.
When the page is called, a controller is created that will execute the query that retrieves the items in the category.
How can I get this controller to remind me every time the page is loaded?
Category page code :
class ProduceCategoryScreen extends StatefulWidget {
static String routeName = "/produceByCategorie";
#override
State<ProduceCategoryScreen> createState() => _ProduceCategoryScreenState();
}
class _ProduceCategoryScreenState extends State<ProduceCategoryScreen> {
static int gridColumn = 1;
static ArticleByCategoryController articleController =
Get.put(ArticleByCategoryController());
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Produitss"),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: Text(
'Green Tomato',
style: TextStyle(
color: Colors.black,
fontSize: 32,
fontWeight: FontWeight.w900,
),
),
),
IconButton(
onPressed: () {
switch (gridColumn) {
case 1:
setState(() {
gridColumn = 2;
});
break;
case 2:
setState(() {
gridColumn = 1;
});
break;
default:
}
},
icon: Icon(Icons.grid_view),
)
],
),
),
Expanded(
child: Obx(
() {
if (articleController.isLoading.value)
return Center(child: CircularProgressIndicator());
else
return AlignedGridView.count(
crossAxisCount: gridColumn,
itemCount: articleController.articleList.length,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
itemBuilder: (context, index) {
return ProductTile(articleController.articleList[index]);
},
);
},
),
),
],
),
);
}
}
The category widget
Category screen
If you want more information you can send me a message on my discord : PascheK7#6324.
I think you are passing same list of data everytime when you execute below line.
"return ProductTile(articleController.articleList[index]);"
articleController.articleList : does this list contain only one category data ?
or it is contain category wise data.
e.g. : articleController.articleList[0] = category 1 list
articleController.articleList[1] = category 2 list
articleController.articleList[2] = category 3 list --> this way, it shouldn't make problem.
If articleList contain only one category data, then issue can happen that you get same data everytime. bcoz you are passing only index, but not category wise data.

How to show seperate DataTables and Text for each map in json list?

I want to show json data with DataTable() but i get error:
type '(dynamic) => Column?' is not a subtype of type '(dynamic) => DataTable' of 'f'
My question is how i can resolve the error but most importantly how can i iterate through the list block_data and then for each map show header and title in seperate DataTable() where they are on top of eachother and eachdescription below each DataTable()?
This is the view where i call the method setTestData() which awaits the Future AppContent().getAppContent() and then set the data to the field testObject and i also initialize setTestData() in current state. I can then use testObject to acces the json data.
My goal is to show EACH map from list block_data as SEPERATE DataTable for my usecase i have to do that. The reason why i want to this like that is because i also want to show the description below DataTable as a seperate Text() widget because it can be too long and in my usecase it has to be below the table
I now have this AppView statefull widget which i want to use to show each DataTable() seperatly based on each map from list block_data. I am not sure if i do it the right way but right now i get the error so it is more unclear if i can even achieve my goal this way:
class AppView extends StatefulWidget {
const AppView({Key? key}) : super(key: key);
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
late Map<String, dynamic> testObject = {};
setTestData() async {
await AppContent()
.getAppContent()
.then((result) => setState(() => testObject = result));
}
#override
void initState() {
setTestData();
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
key: _scaffoldKey,
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
leading: IconButton(
icon: const Icon(Icons.menu,
size: 40), // change this size and style
onPressed: () => _scaffoldKey.currentState?.openDrawer(),
),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Column(
children: [
FutureBuilder(
future: AppContent().getAppContent(),
builder: (context, snapshot) {
if (snapshot.hasData) {
debugPrint(testObject["data"]["views"]["books"][0]
["block_data"]
.toString());
return Column(
children: [
Container(
alignment: Alignment.topLeft,
child: (testObject["data"]["views"]["books"]
[0]["block_data"])
.map<DataTable>((object) {
if (object.containsKey("header")) {
return Column(
children: [
DataTable(
horizontalMargin: 0,
columnSpacing: 75,
columns: <DataColumn>[
DataColumn(
label: Container(
padding: EdgeInsets.zero,
child: Text(
object["header"]
.toString(),
)),
),
],
rows: <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text(
object['title']
.toString(),
DataCell(Text(object['date'].toString()
))
],
),
],
),
Text(object["description"]
.toString())
],
);
} else {
Container();
}
}).toList()),
Text(
"This is another placeholder for another list")
],
);
} else {
return CircularProgressIndicator();
}
}),
],
);
}, childCount: 1),
),
],
),
),
);
}
}
This is the method AppContent().getAppContent() which grabs the json:
class AppContent {
Future<Map<String, dynamic>> getAppContent() async {
String jsonData = await rootBundle.loadString('assets/test.json');
Map<String, dynamic> data = jsonDecode(jsonData);
return data;
}
}
And this the json which i call:
{
"data": {
"views": {
"books": [
{
"block_type": "list",
"block_data": [
{
"header": "FAQ",
"long_text_type": "description",
"title": "Service fees",
"date": "19-01-2022",
"description": "Information about fees and surcharges."
},
{
"header": "FAQ",
"long_text_type": "description",
"title": "Returns & Refunds",
"date": "03-06-2022",
"description": "How to return products and recieve refunds.."
}
]
}
}
}
}
Edit
I want it to look like the picture below where i have a datable for and description below that. But i want it for each map in list block_data and so i want to show the next map below the description and then show basicly DataTable -> description -> DataTable -> description. But i want to iterate through list block_data and generate DataTable and Text based on maps inside list which could be more than just two maps
There are multiple issues here.
Your JSON has an error (probably a copy/paste error, but double-check it).
Instead of
"block_data": "block_data": [ you should have "block_data": [.
Moreover, a bracket is missing at the end of your JSON file (but again, I guess that it's because you only showed a part of your file to help us investigate your problem)
The error you wrote in your question is related to your .map<DataTable>((object) {
When using the .map, the Object you specify is the return type of your mapping. In your case you're returning a Column and not a DataTable.
If you want to iterate on a list, and create a list of Widgets in return, you can use this instead:
.map<Widget>((object) {
Finally, and the most important point of this answer : you're having problems here because you're not converting your JSON file in a Dart Object you can easily manipulate.
You can simply paste your JSON file on this website : https://javiercbk.github.io/json_to_dart/ and retrieve the code to add to your project.
Then, you'll have a model with a fromJson and a toJson methods.
Thanks to those methods, you'll be able to create Dart Objects from your JSON values, and thus to create your Widgets easily.
With this answer, you should be good to go. Add a comment if I need to add more details.

Flutter provider consumer removes my items

I'm trying to build a sort function in order to sort JSON data.
For this, I have a button that opens a "showModalBottomSheet".
Within it I can choose the following data of the school class numbers.
So in my data I have 6 classrooms when loading in my constructor.
My filter is represented by buttons which are active or not if the filter contains the number of the classroom. My code works pretty much, my problem is that when I select a filter button in order to activate or not the filter, the button is deleted instead of staying but changing color
My notifier :
class TablesNotifier with ChangeNotifier {
// Services
// ---------------------------------------------------------------------------
final jsonSelectorService = locator<JsonSelectorService>();
// Variables
// ---------------------------------------------------------------------------
//all data from my classerooms in JSON
List<ClassroomModel> classrooms;
// Data that I will display and reconstruct based on my filter parameters
List<ClassroomModel> classroomsFiltered;
List<int> numberOfClassrooms = List();
// Model which will store the parameters of my filters and as a function I will load the data to display
FilterClassroomsModel filterClassroomsModel = FilterClassroomsModel();
// Constructor
// ---------------------------------------------------------------------------
TablesNotifier(){
_initialise();
}
// Initialisation
// ---------------------------------------------------------------------------
Future _initialise() async{
classrooms = await jsonSelectorService.classrooms('data');
classroomsFiltered = classrooms ;
// I install the number of existing classrooms
// Here the result is [1,2,3,4,5,6]
classrooms.forEach((element) {
if(!numberOfClassrooms.contains(element.type)){
numberOfClassrooms.add(element.type);
}
});
// I install the number of classrooms activated by default in my filter
// As I decide to display all my classrooms by default
// My filter on the classrooms must contain all the loaded classrooms
filterClassroomsModel.classrooms = numberOfClassrooms;
notifyListeners();
}
// Functions public
// ---------------------------------------------------------------------------
void saveClassroomsSelected(int index)
{
// Here my classroom model also contains the numbers of the classrooms that I want to filter
if(filterClassroomsModel.classrooms.contains(index)){
filterClassroomsModel.classrooms.remove(index);
}else{
filterClassroomsModel.classrooms.add(index);
}
notifyListeners();
}
}
I have identified that in my function initialize () if I change my code by this it works :
filterClassroomsModel.classrooms= numberOfClassrooms; // this
filterClassroomsModel.classrooms= [1,2,3,4,5,6]; // By this
I am losing the dynamic side of my classroom calculation and that does not suit me. But I don't understand this behavior.
My view :
class TableScreen extends StatelessWidget {
final String title;
TableScreen({Key key, #required this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: MenuDrawerComponent.builder(context),
appBar: AppBar(
backgroundColor: AppColors.backgroundDark,
elevation: 0,
centerTitle: true,
title: Text(title),
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.sort),
label: Text('Filter'),
onPressed: () async{
slideSheet(context);
},
backgroundColor: AppColors.contrastPrimary,
),
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context)
{
var _tableProvider = Provider.of<TablesNotifier>(context);
if(_tableProvider.chargesFiltered == null){
return Center(
child: CircularProgressIndicator(
backgroundColor: AppColors.colorShadowLight,
),
);
}else{
return Column(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(top: 10, right : 20, left : 20),
child: ListView.builder(
itemCount: _tableProvider.classroomsFiltered.length,
itemBuilder: (context, index){
return Container(
child: Column(
children: [
Row(
// Some classrooms data
),
],
),
);
},
),
)
),
],
);
}
}
void slideSheet(BuildContext context) {
var _tableProvider = Provider.of<TablesNotifier>(context, listen:false);
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
builder: (context) {
return Wrap(
children: [
Container(
color: Color(0xFF737373),
child: Container(
child: Column(
children: <Widget>[
// Some filters ...
// Here I want to rebuild the list of button for show the changes
ChangeNotifierProvider.value(
value: _tableProvider,
child: Consumer<TablesNotifier>(
builder: (context, model, child){
return _listOfClassrooms(context);
}
),
),
],
),
),
),
]
);
});
}
Widget _listOfClassrooms(BuildContext context){
var _tableProvider = Provider.of<TablesNotifier>(context);
List<Widget> list = List<Widget>();
var listClassrooms = _tableProvider.numberOfClassrooms;
var filterClassrooms = _tableProvider.filterClassroomsModel.classrooms;
for (var i = 0; i < listClassrooms.length; i++) {
int selectIndex = 0;
if(filterClassrooms.contains(listClassrooms[i])){
selectIndex = listClassrooms[i];
}
list.add(
RadioComponent(
text: "${listClassrooms[i]}",
index: listClassrooms[i],
width: (MediaQuery.of(context).size.width - 56) /3,
selectedIndex: selectIndex,
onPressed: _tableProvider.saveChargesSelected,
),
);
}
return Wrap(
spacing: 8.0, // gap between adjacent chips
runSpacing: 8.0, // gap between lines
children: list
);
}
}
My FilterClassroomsModel :
class FilterClassroomsModel {
int order;
int sort;
List<int> classrooms;
FilterClassroomsModel ({
this.order = 0,
this.sort = 0,
this.classrooms = const[],
});
#override
String toString() {
return '{ '
'${this.order}, '
'${this.sort}, '
'${this.classrooms}, '
'}';
}
}
EDIT : Resolved topic. Thanks to Javachipper.
In the notifier I replace that :
filterClassroomsModel.classrooms = numberOfClassrooms;
By that :
filter.classrooms = List<int>();
filter.classrooms.addAll(numberOfClassrooms);
change this:
filterClassroomsModel.classrooms = numberOfClassrooms;
to:
filterClassroomsModel.classrooms.addAll(numberOfClassrooms);
Update (you can also do it like this):
filterClassroomsModel.classrooms= new List<int>();
filterClassroomsModel.classrooms.addAll(numberOfClassrooms);

How do I sort items in a list by DateTime in Flutter/Dart?

I'm making an app for my Uncle which he asked me to make to challenge me. I thought this was the perfect opportunity to learn Flutter/Dart. I have this code (below), but i need it to organise a list by DateTime (line 77). Each entry has a string id, a DateTime variable , a string name and some body text, at a bare minimum.
import 'package:flutter/material.dart';
import 'package:gym_tracker/model/diary_entry.dart';
import 'package:gym_tracker/utils/store.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => new HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> {
List<Entry> entries = getEntries();
List<String> userFavorites = getFavoritesIDs();
// Inactive widgets are going to call this method to
// signalize the parent widget HomeScreen to refresh the list view.
void _handleFavoritesListChanged(String entryID) {
// Set new state and refresh the widget:
setState(() {
if (userFavorites.contains(entryID)) {
userFavorites.remove(entryID);
} else {
userFavorites.add(entryID);
}
});
}
#override
Widget build(BuildContext context) {
Column _buildEntries(List<Entry> entriesList) {
return Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: entriesList.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(entriesList[index].name),
);
},
),
),
],
);
}
const double _iconSize = 20.0;
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: PreferredSize(
// We set Size equal to passed height (50.0) and infinite width:
preferredSize: Size.fromHeight(50.0),
child: AppBar(
backgroundColor: Colors.white,
elevation: 2.0,
bottom: TabBar(
labelColor: Theme.of(context).indicatorColor,
tabs: [
Tab(icon: Icon(Icons.wb_sunny, size: _iconSize)),
Tab(icon: Icon(Icons.date_range, size: _iconSize)),
Tab(icon: Icon(Icons.bookmark, size: _iconSize)),
Tab(icon: Icon(Icons.settings, size: _iconSize)),
],
),
),
),
body: Padding(
padding: EdgeInsets.all(5.0),
child: TabBarView(
// Replace placeholders:
children: [
// Display entries today:
_buildEntries(entries.where((entry) => entry.dateTime.day == DateTime.now().day && entry.dateTime.month == DateTime.now().month && entry.dateTime.year == DateTime.now().year).toList()),
// Display all entries:
_buildEntries(entries.where((entry) => ).toList()), //Help Here Please!
// Display favorite entries:
_buildEntries(entries.where((entry) => userFavorites.contains(entry.id)).toList()),
Center(child: Icon(Icons.settings)),
],
),
),
),
);
}
}
Realistically, this can be done in two ways. One would be to programatically sort the list of entries, while the other would be to get them sorted from wherever you're storing them.
In most situations, I'd argue that it would be better to get the items sorted from the database where you're storing them. And the same goes for just showing the ones from today or the favourites. That depends on which database you're using - but for example, if you're using SQFlite, it would be something like this: db.query(table, orderBy: "column_1 ASC, column_2 DESC"); if column_1 were your date and you wanted oldest to newest. Doing it this way would also allow you to use paging so you're not querying for all of the items in the table at once, which is a good practice if you're dealing with a lot of items.
If there are only a fairly low number of items (most phones should be able to handle <1000 items fairly easily without causing any UI stutter), it's a bit different situation, and doing it on the items straight in dart is a more viable option.
In this case, all you need to do is sort the list based on the date.
To do that, you can call entries..sort((item1, item2) => item1.date.compareTo(item2.date)) assuming .date is the date you're trying to order by. Note that this will sort the same list inline - if you don't want to change the original list, then first make a copy i.e. List.from(entries)....