Flutter dynamically update date and time - list

I am new to flutter and this is my first app. I am trying to make a to do list app and want to display the time left for each task in the subtitle. I have a listview and in each element I want the have the subtitle display the minute, counting downwards towards 0. Can anyone help me with this ? Thanks!
Code : -
class toDoListState extends State<toDoList>
{
List<String> tasks = [];
List<String> completedTasks = [];
List<String> descriptions = [];
List<bool> importance = [];
List<String> time2completion = [];
List<DateTime> time = [];
Widget buildToDoList()
{
return new ListView.builder
(
itemBuilder: (context, index)
{
if(time2completion.length > 0 && index < time2completion.length && time2completion[index] != "none")
{
if(time2completion[index] == "30 minutes")
{
time[index] = DateTime.now().add(Duration(minutes: 30));
}
else if(time2completion[index] == "1 hour")
{
time[index] = DateTime.now().add(Duration(hours: 1));
}
else if(time2completion[index] == "12 hours")
{
time[index] = DateTime.now().add(Duration(hours: 12));
}
else if(time2completion[index] == "1 day")
{
time[index] = DateTime.now().add(Duration(days: 1));
}
}
if(index < tasks.length)
{
return row(tasks[index], descriptions[index], index);
}
},
);
}
Widget row(String task, String description, int index)
{
return Dismissible(
key: UniqueKey(),
background: Container(color: Colors.red, child: Align(alignment: Alignment.center, child: Text('DELETE', textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 18),))),
direction: DismissDirection.horizontal,
onDismissed: (direction) {
setState(() {
tasks.removeAt(index);
if(completedTasks.contains(task))
{
completedTasks.removeAt(index);
}
descriptions.removeAt(index);
importance.removeAt(index);
});
Scaffold.of(context).showSnackBar(SnackBar(content: Text(task+" dismissed")));
},
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text(task, style: (completedTasks.contains(task)) ? TextStyle(decoration: TextDecoration.lineThrough) : TextStyle(),),
subtitle: Text((time[index].difference(DateTime.now()).toString())),
value: completedTasks.contains(task),
onChanged: (bool value) {
setState(() {
if(!completedTasks.contains(task))
{
completedTasks.add(task);
}
else
{
completedTasks.remove(task);
}
});
},
));
}
}

You can use a timer to calculate the time differences every minute.
Timer.periodic(
Duration(minutes: 1),
(Timer t) => setState(() {
// your calculation here
}),
);
The following will create a timer object in your stateful widget and dispose of it when you navigate away from the view:
Timer _timer;
#override
void initState() {
_timer = Timer.periodic(
Duration(minutes: 1),
(Timer t) => setState(() {
// your calculation here
}),
);
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}

You can achieve this by following:
Suppose you already have saved date in your todo and have already a date available in
form of DateTime object here in my example I am assuming savedDateTime which can be achieved either by assigning it using .hour , .sec or parsing it from string.
Now what you do is to find what time is left that is differnce
//already assumed saved date as a DateTime object that is savedDateTime
// it contains our saved date of note
//now
final currentDateTime = DateTime.now();
final difference = currentDateTime.difference(savedDateTime);
difference in Seconds,Hours,Minutes,Days is given by
print(difference.inSeconds);
print(difference.inHours);
print(difference.inMinutes);
print(difference.inDays);

Related

How to display tasks that are not "checked" on the other screen?

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

Remove User Defined Item from List

