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

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 :)

Related

Flutter: TCP socket connection fails on real device

I'm posting this even though I already solved the issue, because I spent more than an hour trying to figure out what was causing it, and might help someone else.
I have a simple application that connects to a server through a TCP socket. It works fine inside the debugger with the device emulator, but when I deploy it on a real device it fails to connect immediately.
Further investigations led me to finding out that the socket was actually throwing the following exception:
SocketException: Connection failed (OS Error: Operation not permitted, errno = 1), address = 192.168.1.46, port = 40001
Code sample
Connect button opens a socket on _host:_port
Send Radar Distance button sends a message formatted like {"distance": _distance, "angle": _angle} on the socket.
Status and Message fields show info about the socket status and eventually useful infos.
main.dart
import 'package:flutter/material.dart';
import 'views/view_test.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const ViewTest(),
);
}
}
views/view_test.dart
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
const List<String> materialTypes = <String>['PLASTIC', 'GLASS'];
class ViewTest extends StatefulWidget {
const ViewTest({Key? key}) : super(key: key);
#override
State<ViewTest> createState() => _ViewTestState();
}
class _ViewTestState extends State<ViewTest> {
String _distance = "";
String _angle = "";
String _status = "";
String _message = "";
Socket? _socket;
final String _host = "192.168.1.46";
final int _port = 4001;
Future<void> _sendMessage(String message) async {
print("Sent message $message");
_socket!.write(message);
await Future.delayed(const Duration(seconds: 1));
}
void _connect(String ip, int port) async {
Socket? sock;
try {
sock =
await Socket.connect(ip, port, timeout: const Duration(seconds: 3));
_socket = sock;
setState(() {
_status = "Connected to $ip:$port.";
_message = "";
});
// listen for responses from the server
_socket!.listen(
// handle data from the server
(Uint8List data) {
final serverResponse = String.fromCharCodes(data);
setState(() {
_message = serverResponse;
});
print(serverResponse);
},
// handle errors
onError: (error) {
setState(() {
_status = "Disconnected.";
_message = "Error: $error";
});
print("Error: $error");
_socket!.destroy();
_socket = null;
},
// handle server ending connection
onDone: () {
setState(() {
_status = "Disconnected.";
_message = 'Server left.';
});
print('Server left.');
_socket!.destroy();
_socket = null;
},
);
} catch (e) {
setState(() {
_status = "Connection failed.";
_message = e.toString();
});
print("Error: ${e.toString()}");
}
}
void _disconnect() {
if (_socket != null) _socket!.destroy();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Center(child: Text("Radar Test")),
),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
setState(() {
_status = "";
_message = "";
});
_disconnect();
_connect(_host, _port);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_socket == null ? 'Connect' : 'Reconnect',
style: const TextStyle(fontSize: 22.0),
),
),
),
const SizedBox(height: 50.0),
TextField(
onChanged: (text) {
_distance = text;
},
keyboardType:
const TextInputType.numberWithOptions(decimal: false),
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(
RegExp(r'[0-9]+'), // this regex allows only decimal numbers
)
],
decoration: const InputDecoration(
hintText: '100',
border: UnderlineInputBorder(),
labelText: 'Distance',
),
),
TextField(
onChanged: (text) {
_angle = text;
},
keyboardType:
const TextInputType.numberWithOptions(decimal: false),
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(
RegExp(r'[0-9]+'), // this regex allows only decimal numbers
)
],
decoration: const InputDecoration(
hintText: '90',
border: UnderlineInputBorder(),
labelText: 'Angle',
),
),
const SizedBox(height: 50.0),
Text("Status: $_status"),
Text("Message: $_message"),
],
),
),
),
floatingActionButton: ElevatedButton(
onPressed: _socket == null
? null
: () {
// test
_sendMessage(
'{"distance": ${_distance.isEmpty ? 100 : int.parse(_distance)}, "angle": ${_angle.isEmpty ? 90 : int.parse(_angle)}}');
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Send Radar Distance',
style: TextStyle(fontSize: 22.0),
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
Turns out I had to give the application Internet permissions, as stated in Flutter docs about Networking. So I added the following line to AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
NB: AndroidManifest.xml manifest is located in the following location:
android > app > src > main > AndroidManifest.xml

How to convert time value into double/int value?

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

how to save data and show it even after closing the app in flutter

class SimpleDialog extends StatelessWidget {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _textEditingController =
TextEditingController();
String? baseurl;
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Base URL'),
content: Form(
key: _formKey,
child: TextFormField(
keyboardType: TextInputType.multiline,
maxLines: null,
controller: _textEditingController,
decoration: InputDecoration(
hintText: "Please Enter Base Url",
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(15)),
),
validator: (value) {
// return value!.isNotEmpty ? null : "Enter Base Url";
return Uri.parse(value.toString()).host == ''
? "Enter Base Url"
: null;
},
)),
actions: <Widget>[
Center(
child: InkWell(
onTap: () {
if (_formKey.currentState!.validate()) {
baseurl = _textEditingController.text.toString().trim();
checkBaseUrl(baseurl, context);
print('baseurl=====base------$baseurl');
}
},
child: Container(
width: 100,
height: 40,
padding: EdgeInsets.all(12.0),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(20.0),
),
child: Text(
"Connect",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
),
)
],
);
}
Future<void> checkBaseUrl(String baseurl, context) async {
Response response;
try {
response = await http.get(Uri.parse(baseurl));
print(await response);
if (response.statusCode == 200) {
var snackBar = SnackBar(content: Text('Connected Successfully'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
var name = await baseurl;
await Prefs().seturlBase(name);
print(await '---pred---prefp---$name');
baseurl = await Prefs().geturlBase().toString();
Navigator.of(context).pop();
} else {
var snackBar = SnackBar(content: Text('Connection failed'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
Navigator.of(context).pop();
}
} catch (e) {
var snackBar = SnackBar(content: Text('Connection failed'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
Navigator.of(context).pop();
}
Class Prefs:
Future<void> seturlBase(String urlBase) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString(this.urlBase, urlBase);
}
Future<String> geturlBase() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String urlBase;
urlBase = await prefs.getString(this.urlBase) ?? '';
return urlBase;
}
Check out the image, I need to save the URL as an input. After closing the app the URL should save and when users again open their app URL should be there for easy going.
How to achieve it, I used shared preferences.
.....................................................Thank you...........................
firt create a const for your base url
const String keyBaseUrl = 'baseUrl'
you can save it like this.
final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
sharedPrefs.setString(keyBaseUrl, baseurl);
and you can get it like this.
SharedPreferences prefs = await SharedPreferences.getInstance();
String? baseurl = prefs.getString(keyBaseUrl);

city selection in flutter weather app - Flutter

hope you doin' well
I'm a newbie to flutter and I'm working on a basic weather app as a starter. now, somehow everything is okay, except the city name. I don't know how to implement city search feature and get data from api based on the location. In my code, I defined city name manually.
here is my code:
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:hava_chitor/Carousel.dart';
import 'package:hava_chitor/UnderCarousel.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var temp;
var name;
var humidity;
var description;
var city = 'London';
CarouselController buttonCarouselController = CarouselController();
Future getWeather() async {
http.Response response = await http.get(
"http://api.openweathermap.org/data/2.5/weather?q=$city&units=metric&appid=apikey");
var results = jsonDecode(response.body);
setState(() {
this.temp = results['main']['temp'];
this.name = results['name'];
this.humidity = results['main']['humidity'];
this.description = results['weather'][0]['main'];
});
}
#override
void initState() {
super.initState();
this.getWeather();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hava Chitor?',
theme: ThemeData(
primaryColor: Color(0xff424242),
),
home: Scaffold(
drawer: Drawer(),
appBar: AppBar(
actions: [
Padding(
padding: const EdgeInsets.all(15),
child: GestureDetector(
onTap: () {
print('kir');
},
child: Icon(
Icons.add_circle_outline,
color: Color(0xff5d5f64),
),
),
)
],
backgroundColor: Colors.transparent,
elevation: 0,
iconTheme: IconThemeData(color: Color(0xff5d5f64)),
title: Text(
'Hava Chitor?',
style: TextStyle(
color: Color(0xff5d5f64),
fontFamily: 'Sans',
fontWeight: FontWeight.w700),
),
centerTitle: true,
),
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 1.0),
child: Column(
children: [
Carousel(name),
UnderCarousel(temp, description, humidity),
],
),
),
),
);
}
}
you need to add a TextField and a TextFieldController and call the function getWeather when the user finish writing the city name, something like this
String city;
TextEditingController textEditingController = TextEditingController();
TextField(
controller: textEditingController,
onSubmitted: (value){
city = textEditingController.text;
getWeather();
},
),
what you need to do is add TextField for the user to input the city name.
the way you can do that is shown before. The next thing you have to do is write a function to fetch weather data with city name.
var url =
'http://api.openweathermap.org/data/2.5/weather?q=\$city&units=metric&appid=apikey';
class CityWeatherData {
final String city, temprateure, humidity; //...
CityWeatherData({
this.city,
this.temprateure,
this.humidity,
// ...
});
factory CityWeatherData.fromJson(Map<String, dynamic> json) {
return CityWeatherData(
city: json['city'],
humidity: json['city']['humidity'],
temprateure: json['city']['temp'],
// ...
);
}
}
Future<CityWeatherData> fetchWeatherDataBycity() async {
final response = await http.get(url);
if (response.statusCode == 200) {
return CityWeatherData.fromJson(jsonDecode(response.body));
} else {
throw Exception('failed to fetch data.');
}
}
I depending on the JSON data you get you have to edit the fromjson method.
I hope this was helpful!

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 ;)

Categories

Resources