How to convert time value into double/int value? - android

Is there a way to convert time value into double or int? Here's is the code to my timer which I wanted to change into double value because I wanted it to hold the time value as a double so later on I want to calculate the total price based on the data from the timer.I've tried the convert method but its seems to failed and I try to search other solution but mostly it was python language solution.
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:latestfyp/QR/qrexit.dart';
import 'package:path/path.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import '../QR/qrenter.dart';
class CountdownPage extends StatefulWidget {
const CountdownPage({Key? key}) : super(key: key);
#override
State<CountdownPage> createState() => _CountdownPageState();
}
class _CountdownPageState extends State<CountdownPage> {
Barcode? result;
QRViewController? controller;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
dynamic data;
dynamic data1;
double test = 0.000083333;
var total;
static const countdownDuration = Duration(seconds: 0);
Duration duration = Duration();
Timer? timer;
bool isCountdown = true;
void initState(){
super.initState();
startTimer();
reset();
}
// #override
// void reassemble() {
// super.reassemble();
// if (Platform.isAndroid) {
// controller!.pauseCamera();
// }
// controller!.resumeCamera();
// }
void reset(){
if (isCountdown){
setState(() => duration = countdownDuration);
}
else{
setState(() => duration = Duration());
}
}
void addTime(){
final addSeconds = isCountdown? 1 : 1;
setState(() {
final seconds = duration.inSeconds + addSeconds;
if(seconds <0) {
timer?.cancel();
}
else{
duration = Duration(seconds: seconds);
}
});
}
void startTimer(){
timer = Timer.periodic(Duration(seconds: 1),(_) => addTime());
}
void stopParking(BuildContext context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => QR()));
}
Future<void> _navigateAndDisplaySelection(BuildContext context) async {
// Navigator.push returns a Future that completes after calling
// Navigator.pop on the Selection Screen.
final result = await Navigator.push(
context,
// Create the SelectionScreen in the next step.
MaterialPageRoute(builder: (context) => const QR()),
);
}
void stopTimer({bool resets = true}){
// data = duration *60;
if (resets){
reset();
}
setState(() => timer?.cancel());
}
#override
Widget build(BuildContext context) => Scaffold(
body: Center(child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
buildTime(),
// const SizedBox(height: 70),
buildButtons(),
// TextButton(
// child: Text('Scan to End Parking Time'),
// onPressed: (){
// Navigator.push(context,MaterialPageRoute(builder: (context) => QR()));
// },
// ),
],
)),
);
Widget buildButtons(){
final isRunning = timer == null? false : timer!.isActive;
// final isCompleted = duration.inSeconds == 0;
return isRunning
?Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
child: Text('Start timer'),
onPressed:(){startTimer();},
),
TextButton(
child: Text('Stop'),
onPressed:(){
if(isRunning){
stopTimer(resets: false,);
}
},
),
TextButton(
child: Text('Cancel'),
onPressed: stopTimer,
),
TextButton(
child: Text('Scan to end parking'),
// onPressed: () => QR(),
onPressed: (){
data = timer;
data1 = duration * 60 * 60;
total = data1.toString() ;
Navigator.push(this.context,MaterialPageRoute(builder: (context) => QRExit(price: total.toString(), duration:data.toString())));
},
),
],
)
:TextButton(
child: Text('Start Timer'),
onPressed:(){
startTimer();
}
);
}
Widget buildTime() {
String twoDigits(int n) => n.toString().padLeft(2,'0');
// final hours = twoDigits(duration.inHours);
// final minutes = twoDigits(duration.inMinutes.remainder(60));
final seconds = twoDigits(duration.inSeconds.remainder(60));
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// buildTimeCard(time: hours , header: 'Hours'),
// const SizedBox(width: 8),
// buildTimeCard(time: minutes , header: 'Minutes'),
const SizedBox(width: 8),
buildTimeCard(time: seconds , header: 'Seconds'),
],
);
}
Widget qr(){
return Scaffold(
body: Column(
children: <Widget>[
Expanded(flex: 4, child: _buildQrView(this.context)),
Expanded(
flex: 1,
child: FittedBox(
fit: BoxFit.contain,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.all(25),
child: SizedBox(
height: 15,
width: 55,
child: ElevatedButton(
onPressed: () async{
await controller?.resumeCamera();
},
child: Text('Scan', style: TextStyle(fontSize: 10)),
),
),
)
],
),
],
),
),
)
],
),
);
}
Widget _buildQrView(BuildContext ctx) {
// For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
var scanArea = (MediaQuery.of(ctx).size.width < 400 ||
MediaQuery.of(ctx).size.height < 400)
? 150.0
: 300.0;
// To ensure the Scanner view is properly sizes after rotation
// we need to listen for Flutter SizeChanged notification and update controller
return QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape(
borderColor: Colors.red,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: scanArea),
onPermissionSet: (ctrl, p) => _onPermissionSet(ctx, ctrl, p),
);
}
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
});
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
// data = result;
if(result != null){
// MaterialPageRoute(builder: (context) => Test());
Navigator.push(
this.context,
MaterialPageRoute(builder: (context) => const CountdownPage()));
}
});
});
}
void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
if (!p) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('no Permission')),
);
}
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
Widget buildTimeCard({required String time, required String header}) =>
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.black,
),
child:
Text(
time,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 60,
),
),
),
const SizedBox(height: 20),
Text(header)
],
);
}

