I have problems wit passing data to another class and trying to solve it.
Basicly I'm getting some values from textfields and storing them in a list.
ElevatedButton(
child: Text("Add Task"),
onPressed: () {
setState(() {
itemList.add(
ListItems(
title: _titleController.text,
text: _textController.text,
selectedDayTime: _selectedDay,
),
);
Navigator.pop(context);
});
},
),
I have also another class to show these items as a listview.Everytime I add new item to the list, I will show this data as listview item. However, I don't know how to pass the itemList from AddTask class to HomePage class.
ListItems class:
class ListItems {
String? title;
String? text;
DateTime? selectedDayTime;
ListItems(
{required this.title, required this.text, required this.selectedDayTime});
}
My question is how can I pass the itemList from AddTask class and use it in my HomePage class.
Related
So far I have declared a FavoriteItems class like so:
enum Type {
a,
b,
c,
}
class FavoriteItem {
final String id;
final String itemType;
final String title;
final String icon;
final String item;
final Type type;
FavoriteItem({
required this.id,
required this.itemType,
required this.title,
required this.icon,
required this.item,
required this.type,
});
FavoriteItem copyWith({
String? id,
String? itemType,
String? title,
String? icon,
String? item,
Type? type,
}) {
assert(tags != null);
return FavoriteItem(
id: id ?? this.id,
itemType: itemType ?? this.itemType,
title: title ?? this.title,
icon: icon ?? this.icon,
item: item ?? this.item,
type: type ?? this.type,
);
}
}
And have succesfully implemented a manager to add items from other lists to this list, and from the favorites screen to be able to safely delete it... buuut, On the screen where the items to be favorited are I run into the problem of not being able to write the propper getter to be able to remove this element from the favorites list when clicking again on the heart icon.
IconButton(
onPressed: () {
if (_isFavorite == false) {
setState(() {
_isFavorite = true;
});
final manager =
Provider.of<FavoritesManager>(context, listen: false);
manager.addItem(
FavoriteItem(
id: widget.mitem.id,
itemType: widget.mitem.itemType,
title: widget.mitem.title,
icon: widget.mitem.icon,
item: widget.mitem.item,
type: Type.a),
);
}
if (_isFavorite == true) {
setState(() {
_isFavorite = !_isFavorite;
});
final manager =
Provider.of<FavoritesManager>(context, listen: false);
manager.deleteItem(favoriteItem
.indexWhere(widget.mitem.id = favoriteItems.id));
}
},
icon: Icon(_isFavorite ? Icons.favorite : Icons.favorite_border),
)
I have tried things like
final FavoriteItem favoriteitem
And
final _favoriteItems = <FavoriteItem>[];
List<FavoriteItem> get favoriteItems => List.unmodifiable(_favoriteItems);
But they all either show errors at the very "getter" line or later mention that the favoriteitem does not have an id declared.
My question is, how do I properly call for the favorite items list so as to be able to use the indexwhere method to successfully remove the item from the favorites list?
I am of course a complete begginner
for removing an item from your favelist you can write manager.removeWhere((element) => element.id == widget.mitem.id);
I would suggest rethinking the data structures you are using rather than trying to delete an item from a List using indexWhere. If you use a Set instead of a List to track which of the items have been favorited, you can delete an item in O(1) by simply calling the remove method. Whereas indexWhere on a List will be O(n).
Try this demo:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<FavoritesManager>.value(
value: FavoritesManager(),
),
],
child: const MaterialApp(
home: FavoritesPage(),
),
),
);
}
class FavoritesPage extends StatelessWidget {
const FavoritesPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Consumer<FavoritesManager>(
builder: (context, manager, child) => Scaffold(
appBar: AppBar(
title: const Text('Favorites Page'),
),
body: Column(
children: [
for (final item in manager.items)
ListTile(
title: Text(item.title),
leading: Text(item.icon),
trailing: IconButton(
onPressed: () => manager.isFavorite(item)
? manager.removeFavorite(item)
: manager.addFavorite(item),
icon: Icon(
manager.isFavorite(item)
? Icons.favorite
: Icons.favorite_border,
),
),
),
],
),
),
);
}
}
class FavoritesManager extends ChangeNotifier {
// favorites contains only those items that have been marked
// as a favorite.
final Set<Item> _favorites = {};
// _items contains all of the items whether they have been
// marked as favorite or not.
// The list has been populated with some example data.
final List<Item> _items = const [
Item(
id: '0',
itemType: 'A Type',
title: 'A Title',
icon: 'A Icon',
item: 'A Item',
type: Type.a,
),
Item(
id: '1',
itemType: 'B Type',
title: 'B Title',
icon: 'B Icon',
item: 'B Item',
type: Type.b,
),
Item(
id: '2',
itemType: 'C Type',
title: 'C Title',
icon: 'C Icon',
item: 'C Item',
type: Type.c,
),
];
List<Item> get items => List<Item>.unmodifiable(_items);
bool isFavorite(Item item) {
return _favorites.contains(item);
}
void addFavorite(Item item) {
_favorites.add(item);
notifyListeners();
}
void removeFavorite(Item item) {
_favorites.remove(item);
notifyListeners();
}
}
enum Type {
a,
b,
c,
}
// renamed FavoriteItem to Item to be less confusing,
// since not every item will be favorited.
class Item {
final String id;
final String itemType;
final String title;
final String icon;
final String item;
final Type type;
const Item({
required this.id,
required this.itemType,
required this.title,
required this.icon,
required this.item,
required this.type,
});
Item copyWith({
String? id,
String? itemType,
String? title,
String? icon,
String? item,
Type? type,
}) {
return Item(
id: id ?? this.id,
itemType: itemType ?? this.itemType,
title: title ?? this.title,
icon: icon ?? this.icon,
item: item ?? this.item,
type: type ?? this.type,
);
}
// Add == and hashCode so that this class can be used
// in data structures that use hashing such as Set.
#override
bool operator ==(Object other) {
return other is Item && id == other.id;
}
#override
int get hashCode => id.hashCode;
}
Here is what I'm trying to do.
Here is my ListItems class:
class ListItems {
String? title;
String? text;
DateTime? selectedDayTime;
ListItems(
{required this.title, required this.text, required this.selectedDayTime});
}
In my AddTask class, I'm trying to add some items to my list. Here is my code:
ElevatedButton(
onPressed: () {
setState(() {
itemList.add(
ListItems(
title: _titleController.text,
text: _textController.text,
selectedDayTime: _selectedDay,
),
);
Navigator.pop(context);
});
print(itemList.length);
},
child: Text("Add Task"),
),
itemList is a List list. However, everytime I try to add new item to my itemList, itemList.length never changes. I dont know why this happening so I need help.
Swap Navigator with print line.
ElevatedButton(
onPressed: () {
setState(() {
itemList.add(
ListItems(
title: _titleController.text,
text: _textController.text,
selectedDayTime: _selectedDay,
),
);
print(itemList.length);
//Navigator.pop(context); keep it here also
});
Navigator.pop(context);
},
child: Text("Add Task"),
),
Does it make any difference if you include your print statement inside the setState? I don't see any reason for your code not to work.
You could also try to type your itemList to be of type List<ListItems>, if you haven't already. I don't think it's causing any issues, but it can't hurt to be thorough.
I have a list of profile fields that I would like to create using a single widget, but I'm new to Flutter and can't seem to work out one single thing: passing a list variable through a parameter to a drop down menu. I have been able to create many drop down field widgets that work just fine, using (for example) _countries.map(), but now that I'm trying to convert the drop down field to a single widget as not to repeat myself, it gives me an error Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<DropdownMenuItem<String>>' when I try to pass it through a variable.
I have the following code (of course removing all that I believe to be unnecessary):
class _ProfileDataState extends State<ProfileData> {
final _countries = DropDownLists.countries;
String _country;
var listType;
Widget profileDropDown(var list, var listType) {
return Card(
onTap: () async {
AlertDialog(
content: DropdownButtonFormField<String>(
isExpanded: true,
items: list.map((String value) { // <-- If I test this with _countries.map(), it works
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
isDense: true,
value: listType,
onChanged: (value) {
FocusScope.of(context).requestFocus(FocusNode());
setState(() {
this.listType = value;
});
},
)
)
}
)
}
#override
Widget build(BuildContext context) {
return profileDropDown(_countries, _country),
...
I have a ListView inside a StatelessWidget. It has items and every item contains a checkbox. When someone checks an item, I want the ListView to send this as a parameter to another page. But when I do that, it's giving me this error:
I/flutter ( 7067): The following UnsupportedError was thrown while handling a gesture:
I/flutter ( 7067): Unsupported operation: Cannot add to an unmodifiable list
I/flutter ( 7067): When the exception was thrown, this was the stack:
and this is my code
class StudentsList extends StatelessWidget {
final List<Child> mList;
StudentsList({this.mList});
#override
Widget build(BuildContext context) {
List<Child> selectedList = [];
return Container(
margin: EdgeInsets.only(top: 50, bottom: 20),
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: mList == null ? 0 : mList.length,
padding: EdgeInsets.only(right: 10),
itemBuilder: (BuildContext context, int position) {
return GestureDetector(
onTap: () {
if (selectedList.isEmpty) {
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) => SolokPage(
mChildList: [mList[position]],
isTeacher: true,
),
),
);
} else {
if (!selectedList.contains(mList[position])) {
selectedList.add(mList[position]);
}
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) => SolokPage(
mChildList: selectedList,
isTeacher: true,
),
),
);
}
},
child: StudentItem(
student: mList[position],
),
);
},
),
);
}
}
Stateless Widget properties are meant to be immutable
class StudentsList extends StatelessWidget {
// final means, flutter will not change value in future
final List<Child> mList;
StudentsList({this.mList});
Why ?
Because Flutter expects no business logic resides in StatelessWidget.
If we need to add new Student in Student list, it is considered as business logic.
If we need to delete some Student in Student list, it is considered as business logic.
So by using stateless widget, Flutter will only focuses on How it will be displayed on Screen, what is the width, the constraints and etc.
That's why we found final syntaxes before class properties in StatelessWidget.
Similiar to our college life. Our Grades that marked in final report, will not change even after we graduate from university. As it said to be in Final Report, then it must be final.
Stateful Widget properties are mutable
Why ?
Because flutter expects business logic resides in StatefulWidget.
Changes to be made
So I suggest to change StudentsList Widget, from this :
class StudentsList extends StatelessWidget {
final List<Child> mList; // this is the issue
StudentsList({this.mList});
to this one :
class StudentsList extends StatefulWidget {
#override
_StudentsListState createState() => _StudentsListState();
}
class _StudentsListState extends State<StudentsList> {
// final List<Child> mList; // Do not mark this as final
List<Child> mList;
...
}
Working Repository
You may look working repository that is closely-related to your issue. Github
Stateless Widgets property cannot be immutable means in simple words is that it should not contain any non-final variables.
Simply convert it to Stateful widget and inside the class _StudentsListState create your variable WITHOUT the final keyword because you are modifying the value of that List.
If you want to keep stateless (IE you just need to return some data or maybe youre using hooks) you could also try toList() to create a copy, then modify, then replace the original list
I encountered this problem in a simple function, and I solved it like this.
Future<void> createProduct({required Product product, required List<File> images}) async {
for (final image in images) {
final imageId = const Uuid().v4();
final compressedimage = await ImageCompress.instance.compressFile(image);
final taskSnapShot = await StorageService.instance.uploadProductPhoto(
file: compressedimage,
productId: product.productId,
childUUID: imageId,
);
final downloadURL = await taskSnapShot.ref.getDownloadURL();
product.imagesUrl.add(downloadURL);
// sendProduct.addImages(downloadURL: downloadURL);
}
await _collection.doc().set(
product.toMap(),
);
}
turn it into this.
Future<void> createProduct({required Product product, required List<File> images})
async {
List<String> newUrls = [];
for (final image in images) {
final imageId = const Uuid().v4();
final compressedimage = await ImageCompress.instance.compressFile(image);
final taskSnapShot = await StorageService.instance.uploadProductPhoto(
file: compressedimage,
productId: product.productId,
childUUID: imageId,
);
final downloadURL = await taskSnapShot.ref.getDownloadURL();
newUrls.add(downloadURL);
// sendProduct.addImages(downloadURL: downloadURL);
}
final sendProduct = product.copyWith(imagesUrl: newUrls );
await _collection.doc().set(
sendProduct.toMap(),
);
}
I cant seem to figure out how to get all of my items in my list to display in the list view
Currently, when I click my button to display the list, only one items shows up. If I click back, and click main button again, it shows 2 items from the list. Rinse and repeat, 3 items. I can't seem to debug with print statements to see where my error lies. When I try print(trails) or other variations, it says Instance of trail model (not very helpful). Any ideas?
Here's my code:
class HomeScreen extends State<MyApp> {
int counter = 0;
Future<List<TrailModel>> fetchData() async {
counter++;
var response = await get(
'https://www.hikingproject.com/data/get-trails?lat=39.733694&lon=-121.854771&maxDistance=10&key=200419778-6a46042e219d019001dd83b13d58aa59');
final trailModel = TrailModel.fromJson(json.decode(response.body));
//trails.add(trailModel);
setState(() {
trails.add(trailModel);
});
return trails;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("HikeLocator")),
body: new RaisedButton(
child: Text("click me"),
onPressed: () async {
final trails = await fetchData();
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new ListScreen(trails)),
);
},
),
));
}
}
class ListScreen extends StatelessWidget {
final List<TrailModel> trails;
ListScreen(this.trails);
#override
Widget build(BuildContext ctxt) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Here are your trails"),
),
body: TrailList(trails),
);
}
}
class TrailList extends StatelessWidget {
final List<TrailModel> trails;
TrailList(this.trails);
Widget build(context) {
return ListView.builder(
itemCount: trails.length,
itemBuilder: (context, int index) {
Object myText = json.encode(trails[index].trails);
List<dynamic> myText2 = json.decode(myText);
return Text(myText2[index]['name']);
},
);
}
}
class TrailModel {
Object trails;
TrailModel(this.trails);
TrailModel.fromJson(Map<String, dynamic> parsedJson) {
trails = parsedJson['trails'];
}
}
I think my problem might lie in the fetchData(), but I'm not entirely sure. Trying to at least print out the values to limit where my problem might be. (Is it only adding 1 to the list each time I press the button? Is it only rendering one when I click it? Is it fetching all the data each click or only one json object? etc.)
Thank you kindly for any assistance. Sorry, I'm kind of new to dart, so this is a huge learning curve for
There are a couple problems in your code. The main reason this doesn't work as you expect is because you're parsing all elements of the json into one TrailModel object, but then your code assumes that you'll have multiple TrailModel objects.
The easiest way to fix it up and get it working is to use the list from TrailModel.trails instead of the one in the widget.
First, in ListScreen, pass just the first element in the list.
class ListScreen extends StatelessWidget {
final List<TrailModel> trails;
...
#override
Widget build(BuildContext ctxt) {
return new Scaffold(
...
body: TrailList(trails.first),
);
}
}
Then, in TrailList, use the trails list you have from TrailModel:
class TrailList extends StatelessWidget {
final TrailModel model;
TrailList(this.model);
Widget build(context) {
return ListView.builder(
itemCount: model.trails.length,
itemBuilder: (context, int index) {
final trail = model.trails[index];
...
},
);
}
}
When I try print(trails) or other variations, it says Instance of trail model (not very helpful)
print uses the output of the toString method in your classes. You're seeing Instance of trail model because that's the default implementation you get from the super class Object. You can override it to get something more useful:
class TrailModel {
#override
String toString() => 'trails=$trails';
}