Flutter Auto Countdown Implementation for Fitness Workout - flutter

I'm working on a Flutter Workout App and I'm having some issues implementing the Workout Sequence and Timer.
I was able to implement the auto countdown and workout sequence however, the countdown skips numbers between and the workout sequence runs only for the first 2 sets.
For example, if an exercise has 4 sets, the cycle only runs for the first two and it's glitchy. I need help with optimized code to help me achieve the workout sequence
JSON DATA
{
"data": {
"day1": [
{
"id": 1,
"title": "Reclining Triceps Press",
"equipment": "Pull Up Bar",
"level": "Beginner",
"reps": "15",
"rest": "45 Seconds",
"sets": "4",
"image": "https://wicombit.com/demo/fitpro/images/exercise_1519941887.jpg",
"video": null,
},
{
"id": 10,
"title": "Plank with Arm Raise",
"equipment": "Pull Up Bar",
"level": "Intermediate",
"reps": "12",
"rest": "30 Seconds",
"sets": "3",
"image": "https://wicombit.com/demo/fitpro/images/exercise_1519938568.jpg",
"video": null,
},
{
"id": 3,
"title": "90-degree Static Hold",
"equipment": "Pull Up Bar",
"level": "Beginner",
"reps": "12",
"rest": "45 Seconds",
"sets": "3",
"image": "https://wicombit.com/demo/fitpro/images/exercise_1519940878.jpg",
"video": null,
},
{
"id": 5,
"title": "Single-arm Medicine Ball Pushup",
"equipment": "Kettlebells",
"level": "Elite",
"reps": "8",
"rest": "45 Seconds",
"sets": "3",
"image": "https://wicombit.com/demo/fitpro/images/exercise_1519940316.jpg",
"video": null,
"status": "draft"
}
],
}
}
I have tried the below implementation but not getting the results I want.
Dart Implementation
class WorkouStartSequenceScreen extends StatefulWidget {
final exercise;
WorkouStartSequenceScreen({super.key, required this.exercise});
#override
State<WorkouStartSequenceScreen> createState() =>
_WorkouStartSequenceScreenState();
}
class _WorkouStartSequenceScreenState extends State<WorkouStartSequenceScreen> {
late Timer _startTimer;
late Timer _restTimer;
late int totalNoOfExercisesInDay;
int currentExerciseIndex = 0;
late int totalNoOfSets;
late int totalNoOfReps;
late int totalRestTime;
int currentSetNo = 0;
int currentRepNo = 0;
int currentRestTime = 0;
bool dayWorkoutComplete = false;
int startUpCountdown = 10;
StreamController<int> _startEvents = BehaviorSubject();
StreamController<int> _restEvents = BehaviorSubject();
#override
void initState() {
totalNoOfExercisesInDay = widget.exercise.length;
totalNoOfReps = int.parse(widget.exercise[currentExerciseIndex].reps);
totalNoOfSets = int.parse(widget.exercise[currentExerciseIndex].sets);
totalRestTime = int.parse(widget.exercise[currentExerciseIndex].rest
.replaceAll(new RegExp(r'[^0-9]'), ''));
currentRestTime = totalRestTime;
_startEvents.add(startUpCountdown);
super.initState();
}
#override
void dispose() {
_startTimer.cancel();
_restTimer.cancel();
// _startEvents.close();
// _restEvents.close();
super.dispose();
}
void _showStartupDialog() async {
_startTimer = Timer.periodic(Duration(seconds: 1), (timer) {
(startUpCountdown > 0) ? startUpCountdown-- : _startTimer.cancel();
_startEvents.add(startUpCountdown);
});
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext builderContext) {
_startTimer = Timer(Duration(seconds: 10), () {
Navigator.of(context).pop();
});
return AlertDialog(
backgroundColor: AppStyles.primaryColor,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Your workout starts in',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white, fontSize: SizeConfig.font20)),
SizedBox(height: SizeConfig.height20),
StreamBuilder<int>(
stream: _startEvents.stream,
builder:
(BuildContext context, AsyncSnapshot<int> snapshot) {
return Text('${snapshot.data.toString()}',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.font50));
}),
],
),
);
}).then((val) {
if (_startTimer.isActive) {
_startTimer.cancel();
}
});
}
void startRestTimer() async {
_restTimer = Timer.periodic(Duration(seconds: 1), (timer) {
(currentRestTime > 0) ? currentRestTime-- : _restTimer.cancel();
_restEvents.add(currentRestTime);
});
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext builderContext) {
_restTimer = Timer(Duration(seconds: totalRestTime), () {
Navigator.of(context).pop();
});
return AlertDialog(
backgroundColor: AppStyles.primaryColor,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('REST',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white, fontSize: SizeConfig.font20)),
SizedBox(height: SizeConfig.height20),
StreamBuilder<int>(
stream: _restEvents.stream,
builder:
(BuildContext context, AsyncSnapshot<int> snapshot) {
return Text('${snapshot.data.toString()}',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.font50));
}),
],
),
);
}).then((_) {
print("Next Set Starting");
setState(() {
currentRepNo = 0;
currentRestTime = int.parse(widget.exercise[currentExerciseIndex].rest
.replaceAll(new RegExp(r'[^0-9]'), ''));
startExercise();
});
_restTimer.cancel();
});
}
void startExercise() {
const oneSec = const Duration(seconds: 2);
new Timer.periodic(
oneSec,
(Timer timer) {
if (currentRepNo == totalNoOfReps) {
setState(() {
timer.cancel();
startRestTimer();
currentSetNo++;
if (currentSetNo == totalNoOfSets) {
currentExerciseIndex++;
}
});
} else {
setState(() {
currentRepNo++;
});
}
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey[100],
appBar: AppBar(
backgroundColor: AppStyles.primaryDark,
leading: GestureDetector(
onTap: () => Get.back(),
child: Icon(FontAwesomeIcons.xmark,
color: AppStyles.appSecondaryColor)),
),
body: Column(children: [
Image.network(widget.exercise[currentExerciseIndex].image),
SizedBox(height: SizeConfig.height20),
TitleTextWidget(
titleText: widget.exercise[currentExerciseIndex].title,
titleSize: SizeConfig.font25,
titleTextMaxLines: 3),
SizedBox(height: SizeConfig.height50),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
Column(children: [
Row(children: [
TitleTextWidget(
titleText: ("$currentRepNo"),
titleSize: SizeConfig.font80,
),
Text("/", style: TextStyle(fontSize: SizeConfig.font80)),
DescriptionTextWidget(
descriptionText: widget.exercise[currentExerciseIndex].reps,
descriptionSize: SizeConfig.font80,
fontFamily: 'Raleway'),
]),
SizedBox(height: SizeConfig.height20),
DescriptionTextWidget(
descriptionText: 'Reps',
fontFamily: 'Raleway',
descriptionSize: SizeConfig.font30)
]),
Column(children: [
Row(children: [
TitleTextWidget(
titleText: currentSetNo.toString(),
titleSize: SizeConfig.font80,
),
Text("/", style: TextStyle(fontSize: SizeConfig.font80)),
DescriptionTextWidget(
descriptionText: widget.exercise[currentExerciseIndex].sets,
descriptionSize: SizeConfig.font80,
fontFamily: 'Raleway'),
]),
SizedBox(height: SizeConfig.height20),
DescriptionTextWidget(
descriptionText: 'Sets',
fontFamily: 'Raleway',
descriptionSize: SizeConfig.font30)
])
]),
SizedBox(height: SizeConfig.height30),
GestureDetector(
onTap: () {
_showStartupDialog();
Future.delayed(const Duration(seconds: 10), () {
startExercise();
});
},
child: Container(
height: SizeConfig.height70,
width: double.maxFinite,
margin: EdgeInsets.symmetric(
horizontal: SizeConfig.width20, vertical: SizeConfig.height5),
decoration: BoxDecoration(
color: AppStyles.primaryColor,
borderRadius: BorderRadius.circular(
SizeConfig.radius15,
)),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: SizeConfig.width40,
vertical: SizeConfig.height10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(FontAwesomeIcons.play, color: Colors.white),
SizedBox(width: SizeConfig.width10),
Text(
"Start",
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.font25,
fontWeight: FontWeight.w500),
),
],
),
),
),
)
]),
);
}
}