Here is what I understand:
// convert hours & min into double
String getTime(int hours, int minutes) {
String hour = hours.toString();
String minute = minutes.toString();
if (hours < 10) {
hour = '0$hours';
}
if (minutes < 10) {
minute = '0$minutes';
}
return '$hour.$minute';
}
You can change this according to your need and convert a string into double/int.

If you want to encode a duration of 1h39m to a floating-point value of 1.39, you're just taking the number of leftover minutes, dividing it by 100, and adding the number of hours:
double encodeDurationToDouble(Duration duration) {
var hours = duration.inHours;
var minutes = duration.inMinutes % 60;
return hours + minutes / 100;
}
void main() {
var durations = [
const Duration(hours: 0, minutes: 59),
const Duration(hours: 3, minutes: 7),
const Duration(hours: 25, minutes: 12),
];
// Prints:
// 0.59
// 3.07
// 25.12
for (var d in durations) {
print(encodeDurationToDouble(d));
}
}
Although be aware of fundamental inaccuracies when using floating-point numbers.
You alternatively could encode 1h39m as an integer 139 by multiplying the number of hours by 100 and then adding the leftover minutes:
int encodeDurationToInt(Duration duration) {
var hours = duration.inHours;
var minutes = duration.inMinutes % 60;
return hours * 100 + minutes;
}
Or you could just use duration.inMinutes to obtain the total number of minutes instead of encoding it in uncommon ways. You still haven't explained why you want duration encoded in such a way, so it's unclear why you can't just use that.

You can use:
countdownDuration.inSeconds
which will return the whole Duration in seconds, also there are other units available like minutes, microseconds, and hours.
Update: You can calculate your duration in hours like this:
countdownDuration.inMinutes/60

Related

Flutter speech to text plugin continuous listening problem

