I have a To Do List with things to do daily like 'Drink Water' and so on. So now I want that when the user has checked a To Do, the next day it should be unchecked again. How can I implement this?
Right now my Code looks like this:
class ToDoScreen extends StatefulWidget {
const ToDoScreen({Key key}) : super(key: key);
#override
_ToDoScreenState createState() => _ToDoScreenState();
}
class _ToDoScreenState extends State<ToDoScreen> {
List toDos = ['Train Today', 'Drink Water'];
bool value = false;
void addToDo(String newToDo) {
setState(() {
toDos.add(newToDo);
});
Navigator.of(context).pop();
}
void newEntry() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: TextField(
onSubmitted: addToDo,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
icon: Icon(Icons.text_snippet_outlined),
labelText: 'New To Do'),
),
);
});
}
As Yeasin Sheikh said in the comment, first of all you need to save your data somewhere, otherwhise you don't have nothing to reset.
I read in the comments that you'd like to use Firebase.
So I suppose you'll be storing data in Firebase Firestore.
The best way to perform such reset, in my opinion, is to use a Firebase Function (docs).
You could see them as server side code. From there you can define a function to access your DB and reset the data.
To trigger the function once a day, you should use an external service to call the function.
I found this question which could help you: here
while using firebase you can use firebase function as Gangemi said.
btw my approach will happen on AppSite, you need to check the reset function inside the main class every time it starts or before showing data.
if(DateTime.now().day != savedDate.day) reset();
also you can add expired DateTime and use conditional state.
DateTime.now().add(Duration(days:1))
and use
if(DateTime.now().day == savedDate.day) reset().
reset() means you are iterating list. on firebase/storage.
here
reset() means delete data from storage
Related
I have a stateful widget where there is a list like this,
import 'package:flutter/material.dart';
import 'package:todoapp/widgets/tasks_tile.dart';
import 'package:todoapp/models/Tasks.dart';
class TasksList extends StatefulWidget {
#override
_TasksListState createState() => _TasksListState();
}
class _TasksListState extends State<TasksList> {
List<Task> task = [
Task(
taskTitle: "Buy Bread",
),
Task(
taskTitle: "Buy Cream",
),
Task(
taskTitle: "Buy Beer",
)
];
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: task.length,
itemBuilder: (context,index){
return TaskTile(isChecked: task[index].isDone,
taskText: task[index].taskTitle,
callBackCheckBox: (bool checkBoxState){
setState(() {
task[index].toogleDone();
});
});
});
}
}
How do I access this list in stateless widget in another dart file? I am new to flutter. And please explain the concept behind it.
Aman Chaudhary to access task list it's done by passing all needed values in Stateless widget contructor in another dart file.
Here you can see that your ListView.builder is returning the stateless widget with all required parameters:
return ListView.builder(
itemCount: task.length,
itemBuilder: (context,index){
return TaskTile(isChecked: task[index].isDone,
taskText: task[index].taskTitle,
callBackCheckBox: (bool checkBoxState){
setState(() {
task[index].toogleDone();
});
});
});
This means whenever the callBackCheckBox is handled the stateful widget is refreshed and the ListView.builder updates his children with all task list updates.
My flutter ListView doesn't update when my setState runs in my State class.
Yes, My main class is a stateful widget, incase anyone was wondering
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _MyAppState();
}
My setState function
setState(() {
if (price == "null") {
items.add("Item ${counter + 1}: error");
print("null");
} else {
items.add("Item ${counter + 1}: $price");
print("$price");
totalPrice += price;
}
counter++;
});
});
Before I placed my ListView within a Container -> Column -> Expanded it was working fine. But after I added it, it stopped updating when my setState ran
body: new Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Dismissible(
key: Key(item),
onDismissed: (direction) {
setState(() {
items.removeAt(index);
counter--;
});
Scaffold.of(context).showSnackBar(
SnackBar(content: Text("$item deleted")));
},
background: Container(color: Colors.red),
child: ListTile(title: Text('$item')),
);
},
),
),
Could someone who is more knowledgeable in flutter educate me on what's going on here. I don't think adding the ListView into a container should make that much of a difference to how it works right?
Doing computations in setstate somehow caused this problem. Instead I did the computations in build since and I used setstate to only add to the list. It’ll then trigger the build and the rest of the things happen there. This solved my problem
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';
}
I'm new to Flutter and I'm having an issue understanding how states work.
I've setup a page in my app as a StatefulWidget and then inside the sate for that page one of the main components is a separate StatefulWidget.
The inner widget shows a question and allows the user to enter an answer.
What I then wanted to do was show the next answer on the same page.
I have my inner widget callback to my page state once the answer has been entered.
I attempted to replace my inner widget with another copy, however the state is not reset. That is a new state is not created and initState() is never called again.
Is there a specific call or method I should be using to achieve this?
Edit: so for example if I had the following
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState(){
changeContent();
}
Widget innerWidget;
void changeContent(){
setState((){
innerWidget = new CustomStatefullWidget();
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
innerWidget
],
),
),
);
}
}
Here CustomStatefullWidget is a class I've created as a stand alone interactive part of the page. The actual content shouldn't matter for this example.
What I had expected was that whenever changeContent() was called then the innerWidget would have a new state but that isn't the case. The state for that is already stored in the tree and isn't refreshed when a new instance of the widget is created.
How can I force flutter to clear that part of the tree or recognize I want the state reset?
That is the expected behavior. When a parent rebuild flutter compare the new widget tree to the old one.
This way, flutter is able to know which widgets got "updated" and which got added/removed.
For added widgets, it'll create a new state. initState is called. For updated widgets ; flutter will reuse the old widget and call didUpdateWidget by passing as parameter the old "widget" part as parameter. As for removed widget, it'll call dispose.
In your case, you expected that initState would be called once again. But in fact it got "updated".
From this point you have two solutions to reset the state :
Implement a didUpdateWidget. Which would reset your state or do whatever else you want.
This is the optimal approach, which you should consider. As you don't necessarily want to reset the children of your "new statefulwidget".
There's also the "lazy" solution, which is to tell flutter "Hey, this is a different widget. don't reuse the old one". That is achieved using Key.
More info in key usage here
That would be as simple as changing your changeContent method to the following:
Widget innerWidget;
void changeContent(){
setState((){
innerWidget = new CustomStatefullWidget(key: new UniqueKey());
}
}