Related

Two orders being generated instead of one in Flutter app

I am trying to make an e-commerce app that allows a user to place an order by either making an online payment using Razorpay or opting for cash on delivery (CoD).
Things work fine for the CoD part, it is in the razorpay powered order that duplicate orders are being created.
My approach: In the checkout screen, I am having two buttons, one each for CoD and online payment using Razorpay. I have a bool isCod initially set to true when the user first navigates to the page. Clicking on either of these buttons sets the isCod variable's values accordingly.
Now, when the user goes ahead with Razorpay payments, once the payment completes successfully two orders are being created at Firestore cloud database (in fact they get created at the same timestamp).
My Code:
class CheckoutScreen extends StatefulWidget {
const CheckoutScreen({super.key, required this.product});
final Map<String, dynamic> product;
#override
State<CheckoutScreen> createState() => _CheckoutScreenState();
}
class _CheckoutScreenState extends State<CheckoutScreen> {
bool isCod = true;
final String? currUserId = FirebaseAuth.instance.currentUser?.uid;
var _razorpay = Razorpay();
int? amount;
final _random = Random();
#override
void initState() {
super.initState();
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
generateDropDownValues();
}
void _handlePaymentSuccess(PaymentSuccessResponse response) {
// Do something when payment succeeds
print(response);
//Add current product to Current Orders array
currentOrders.add({
'orderId': Random().nextInt(1000),
'order': [{
'product': widget.product,
'qty': dropDownValue,
}],
'date': DateTime.now(),
'amount': buyNowValue
});
FirebaseFirestore.instance.collection('customers').doc(currUserId).update({
'currentOrders': currentOrders,
});
buyNowValue = 0;
Navigator.pushNamed(context, orderSuccess);
_razorpay.clear();
}
void _handlePaymentError(PaymentFailureResponse response) {
// Do something when payment fails
print("Payment Failed");
}
void _handleExternalWallet(ExternalWalletResponse response) {
// Do something when an external wallet is selected
}
Future<String?> generateRzpOrderId() async {
try {
String basicAuth = 'Basic ${base64Encode(utf8.encode('${razorpayId}:${razorpaySecret}'))}';
Map<String, dynamic> orderData = {
"amount": (buyNowValue*100).toInt(),
"currency": "INR",
'receipt': 'CG_${1000 + _random.nextInt(9999 - 1000)}'
};
var res = await http.post(
Uri.https("api.razorpay.com", "v1/orders"),
headers: <String, String>{
'Authorization': basicAuth,
'Content-Type': 'application/json'
},
body: json.encode(orderData),
);
print(res.body);
if ((json.decode(res.body)).containsKey('error')) {
return null;
} else {
return (json.decode(res.body))['id'];
}
} catch (e) {
print(e);
throw e;
}
}
Future openGateWay() async {
generateRzpOrderId().then((value){
var options = {
'key': 'rzp_live_hUk8LTeESrQ6lL',
'amount': (buyNowValue*100).toInt(), //in the smallest currency sub-unit.
'currency': "INR",
'name': 'Cattle GURU',
'description': 'Online purchase of cattle food',
'order_id': value, // Generate order_id using Orders API
'timeout': 300, // in seconds
'prefill': {
'contact': phoneNumber
}
};
try {
_razorpay.open(options);
} catch (e) {
print(e);
}
});
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
}
#override
void dispose() {
_razorpay.clear();
super.dispose(); // Removes all listeners
}
int dropDownValue = 1;
List dropDownValues = [];
generateDropDownValues(){
for(int i = 0; i < buyNowProduct['units']; i++){
dropDownValues.add(i+1);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 0.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 2.h,),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: ProductListTile(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context) =>
ProductScreen(
product: widget.product,
isCarted: false,
id: widget.product['prodId'],
prodQty: dropDownValue,
)));
},
imgUrl: widget.product['imgUrls'][0],
productDeliveryDays: widget.product['deliveryDays'],
date: DateTime.now(),
productName: widget.product['name'],
productWeight: widget.product['weight'].toDouble(),
price: widget.product['price'].toDouble(),
mrp: widget.product['mrp'].toDouble(),
protein: widget.product['protein'].toDouble(),
fibre: widget.product['fibre'].toDouble(),
fat: widget.product['fat'].toDouble(),
isCarted: false,
onSubtract: (){},
onAdd: (){},
qty: qty,
width: 0,
height: 0,
onAddToCart: (){},
onBuyNow: (){},
),
),
// SizedBox(height: 1.h,),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Row(
children: [
Text(isEnglish ? "Select quantity" : "संख्या चुनें", style: globalTextStyle.copyWith(color: black, fontSize: 4.w, fontWeight: FontWeight.bold),),
SizedBox(width: 2.5.w,),
DropdownButton(
value: dropDownValue,
style: globalTextStyle.copyWith(color: grey, fontWeight: FontWeight.bold),
iconEnabledColor: grey,
items: dropDownValues.map((items) => DropdownMenuItem(value: items, child: Text(items.toString()))).toList(),
onChanged: (newValue){
setState(() {
dropDownValue = newValue as int;
buyNowValue = dropDownValue*auxBuyNowValue;
});
},
)
// CustomDropDown(items: dropDownValues, dropdownvalue: dropDownValue)
],
),
),
SizedBox(height: 1.h,),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Text(isEnglish ? "Deliver to" : "इस पते पर पहुंचाएं", style: globalTextStyle.copyWith(color: black, fontSize: 4.w, fontWeight: FontWeight.bold),),
),
SizedBox(height: 1.h,),
AddressCard(
onTap: (){},
isDefault: false,
name: firestoreCurrentAddress['name'],
address: "${firestoreCurrentAddress['houseNum']}, ${firestoreCurrentAddress['village']}, ${firestoreCurrentAddress['district']}, ${firestoreCurrentAddress['state']}, ${firestoreCurrentAddress['pinCode']}",
onEditTap: (){
// Navigator.push(context, MaterialPageRoute(builder: (context) => EditAddressScreen(address: addressTiles[1], addressIndex: 1, isDefault: false,)));
Navigator.pushNamed(context, myAddresses);
},
onDefaultTap: (){
},
onRemoveTap: (){},
),
SizedBox(height: 1.h,),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Row(
children: [
InkWell(
onTap: (){
setState(() {
isCod= true;
});
},
child: Container(
width: 44.w,
height: 15.w,
decoration: BoxDecoration(
border: Border.all(color: primary),
borderRadius: BorderRadius.all(Radius.circular(2.w)),
color: isCod ? primary : primaryLight,
),
child: Center(
child: Text(isEnglish ? "Cash on Delivery (COD)" : "डिलवरी पर नकदी", style: globalTextStyle.copyWith(color: isCod ? white : black, fontSize: 3.5.w, fontWeight: FontWeight.bold),),
),
),
),
SizedBox(width: 2.w,),
InkWell(
onTap: (){
setState(() {
isCod = false;
});
},
child: Container(
width: 44.w,
height: 15.w,
decoration: BoxDecoration(
border: Border.all(color: primary),
borderRadius: BorderRadius.all(Radius.circular(2.w)),
color: isCod ? primaryLight : primary,
),
child: Center(
child: Text(isEnglish ? "Online Payment" : "ऑनलाइन भुगतान", style: globalTextStyle.copyWith(color: isCod ? black : white, fontSize: 3.5.w, fontWeight: FontWeight.bold),),
),
),
),
],
),
),
SizedBox(height: 1.h,),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: CustomButton(width: 90.w, height: 15.w, color: primary,
onTap: () async {
if(isCod == false) {
await openGateWay();
} else {
//Add current product to Current Orders array
currentOrders.add({
'orderId': Random().nextInt(1000),
'order': [{
'product': widget.product,
'qty': dropDownValue,
}],
'date': DateTime.now(),
'amount': buyNowValue
});
FirebaseFirestore.instance.collection('customers').doc(currUserId).update({
'currentOrders': currentOrders,
});
buyNowValue = 0;
Navigator.pushNamed(context, orderSuccess);
}
},
text: isEnglish ? "Pay ${buyNowValue.toCurrencyString(leadingSymbol: "₹", useSymbolPadding: true)}" : "${buyNowValue.toCurrencyString(leadingSymbol: "₹", useSymbolPadding: true)} भुगतान करें ", fontColor: white, borderColor: primary),
)
],
),
),
),
);
}
}
I would like to know what is wrong in my code that results in the duplicate order being created and how this issue can be solved.