Currently, I am developing an application which can connect an Arduino toy car and control it by voice commands using Bluetooth. For a better user experience, I display all the commands in a chat application user interface, so the user can see the commands he spoke (see the picture). But the problem is when the user spoke a sentence (more than 2 words), it split those words and adds them to the chat messages list separately while speaking. I need to hold this until the user finishes his speaking, and then add it to the chat messages list as a sentence. Not as separate words. I used flutter speech_to_text plugin. Let me know If you need additional information.
Here is the listening function
bool _isListening = false;
String _text = '';
late stt.SpeechToText _speech;
void _listen() async {
if (!_isListening) {
bool available = await _speech.initialize(
onStatus: (val) => print('onStatus: $val'),
onError: (val) => print('onError: $val'),
);
if (available) {
setState(() => _isListening = true);
_speech.listen(
onResult: (val) {
setState(() {
_text = val.recognizedWords.toLowerCase();
if (val.recognizedWords
.toLowerCase()
.contains('come back')) {
_cocoCommandInput = 7;
_addVoiceTextToChat(
_text, DateFormat.jms().format(DateTime.now()));
_isListening = false;
} else if (val.recognizedWords.toLowerCase().contains('coco')) {
_cocoCommandInput = 8;
_addVoiceTextToChat(
_text, DateFormat.jms().format(DateTime.now()));
_isListening = false;
} else {
_addVoiceTextToChat(
_text, DateFormat.jms().format(DateTime.now()));
}
});
});
}
} else {
setState(() => _isListening = false);
_speech.stop();
_text = 'Press the button and start speaking';
}
}
Here is the function for adding the messages to the chat messages list
void _addVoiceTextToChat(String text, String date) async {
if (text.isNotEmpty) {
try {
// connection!.output.add(Uint8List.fromList(utf8.encode("$text\r\n")));
// await connection!.output.allSent;
setState(() {
messages.add(_Message(text: text, date: date));
});
Future.delayed(const Duration(milliseconds: 300)).then((_) {
listScrollController.animateTo(
listScrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut);
});
} catch (e) {
print('shit something is wrong');
// setState(() {});
}
}
}
Here is the chat bubble list
Widget build(BuildContext context) {
final List<Row> list = messages.map((message) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
message.date,
style: greyText,
),
Container(
padding: const EdgeInsets.all(12.0),
margin: const EdgeInsets.only(bottom: 8.0, right: 8.0),
decoration: const BoxDecoration(
color: Colors.deepOrange,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(6),
topRight: Radius.circular(25),
bottomLeft: Radius.circular(25),
bottomRight: Radius.circular(25),
),
),
child: Text(
(text) {
return text == '/shrug' ? '¯\\_(ツ)_/¯' : text;
}(message.text.trim()),
style: whiteText),
),
],
),
],
);
}).toList();

What is problem with this Flutter Overlay

