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.
Related
I'm trying to create a List of values like
{
"name1" : "one"
},
where I get the "one" mentioned above from a textfield. Suppose I input three of such values in three separate textFields.
Now I have to create a List called "names" as follows when I press a submit button
names: [
{
"name1" : "one"
},
{
"name2" : "two"
},
{
"name3" : "three"
},
]
here "one", "two", "three" are the textField values and I need to generate the rest when there is a button click event
(I have to create many of these for a http post I'll have to do later, So please feel free to let me know a better method if exists or please correct me if the procedure I'm heading with is wrong)
You need to add all TextField value in list look like below on button tap event
var nameOne = TextEditingController();
var nameTwo = TextEditingController();
var nameThree = TextEditingController();
List<Map<String,String>> names = [];
void _submite() {
if (nameOne.text.isEmpty) {
print('Enter nameOne name');
} else if (nameTwo.text.isEmpty) {
print('Enter nameTwo name');
} else if (nameThree.text.isEmpty) {
print('Enter nameThree name');
} else {
names.add({"name1": nameOne.text});
names.add({"name2": nameTwo.text});
names.add({"name3": nameThree.text});
}
}
After call _submite() method
//OUTPUT : [{"name1" : "one"},{"name2" : "two"},{"name3" : "three"},]
you can do like this:
List names = [
{"name1": "one"},
{"name2": "two"},
{"name3": "three"},
];
#override
Widget build(BuildContext context) {
return Container(
height: 300,
width: 300,
child: ListView.builder(
itemCount: names.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
//do what you want here for each item
print(names[index].keys.first.toString());
},
child: Text(names[index].keys.first.toString()));
},
),
);
}
here you generate a ListView of Texts that are wrapped with GestureDetector,
so you generate a list of Texts according to your list, and when you click on one of the texts you can do what ever you want according to the item you clicked on.
so for example, the output for the previous code would look like this:
and when you click on one of the texts, you can do what you whatever you want according to the clicked item (here I just print the item to the consol).
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.
I am looking at my code and wondering for 2 hours now without luck so I will ask for help here.
I have a button, when I press it, it displays a random item from the list view. The problem is I also have a check box on the list view with each item. I do not want it to (Shuffle through the items with the checkbox ticked) only to shuffle through the Task in the list view that are unchecked/unticked/are not done.
Here is my code
class TaskData extends ChangeNotifier {
List<Task> _tasks = [
Task(name: "item1"),
Task(name: "item2"),
Task(name: "item3"),
];
UnmodifiableListView<Task> get tasks {
return UnmodifiableListView(_tasks);
}
int get taskCount {
return _tasks.length;
}
// <<Here is the code that shuffles through list
Future<String> rann() async {
return (_tasks.toList()..shuffle()).first.name;
}
void addTask(String newTaskTitle) {
final task = Task(name: newTaskTitle);
_tasks.add(task);
notifyListeners();
}
void updateTask(Task task) {
task.toggleDone();
notifyListeners();
}
In another script I have this one
class Task {
final String name;
bool isDone;
Task({required this.name, this.isDone = false});
void toggleDone() {
isDone = !isDone;
}
}
In another script file I have this code
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 0),
child: FutureBuilder(
future: Provider.of<TaskData>(context).rann(),
builder: (context, snapshot) {
return Align(
alignment: Alignment.center,
child: Text(
"${snapshot.data}",
//softWrap: true,
textAlign: TextAlign.center,
//textWidthBasis: TextWidthBasis.longestLine,
style: TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w700),
),
);
},
),
),
In another script I have this one
class TasksList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<TaskData>(
builder: (context, taskData, child) {
return ListView.builder(
itemBuilder: (context, index) {
final task = taskData.tasks[index];
return TaskTile(
taskTitle: task.name,
isChecked: task.isDone,
checkboxCallback: (checkboxState) {
taskData.updateTask(task);
},
);
},
itemCount: taskData.taskCount,
);
},
);
}
}
Any help would be appreciated!
Edit : I also forgot to include this part of code
class TaskTile extends StatelessWidget {
final bool isChecked;
final String taskTitle;
final Function(bool?) checkboxCallback;
final VoidCallback longPressCallback;
TaskTile(
{required this.isChecked,
required this.taskTitle,
required this.checkboxCallback,
required this.longPressCallback});
#override
Widget build(BuildContext context) {
return ListTile(
onLongPress: longPressCallback,
title: Text(
taskTitle,
// at the bottom, it sets decoration of text if isChecked is true, if its not its null
style: TextStyle(
decoration: isChecked ? TextDecoration.lineThrough : null),
),
trailing: Checkbox(
activeColor: Colors.blue,
value: isChecked,
onChanged: checkboxCallback,
),
);
}
}
updated:
class TaskData extends ChangeNotifier {
List<Task> _undoneTasksShuffled = []
// you don't need anymore the rann method() instead you should iterate over this listView
UnmodifiableListView<Task> get undoneTasksShuffled => UnmodifiableListView<Task>(_undoneTasksShuffled);
#override
void notifyListeners() {
//this updates _undoneTasksShuffled every time you call notifyListeners
_undoneTasksShuffled = _tasks.where((e)=> !e.isDone).toList()..shuffle();
super.notifyListeners();
}
...
}
I think you only need to filter the results before get a random element. you need to modify your rann method for something like
//you don't really need a future method because you don't have async code
String rann() {
final r = Random();
final undoneTasks = _tasks.where((e)=> !e.isDone).toList();
//this is for avoid RangeException on list. you can return any other thing
if(undoneTasks.isEmpty) return '';
// i think that you don't really need to shuffle whole list, you only need a random element
return undoneTasks[r.nextInt(undoneTasks.length)].name;
}
i hope this solves your question
On this example im trying to add to the CheckboxListTile the values i have saved on my List.
But for somereason im stuck here and i cant figure it out how to solve this problem.
Global List
List<String> _lstNomeData = new List<String>();
Values Map
Map<String, bool> values = {
'$_lstNomeData[index]': false,
};
Get selected checkbox values
var tmpArray = [];
getCheckboxItems() {
values.forEach((key, value) {
if (value == true) {
tmpArray.add(key);
}
});
print(tmpArray);
tmpArray.clear();
}
Body
body: Column(
children: <Widget>[
Expanded(
child: ListView(
children: values.keys.map((String key) {
return new CheckboxListTile(
title: new Text(key),
value: values[key],
activeColor: Colors.blue,
checkColor: Colors.white,
onChanged: (bool value) {
setState(() {
values[key] = value;
});
},
);
}).toList(),
),
),
],
)
Error
Print of the error displayed
You are on the right track. You can do two things here:
Prefill the map with false for every key (what you are trying to do)
Assume that if the map does not have a key, the answer is false (default)
Second approach is probably even better because by prefilling the map with false you could not distinct between false being actually answered by the user or if it was set by default. If a key is not in the map you know that the user has not answered the question so far. I will go and show how to work with the second approach:
Keep your global list as it is:
List<String> _lstNomeData = [];
Initialise the map (which represents the answers from the user for each question) with an empty Map:
Map<String, bool> answers = {};
Now correctly reference those answers in your Checkbox widget and make use of the onChanged property:
ListView(
children: _lstNomeData.map((key) =>
CheckboxListTile(
title: Text(key),
// Here we check if the answers map has this key and if it does, use the bool value, otherwise set it to false (user has not answered this so far)
value: answers[key] ?? false,
activeColor: Colors.blue,
checkColor: Colors.white,
onChanged: (answer) =>
setState(() => answers[key] = answer)
),
).toList(),
),
I have kind of a form where I can add cards, each having 5 textfields and 2 switches. I would like to use a method to build the switch code (and the textfield code, but that is working). However, the switches refuse to show their intended state. I saw couple of similar questions. However, most were solved with a list view listing all switched/checkboxes next to one another (I have multiple cards with multiple textfields and multiple switches, each). This was close, but I don't really understand the answer (within the comments)
Actually some answers come up with the same (I guess more or less same because mine isn't working) code storing the switch state in a bool list. When debugging I can see that the values are correctly stored in the list. However, the changed value is not rendered upon state change.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
var descrTECs = <TextEditingController>[];
var fixedSCs = [true]; //storing the switch values
var cards = <Card>[]; // storing the list of cards with forms
SizedBox createTextField(String placeholderStr, double fieldWidth) {
var tFieldController = TextEditingController();
switch (placeholderStr) { //switching placeholder to assign text controller to correct controller list
case "Description":
descrTECs.add(tFieldController);
break;
}
return SizedBox(width: fieldWidth, height: 25,
child: CupertinoTextField(
placeholder: placeholderStr,
controller: tFieldController,
),
);
}
SizedBox createSwitch(int pos) {
return SizedBox(width: 50, height: 25,
child: CupertinoSwitch(
value: fixedSCs[pos],
onChanged: (value) {
setState(() => fixedSCs[pos] = value); // value is stored in fixedSCs but not rendered upon rebuild
},
)
);
}
Card createCard() {
return Card(
child: Row(children: <Widget>[
Text('#p${cards.length + 1}:'),
Column(
children: <Widget>[
createSwitch(cards.length),
createTextField("Description", 70.0),
],),
],),
);
}
#override
void initState() {
super.initState();
cards.add(createCard()); // first card created upon start
}
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder( // List Builder to show all cards
itemCount: cards.length,
itemBuilder: (BuildContext context, int index) {
return cards[index];
},
),
),
RaisedButton(
child: Text('add new'),
onPressed: () => setState(() {
fixedSCs.add(true); // bool element created to create next card
cards.add(createCard());} // create next card
),
),
],
),
),);
}
}
One thing I do not understand in general: Upon rebuild after a state change cards.length} should be my number of cards, let's say 3. And when it renders the 1st card, it passes the line Text("#p${cards.length + 1}"), so it should show #p3 and not #p1. What do I get wrong here?
I meanwhile got this working with quite some logic changes.
I put the switch builder into a stateless widget
class createSwitch extends StatelessWidget {
const createSwitch({
this.label, this.margin=const EdgeInsets.all(0.0), this.width, this.height, this.value, this.onChanged});
final String label; final EdgeInsets margin; final double width; final double height; final bool value;
final Function onChanged;
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
Expanded(child: Text(label)),
CupertinoSwitch(
value: value,
onChanged: (bool newValue) {onChanged(newValue);},
),
],
),
),
} }
In the parent stateful controller I created a list to store the switches' state var parameterSCs = [true]; and each time I add a card I add a value whith clicking the button onPressed: () => setState(() {parameterSCs.add(true);}
I no longer store the cards widgets as a list. Instead, I build them directly in the code within a ListView.builder
ListView.builder(
itemCount: parameterSCs.length,
itemBuilder: (BuildContext context, int index) {
return Card( ...
In my real code I have 2 switches per card, so I always add 2 elements and the ListView count is then half of the parameterSCs' length.
I tried loads of approaches, this was the only one working