How can I get the number of "read" = "true" in the following code

Here I am getting the response like the following in the postman
[
{
"createdAt": "2022-09-19T18:43:27.404Z",
"title": "title 1",
"description": "description 1",
"lastdate": "lastdate 1",
"status": "status 1",
"id": "1",
"read": "false"
},
{
"createdAt": "2022-09-20T04:01:37.296Z",
"title": "title 2",
"description": "description 2",
"lastdate": "lastdate 2",
"status": "status 2",
"id": "2",
"read": "false"
}
]
I want to get the number(Count) of read value == false in the code. So that I can set that count to one notification badge
class NotificationView extends StatefulWidget {
const NotificationView({Key? key}) : super(key: key);
#override
State<NotificationView> createState() => _NotificationViewState();
}
class _NotificationViewState extends State<NotificationView>
with ChangeNotifier {
NotificationViewModel? _notificationViewModel;
final NotificationHelper _notificationHelper = NotificationHelper();
ValueNotifier<int> notificationCounter =
ValueNotifier(SideMenuView.notificationCount.value);
ValueNotifier<int> unreadNotificationCount = ValueNotifier(0);
#override
void initState() {
super.initState();
_notificationHelper.initialiseNotifications();
_notificationHelper.sendNotification('Hi', 'This is to test notifications');
notificationCounter.value++;
SideMenuView.notificationCount.value = notificationCounter.value;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_notificationViewModel =
Provider.of<NotificationViewModel>(context, listen: false);
_notificationViewModel!.getNotifications();
});
}
#override
void dispose() {
super.dispose();
notificationCounter.dispose();
}
#override
Widget build(BuildContext context) {
final sideMenuViewModel = Provider.of<SideMenuViewModel>(context);
_notificationViewModel = Provider.of<NotificationViewModel>(context);
Size size = MediaQuery.of(context).size;
return Scaffold(
body: ChangeNotifierProvider<NotificationViewModel>(
create: (context) => NotificationViewModel(),
child: Consumer<NotificationViewModel>(
builder: (context, notificationViewModel, child) {
return ListView.builder(
itemCount: _notificationViewModel!.notificationModel.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
sideMenuViewModel.setRouteName(RoutesName.inspections);
},
child: SizedBox(
child: Card(
margin: const EdgeInsets.fromLTRB(20, 5, 20, 15),
color: AppColors.veryLightGrey,
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: ConstrainedBox(
constraints: const BoxConstraints(minHeight: 120.0),
child: Container(
margin: const EdgeInsets.fromLTRB(5, 5, 15, 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: size.height * 0.01),
Text(
_notificationViewModel!
.notificationModel[index].read!,
style: const TextStyle(
fontSize: 18.0,
fontFamily: "Helvetica",
fontWeight: FontWeight.bold),
),
SizedBox(height: size.height * 0.02),
Text(
_notificationViewModel!
.notificationModel[index].description!,
style: const TextStyle(
fontSize: 14.0,
fontFamily: "Helvetica",
),
),
],
),
),
),
),
),
);
});
}),
));
}
}
You can get count like this.
int count =0;
_notificationViewModel!.notificationModel.forEach((element){
if(element.read == false){
count =count+1;
}
});
if read comes with different type data such "read":false and "read":3 then, you can check runtimeType,
int readCount = 0;
if(json["read"] is int){
readCount = json["read"];
}