There are other ways of handling this overlay, but AFAIK this should work, and it does sometimes, but not always on Android. On Windows it appeared to work perfectly. I have the latest Flutter (3.0.3) and the latest Dart (2.17.5) and Flutter doctor shows no problems. I have tested this on 2 physical devices (Android 6.0.1 - level 23, and Android 11 - level 30). I have also tested it on an emulator (Android 11 - level 30). The results on all three were very similar - the overlay doesn't always show. I also tested it on Windows and it worked perfectly for all attempts (30).
The code below is a cut-down version of the code to demonstrate the problem. The problem is that the overlay does not always display. After a reboot it will often work well for about 10-12 reloads of the program, and then it is inconsistent, sometimes showing the overlay and sometimes not. As I said, it worked perfectly on Windows.
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
const D_OVERLAY_HEIGHT = 290.0;
const D_BTN_HEIGHT = 45.0;
void main() => runApp(OverlayTestApp());
class OverlayTestApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: OverlayTest(),
);
}
}
class OverlayTest extends StatefulWidget {
#override
_PageOverlayTest createState() => _PageOverlayTest();
}
//==== THE MAIN PAGE FOR THE APP ====//
class _PageOverlayTest extends State<OverlayTest> {
TextEditingController? _wController = TextEditingController();
ClassOverlay? _clOverlay;
#override
Widget build(BuildContext context) {
WidgetsFlutterBinding.ensureInitialized();
WidgetsBinding.instance
.addPostFrameCallback((_) => fnOnBuildComplete(context));
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back), onPressed: () => fnExit()),
title: Center(child: Text('Overlay Test'))),
body: WillPopScope(
onWillPop: () async => await _fnDispose(),
child: Column(
children: [
SizedBox(height: 50),
TextField(
controller: _wController,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(3),
),
),
style: TextStyle(fontSize: 16),
autofocus: true,
showCursor: true,
readOnly: true,
toolbarOptions: ToolbarOptions(
copy: false,
cut: false,
paste: false,
selectAll: false,
),
),
],
),
));
}
fnExit() {
dispose();
SystemNavigator.pop();
exit(0);
}
#override
void dispose() {
_fnDispose();
super.dispose();
}
Future<bool> _fnDispose() async {
if (_wController != null) {
_wController!.dispose();
_wController = null;
}
if (_clOverlay != null) {
_clOverlay!.fnDispose();
}
return true;
}
void fnOnBuildComplete(BuildContext context) async {
if (_clOverlay == null) {
double dScreenWidth = MediaQuery.of(context).size.width;
dScreenWidth = dScreenWidth < 510 ? dScreenWidth : 500;
double dScreenHeight = MediaQuery.of(context).size.height;
_clOverlay = ClassOverlay(dScreenWidth, dScreenHeight - 320);
}
_clOverlay!.fnInitContext(context, _wController!);
}
}
//==== CLASS FOR THE OVERLAY ====//
class ClassOverlay {
TextEditingController? _wCtlText;
OverlayEntry? _wOverlayEntry;
final double dScreenWidth;
final double dOverlayTop;
Material? _wOverlayWidget;
ClassOverlay(this.dScreenWidth, this.dOverlayTop) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top]);
_wOverlayWidget = fnCreateOverlayWidget(dScreenWidth: dScreenWidth);
}
//==== AFTER BUILD OF CALLER INITIALIZE WITH CONTEXT AND OVERLAYSTATE ====//
void fnInitContext(BuildContext context, TextEditingController wCtlText) {
try {
_wCtlText = wCtlText;
if (_wOverlayEntry != null) {
_wOverlayEntry!.remove();
_wOverlayEntry = null;
}
if (_wOverlayWidget == null) {
throw ('_wOverlayWidget is null');
}
_wOverlayEntry = OverlayEntry(builder: (context) {
return Positioned(left: 0, top: dOverlayTop, child: _wOverlayWidget!);
});
OverlayState? wOverlayState = Navigator.of(context).overlay;
if (wOverlayState == null) {
throw ('Failed to get OverlayState');
}
if (_wOverlayEntry == null) {
throw ('OverlayEntry is null');
}
wOverlayState.insert(_wOverlayEntry!);
if (!(wOverlayState.mounted)) {
wOverlayState.activate();
}
} catch (vError) {
_wCtlText!.text = '**** Fatal Error ${vError.toString()}';
}
}
void fnDispose() {
if (_wOverlayEntry != null) {
_wOverlayEntry!.remove();
_wOverlayEntry = null;
}
}
}
//==== CLASS - OVERLAY WIDGET ====//
Material fnCreateOverlayWidget({required double dScreenWidth}) {
final double dBtnWidth = (dScreenWidth - 60) / 10;
final double dLineSeparator = 10;
return Material(
child: Container(
height: (D_OVERLAY_HEIGHT),
width: dScreenWidth - 10,
padding: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.grey.shade500, width: 2),
borderRadius: BorderRadius.circular(10)),
child: Column(
children: [
fnCreateRow('1234567890', dBtnWidth),
SizedBox(height: dLineSeparator),
fnCreateRow('qwertyuiop', dBtnWidth),
SizedBox(height: dLineSeparator),
fnCreateRow('asdfghjkl', dBtnWidth),
SizedBox(height: dLineSeparator),
fnCreateRow(',zxcvbnm.', dBtnWidth),
SizedBox(height: dLineSeparator),
fnCreateRow('1234567890', dBtnWidth)
],
),
));
}
//==== FUNCTION - CREATE A ROW OF TEXT BUTTONS ====//
Row fnCreateRow(String sRow, double dBtnWidth) {
List<Widget> wRow = [];
double dSpace = sRow.length == 10 ? 0 : (dBtnWidth * (10 - sRow.length)) / 2;
if (dSpace > 0) {
wRow.add(SizedBox(width: dSpace));
}
for (int iNdx = 0; iNdx < sRow.length; iNdx++) {
double dFontSize = sRow[iNdx] == '.' || sRow[iNdx] == ',' ? 30 : 20;
wRow.add(SizedBox(
height: D_BTN_HEIGHT,
width: dBtnWidth,
child: TextButton(
onPressed: () {},
child: Text(sRow[iNdx],
style: TextStyle(
color: Colors.black,
fontSize: dFontSize,
fontWeight: FontWeight.w400)),
)));
}
if (dSpace > 0) {
wRow.add(SizedBox(width: dSpace));
}
return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: wRow);
}
It took a lot of time to find the reason for this problem. I was not aware of the fact that MediaQuery can return zero for the height or width because of a timing issue, and the application program must cater for that. It appears that this was working correctly, but the height and/or width was set to zero. There must be a lot of programs that don't check for that.