Hi I wanted to remove the Data from my List during onTap but I am unable to do so.
This is the code:
Widget buildUser(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index){
DocumentSnapshot user = snapshot.data.documents[index];
final style = _selectedBusStop.contains(ListClass(user.data['BusstopName'], user.data['location'].latitude.toString(), user.data['location'].longitude.toString()))
? TextStyle(
fontSize: 18,
color: Colors.blueAccent,
fontWeight: FontWeight.bold,
): TextStyle(fontSize: 18);
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
user.data['image']
),
),
title: Text(user.data['BusstopName'], style: style),
subtitle: Text('Operating Hour: ' + user.data['OperatingHour'], style: style),
trailing:
_selectedBusStop.contains((ListClass(user.data['BusstopName'], user.data['location'].latitude.toString(), user.data['location'].longitude.toString()))) ? Icon(Icons.check, color: Colors.blueAccent, size: 26) : null,
onTap: (){
if(_selectedBusStop.contains(ListClass(user.data['BusstopName'], user.data['location'].latitude.toString(), user.data['location'].longitude.toString()))){
setState(() {
_selectedBusStop.removeWhere((val) => val == ListClass(user.data['BusstopName'], user.data['location'].latitude.toString(), user.data['location'].longitude.toString()));
print (_selectedBusStop);
});
}
},
onLongPress: (){
setState(() {
_selectedBusStop.add(ListClass(user.data['BusstopName'], user.data['location'].latitude.toString(), user.data['location'].longitude.toString()));
print(_selectedBusStop);
});
}
);
},
);
}
This is the Class:
class ListClass{
String Bname;
String Blat;
String Blng;
ListClass(this.Bname, this.Blat, this.Blng);
#override
String toString(){
return '{${this.Bname}, ${this.Blat}, ${this.Blng}}';
}
}
Any idea where went wrong? Thank you in advance.
Update
List _selectedBusStop = [];
_selectedBusStop is empty List and upon LongPress it will add data into the List and upon onPress it will remove the data if the data already exist in the List.
#randomstudent the issue is the instance, when you comaparing two values with different instance but even value is same, it returns false,
for example to understand simply.
void main() {
final a = IntTestWrapper(3);
final b = IntTestWrapper(3);
print(a==b);
}
class IntTestWrapper {
IntTestWrapper(this.a);
final int a;
}
Output: false
If you want to compare, compare using equatable
if you change like this
class IntTestWrapper extends Equatable {
IntTestWrapper(this.a);
final int a;
#override
List<Object> get props => [a];
}
then for this
void main() {
final a = IntTestWrapper(3);
final b = IntTestWrapper(3);
print(a==b);
}
Output will be true.
To print you can override toString
void main() {
final a = IntTestWrapper(3);
final b = IntTestWrapper(3);
print(a);
}
class IntTestWrapper extends Equatable {
IntTestWrapper(this.a);
final int a;
#override
List<Object> get props => [a];
#override
String toString() => 'The value of a is :$a';
}
Output: The value of a is :3

How to perform lazy loading or infinity loading in flutter?

I am trying to get lazy loading output with flutter. I could do lazy loading only with a generated array given by flutter as an example. But I couldn't get the same output when integrating with Rest API. How to perform lazy loading with an API in a flutter?
I call an api with pagination ,
here is my code :
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class TestLazyLoading extends StatefulWidget {
#override
_TestLazyLoadingState createState() => new _TestLazyLoadingState();
}
class _TestLazyLoadingState extends State<TestLazyLoading> {
static const String _url = 'https://api.coinranking.com/v1/public/coins';
ScrollController controller;
int _totalCount = 0;
int _limit = 20;
int _offset = 0;
List<String> items = [];
bool _isLoading = true;
#override
void initState() {
super.initState();
controller = new ScrollController()..addListener(_scrollListener);
_getData(limit: _limit, offset: _offset);
}
#override
void dispose() {
controller.removeListener(_scrollListener);
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Scrollbar(
child: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
if (items.length-1 == index && _isLoading ) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Container(
child: Text(items[index]),
height: 38,
alignment: Alignment.centerLeft,
margin: EdgeInsets.all(5),
padding: EdgeInsets.all(10),
color: Colors.grey[200],
);
}
},
itemCount: items.length,
),
),
),
);
}
void _scrollListener() {
if (controller.position.extentAfter < 50) {
if (!_isLoading && _totalCount > items.length) {
_offset += _limit;
_getData(limit: _limit, offset: _offset);
}
}
}
void _getData({#required int limit, #required int offset}) async {
setState(() {
_isLoading = true;
});
http.Response response =
await http.get('$_url?limit=$limit&offset=$offset');
if (response.statusCode == 200) {
var jsonResponse = jsonDecode(response.body);
_totalCount = jsonResponse['data']['stats']['total'];
List<dynamic> coinList = jsonResponse['data']['coins'];
for (dynamic coin in coinList) {
items.add(coin['symbol']);
}
setState(() {
_isLoading = false;
});
}
}
}
also add dependncy for http in you pubspec.yaml :
dev_dependencies:
flutter_test:
sdk: flutter
http: ^0.12.1
finally call TestLazyLoading widget in the main for testing

How to send a List object through a callback function in Flutter?

