I am trying to build a list where each item contains an icon on the far side. However, when I try and access the list when more than one item is added I get the error:
Invalid value: Only valid value is 0: 1
Here is the code:
import 'package:flutter/material.dart';
import './images.dart';
class LikedList extends StatefulWidget {
#override
_LikedListState createState() => _LikedListState();
}
class _LikedListState extends State<LikedList> {
static List<bool> _likes = List.filled(ImagesState.likes.length,true);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Liked List'),
),
body: ListView.separated(
separatorBuilder: (context, index) => Divider(),
itemCount: ImagesState.likes.length,
itemBuilder: (context, index) {
final item = ImagesState.likes[index];
return ListTile(
title: Text(item),
trailing: IconButton(
icon: _likes[index]
? Icon(
Icons.favorite_border,
color: Colors.grey,
)
: Icon(
Icons.favorite,
color: Colors.red,
),
onPressed: () {
setState(() {
print(_likes);
_likes[index] = !_likes[index];
print(_likes);
});
},
),
onLongPress: () {
setState(() {
print(ImagesState.likes[index]);
ImagesState.likes.removeAt(index);
});
},
);
},
),
);
}
}
Does anyone know why this is happening?
Similar suggestions point to itemCount not being set, but I am using that.
You appear to be creating a fixed length list with the statement:
static List<bool> _likes = List.filled(ImagesState.likes.length,true);
That will create a filled fixed length list of length .likes.length, containing values 'true'.
You need to add a third parameter 'growable: true' in your method call to be able to add to the list.
Related
I am new to Flutter and currently building an app to log spasms that happens due to spasticity. This is somewhat like a ToDo style app in structure. So I have a list in my home.dart file that a ListViewBuilder to display my Spasm objects. What I want to do is to create a Spasm object in recordSpasm.dart and add it to the list in home.dart. How do I do that? I´ll post my code here:
home.dart
import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:spasmlogger/classses/spasm.dart';
class Home extends StatefulWidget {
const Home({ Key? key }) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
List<Spasm> Spasms = [
Spasm("Extremely strong", "Upper body", "Cannot control left arm"),
Spasm("Extremely strong", "Upper body", "Cannot control left arm")
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SpasmLogger"),
actions: <Widget>[
IconButton(
icon: Icon(MdiIcons.help),
onPressed: () {
Navigator.pushNamed(context, '/about');
},
)
],
),
floatingActionButton: FloatingActionButton(
child: Icon(MdiIcons.plus),
onPressed: () {
Navigator.pushNamed(context, "/recordSpasm");
}
),
body: Padding(
padding: EdgeInsets.fromLTRB(16, 16, 16, 16),
child: ListView.builder(
itemCount: Spasms.length,
itemBuilder: (BuildContext context, int index){
return Card(
child: ListTile(
title: Text(Spasms[index].strength + " spasm detected in " + Spasms[index].bodyPart),
subtitle: Text(Spasms[index].comment)
)
);
},
)
),
);
}
}
recordSpasm.dart
import 'package:flutter/material.dart';
import 'package:spasmlogger/classses/spasm.dart';
class RecordSpasm extends StatefulWidget {
const RecordSpasm({ Key? key }) : super(key: key);
#override
_RecordSpasmState createState() => _RecordSpasmState();
}
class _RecordSpasmState extends State<RecordSpasm> {
#override
String Strength = "Regular strength";
List<String> Strengths = ["Regular strength", "Mildly stronger", "Severely Strong", "Extremely strong"];
String BodyPart = "Lower body";
List<String> BodyParts = ["Lower body", "Upper body", "Head"];
TextEditingController comment = new TextEditingController();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Record spasm")
),
body:
Padding(
padding: EdgeInsets.all(16),
child: Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget> [
Text("Spasm strength"),
DropdownButton(
value: Strength,
items: Strengths.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String? value) {
setState(() {
Strength = value!;
});
},
),
Text("Part of body"),
DropdownButton(
value: BodyPart,
items: BodyParts.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String? value) {
setState(() {
BodyPart = value!;
});
},
),
Text("Comments"),
TextFormField(
maxLines: 5,
controller: comment,
),
ElevatedButton(
onPressed: () {
// Add object to the list in home.dart
print(Strength);
print(BodyPart);
print(comment.text);
},
child: Text("Record spasm")
)
]
)
)
)
);
}
}
Navigator.push returns a Future when the pushed page is popped. So you just need to add the Spasm object in the recordSpasm.dart:
ElevatedButton(
onPressed: () {
Navigator.pop(context,Spasm(Strength, BodyPart, comment.text));
},
child: Text("Record spasm")
)
and retrieve the object and "refresh" the page in the home.dart
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.pushNamed(context, "/recordSpasm").then((value) {
if (value != null && value is Spasm) {
setState(() {
// if this doesn't work add the value to the list then call setState
Spasms.add(value);
});
}
});
}),
Just a tip, in dart variable names should be lowercase (eg: String strength) :)
first you need to access to you list. you have 2 ways to do that
make the list static like this static List yourListName = [];
for the other way you don't need to do anything right now
so if you use way 1 then you can add something to your list like this:
import 'thePathFromTheFileThatHaveTheListIn';
...
// here we add something to your list without building the whole class again
TheClassWhereTheListWasIn.yourListName.add(...);
and if you use way 2 then you can add something to your list like this:
import 'thePathFromTheFielThatHaveTheListIn';
...
// here we add something to your list but here we build the whole class again
// and then add something to your list
TheClassWhereTheListIsIn().yourListName.add(...);
I use provider library state management for doing add to cart and basically i am a bit beginner in provider. So the issue i am facing is for example there are three products laptop , iphone x & keyboard. Now if i put laptop two times in the cart then in cart page it displays two laptop card widgets, instead i want to display only one card widget in that laptop qty: 2. And second issue is that i have implemented + and - button in each card widget in cart page and if i click on + or - button then it should reflect on qty and also on total price. Really appreciate if you help me in this problem.
main.dart
void main() {
runApp(ChangeNotifierProvider(
create: (context) => Cart(),
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<Item> items = [
Item(title: 'laptop ', price: 500.0),
Item(title: 'iphone x ', price: 400.0),
Item(title: 'keyboard ', price: 40.0),
];
#override
Widget build(BuildContext context) {
return Consumer<Cart>(builder: (context, cart, child) {
return Scaffold(
appBar: AppBar(
title: Text('Shopping cart'),
actions: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.shopping_cart,
color: Colors.white,
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CheckoutPage()));
},
),
Text(cart.count.toString())
],
),
)
],
centerTitle: true,
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].title),
subtitle: Text(items[index].price.toString()),
trailing: Icon(Icons.add),
onTap: () {
cart.add(items[index]);
},
);
},
),
);
});
}
}
CheckoutPage.dart
class CheckoutPage extends StatefulWidget {
#override
_CheckoutPageState createState() => _CheckoutPageState();
}
class _CheckoutPageState extends State<CheckoutPage> {
#override
Widget build(BuildContext context) {
return Consumer<Cart>(
builder: (context, cart, child) {
return Scaffold(
appBar: AppBar(
title: Text('Checkout Page [\$ ${cart.totalPrice}]'),
actions: [
TextButton(
onPressed: () {
print(cart.totalPrice);
},
child: Text('Check'))
],
),
body: cart.basketItems.length == 0
? Text('no items in your cart')
: ListView.builder(
itemCount: cart.basketItems.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(cart.basketItems[index].title),
subtitle: Row(
children: [
TextButton(onPressed: () {}, child: Text('+')),
Text(cart.basketItems[index].qty.toString()),
TextButton(onPressed: () {}, child: Text('-')),
],
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
cart.remove(cart.basketItems[index]);
},
),
),
);
},
));
},
);
}
}
Item.dart
class Item {
String title;
double price;
Item({this.title, this.price});
}
Cart.dart
class Cart extends ChangeNotifier {
List<Item> _items = [];
double _totalPrice = 0.0;
void add(Item item) {
_items.add(item);
_totalPrice += item.price;
notifyListeners();
}
void remove(Item item) {
_totalPrice -= item.price;
_items.remove(item);
notifyListeners();
}
int get count {
return _items.length;
}
double get totalPrice {
return _totalPrice;
}
List<Item> get basketItems {
return _items;
}
}
Hmm try before adding item add a certain function that will look up for the duplicate item like this
e.g. inside on add
Add qty on you class on item.dart so that in every add item you should have default qty to one then goes this below.
class Item {
String title;
double price;
int qty;
Item({this.title, this.price,this.qty});
}
void add(Item item) {
final itemIsExist = _items.where((e)=> e.title == item.title);
if(itemIsExist.isNotEmpty){
// if item exist and you want to add +1 on qty
final addQty = _items.firstWhere((e)=> e.title == item.title);
addQty.qty= addQty.qty+1;
// do your thing here to calculate again the total
}else{
_items.add(item);
_totalPrice += item.price;
notifyListeners();
}
}
I suggest creating another variable on base class and extend it for model, But now let's follow your way.
We can create a map to iterate items on _CheckoutPageState and create a Set, but we need to count the item quantity,
We can take the help of map in this case and place it just under Consumer builder before returning Scaffold
Map<String, int> itemsMap = {};
for (final item in cart._items) {
if (!itemsMap.containsKey(item.title)) {
itemsMap.putIfAbsent(item.title, () => 1);
} else {
itemsMap.update(item.title, (value) => itemsMap[item.title]! + 1);
}
}
And uses will be like
itemBuilder: (context, index) {
final keys = itemsMap.keys.toList();
final count = itemsMap.values.toList();
return Card(
child: ListTile(
title: Text(keys[index].toString()),
subtitle: Row(
children: [
TextButton(onPressed: () {}, child: Text('+')),
Text(count[index].toString()),
TextButton(onPressed: () {}, child: Text('-')),
],
),
State class
class _CheckoutPageState extends State<CheckoutPage> {
#override
Widget build(BuildContext context) {
return Consumer<Cart>(
builder: (context, cart, child) {
Map<String, int> itemsMap = {};
for (final item in cart.basketItems) {
if (!itemsMap.containsKey(item.title)) {
itemsMap.putIfAbsent(item.title, () => 1);
} else {
itemsMap.update(item.title, (value) => itemsMap[item.title]! + 1);
}
}
return Scaffold(
appBar: AppBar(
title: Text('Checkout Page [\$ ${cart.totalPrice}]'),
actions: [
TextButton(
onPressed: () {
print(cart.totalPrice);
},
child: Text('Check'))
],
),
body: cart.basketItems.length == 0
? Text('no items in your cart')
: ListView.builder(
itemCount: itemsMap.length,
itemBuilder: (context, index) {
final keys = itemsMap.keys.toList();
final count = itemsMap.values.toList();
return Card(
child: ListTile(
title: Text(keys[index].toString()),
subtitle: Row(
children: [
TextButton(
onPressed: () {
cart.add(
Item(
title: keys[index].toString(),
price: keys[index].trim() == "laptop"
? 500
: keys[index].trim() == "iphone x"
? 400
: 40,
),
);
},
child: Text('+')),
Text(count[index].toString()),
TextButton(
onPressed: () {
cart.remove(Item(
title: keys[index].toString(),
price: keys[index].trim() == "laptop"
? 500
: keys[index].trim() == "iphone x"
? 400
: 40,
));
},
child: Text('-')),
],
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
cart.remove(cart.basketItems[
index]); // remove match all on remove method
},
),
),
);
},
));
},
);
}
}
I am trying to make a list view of songs using audioquery but the list is not showing I ran it with no-null safety, this is my tracks.dart file whats wrong with my code?
I am getting only the appbar in the output
import 'package:flutter/material.dart';
import 'package:flutter_audio_query/flutter_audio_query.dart';
import 'dart:io';
class Tracks extends StatefulWidget {
_TracksState createState() => _TracksState();
}
class _TracksState extends State<Tracks> {
final FlutterAudioQuery audioQuery = FlutterAudioQuery();
List<SongInfo> songs = [];
void initState() {
super.initState();
getTracks();
}
void getTracks() async {
songs = await audioQuery.getSongs();
setState(() {
songs = songs;
});
}
Widget build(context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
leading: Icon(Icons.music_note, color: Colors.black),
title: Text('Music App', style: TextStyle(color: Colors.black)),
),
body: ListView.separated(
separatorBuilder: (context, index) => Divider(),
itemCount: songs.length,
itemBuilder: (context, index) => ListTile(
leading: CircleAvatar(
backgroundImage: songs[index].albumArtwork == null
? AssetImage('assets/img.jpg') as ImageProvider
: FileImage(File(songs[index].albumArtwork)),
),
title: Text(songs[index].title),
subtitle: Text(songs[index].artist),
onTap: () {},
),
),
);
}
}
One of the solutions for your problem would be using using only the songs = await audioQuery.getSongs(); line in the initState() function.
But if you want to keep using your getTracks() function, I think you have to declare the songs variable (at the first line of the method) and then use the 'this' keyword in the setState() method (to specify the global songs List reference being initialized).
I want to change the color of a ListTile text on clicking on the tile how can I do that also the color should only be changed for a specific selected tile.
My approach is as following:
ListView.builder(
itemCount: _antigen.plantAntigens.length,
itemBuilder: (BuildContext cntxt, int index) {
return ListTile(
title: Text(
_antigen.plantAntigens[index],
style: TextStyle(
color: controller.isSelected ? Colors.red : Colors.black87),
),
onTap: () {
controller.toogle();
});
},
),
The code for controller is as following:
bool isSelected = false.obs;
toogle() {
isSelected = !isSelected;
}
Just create a list in your controller that stores the selected index
var plantAntigensSelected = [].obs;
toogle(int index) {
if (plantAntigensSelected.contains(index)) {
plantAntigensSelected.remove(index);
} else {
plantAntigensSelected.add(index);
}
}
And your ListView like this
ListView.builder(
itemCount: plantAntigens.length,
itemBuilder: (BuildContext cntxt, int index) {
return ListTile(
title: Obx(
() => Text(
plantAntigens[index],
style: TextStyle(
color:
controller.plantAntigensSelected.contains(index)
? Colors.red
: Colors.black87),
),
),
onTap: () {
controller.toogle(index);
});
},
)
The controller TileColorX will hold which tile has been selected (by using the index provided by ListView.builder).
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ListViewBoxConstraintsPage extends StatelessWidget {
final List<String> _items = ['first', 'second', 'third'];
#override
Widget build(BuildContext context) {
TileColorX tcx = Get.put(TileColorX());
return Scaffold(
appBar: AppBar(
title: Text('ListView Constraints'),
),
body: ListView.builder(itemCount: _items.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Obx(
() => Text('${_items[index]} item',
style: TextStyle(
color: tcx.selectedIndex.value == index ? Colors.green : Colors.red)
)),
onTap: () => tcx.toggle(index),
);
}),
);
}
}
class TileColorX extends GetxController {
RxInt selectedIndex = 0.obs;
void toggle(int index) => selectedIndex.value = index;
}
Please try to use setState in the onTap as shown below
onTap: () {
setState(() {
controller.toogle();
});
});
You have to add .value to the isSelected in your controller like this:
bool isSelected = false.obs;
toogle() {
isSelected.value = !isSelected;
}
I started to use providers but I have a problem. I want to get the index of items that are in an other list in an other screen. How can i get them ? I have two screens: a home screen and a favorite screen and I have a listView in each. I want to get the index of the item in the home screen when it is remove from the favorite screen. This is the link of my code on GitHub : https://github.com/Rianou20/my_app_from_scratch/tree/master/my_app_from_scratch. And some relevant parts of my code :
favModel.dart
class FavModel extends ChangeNotifier {
List<Item> favList = [];
List<bool> isInFav = [];
addInFavorite(title, description, index){
Item item = Item(title: title, description: description, );
favList.add(item);
isInFav[index] = true;
notifyListeners();
}
removeOfFavorite(int index, int index2){
favList.removeAt(index);
isInFav[index2] = false;
notifyListeners();
}
implement(){
isInFav.add(false);
}
}
favorite_screen.dart
class Favorite extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Favorite'),
),
body: Consumer<FavModel>(
builder: (context, favModel, child) {
return ListView.builder(
itemCount: favModel.favList.length,
itemBuilder: (context, index) {
return TextObject(favModel.favList[index].title,
favModel.favList[index].description),
Padding(
padding: const EdgeInsets.all(7.0),
child: GestureDetector(
child: Icon(
Icons.favorite,
color: Colors.red,
size: 32,
),
onTap: () {
favModel.removeOfFavorite(index, index);
}),
),
});
},
),
);
}
}
home_screen.dart
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
actions: [
IconButton(
icon: Icon(Icons.favorite_border),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) {
return Favorite();
},
),
),
),
],
),
body: Consumer<FavModel>(builder: (context, favModel, child) {
return ListView.builder(
shrinkWrap: false,
itemCount: itemData.length,
itemBuilder: (context, index) {
favModel.implement();
return TextObject(
itemData[index].title, itemData[index].description),
Padding(
padding: const EdgeInsets.all(7.0),
child: GestureDetector(
child: Icon(
favModel.isInFav.elementAt(index)
? Icons.favorite
: Icons.favorite_border,
color:
favModel.isInFav[index] ? Colors.red : null,
size: 32,
),
onTap: () {
favModel.isInFav[index]
? null
: Provider.of<FavModel>(context,
listen: false)
.addInFavorite(
itemData[index].title,
itemData[index].description,
index,
);
}),
);
});
}),
);
}
}
Where I want to get the index is in the favorite_screen.dart at this line favModel.removeOfFavorite(index, index);
Without knowing the exact use case, you can potentially store the removed values in a list and use them on your home screen.
class FavModel extends ChangeNotifier {
List<Item> favList = [];
List<bool> isInFav = [];
List<int> _removedItemIndexList = []
get removedItemIndexList => _removedItemIndexList;
addInFavorite(title, description, countdown, imageURL, index){
Item item = Item(title: title, description: description, countdown:countdown, imageURL: imageURL);
favList.add(item);
isInFav[index] = true;
notifyListeners();
}
removeOfFavorite(int index, int index2){
favList.removeAt(index);
isInFav[index2] = false;
_addToRemovedIndexList(index);
notifyListeners();
}
void _addToRemovedIndexList(int index) {
_removedItemIndexList.add(index);
}
implement(){
isInFav.add(false);
}
}
And then use on home_sreen.dart as
...
body: Consumer<FavModel>(builder: (context, favModel, child) {
List<int> removedIndexes = favModel.removedItemIndexList;
return ListView.builder( ... ) };
Note that the FavModel provider class must be lifted above then home_screen.dart on the widget tree in order to be able to access its values. i.e. you would want to do something like this in your main.dart
...
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: FavModel(),
),
],
child: MaterialApp(...