Alert dialog state not changing outside the function in flutter

When I tried to change the state from outside the function the state is not changing.
void _showAlertDialog(BuildContext context) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: const Text("Diabetes Prediction"),
content: StatefulBuilder(
return _predictedResult == "" ? Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
Text(loadingText),
],
) : Text(_predictedResult);
},
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Elevated button helps in calling the _showAlertDialog the loadingText is declared inside the class
ElevatedButton(
child: const Text("Predict"),
onPressed: () async {
// Pregnancies, Glucose, Blood Pressure, Insulin, Skin Thickness, Pedigree Function, Weight,
// Height, Age
_predictedResult = "";
loadingText = "";
var data = _formatData();
var result = Serializer().serialize(data);
_showAlertDialog(context);
setState(() {
loadingText = "Sending data to server...";
});
await Future.delayed(const Duration(seconds: 2), (){
});
setState(() {
loadingText = "Analyzing data...";
});
// await Future.delayed(const Duration(seconds: 2), (){
// print("data received");
// });
await _predict(result);
},
),
The output comes as Sending data to server...
String _predictedResult = '';
StreamController<String>? controller;
String loadingText = '';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[900],
body: Center(
child: ElevatedButton(
child: const Text("Predict"),
onPressed: () async {
controller = StreamController<String>();
// Pregnancies, Glucose, Blood Pressure, Insulin, Skin Thickness, Pedigree Function, Weight,
// Height, Age
_predictedResult = "";
loadingText = "";
_showAlertDialog(context);
controller!.add("Sending data to server...");
await Future.delayed(const Duration(seconds: 2), () {});
controller!.add("Analyzing data...");
await Future.delayed(const Duration(seconds: 2), () {
print("data received");
});
controller!.add("data received!");
},
),
),
);
}
void _showAlertDialog(BuildContext context) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: const Text("Diabetes Prediction"),
content: StreamBuilder(
stream: controller!.stream,
builder: (context, AsyncSnapshot<String> snap) {
return _predictedResult == ""
? Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
Text(snap.data ?? "Loading..."),
],
)
: Text(_predictedResult);
},
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Use Stream
Flutter Stream Basics for Beginners

Firebase Basic Query for datetime Flutter