How to fetch a list of maps to a calendar flutter

I have a response API here -
{
"code": 0,
"message": "All the revisions of current user ",
"data": [
{
"id": 15,
"box_id": 31,
"user_id": 53,
"revision_type": "1",
"revision_date": "2021-05-30",
"revision_location": "gafsa",
"revision_title": "Une visite technique est important avant le 30-05-2021",
"kilometrage_pour_vidange": null,
"repeat_revision": 0,
"revision_status": 0,
"kilometrage_last_vidange": null,
"Kilometrage_revision": null
},
{
"id": 16,
"box_id": 31,
"user_id": 53,
"revision_type": "0",
"revision_date": "2021-06-26",
"revision_location": "tyyu",
"revision_title": "ygyyii",
"kilometrage_pour_vidange": 8655,
"repeat_revision": 0,
"revision_status": 0,
"kilometrage_last_vidange": null,
"Kilometrage_revision": null
},
{
"id": 17,
"box_id": 31,
"user_id": 53,
"revision_type": "2",
"revision_date": "2021-06-20",
"revision_location": "STAR",
"revision_title": "Votre prochain renovellement de l'assurance sera le 20-06-2021 avec l'agence STAR",
"kilometrage_pour_vidange": null,
"repeat_revision": 0,
"revision_status": 0,
"kilometrage_last_vidange": null,
"Kilometrage_revision": null
},
{
"id": 18,
"box_id": 31,
"user_id": 53,
"revision_type": "3",
"revision_date": "2021-06-20",
"revision_location": "sfax",
"revision_title": "véhicule en panne",
"kilometrage_pour_vidange": null,
"repeat_revision": 0,
"revision_status": 0,
"kilometrage_last_vidange": null,
"Kilometrage_revision": 87654
}
],
"error": [],
"status": 200
}
I can show event already exist from my API to Table Calendar dynamically with no problem , All things work fine . But I would like to display more than one variable on my card . At the moment i can display only "revisionLocation" . How i can fecth other veriable to the screen
my code :
class _RevisionState extends State<Revision> with TickerProviderStateMixin {
//debut code events
CalendarController _controller;
Map<DateTime, List<dynamic>> _events;
List<dynamic> _selectedEvents;
TextEditingController _eventController, dateController;
SharedPreferences prefs;
int status;
bool _autovalidate = false;
int status1 = 0;
// final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final _formKey = GlobalKey<FormState>();
RevisionApi revisionApi = RevisionApi();
TextEditingController _Kilometrage_revisionController =
TextEditingController();
TextEditingController _KilometrageController = TextEditingController();
TextEditingController _EmplacementController = TextEditingController();
TextEditingController _DateController = TextEditingController();
/* TextEditingController _repeat_revisionController =
TextEditingController(text: "non");*/
TextEditingController _revision_titleController = TextEditingController();
TextEditingController _revision_agenceController = TextEditingController();
// TextEditingController _eventController = TextEditingController();
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getTask1().then((val) => setState(() {
_events = val;
}));
//print( ' ${_events.toString()} ');
});
super.initState();
_controller = CalendarController();
_eventController = TextEditingController();
_events = {};
initializeDateFormatting();
_selectedEvents = [];
prefsData();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
void _onDaySelected(DateTime day, List events) {
// print('CALLBACK: _onDaySelected');
setState(() {
_selectedEvents = events;
});
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return SafeArea(
minimum: const EdgeInsets.only(top: 20.0),
child: Scaffold(
backgroundColor: Color(0xFF050127),
appBar: AppBar(
backgroundColor: Color(0xFF010611),
iconTheme: IconThemeData(color: Colors.white),
automaticallyImplyLeading: true,
centerTitle: true,
title: Text(
widget.title = 'Révision',
style: TextStyle(
color: Colors.white,
),
//textHeightBehavior: ,
),
elevation: 0.0,
leading: GestureDetector(
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => SideBar()));
},
child: Icon(CommunityMaterialIcons.segment),
),
actions: [
Icon(
Icons.search, /*color: Colors.black87*/
),
SizedBox(
width: 10,
),
]),
body: SingleChildScrollView(
child: Container(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(left: 10),
height: size.height * 0.05,
width: size.width * 1,
decoration:
BoxDecoration(color: Colors.white.withOpacity(0.2)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
DateFormat("dd-MM-yyyy hh:mm:ss")
.format(DateTime.now()),
style: TextStyle(
color: Colors.white,
fontSize: 16,
letterSpacing: 1),
),
IconButton(
icon: Icon(
CommunityMaterialIcons.calendar_plus,
color: KYellow,
),
onPressed: () {
_showAddDialog();
})
],
),
)
],
),
Row(
children: [
Expanded(
child: (_buildTableCalendarWithBuilders()),
),
],
),
_buildEventList(),
],
),
)),
),
);
}
_showAddDialog() async {
await showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.white,
title: Text("Ajouter un évènement"),
content: StatefulBuilder(builder: (
BuildContext context,
StateSetter setState,
) {
return SingleChildScrollView(
//
child: Form(
key: _formKey,
autovalidate: _autovalidate,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 10),
child: DropdownButtonFormField(
decoration: InputDecoration(
hoverColor: Colors.white,
/* contentPadding: EdgeInsets.only(
left: 10, right: 15, top: 15),*/
labelText: 'Type',
alignLabelWithHint: true,
labelStyle: TextStyle(
color: kPrimaryColor,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
),
dropdownColor: Colors.white,
value: status,
items: <DropdownMenuItem>[
DropdownMenuItem(
// value: 'videnge',
value: 0,
child: InkWell(
child: Text('Vidange'),
hoverColor: Colors.indigo,
),
),
DropdownMenuItem(
// value: 'visite technique',
value: 1,
child: Text('Visite technique'),
),
DropdownMenuItem(
// value: 'assurance véhicule',
value: 2,
child: Text('Assurance véhicule'),
),
DropdownMenuItem(
// value: 'autre',
value: 3,
child: Text('Autre'),
),
],
onChanged: (value) {
setState(() {
status = value;
});
},
),
)),
]),
if (status == 0) vidangeDropdown(),
if (status == 1) visiTechniqueDropdown(),
if (status == 2) assuranceDropdown(),
if (status == 3) autresDropdown(),
actions: <Widget>[
TextButton(
child: Text(
"Enregistrer",
style: TextStyle(
color: Colors.red, fontWeight: FontWeight.bold),
),
onPressed: () {
if (_eventController.text.isEmpty == null) return;
setState(() {
if (_events[_controller.selectedDay] != null) {
_events[_controller.selectedDay]
.add(_eventController.text);
} else {
_events[_controller.selectedDay] = [
_eventController.text
];
}
prefs.setString(
"events", json.encode(encodeMap(_events)));
_eventController.clear();
setRevision();
// Navigator.of(context).pop();
// Navigator.pop(context);
});
// Navigator.of(context).pop();
},
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Retour'),
),
],
));
setState(() {
_selectedEvents = _events[_controller.selectedDay];
});
}
Widget _buildEventList() {
return Column(children: [
..._selectedEvents.map(
(event) => Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(
CommunityMaterialIcons.oil,
color: KYellow,
),
SizedBox(
width: 10,
),
Text(
event,
style: TextStyle(
color: KYellow,
fontSize: 16,
fontWeight: FontWeight.w500),
)
],
),
SizedBox(
height: 20,
),
Text(
'Votre véhicule atteint 45 000 Km un vidange est important'),
SizedBox(
height: 20,
),
Text(
'Dernier visite effectuée le 23/12/2020',
style: TextStyle(color: Colors.indigo[400]),
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: Icon(
CommunityMaterialIcons.file_clock,
size: 35,
color: KYellow,
),
onPressed: () {},
),
SizedBox(
width: 30,
),
IconButton(
icon: Icon(
CommunityMaterialIcons.content_save_edit_outline,
size: 35,
color: KYellow,
),
onPressed: () {
if (event == 'Vidange') {
showDialog(
context: context,
builder: (BuildContext context) =>
_buildUpdateVidengeEvent(context),
);
} else
showDialog(
context: context,
builder: (BuildContext context) =>
_buildUpdateEvent(context),
);
},
)
],
)
],
))),
),
)
]);
}
Future<Map<DateTime, List>> getTask1() async {
Map<DateTime, List> mapFetch = {};
List<Datum> event = await revisionApi.getAllRevision();
for (int i = 0; i < event.length; i++) {
var createTime = DateTime(event[i].revisionDate.year,
event[i].revisionDate.month, event[i].revisionDate.day);
var original = mapFetch[createTime];
if (original == null) {
print("null");
mapFetch[createTime] = [event[i].revisionLocation];
} else {
// print(event[i].revisionLocation);
mapFetch[createTime] = List.from(original)
..addAll([event[i].revisionLocation]);
}
}
print(mapFetch);
return mapFetch;
}
}
and this is the screen contain list of cards :
I would like to add "revision_title" and "revision_date" instead static string in the body of the card . How i can do that ?
thanks in advance
I don't know what the selectedEvents contain but if you want to use several parameters you should probably convert your data to a Dart class or a Map structure that encapsulates all the data required to build those widgets and use those parameters properly where needed.
From what the I see the map is for changing each element of the list into another one. If your events are the elements [revision_location, revision_title] you are actually building two widgets one with the first parameter and a second one with the second parameter. So if you want to display a single element you need to build a single widget with the elements at each index. And if you want to build several items you need to iterate through a List<List> or List<Map> or List<DartClassWithData> to fetch all data for each widget you build
➕ Also if for some reason the app becomes international you may consider using the intl official package to use DateFormat to format the dates according to each country, as long as it is local you may directly write the string you get the specific way you require.
I hope you find a solution and if you can't. At least add extra information about what problem you have fetching all the information inside a single encapsulated class 🤗.