What I've already done
I have a stateful widget that generates a ListView on screen.
class AppListView extends StatefulWidget {
final ValueChanged onChange;
final List<MatchList> matchList;
final ValueChanged finalBetList;
AppListView({this.onChange, this.matchList, this.finalBetList});
#override
_AppListViewState createState() => _AppListViewState();
}
class _AppListViewState extends State<AppListView> {
int counter = 0;
List<MatchList> betList = List<MatchList>();
I have a Home Screen that calls this Stateful Widget. In here I am using a callback function (onChange) to get the counter value from the widget. Everything is working perfect.
Stack(children: [
AppListView(
matchList: matchList,
//callback function brings the counter value from ListView class
onChange: (value) {
setState(() {
counter = value;
});
},
),
Positioned(
child: Padding(
padding: const EdgeInsets.only(top: 280.0, left: 330.0),
child: Container(
width: 60,
height: 60,
child: FloatingActionButton(
onPressed: () {
counter > 0
? Navigator.pushNamed(context, BetScreen.id)
: print('you shall not pass');
},
child: Text(
counter.toString(),
style: kTextStyleAppBarTitle,
),
),
),
),
)
])
What is the problem
But when I am trying to call that widget from another screen with a similar callback function(finalBetList), I got "The method 'call' was called on null. Receiver: null" error. Actually everything that I do is the same as the other example that works fine. I can't find what I'm missing. Is it something about Lists?
class _BetScreenState extends State<BetScreen> {
List<MatchList> betList = List<MatchList>();
int counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
currentBalance: '\u0024' + '200.00',
userLevel: 'Level 30',
userName: 'Mark',
),
body: Container(
child: AppListView(
finalBetList: (value) {
setState(() {
//counter = value;
betList = value;
//print(betList);
});
},
matchList: betList,
),
),
);
}
}
After Edit
I added the cod of AppListView widget
class AppListView extends StatefulWidget {
final ValueChanged onChange;
final List<MatchList> matchList;
final ValueChanged finalBetList;
AppListView({this.onChange, this.matchList, this.finalBetList});
#override
_AppListViewState createState() => _AppListViewState();
}
class _AppListViewState extends State<AppListView> {
int counter = 0;
List<int> betList = List<int>();
#override
Widget build(BuildContext context) {
children: <Widget>[
AppButton.buildAppButton(
context,
AppButtonType.BETSELECTION,
widget.matchList[index].homeOdds,
kCategoryButtonDimensions,
color: widget.matchList[index].homeSelected
? Colors.yellow
: Colors.white,
onPressed: () {
if (widget.matchList[index].drawSelected ||
widget.matchList[index].awaySelected) {
widget.matchList[index].drawSelected =
false;
widget.matchList[index].awaySelected =
false;
counter--;
//betList part
if (betList.length > 0)
betList
.remove(widget.matchList[index].id);
}
widget.matchList[index].homeSelected =
!widget.matchList[index].homeSelected;
if (widget.matchList[index].homeSelected) {
counter++;
betList.add(widget.matchList[index].id);
} else {
counter--;
if (betList.length > 0)
betList.remove(widget.matchList[index]
.id); //if selected, add to betList to send BetScreen
}
widget.onChange(counter);
print(betList);
widget.finalBetList(betList);
setState(() {});
},
),

Flutter: `notifyListeners` not updating the list

I am trying to list categories of products in my app, and i am using provider package for state management. During the build time its shows the list is empty even though it is not, then I add a click event to update the list that time it works.
I call the getAllCategories() function during the splash screen and the vallCategoryList has values.
This is my category class
class Category with ChangeNotifier{
int catId;
String catName;
String catThumbnail;
List<SetCategory> allCategoryList = [];
void getAllCategories() async {
String categoryUrl = 'https://app.ecwid.com/api/ccccccc';
Response allCategory = await get(categoryUrl);
print('getAllCategories');
if (allCategory.statusCode == 200) {
var categoryData = allCategory.body;
int totalcount = jsonDecode(categoryData)['count'];
if (allCategoryList.length != totalcount) {
allCategoryList.clear();
for (int i = 0; i < totalcount; i++) {
allCategoryList.add(SetCategory(
catId: jsonDecode(categoryData)['items'][i]['id'],
catName: jsonDecode(categoryData)['items'][i]['name'],
catThumbnail: jsonDecode(categoryData)['items'][i]['thumbnailUrl'],
));
}
}
}
print('allcategorylist length ${allCategoryList.length}');
notifyListeners();
}
}
class SetCategory {
int catId;
String catName;
String catThumbnail;
SetCategory(
{ this.catId, this.catName, this.catThumbnail});
}
My code for screen
class HomeScreen extends StatefulWidget {
static const String id = 'homeScreen';
// static int reload = 0;
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
final category = Provider.of<Category>(context);
print('category length ${category.allCategoryList.length}'); // here it shows length as 0 even though it has a value of 16.
return Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Category ${category.allCategoryList.length}',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
textAlign: TextAlign.start,
),
),
InkWell(
onTap: () {
category.getAllCategories(); // when i tap here this updates the list
}),
],
),
Did you use ChangeNotifierProvider in your Widget as shown here
If you just used Provider it is not updating but just makes the object accessible from the descendants
Solved by adding a Consumer, changed code like this
child: Consumer<Category>(
builder: (_,category,__){
return ListView.builder();
}