I am trying to write a program to check if the time selected by the user already exists in the firebase firestore or not. If it does then I navigate back to the page where they select time again.
But as of now, I am succeeded in sending the date and time to firebase and but not the latter part.
DateTime _eventDate;
bool processing;
String _time;
bool conditionsStatisfied ;
#override
void initState() {
super.initState();
_eventDate = DateTime.now();
processing = false ;
}
inside showDatePicker()
setState(() {
print('inside the setState of listTile');
_eventDate = picked ;
});
inside the button (SAVE):
onPressed: () async {
if (_eventDate != null) {
final QuerySnapshot result = await FirebaseFirestore
.instance
.collection('events')
.where('event_date', isEqualTo: this._eventDate)
.where('selected_time', isEqualTo: this._time)
.get();
final List <DocumentSnapshot> document = result.docs;
if (document.length > 0) {
setState(() {
print('inside the method matching conditions');
showAlertDialogue(context);
});
}else{
final data = {
// "title": _title.text,
'selected_time ': this._time,
"event_date": this._eventDate
};
if (widget.note != null) {
await eventDBS.updateData(widget.note.id, data);
} else {
await eventDBS.create(data);
}
Navigator.pop(context);
setState(() {
processing = false;
});
}
};
some guidance needed on how do I resolve this issue!
Also, because of the else statement now the program won't write the date into firestore.
After Alot of research, I came to realize that if you send the data from calendar in DateTime format then, because of the timestamp at the end of the Date it becomes impossible to match to dates. Hence I formatted the DateTime value into (DD/MM/YYYY).
Here is the rest of the code for reference:
class _AddEventPageState extends State<AddEventPage> {
String _eventDate;
bool processing;
String _time;
#override
void initState() {
super.initState();
// _eventDate = DateTime.now();
processing = false ;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Please select a date'),),
body: Column(
children: [
hourMinute30Interval(),
Text('$_time'),
ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: <Widget>[
ListTile(
title: Text(
'$_eventDate'),
onTap: () async {
DateTime picked = await showDatePicker(context: context,
initialDate: DateTime.now(),
firstDate: DateTime(DateTime.now().year - 1),
lastDate: DateTime(DateTime.now().year + 10),);
if (picked != null) {
setState(() {
print('inside the setState of listTile');
_eventDate = DateFormat('dd/MM/yyyy').format(picked) ;
});
}
},
),
SizedBox(height: 10.0),
ListTile(
title: Center(
child: Text('Select time for appointment!', style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
processing
? Center(child: CircularProgressIndicator())
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(30.0),
color: Theme
.of(context)
.primaryColor,
child:MaterialButton(
child: Text('SAVE', style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
)),
onPressed: () async {
if (_eventDate != null) {
AddingEventsUsingRajeshMethod().getAvailableSlots(
_eventDate, _time).then((QuerySnapshot docs) async {
if (docs.docs.length == 1) {
showAlertDialogue(context);
}
else{
final data = {
// "title": _title.text,
'selected_time': this._time,
"event_date": _eventDate,
};
if (widget.note != null) {
await eventDBS.updateData(widget.note.id, data);
} else {
await eventDBS.create(data);
}
Navigator.pop(context);
setState(() {
processing = false;
});
}
});
}
}
),
),
),
],
),
],
),
);
}
showAlertDialogue method :
showAlertDialogue(BuildContext context) {
Widget okButton = FlatButton(onPressed: (){
Timer(Duration(milliseconds: 500), () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => datePicker()),
);
});
}, child: Text(' OK! '));
AlertDialog alert = AlertDialog(
title: Text('Slot unavailable'),
content: Text('This slot is already booked please select another slot'),
actions: [
okButton,
],
);
showDialog(context: context ,
builder: (BuildContext context){
return alert ;
}
);
}
The hourMinute30Interval() is nothing but a Widget that returns a timePickerSpinner which is a custom Widget. Tap here for that.
The Query that is run after passing the _eventDate and _time is in another class, and it goes as follows :
class AddingEventsUsingRajeshMethod {
getAvailableSlots(String _eventDate , String _time){
return FirebaseFirestore.instance
.collection('events')
.where('event_date', isEqualTo: _eventDate )
.where('selected_time', isEqualTo: _time)
.get();
}
}
You can name it something prettier ;)

How to "Stop Flutter from rebuilding widgets" after app is closed