How to make a button select all Flutter checkboxes?

The problem is that I cannot make the button select all checkboxes, since I am using an automatically generated list of checkboxes.
Since I created a class with a list item, WordBlock, which is a container with a Checkbox and each checkbox has its own checked value, and therefore I can not select everything in any way. And if I set the checked value through the constructor, then it selects everything, but the onChanged () method does not work and the value of one checkbox does not change. But you need to be able to select the checkboxes one at a time or all at once, select all with the button.
Here is the code for my WordBlock class that appears in the list.
class WordBlock extends StatefulWidget {
final bool checkAll;
WordBlock(this.checkAll);
#override
_WordBlockState createState() => _WordBlockState();
}
class _WordBlockState extends State<WordBlock> {
FlutterTts tts = FlutterTts();
bool checked = false;
Future _speak(String text) async {
await tts.setLanguage('en-US');
await tts.speak(text);
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 35.w),
child: Card(
color: checked ? MyColors().scaffoldBG : Colors.white,
elevation: 4.0,
shadowColor: MyColors().black.withOpacity(0.1),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(25.ssp)),
child: CheckboxListTile(
value: checked,
activeColor: MyColors().purple,
onChanged: (value) {
print('changed');
setState(() {
checked = value;
});
},
title: h2(text: 'Car'),
secondary: Padding(
padding: EdgeInsets.only(top: 10.h),
child: InkWell(
onTap: () => _speak('Car'),
child: Icon(
Icons.volume_up_rounded,
color: MyColors().purple,
size: 60.ssp,
),
),
),
subtitle: Text(
'Машина',
style: TextStyle(color: Color(0xFFB8A98BA), fontSize: 27.ssp),
),
),
),
);
}
}
Here is the code for my page that displays the WordBloc list:
class WordPage extends StatefulWidget {
#override
_WordPageState createState() => _WordPageState();
}
class _WordPageState extends State<WordPage> {
bool visible = true;
double banerHeight;
bool checked = false;
#override
void initState() {
super.initState();
}
Widget _wrapWithBanner() {
if (!visible) {
setState(() {
banerHeight = 0;
});
}
return visible
? Container(
margin: EdgeInsets.only(
left: 35.w, right: 35.w, top: 30.h, bottom: 30.h),
padding: EdgeInsets.symmetric(vertical: 25.h),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.ssp),
color: MyColors().scaffoldBG,
boxShadow: [boxShadow4Y10Blur()]),
child: ListTile(
onTap: () {
print('close');
},
trailing: visible
? InkWell(
onTap: () {
print('tapped');
setState(() {
visible = false;
});
},
child: Icon(Icons.close))
: Container(),
leading: CircleAvatar(),
title: h3bold(text: 'Совет'),
subtitle: Text(
'Чтобы запомнить как можно больше слов, регулярно повторяйте их: каждые два-три часа :)',
style: TextStyle(color: MyColors().black, fontSize: 27.ssp),
),
))
: Container();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: MyColors().white,
appBar: AppBar(
elevation: 2.0,
title: h2(text: 'Работа'),
iconTheme: IconThemeData(size: 20.ssp, color: MyColors().purple),
backgroundColor: MyColors().white,
),
body: Column(
children: [
_wrapWithBanner(),
Row(
children: [
FlatButton( // my selectAll button
onPressed: () {},
child: Row(
children: [
Checkbox(
value: checked,
onChanged: (val) {
setState(() {
checked = val;
});
}),
Text(
'Выделить все',
style: TextStyle(
color: MyColors().purple, fontSize: 27.ssp),
)
],
))
],
),
Expanded(
child: ListView.builder(
itemCount: 4,
itemBuilder: (context, index) {
return WordBlock(checked);
},
))
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: Container(
width: 667.w,
height: 91.h,
child: FloatingActionButton(
backgroundColor: MyColors().purple,
onPressed: () {},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24.ssp)),
child: h2reg(
text: "Добавить в мой словарь", textColor: MyColors().white),
),
),
));
}
}
How can this function be implemented?
If you put bool checked = false; in as
class WordBlock extends StatefulWidget {
bool checked = false;
WordBlock();
#override
_WordBlockState createState() => _WordBlockState();
}
You can change it and reach it from _WordBlockState as widget.checked
Checkbox(
value: widget.checked,
onChanged: (val) {
setState(() {
widget.checked = val;
});
}),
it can be modified by both class and not constant.
try below code for with button all checkbox check and uncheck
Output :-
Code :-
import 'package:flutter/material.dart';
class CheckBoxExample extends StatefulWidget {
const CheckBoxExample({Key? key}) : super(key: key);
#override
State<CheckBoxExample> createState() => _CheckBoxExampleState();
}
class _CheckBoxExampleState extends State<CheckBoxExample> {
List multipleSelected = [];
List checkListItems = [
{
"id": 0,
"value": false,
"title": "Sunday",
},
{
"id": 1,
"value": false,
"title": "Monday",
},
{
"id": 2,
"value": false,
"title": "Tuesday",
},
{
"id": 3,
"value": false,
"title": "Wednesday",
},
{
"id": 4,
"value": false,
"title": "Thursday",
},
{
"id": 5,
"value": false,
"title": "Friday",
},
{
"id": 6,
"value": false,
"title": "Saturday",
},
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 64.0),
child: Column(
children: [
InkWell(
onTap: () => setState(() {
multipleSelected.clear();
for (var element in checkListItems) {
if (element["value"] == false) {
element["value"] = true;
multipleSelected.add(element);
} else {
element["value"] = false;
multipleSelected.remove(element);
}
}
}),
child: const Text(
"Select All",
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
Column(
children: List.generate(
checkListItems.length,
(index) => CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
dense: true,
title: Text(
checkListItems[index]["title"],
style: const TextStyle(
fontSize: 16.0,
color: Colors.black,
),
),
value: checkListItems[index]["value"],
onChanged: (value) {
setState(() {
checkListItems[index]["value"] = value;
});
},
),
),
),
Text(
multipleSelected.isEmpty ? "" : multipleSelected.toString(),
style: const TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}

Only one checkbox to be checked at time in Flutter

I have a 3 check box in my app, which are formed by looping over the tickbox map, in my Application it allows to select multiple checkboxes but I dont want that to happen , only one should be selected at a time, is there any way to do so in flutter.
below is my code.
class _DashboardFilterState extends State<DashboardFilter> {
void showModalSheet() {
List<Map<String, Object>> tickbox;
timeData = [
{"id": "1", "displayId": "Daily"},
{"id": "2", "displayId": "Weekly"},
{"id": "3", "displayId": "Monthly"}
];
showModalBottomSheet<void>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter state) {
return createBox(context, timeData, state);
});
});
}
createBox(BuildContext context,List<Map<String, Object>> tickbox, StateSetter state) {
var tickboxdata = tickbox.map<Widget>((data) {
int id = data["id"];
var dispId = data["displayId"];
return buildTimeData(context, id, dispId, state);
}).toList();
return SingleChildScrollView(
child: LimitedBox(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
// children: metrics,
children: <Widget>[
Container(
child: Column(
children: tickboxdata,
),
),
],
)),
);
}
Widget buildTimeData(
BuildContext context, var id, var disp, StateSetter state) {
return Container(
child: Column(mainAxisSize: MainAxisSize.min,
children: <Widget>[
CheckboxListTile(
value: widget.timeSelected[id],
title: Text(disp),
controlAffinity: ListTileControlAffinity.leading,
onChanged: (bool val) {
manageTimeState(val, id, state);
})
]));
}
void manageTimeState(bool val, var id, StateSetter state) {
state(() {
widget.timeSelected[id] = val;
});
}
let me know if is there any other option to do so thanks
without any package i make the example (null safe)
Output :-
Example code :-
import 'package:flutter/material.dart';
class CheckBoxExample extends StatefulWidget {
const CheckBoxExample({Key? key}) : super(key: key);
#override
State<CheckBoxExample> createState() => _CheckBoxExampleState();
}
class _CheckBoxExampleState extends State<CheckBoxExample> {
String selected = "";
List checkListItems = [
{
"id": 0,
"value": false,
"title": "Sunday",
},
{
"id": 1,
"value": false,
"title": "Monday",
},
{
"id": 2,
"value": false,
"title": "Tuesday",
},
{
"id": 3,
"value": false,
"title": "Wednesday",
},
{
"id": 4,
"value": false,
"title": "Thursday",
},
{
"id": 5,
"value": false,
"title": "Friday",
},
{
"id": 6,
"value": false,
"title": "Saturday",
},
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 64.0),
child: Column(
children: [
Column(
children: List.generate(
checkListItems.length,
(index) => CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
dense: true,
title: Text(
checkListItems[index]["title"],
style: const TextStyle(
fontSize: 16.0,
color: Colors.black,
),
),
value: checkListItems[index]["value"],
onChanged: (value) {
setState(() {
for (var element in checkListItems) {
element["value"] = false;
}
checkListItems[index]["value"] = value;
selected =
"${checkListItems[index]["id"]}, ${checkListItems[index]["title"]}, ${checkListItems[index]["value"]}";
});
},
),
),
),
const SizedBox(height: 100.0),
Text(
selected,
style: const TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
please use package https://pub.dev/packages/grouped_buttons
In onSelected, remove first selected item if more than one selected
code snippet
List<String> _checked = [];
...
CheckboxGroup(
labels: <String>[
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
],
disabled: ["Wednesday", "Friday"],
checked: _checked,
onChange: (bool isChecked, String label, int index) =>
print("isChecked: $isChecked label: $label index: $index"),
onSelected: (List selected) => setState(() {
if (selected.length > 1) {
selected.removeAt(0);
print('selected length ${selected.length}');
} else {
print("only one");
}
_checked = selected;
}),
),
full code
/*
Name: Akshath Jain
Date: 3/15/19
Purpose: example app for the grouped buttons package
*/
import 'package:flutter/material.dart';
import 'package:grouped_buttons/grouped_buttons.dart';
void main() => runApp(GroupedButtonExample());
class GroupedButtonExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Grouped Buttons Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<String> _checked = []; //["A", "B"];
String _picked = "Two";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Grouped Buttons Example"),
),
body: _body(),
);
//
}
Widget _body() {
return ListView(children: <Widget>[
//--------------------
//SIMPLE USAGE EXAMPLE
//--------------------
//BASIC CHECKBOXGROUP
Container(
padding: const EdgeInsets.only(left: 14.0, top: 14.0),
child: Text(
"Basic CheckboxGroup",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
),
),
CheckboxGroup(
labels: <String>[
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
],
disabled: ["Wednesday", "Friday"],
checked: _checked,
onChange: (bool isChecked, String label, int index) =>
print("isChecked: $isChecked label: $label index: $index"),
onSelected: (List selected) => setState(() {
if (selected.length > 1) {
selected.removeAt(0);
print('selected length ${selected.length}');
} else {
print("only one");
}
_checked = selected;
}),
),
//BASIC RADIOBUTTONGROUP
Container(
padding: const EdgeInsets.only(left: 14.0, top: 14.0),
child: Text(
"Basic RadioButtonGroup",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
),
),
RadioButtonGroup(
labels: [
"Option 1",
"Option 2",
],
disabled: ["Option 1"],
onChange: (String label, int index) =>
print("label: $label index: $index"),
onSelected: (String label) => print(label),
),
//--------------------
//CUSTOM USAGE EXAMPLE
//--------------------
///CUSTOM CHECKBOX GROUP
Container(
padding: const EdgeInsets.only(left: 14.0, top: 14.0, bottom: 14.0),
child: Text(
"Custom CheckboxGroup",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
),
),
CheckboxGroup(
orientation: GroupedButtonsOrientation.HORIZONTAL,
margin: const EdgeInsets.only(left: 12.0),
onSelected: (List selected) => setState(() {
_checked = selected;
}),
labels: <String>[
"A",
"B",
],
checked: _checked,
itemBuilder: (Checkbox cb, Text txt, int i) {
return Column(
children: <Widget>[
Icon(Icons.polymer),
cb,
txt,
],
);
},
),
///CUSTOM RADIOBUTTON GROUP
Container(
padding: const EdgeInsets.only(left: 14.0, top: 14.0, bottom: 14.0),
child: Text(
"Custom RadioButtonGroup",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
),
),
RadioButtonGroup(
orientation: GroupedButtonsOrientation.HORIZONTAL,
margin: const EdgeInsets.only(left: 12.0),
onSelected: (String selected) => setState(() {
_picked = selected;
}),
labels: <String>[
"One",
"Two",
],
picked: _picked,
itemBuilder: (Radio rb, Text txt, int i) {
return Column(
children: <Widget>[
Icon(Icons.public),
rb,
txt,
],
);
},
),
]);
}
}
working demo