I have a list named activityList mapped to ActivityDataModel class and I have been using Provider flutter package to receive all values from firestore. I have also have been using a DatePicker package to select dates.
So, when I press on a date and the date is changed, I want to show the total activity data for the day.
In onDateChange of the DatePicker, I use forEach loop to cycle through the list items and extract each value and then compute the total value. ie. each list item has "coins" variable and then use "totalCoins" variable to store total number of coins, after cycling through each "coins" variable in the list item.
But the totalCoins or any total...variable does not seem to be updating for some reason. I used setState to update the values in the UI but that does not seem to work.
ActivityDataModel class
class ActivityDataModel {
int score, coins, time, calories;
String dateTime;
ActivityDataModel(
{required this.score,
required this.coins,
required this.calories,
required this.time,
required this.dateTime});
}
MainActivity.dart
class ActivityHome extends StatefulWidget {
const ActivityHome({Key? key}) : super(key: key);
static const routeName = "/activityhome";
#override
State<ActivityHome> createState() => _ActivityHomeState();
}
class _ActivityHomeState extends State<ActivityHome> {
DateTime selectedDate = DateTime.now();
int coins = 0, score = 0, calories = 0;
double time = 0.0;
int totalCoins = 0, totalScore = 0, totalCalories = 0;
double totalTime = 0.0;
#override
Widget build(BuildContext context) {
final user = Provider.of<UserModel?>(context, listen: true);
List<ActivityDataModel?>? activityList =
Provider.of<List<ActivityDataModel?>?>(context, listen: true);
// print(activityList?.elementAt(2)?.coins.toString());
return Scaffold(
appBar: AppBar(
iconTheme: const IconThemeData(
color: Colors.black, //change your color here
),
backgroundColor: Colors.white,
elevation: 0.0,
centerTitle: true,
title: Text(
"Activity",
style: TextStyle(
fontSize: 25.sp,
color: Colors.black,
fontWeight: FontWeight.bold),
),
),
body: Padding(
padding: EdgeInsets.all(20.0.r),
child: Column(
children: [
DatePicker(
DateTime(2022, 9, 9, 0, 0,
0), //DateTime.now(), // replace with Joining date
height: 100,
width: 80,
initialSelectedDate:
DateTime(2022, 9, 9, 0, 0, 0), //DateTime.now(),
selectionColor: Colors.blueAccent,
daysCount: 10,
onDateChange: (date) {
selectedDate = date;
DateFormat dateFormat = DateFormat("MM-dd-yyyy HH:mm:ss");
activityList?.forEach((activityData) {
DateTime activityDate =
dateFormat.parse(activityData?.dateTime ?? "0");
if (activityDate.day == selectedDate.day) {
print("true");
print(activityDate.day);
print(selectedDate.day);
coins = activityData!.coins;
calories = activityData.calories;
score = activityData.score;
time = (activityData.time / 60);
setState(() {
totalCoins += coins;
totalCalories += calories;
totalScore += score;
totalTime += time;
});
} else {
setState(() {
totalCoins = 0;
totalScore = 0;
totalCalories = 0;
totalTime = 0;
});
}
});
setState(() {});
print(totalCoins);
print(totalCalories);
print(totalScore);
print(totalTime);
},
),
VerticalSpace(10.h),
const Divider(
height: 1,
thickness: 0.3,
color: Colors.black,
),
VerticalSpace(10.h),
ActivityCardWidget(
coins: totalCoins.toString(),
time: totalTime.toStringAsFixed(1),
calories: totalCalories.toString(),
score: totalScore.toString(),
),
],
),
),
);
}
}
The correct o/p only shows for one date and the rest shows zero.
Related
I have a listview.builder with data that you can click on and they will be added to the List, after this List I want to display on another screen (I do it through the provider). When clicking on the selected data for a long time, they should be deleted from the List, but I keep getting an error RangeError(index): Invalid value: Not in inclusive range 0..2: 5
my main screen
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: searchMethodProvider.searchResult.length,
itemBuilder: (context, index) {
return Material(
color: Colors.transparent,
child: InkWell(
onLongPress: (){
setState(() {
searchMethodProvider.searchResult[index]['bool'] = true;
var modelApp = searchMethodProvider.marksList[index]['model'];
var indexApp = searchMethodProvider.marksList[index]['index'];
searchMethodProvider.addMark(indexApp, modelApp);
});
},
onTap: (){
setState(() {
searchMethodProvider.deleteDataIndex(index);
print(searchMethodProvider.dataMark);// searchMethodProvider.deleteDataIndex(index, context);
searchMethodProvider.searchResult[index]['bool'] = false;
});
},
child: Container(
height: 53,
width: double.infinity,
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 0.8,
color: Color.fromRGBO(237, 237, 237, 1)
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(
left: 14.22
),
child: Text(
searchMethodProvider.searchResult[index]['mark'],
style: TextStyle(
fontFamily: ConstantsFonts.sfProRegular,
fontSize: 16,
color: Color.fromRGBO(0, 0, 0, 1)
),
),
),
),
searchMethodProvider.searchResult[index]['bool'] == true ?
Icon(
Icons.keyboard_arrow_down,
size: 18,
color: Color.fromRGBO(87, 184, 238, 1),
) : Container()
],
),
),
),
);
}
)
this is my provider and him functions
class SearchMethodInMarkFilterProvider extends ChangeNotifier {
List<Map<String, dynamic>> marksList = [
{
'mark': 'Aston Martin',
'bool': false,
'index': 1,
'model': ['x5', '23']
},
{
'mark': 'Audi',
'bool': false,
'index': 2,
'model': ['x5', '23']
},
{
'mark': 'BMW',
'bool': false,
'index': 3,
'model': ['x5', '23']
},
];
List dataMark = [];
Map addData = {};
List<Map<String, dynamic>> searchResult = [];
void addMark(int indexApp, List markData){
addData = {
'indexApp': indexApp,
'markData': markData,
};
dataMark.add(addData);
print(dataMark);
}
void deleteDataIndex(int index){
dataMark.removeAt(index);
notifyListeners();
}
SearchMethodInMarkFilterProvider(){
searchResult = marksList;
}
void runFilter(String enteredKeyword) {
List<Map<String, dynamic>> results = [];
if (enteredKeyword.isEmpty) {
results = marksList;
} else {
results = marksList
.where((user) =>
user['mark'].toLowerCase().contains(enteredKeyword.toLowerCase()))
.toList();
}
searchResult = results;
notifyListeners();
}
}
maybe I'm creating the data model incorrectly, that's why I can't delete it properly, I'll be glad of any help
This happened because your listview build on the searchResult list, but you try use its index on marksList. You have two options, either make list with marksList or change your delete function and do that on searchResult.
Sub AddStartOfMonthDates()
'set the starting date
Dim startDate As Date
startDate = DateValue("01/01/2021")
'set the ending date
Dim endDate As Date
endDate = DateValue("12/31/2021")
'set the starting row for the dates
Dim rowNum As Integer
rowNum = 1
'loop through the dates
Do While startDate <= endDate
'add the date to the column
Cells(rowNum, 1).Value = startDate
'increment the row number
rowNum = rowNum + 1
'move to the next month
startDate = DateAdd("m", 1, startDate)
Loop
End Sub
You can also adjust the starting and ending dates, as well as the starting row number, to suit your needs.
I think it will help you.
I'm facing a problem implementing drag and drop in my project. I want dynamically add and delete draggable elements. The problem is that I can't understand how to get the reference to the widget I'm currently moving so that I can understand which coordinates in the list I have to change.
Here is an example of the code where I use the static number of draggable widgets. They are assigned with coordinates from the list. But what if I have to dynamically add and delete those draggable widgets how can I understand which coordinates to change?
So, I have an array of draggble elements and array of coordinates. Widget with index 0 refers to coordinates with index 0. How can I understand which widget I'm currently moving so that I can get the index of it in array of widgets and then change proper coordinates in array of coordinates.
class DragAndDrop extends StatefulWidget {
const DragAndDrop({
Key? key,
this.width,
this.height,
}) : super(key: key);
final double? width;
final double? height;
#override
_DragAndDropState createState() => _DragAndDropState();
}
class _DragAndDropState extends State<DragAndDrop> {
List<double?> _x = [0.0, 20.0];
List<double?> _y = [0.0, 20.0];
int k = -1;
List<Widget> pel = [];
final GlobalKey stackKey = GlobalKey();
#override
Widget build(BuildContext context) {
pel.add(Container(color: Colors.blue));
k++;
Widget drag = Draggable<int>(
data: k,
child: Icon(
Icons.keyboard_arrow_down,
color: Color(0x95000000),
size: 40,
),
feedback: Icon(
Icons.keyboard_arrow_down,
color: Color.fromRGBO(212, 14, 14, 0.584),
size: 40,
),
childWhenDragging: Container(),
onDragStarted: () {},
onDragEnd: (dragDetails) {
setState(() {
final parentPos = stackKey.globalPaintBounds;
if (parentPos == null) return;
if (dragDetails.offset.dx - parentPos.left < 0)
_x[0] = 0;
else if (dragDetails.offset.dx - parentPos.left >
double.parse(widget.width.toString()) - 40)
_x[0] = double.parse(widget.width.toString()) - 40;
else
_x[0] = dragDetails.offset.dx - parentPos.left;
if (dragDetails.offset.dy - parentPos.top < 0)
_y[0] = 0;
else if (dragDetails.offset.dy - parentPos.top >
double.parse(widget.height.toString()) - 40)
_y[0] = double.parse(widget.height.toString()) - 40;
else
_y[0] = dragDetails.offset.dy - parentPos.top;
});
},
);
pel.add(Positioned(
left: _x[0],
top: _y[0],
child: drag,
));
pel.add(Positioned(
left: _x[1],
top: _y[1],
child: Draggable<int>(
data: k,
child: Icon(
Icons.keyboard_arrow_down,
color: Color(0x95000000),
size: 40,
),
feedback: Icon(
Icons.keyboard_arrow_down,
color: Color.fromRGBO(212, 14, 14, 0.584),
size: 40,
),
childWhenDragging: Container(),
onDragStarted: () {},
onDragEnd: (dragDetails) {
setState(() {
final parentPos = stackKey.globalPaintBounds;
if (parentPos == null) return;
_x[1] = dragDetails.offset.dx - parentPos.left; // 11.
_y[1] = dragDetails.offset.dy - parentPos.top;
});
},
),
));
return Stack(
key: stackKey,
fit: StackFit.expand,
children: pel,
);
}
}
extension GlobalKeyExtension on GlobalKey {
Rect? get globalPaintBounds {
final renderObject = currentContext?.findRenderObject();
var translation = renderObject?.getTransformTo(null).getTranslation();
if (translation != null && renderObject?.paintBounds != null) {
return renderObject!.paintBounds
.shift(Offset(translation.x, translation.y));
} else {
return null;
}
}
}
I tried to use a variable that I can assign to Dragble.data field but I'm not able to get it inside the widget.
I am using the syncfusion_flutter_charts package to create a chart. I need to make a value check and display the column with the largest value in red. Tell me how to put a condition / loop correctly in order to check the values of the y-axis and recolor the larger value in red? I will be grateful for help.
chart
class ChartWidget extends StatefulWidget {
const ChartWidget({Key? key}) : super(key: key);
#override
State<ChartWidget> createState() => _ChartWidget();
}
class _ChartWidget extends State<ChartWidget> {
late List<_ChartData> data;
TooltipBehavior? _tooltipBehavior;
#override
void initState() {
data = [
_ChartData('6:00', 18),
_ChartData('7:00', 11),
_ChartData('8:00', 14),
_ChartData('9:00', 5),
_ChartData('10:00', 16),
_ChartData('11:00', 13),
_ChartData('12:00', 15),
_ChartData('13:00', 1),
_ChartData('14:00', 2),
_ChartData('15:00', 15),
_ChartData('16:00', 18),
_ChartData('17:00', 11),
_ChartData('18:00', 14),
_ChartData('19:00', 5),
_ChartData('20:00', 16),
_ChartData('21:00', 13),
_ChartData('22:00', 20),
_ChartData('23:00', 1),
_ChartData('24:00', 2),
];
super.initState();
}
#override
Widget build(BuildContext context) {
return _buildColumnChart();
}
SfCartesianChart _buildColumnChart() {
return SfCartesianChart(
plotAreaBorderWidth: 0,
zoomPanBehavior: ZoomPanBehavior(enablePanning: true),
primaryXAxis: CategoryAxis(
interval: 3,
visibleMaximum: 16,
axisLine: const AxisLine(width: 0),
labelStyle: constants.Styles.xxTinyLtStdTextStyleWhite,
majorTickLines: const MajorTickLines(width: 0),
majorGridLines: const MajorGridLines(width: 0),
),
primaryYAxis:
NumericAxis(isVisible: false, minimum: 0, maximum: 20, interval: 1),
tooltipBehavior: _tooltipBehavior,
series: <ChartSeries<_ChartData, String>>[
ColumnSeries<_ChartData, String>(
dataSource: data,
color: constants.Colors.greyMiddle,
borderColor: constants.Colors.greyChart,
borderWidth: 1,
borderRadius: BorderRadius.circular(4),
xValueMapper: (_ChartData data, _) => data.x,
yValueMapper: (_ChartData data, _) => data.y,
name: 'Test'),
],
);
}
}
class _ChartData {
_ChartData(this.x, this.y);
final String x;
final double y;
}
This is the desired result
You can use pointColorMapper
find the max value
double maxValue = 0;
//loop list of _ChartData to compare its y value and find the max
data.forEach((data){
if(data.y>maxValue) {
maxValue = data.y;
}
});
Then instead of using color use pointColorMapper
//Instead of
color: constants.Colors.greyMiddle,
//use
pointColorMapper: (_ChartData data,_){
if(data.y == maxValue){
return constants.Colors.YourRedColor;
}
else{
return constants.Colors.greyMiddle;
}
},
This was the code I worked with, the final output being 'largest value is 20 and index: 16'
void main() {
var data = [
_ChartData('6:00', 18),
_ChartData('7:00', 11),
_ChartData('8:00', 14),
_ChartData('9:00', 5),
_ChartData('10:00', 16),
_ChartData('11:00', 13),
_ChartData('12:00', 15),
_ChartData('13:00', 1),
_ChartData('14:00', 2),
_ChartData('15:00', 15),
_ChartData('16:00', 18),
_ChartData('17:00', 11),
_ChartData('18:00', 14),
_ChartData('19:00', 5),
_ChartData('20:00', 16),
_ChartData('21:00', 13),
_ChartData('22:00', 20),
_ChartData('23:00', 1),
_ChartData('24:00', 2),
];
double largest_val = 0.0;
int largest_val_index = 0;
for(int k = 0; k < data.length; k++){
if(data[k].y >largest_val){
largest_val = data[k].y;
largest_val_index = k;
}
}
print('largest value is ${largest_val} and index: ${largest_val_index}');
}
class _ChartData {
_ChartData(this.x, this.y);
final String x;
final double y;
}
variable largest_val_index can be used to get the index in list data while largest_val gives the largest value.
void main() should be removed while execution.
To use a specific color to a point, you can make use of pointColorMapper property. Find the maximum y-value from your data source and based on that you can apply color to each point using the pointColorMapper. We have attached the code below
late double yMaximum = 0;
#override
void initState() {
data = [
//Your data
];
getMax(data);
super.initState();
}
void getMax(List<_ChartData> data) {
for (int i = 0; i < data.length; i++)
if (data[i].y > yMaximum) yMaximum = data[i].y;
}
SfCartesianChart(
//Other properties
series: <ChartSeries<_ChartData, String>>[
ColumnSeries<_ChartData, String>(
pointColorMapper: (_ChartData data, _) =>
data.y == yMaximum ? constants.Colors.YourRedColor : constants.Colors.greyMiddle,
),
],
)
Already we have a demo sample to apply the color based on the y-value using the pointColorMapper, which can be found below.
Demo
UG
I want to save to Firestore the last slider value after changes. I tried to do that using onChangeEnd, but instead of last value, I get first picked value.
Examples
What I want to get:
Current slider position is 3. I want to press 3 on slider and slide it to 5 because my answer is 5. I want to be saved in Firestore that my answer is 5. After that, it's not possible to move slider and give new answer.
What I am getting #1:
Current slider position is 3. I press 3 on slider and I want to slide it to 5 because I want my answer to be 5. In Firestore is saved that my answer is 3. After that, it's not possible to move slider and give new answer.
What I am getting #2:
Current slider position is 3. I press 5 on slider (without sliding) and I want my answer to be 5. In Firestore is saved that my answer is 5. After that, it's not possible to move slider and give new answer.
import 'package:flutter/material.dart';
import 'package:gamiforms/services/database.dart';
class LinearScale extends StatefulWidget {
String question, formId;
LinearScale(this.question, this.formId);
#override
LinearScaleState createState() => LinearScaleState(question);
}
class LinearScaleState extends State<LinearScale> {
String question;
LinearScaleState(this.question);
double overall = 3.0;
String overallStatus = "Good";
static final formKey = new GlobalKey<FormState>();
DatabaseService databaseService = DatabaseService();
String answer = "";
bool isLoading = false;
bool enabled = true;
uploadFormData(overall) {
//if (formKey.currentState.validate()) {
setState(() {
isLoading = true;
});
print('overall $overall');
if (overall == 1.0) answer = "Fail";
if (overall == 2.0) answer = "Acceptable";
if (overall == 3.0) answer = "Good";
if (overall == 4.0) answer = "Very good";
if (overall == 5.0) answer = "Excellent";
print('answer $answer');
Map<String, String> answerMap = {
"question": this.question,
"answer": answer,
};
print("${widget.formId}");
databaseService.addAnswerData(answerMap, widget.formId).then((value) {
answer = "";
question = this.question;
setState(() {
isLoading = false;
});
}).catchError((e) {
print(e);
});
}
#override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
var width = screenSize.width;
var height = screenSize.height;
print('TEST $question');
return SizedBox(
width: width,
height: height - 100,
child: Container(
margin: EdgeInsets.only(top: 30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
margin: EdgeInsets.only(left: 16.0),
child: Text(
question,
style: TextStyle(fontSize: 20),
)),
Container(
margin: EdgeInsets.symmetric(vertical: 50.0),
child: Text(
overallStatus,
style: TextStyle(
color: Colors.teal[800],
fontWeight: FontWeight.bold,
fontSize: 30.0),
textAlign: TextAlign.center,
),
),
Expanded(
child: Center(
child: Slider(
value: overall,
onChanged: (value) {
setState(() {
overall = value.round().toDouble();
_getOverallStatus(overall);
});
},
onChangeEnd: enabled
? (value) {
enabled = false;
setState(() {
overall = value.round().toDouble();
uploadFormData(overall);
});
}
: null,
label: '${overall.toInt()}',
divisions: 4,
min: 1.0,
max: 5.0,
),
),
)
],
),
),
);
}
_getOverallStatus(double overall) {
switch (overall.toInt()) {
case 1:
overallStatus = 'Fail';
break;
case 2:
overallStatus = 'Acceptable';
break;
case 3:
overallStatus = 'Good';
break;
case 4:
overallStatus = 'Very good';
break;
default:
overallStatus = 'Excellent';
break;
}
}
}
ive currently got a screen that has a linechart widget this is a stateless widget that uses the Flchart plugin
the datasource for the linechart is uisng a provider that is listening for changes, i also have a Date filter that calls an api and gets the data for that date range and refreshes the data
what i am finding is when i refresh the data for a diffrent date range, i can see the data is being sent to the widget but the line chart is not rendering/rebuilt, if i switch back to the default date range the first loaded chart is displayed correctly.
line chart widget
class LineChartWidget extends StatelessWidget {
final List<FlSpot> list;
final bool isDollar;
LineChartWidget({this.list, this.isDollar});
final DateTime now = DateTime.now();
double minY = 0;
double maxY = 0;
#override
Widget build(BuildContext context) {
list.forEach((f) => print(f.y.toString()));
return list.isEmpty
? Container(
child: Center(
child: Text("No Chart Data..."),
),
)
: ConstrainedBox(
constraints: BoxConstraints.expand(height: 140),
child: LineChart(mainData()),
);
}
static int dse2mse(double daysSinceEpoch) {
return (daysSinceEpoch * 86400000).floor();
}
static double mse2dse(int millisecondsSinceEpoch) {
return millisecondsSinceEpoch / 86400000;
}
String getTitleFunction(double value) {
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(dse2mse(value));
return DateFormat('MMM d').format(dateTime);
}
LineChartData mainData() {
maxY = list[0].y;
minY = list[0].y;
list.forEach((i) {
if (i.y < minY) {
minY = i.y;
}
if (i.y > maxY) {
maxY = i.y;
}
});
return LineChartData(
minX: mse2dse(DateTime(now.year, now.month, 1).millisecondsSinceEpoch),
maxX: mse2dse(now.millisecondsSinceEpoch),
minY: minY - 1.9 * minY,
maxY: maxY + 0.2 * maxY,
clipToBorder: true,
gridData: FlGridData(
show: false,
),
titlesData: FlTitlesData(
show: false,
),
borderData: FlBorderData(
show: false,
),
lineTouchData: LineTouchData(
fullHeightTouchLine: false,
handleBuiltInTouches: true,
getTouchedSpotIndicator:
(LineChartBarData barData, List<int> spotIndexes) {
return spotIndexes.map((spotIndex) {
final FlSpot spot = barData.spots[spotIndex];
if (spot.x == 0 || spot.x == 30 || spot.x == 29) {
return null;
}
return TouchedSpotIndicatorData(
const FlLine(color: Colors.transparent, strokeWidth: 0),
const FlDotData(
dotSize: 5, dotColor: Color.fromRGBO(253, 54, 94, 1)),
);
}).toList();
},
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.white,
fitInsideHorizontally: true,
fitInsideVertically: true,
getTooltipItems: (List<LineBarSpot> touchedBarSpots) {
return touchedBarSpots.map((barSpot) {
final flSpot = barSpot;
if (flSpot.x == 0 || flSpot.x == 30 || flSpot.x == 29) {
return null;
}
return LineTooltipItem(
isDollar
? '${getTitleFunction(flSpot.x)} | \$${flSpot.y}'
: '${getTitleFunction(flSpot.x)} | ${flSpot.y.toStringAsFixed(0)}',
const TextStyle(
color: Colors.black87,
fontFamily: 'NeueMontreal',
letterSpacing: 0.9,
fontWeight: FontWeight.w600,
fontSize: 12),
);
}).toList();
})),
lineBarsData: [
LineChartBarData(
spots: list,
isCurved: true,
colors: [Color.fromRGBO(253, 54, 94, 1)],
curveSmoothness: 0.17,
barWidth: 1,
isStrokeCapRound: true,
dotData: const FlDotData(
show: false,
),
),
],
);
}
}
im not sure whether changing the linechart widget to Stateful would help as i cant call setstate because the data is being refreshed by NotifyListers automatically
thanks
Resolved the issue, with the line chart not upating, MinX and MaxX values need to be updated when the date range changes