So I built my first app. It's a weather app. So far everything works as intended. But there is one problem, whenever I close the app and then reopen it, everything is null (weather forecast, location name, max and min temperature). When I press the refresh button it null is updated to current condition. What I'd like t be able to do is, instead of showing null, I'd like the app to show the last refresh and update it if I press the refresh button. How can I do this.
Keep in mind I'm a newbie.
main.dart:
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'GetLocation.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() {
runApp(AuraWeather());
}
class AuraWeather extends StatefulWidget {
#override
_AuraWeatherState createState() => _AuraWeatherState();
}
class _AuraWeatherState extends State<AuraWeather> {
var apiKey = '5f10958d807d5c7e333ec2e54c4a5b16';
var description;
var city;
var maxTemp;
var minTemp;
var temp;
#override
Widget build(BuildContext context) {
setState(() {
getLocation();
});
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(displayBackground()),
),
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaY: 2, sigmaX: 2),
child: Container(
color: Colors.black.withOpacity(0.5),
child: Scaffold(
backgroundColor: Colors.transparent,
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
children: <Widget>[
Container(
child: Center(
child: Text(
'$city',
style: TextStyle(
fontSize: 35,
color: Colors.white,
),
),
),
),
Container(
child: Icon(
FontAwesomeIcons.locationArrow,
color: Colors.white,
),
),
Container(
margin: EdgeInsets.only(top: 80),
child: Text(
'$temp' + '°',
style: TextStyle(
fontSize: 50,
color: Colors.white,
fontWeight: FontWeight.w600),
),
),
],
),
Container(
margin: EdgeInsets.only(top: 30),
child: Icon(
Icons.wb_sunny,
color: Colors.white,
size: 100,
),
),
Container(
child: Center(
child: Text(
'$maxTemp ° | $minTemp °',
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
),
Container(
child: Text(
'$description',
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
Container(
child: FlatButton(
child: Icon(
Icons.refresh,
color: Colors.white,
size: 40,
),
color: Colors.transparent,
onPressed: () {
setState(
() {
getLocation();
},
);
},
),
),
],
),
),
),
),
),
);
}
// display background images based on current time
displayBackground() {
var now = DateTime.now();
final currentTime = DateFormat.jm().format(now);
if (currentTime.contains('AM')) {
return 'images/Blood.png';
} else if (currentTime.contains('PM')) {
return 'images/Sun.png';
}
}
//getLocation
void getLocation() async {
Getlocation getlocation = Getlocation();
await getlocation.getCurrentLocation();
print(getlocation.latitude);
print(getlocation.longitude);
print(getlocation.city);
city = getlocation.city;
getTemp(getlocation.latitude, getlocation.longitude);
}
//Get current temp
Future<void> getTemp(double lat, double lon) async {
http.Response response = await http.get(
'https://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$lon&appid=$apiKey&units=metric');
//print(response.body);
var dataDecoded = jsonDecode(response.body);
description = dataDecoded['weather'][0]['description'];
temp = dataDecoded['main']['temp'];
temp = temp.toInt();
maxTemp = dataDecoded['main']['temp_max'];
maxTemp = maxTemp.toInt();
minTemp = dataDecoded['main']['temp_min'];
minTemp = minTemp.toInt();
print(temp);
}
}
GetLocation.dart:
import 'package:geolocator/geolocator.dart';
class Getlocation {
double latitude;
double longitude;
var city;
//Get current location
Future<void> getCurrentLocation() async {
try {
Position position = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.best);
latitude = position.latitude;
longitude = position.longitude;
city = await getCityName(position.latitude, position.longitude);
} catch (e) {
print(e);
}
}
//Get city name
Future<String> getCityName(double lat, double lon) async {
List<Placemark> placemark =
await Geolocator().placemarkFromCoordinates(lat, lon);
print('city name is: ${placemark[0].locality}');
return placemark[0].locality;
}
}
Easy solution, would be to use SharedPreferences, then after you refresh weather save every variable to it, like
Future<void> _saveStringInSharedPrefs(String key, String value) async =>
SharedPreferences.getInstance().then((prefs) => prefs.setString(key, value));
for each of the values. You may also want to change vars to types, like double, String and so on.
Then you can add initState to your state, where you set each of the variables to SharedPreferences.getString(variable_key). It will be convenient to use SharedPreferences prefs = SharedPreferences.getInstance() and then call prefs.getString() You could add it in build, but you probably should not, build methods are meant to be really fast, so they could run up to 60 times/s.
Edit:
http.Response response = await http.get(
'https://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$lon&appid=$apiKey&units=metric');
//print(response.body);
await SharedPreferences.getInstance().then((prefs) {prefs.setString('weather_data', response.body}) // add this line
var dataDecoded = jsonDecode(response.body);
// (rest of the code)
This will save your json to SharedPrefs. Now you only need to extract the function that works with JSON and sets the variables. So it would look something like this:
void _setData(String jsonString) {
var dataDecoded = jsonDecode(response.body);
description = dataDecoded['weather'][0]['description'];
// here it would be safer to have temp be of type 'int' instead of 'var' and set it like this:
// temp = dataDecoded['main']['temp'].toInt();
temp = dataDecoded['main']['temp'];
temp = temp.toInt();
maxTemp = dataDecoded['main']['temp_max'];
maxTemp = maxTemp.toInt();
minTemp = dataDecoded['main']['temp_min'];
minTemp = minTemp.toInt();
}
You can then split getTemp like that:
Future<void> getTemp(double lat, double lon) async {
http.Response response = await http.get(
'https://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$lon&appid=$apiKey&units=metric');
//print(response.body);
_setData(response.body);
}
And then, when you launch the app you want it to load the values from SharedPreferences. So add this to _AuraWeatherState:
#override
void initState() {
super.initState();
_loadData();
}
Future<void> _loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_setData(prefs.getString('weather_data'));
}
This should work but I didn't have time to check if it executes. So if you have any other questions, I'll be glad to help :)

Categories

Resources