I have this Account class
import 'package:project/models/category_model.dart';
enum AccountTypes {
cash,
banks,
}
class Account {
AccountTypes type;
double value;
List<BalnceCategory>? categories;
Account({
required this.type,
required this.value,
this.categories,
});
Map<String, dynamic> toJSON() {
return {
"type": type,
"value": value,
"categories": categories,
};
}
}
Map<AccountTypes, List<dynamic>> accounts = {
AccountTypes.cash: [
BalnceCategory(image: "food.png", title: "Food", value: 412.5).toJSON(),
BalnceCategory(image: "shopping.png", title: "Shopping", value: 412.5).toJSON(),
],
AccountTypes.banks: [
BalnceCategory(image: "food.png", title: "Food", value: 1242.63).toJSON(),
BalnceCategory(image: "shopping.png", title: "Shopping", value: 1242.63).toJSON(),
]
};
each Account should contain a list of BalnceCategory
class BalnceCategory {
String image;
String title;
double value;
BalnceCategory({
required this.image,
required this.title,
required this.value,
});
Map<String, dynamic> toJSON() {
return {
"image": image,
"title": title,
"value": value,
};
}
}
Now I want to display this Map Map<AccountTypes, List<dynamic>> accounts in two sections...I will refer to this map as accounts.
So in the first section I want to list all available accounts in something like a Row with a button for each account, so what I did is I mapped through accounts like this accounts.entries.map and returned a button for each account, and these buttons can set a state called currentIndex with it's index.
Now in the second section I want to list all accounts categories depending on the currentIndex state value, so for example if the currentIndex value is 0 I want to display all the categories in cash account, and if the currentIndex value is 1 I want to display all the categories in banks account.
So far all I am done the buttons section and I it is working properly and my problem is in the second section. I tried to do this
Expanded(
child: GridView.builder(
physics: const BouncingScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: mainUnit / 2,
crossAxisSpacing: mainUnit / 2,
childAspectRatio: 3 / 4,
),
itemCount: accounts.keys.length,
itemBuilder: (context, index) {
return accounts.forEach((key, value) {
if (key.index == currentIndex) {
value.map((e) => {Text(e.toString())});
}
});
},
),
),
but it gives me this error: The return type 'void' isn't a 'Widget', as required by the closure's context.
The ItemBuilder should return a widget, you return accounts.forEach(...) that is a void function ( forEach() is a void function/closure).
Try this:
Text( accounts.keys.firstWhere( (item) => item.index == currentIndex,
orElse: ()=> "",).toString() );
BUT!!!!
Wait a moment!!!!!
Why don't you take a look at
https://pub.dev/packages/flutter_sticky_header
You should implement what you need: it displays an header that, in your case, could be a string AccountTypes.cash.toString() or AccountTypes.banks.toString(), and then follow the example and you should obtain what you need ( display grouped cards for AccountTypes ). more over I suggest you to use
https://pub.dev/packages/freezed
It helps you to defined your data class, and to Serialize then to JSON.
For Example:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'account.g.dart';
part 'account.freezed.dart';
#freezed()
class Account with _$Account {
factory Account.cash({
#Default(0) double? value;
List<BalnceCategory>? categories;
}) = CashAccount;
factory Account.bank({
#Default(0) double? value;
List<BalnceCategory>? categories;
}) = BankAccount;
factory Account.fromJson(Map<String, dynamic> json) =>
_$AccountFromJson(json);
}
In that manner you have your data class ( account ) with its type ( cash / bank ) and serializable.
To create an account it is easy :
var myCash = Account.cash(... , ... );
Both CashAccount and BankAccount are two different classes that implement Account ( abstract class ), you can use it in a simple list of Accounts.
then , to chack for bank/cash you can use:
var myAccount = Account.cash(....);
myAccount.when(
bank: (BankAccount account) => ....,
cash: (CashAccount account) => .... ,
),
Related
I'm a newbie Flutter programmer.
Im trying to make a generic grid widget that let me pass an object and create columns and rows dynamically.
I've made it work with an specific object but what i need is to read the names of attributes of an object to dynamically create grid column names and values so i can use the same grid for client or articles or everything.
For example i have a class for clients
class Client {
int id;
String name;
String mail;
Client({required this.id, required this.name, required this.mail});
}
then in my code retrieve a list of clients and pass that to the grid widget as List.
I just need to know how to loop the object recieved to:
get the list of fields names (id, name, mail)
then get its value for example something like
var field_name = element;
obj.field_name.value;
Hope you can understand what im trying to do.
Thank you in advance.
you can try this code
FutureBuilder<List<Client>>(
future: // your future method which return the List<Client>,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final client= snapshot.data![index];
return ListTile(
title: Text(client.name),
leading: CircleAvatar(
child: Text(client.id),
),
subtitle: Text(client.email),
);
},
);
} else if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
return Center(
child: CircularProgressIndicator(),
);
},
},),
let me know if this is working for you.
Say you are getting List of Client Object and Class looks like this
class Client {
int id;
String name;
String mail;
Client({required this.id, required this.name, required this.mail});
}
Now if your question is how can i differentiate some Client from the list and have a separate Grid widget for them?
If yes, lets take example with above given class
We have Client with category, Design, Development etc.
We can simply have a another variable in class such as
class Client {
int id;
String name;
String mail;
//type variable to differentiate , could be String or enum if you know exactly
String type;
Client({required this.id, required this.name, required this.mail});
}
and
GridView.builder(
itemCount: images.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0
),
itemBuilder: (BuildContext context, int index){
Client item = Clients[index];
if(item.type == 'Design')
{
// display designClient item
return DesignGrid();
}
if(item.type == 'Development')
{
// display developmentType item
return DevelopmentGrid();
}
}
)
I have a product named collection,
In that product collection I have saved products with their name and category. Now I need to create a tab bar with all the category list inside products. The problem is there are multiple products with same category. And I want that category to be shown once. How to achieve this?
This is my code:
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance.collection('products') .where("category").snapshots(),
builder: (_, snapshot) {
if (snapshot.hasError) return Text('Error = ${snapshot.error}');
if (snapshot.hasData) {
final docs = snapshot.data!.docs;
return ListView.builder(
itemCount: docs.length,
itemBuilder: (_, i) {
final data = docs[i].data();
Set s = {};
s.add( data["category"]);
return ListTile(
title: Text(s.elementAt(0).toString()),
);
},
);
}
return Center(child: CircularProgressIndicator());
},
)
You must provide ur code ,
guessing this code will help u to implement ur code....
here I have taken Movie instead of product..U may correct it based on ur requirement...
class Movie {
String name;
String category;
String language;
int rating;
Movie(
{required this.name,
required this.category,
required this.language,
required this.rating});
}
void main() {
List<Movie> movielist = [
Movie(name: 'Golmaal', category: 'Comedy', language: 'Hindi', rating: 5),
Movie(name: 'Naseeb', category: 'Classic', language: 'Hindi', rating: 5),
Movie(name: 'Hera Phery', category: 'Comedy', language: 'Hindi', rating: 5)
];
final categories = movielist.map((e) => e.category).toSet();//this will generate unique category list..
print(categories);
// now use this categories to ur design with listview or any list widgets
}
So i have my dart call to my api get method. Btw the way am just learning flutter and dart and trying out basic crud operations I would use to be doing in .net and c#
import 'dart:convert';
import 'package:theapp/models/Players.dart';
import 'package:http/http.dart';
class ApiService {
final String apiUrl = "https://apiurlhidden.com/api";
final String getAllPlayersEndPoint = "/GetAllPlayers/";
Future<List<Player>> getAllPlayers() async {
final getallPlayersUrl = Uri.parse(apiUrl + getAllPlayersEndPoint);
Response res = await get(getallPlayersUrl);
if (res.statusCode == 200) {
List<dynamic> body = jsonDecode(res.body);
List<Player> players =
body.map((dynamic item) => Player.fromJson(item)).toList();
return players;
} else {
throw "Failed to load cases list";
}
}
}
And I have my listview here but it complaining saying key and players do not exist
import 'package:flutter/material.dart';
import 'package:theapp/models/Players.dart';
class PlayerList extends StatelessWidget {
List<Player> players = [];
PlayerList({Key key, this.players}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: players == null ? 0 : players.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {},
child: ListTile(
leading: Icon(Icons.person),
title: Text(players[index].firstName),
subtitle: Text(players[index].surname.toString()),
),
));
});
}
}
My Model
class Player {
final int id;
final int type;
final String playerLevel;
final String firstName;
final String surname;
Player(this.id, this.type, this.playerLevel, this.firstName, this.surname);
factory Player.fromJson(Map<String, dynamic> json) {
return Player(
json['id'],
json['type'],
json['playerlevel'],
json['firstname'],
json['surname'],
);
}
#override
String toString() =>
'Players{id: $id, firstName: $firstName, lastName: $surname}';
}
Is there any reason why it should not recognize players and key in my list view page also how do I get the items to appear in the listview.
Picture only added to show the context in the items I mentioned above. Also coming from a .net background I would normally use an observable collection so it gets any changes in data in real-time am I using the correct approach for that.
Use required keyword to make parameters mandatory.
PlayerList({required Key key, required this.players}) : super(key: key);
Named parameters are optional unless they’re explicitly marked as required.
See Parameters for details.
I'm a new flutter developer and would like to use multiple filters on my List.
I'm using a ListView :
ListView.builder(
itemBuilder: (ctx, index) =>
MealCard(_customList[index]),
itemCount: _customList.length,
),
and my Meal class have the following properties:
Meal {
final String id,
final String title,
final String imgUrl,
final bool isLowCalorie,
final bool isVegan,
final bool isBreakfat,
final bool isDinner,
final bool isLunch}
I'd like to apply different filters based on the booleans included within my class; I have some methods that look something like this:
List<Meal> get breakfastFilter {
return _meals.where((element) => element.isBreakfast).toList();
}
List<Meal> get lunchFilter {
return _meals.where((element) => element.isLunch).toList();
}
List<Meal> get dinnerFilter {
return _meals.where((element) => element.isDinner).toList();
However these methods allow me to create a single list, what if I wish to add additional filters together and create a combination of two lists.
I know I'm late to the game, so let me throw my name in the hat. I'd refactor it a bit so as to support more than one filter, otherwise you won't know upfront how many filters will be applied.
I'd create an enum called MealType:
enum MealType {
none,
isLowCalorie,
isVegan,
isBreakfast,
isDinner,
isLunch
}
Refactor the class to take a list of MealType values:
class Meal {
final String? id;
final String? title;
final String? imgUrl;
final List<MealType>? mealTypeFilter;
Meal({ this.id, this.title, this.imgUrl, this.mealTypeFilter });
}
The data would look like this:
List<Meal> meals = [
Meal(
id: '1001',
title: 'Pancakes',
imgUrl: '',
mealTypeFilter: [
MealType.isBreakfast
]
),
Meal(
id: '1002',
title: 'Chicken Wings',
imgUrl: '',
mealTypeFilter: [
MealType.isLunch
]
),
Meal(
id: '1003',
title: 'Yogurt',
imgUrl: '',
mealTypeFilter: [
MealType.isLowCalorie,
MealType.isBreakfast,
MealType.isVegan
]
)
];
I'd collect the applied filters in a Set to avoid duplicates, and filter the meals whether the mealTypeFilter collection contains any of the applied filters:
Set<MealType> appliedFilters = {};
List<Meal> filteredList = [];
List<Meal> getFilteredList() {
if (appliedFilters.isEmpty) {
return meals;
}
return meals.where((m) => m.mealTypeFilter!.any((f) => appliedFilters.contains(f))).toList();
}
I'd call the getFilteredList() method every time in the build method when I trigger a button that contains the filter to be applied and populate the filteredList with which I render a ListView with the results:
#override
Widget build(BuildContext context) {
filteredList = getFilteredList();
// rest of the code here
}
See Gist for the full code, and run it through DartPad so you can see it in action, like this:
Hope this works.
You can do it something like this:
bool filterItem(element){
return element.isVegan&&element.isBreakfat;
}
_meals.where((element) => filterItem(element)).toList();
There's many ways to do it but you can try to use boooleans combined with optional parameters like this:
getMealFilter(_meals,
{bool lookForBreakfast = false,
bool lookForVegan = false,
bool lookForLunch = false}) {
return _meals
.where((element) =>
(lookForBreakfast && element.isBreakfast) ||
(lookForVegan && element.isBreakfast) ||
(lookForLunch && element.isLunch))
.toList();
}
I just put 3 filters, but you can easily add the rest. How this works is that if, for example, lookForBreakfast is false, element.isBreakfast is ignored. (false and something is always false), and so on for the others parameters.
This is my model:
class Foo {
final String name;
final String emailId;
Foo(this.name, this.emailId);
factory Foo.fromJson(Map<String, dynamic> json) {
return Foo(
json['name'] as String,
json['email_id'] as String,
);
}
}
I want to create its object by letting user enter text in the TextField, for that I decided to use Map.
Map<String, dynamic> map = {};
My first text field:
TextFormField(
onSaved: (s) => map['name'] = s,
)
and second:
TextFormField(
onSaved: (s) => map['email_id'] = s,
)
I'm creating an instance using:
final foo = Foo.fromJson(map);
Is there any better way of creating an object without modifying my model class (final fields as well as non-nullable fields) except for creating various fields to store the value and then calling Foo() constructor.
There is many ways to solve this, my recommendation is first to load the data, and then modify anything in the ui without touching the class.
First define your data:
// Example fetch fooFromJson(String)
final Foo foo = Foo(name: 'name', email: 'stack#overflow.com');
// Text controllers
late final TextEditingController _controllerName;
late final TextEditingController _controllerEmail;
// Form key
final formKey = GlobalKey<FormState>();
Define your #initState method:
#override
void initState() {
_controllerName = TextEditingController(text: foo.name);
_controllerEmail = TextEditingController(text: foo.email);
super.initState();
}
This way, the data will fetch from the backend just one time, and will show data to the UI without touching the class.
Then in your body widget:
return Form(
key: formKey,
child: ListView(
padding: const EdgeInsets.all(100),
shrinkWrap: true,
children: [
TextFormField(
controller: _controllerName,
//validator: myValidator,
),
TextFormField(
controller: _controllerEmail,
//validator: myValidator,
),
const SizedBox(height: 20),
ElevatedButton(child: const Text('Save'), onPressed: _onSaved),
],
),
);
When you click save button, set the data to your class again (if you don't want to put the data again to the class go to the end of the answer):
_onSaved() {
if (formKey.currentState!.validate()) {
foo.name = _controllerName.text;
foo.email = _controllerEmail.text;
}
}
Result:
On the other hand, Form is use to validate data, for example you want "Text should be 10 characters maximum":
TextFormField(
controller: _controllerName,
validator: _nameValidator,
autovalidateMode: AutovalidateMode.onUserInteraction,
)
And the function:
String? _nameValidator(String? text) {
return text!.length < 10 ? 'Name should have 10 characters at least' : null;
}
Result when you click save:
The data will not be saved until validation is correct.
Also if you want to fetch data from the internet or database, I recommend to improve your data class, for example you have the following Json:
{
"name" :"",
"email" : ""
}
The class should be:
import 'dart:convert';
Foo fooFromJson(String str) => Foo.fromJson(json.decode(str));
String fooToJson(Foo data) => json.encode(data.toJson());
class Foo {
Foo({
this.name,
this.email,
});
String name;
String email;
factory Foo.fromJson(Map<String, dynamic> json) => Foo(
name: json["name"],
email: json["email"],
);
Map<String, dynamic> toJson() => {
"name": name,
"email": email,
};
}
So you can POST foo.toJson() to whatever API you have.
This is a good tool that can help you with that https://app.quicktype.io/