import 'package:flutter/material.dart'; import 'global_helper_functions.dart'; import 'package:intl/intl.dart'; import 'package:cached_network_image/cached_network_image.dart'; class Weather extends StatefulWidget { final String weatherConditions; final String weatherConditionsIcon; final int weatherLocationID; final double weatherCurrentTemp; final double weatherCurrentPressure; final int weatherCurrentHumidity; final double weatherCurrentTempMin; final double weatherCurrentTempMax; final int weatherCurrentVisibility; final double weatherCurrentWindSpd; final int weatherCurrentWindDir; final int weatherCurrentSunrise; final int weatherCurrentSunset; final int weatherCurrentDT; final int timeOffSet; final void Function() parentAction; Weather({ Key key, this.weatherLocationID, this.weatherConditions, this.weatherConditionsIcon, this.weatherCurrentTemp, this.weatherCurrentPressure, this.weatherCurrentHumidity, this.weatherCurrentTempMin, this.weatherCurrentTempMax, this.weatherCurrentVisibility, this.weatherCurrentWindSpd, this.weatherCurrentWindDir, this.weatherCurrentSunrise, this.weatherCurrentSunset, this.weatherCurrentDT, this.timeOffSet, this.parentAction, }) : super(key: key); @override _WeatherState createState() => _WeatherState(); } class _WeatherState extends State { @override Widget build(BuildContext context) { Map updatedAt = get3WayTime(widget.weatherCurrentDT, widget.timeOffSet); Map sunrise = get3WayTime(widget.weatherCurrentSunrise, widget.timeOffSet); Map sunset = get3WayTime(widget.weatherCurrentSunset, widget.timeOffSet); final String windDirection = getDirection(widget.weatherCurrentWindDir); final int visibilityMeters = getMeters(widget.weatherCurrentVisibility); final double textHeight = 1.5; final double _deviceWidth = MediaQuery.of(context).size.width; final int _now = newTimeStamp(); final int _staleness = (_now - widget.weatherCurrentDT); final bool _stale = (_staleness > 3600); Expanded _refresh() { return _stale ? Expanded( flex: 4, child: Container( child: IconButton( icon: Icon(Icons.refresh), tooltip: 'refresh weather', iconSize: 60, color: candyApple, onPressed: () { widget.parentAction(); }, ), ),) : Expanded(child: Container()); } Expanded _staleNotice() { return _stale ? Expanded( flex: 6, child: RichText( textAlign: TextAlign.justify, text: TextSpan( children: [ TextSpan( text: 'The Current Weather Report is stale by more than ', style: TextStyle( color: Colors.black, fontSize: 16, ), ), TextSpan( text: '${(_staleness / 3600).floor()} ', style: TextStyle( color: candyApple, fontSize: 16, ), ), TextSpan( text: 'hours', style: TextStyle( color: candyApple, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: '. You probably want to check your network connection and then ', style: TextStyle( color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'refresh', style: TextStyle( color: candyApple, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: '.', style: TextStyle( color: Colors.black, fontSize: 16, ), ), ], ), ), ) : Expanded(child: Container()); } Column _currentConditions() { return Column( children: [ Text( 'Current Conditions:', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), Text( '${weatherConditions(widget.weatherConditions)}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 18, ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'Humidity: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${widget.weatherCurrentHumidity}%', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), ], ); } Column _temps() { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ RichText( // temp text: TextSpan( children: [ TextSpan( text: 'Temp: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${widget.weatherCurrentTemp.round()}\u00B0', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'F', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: ', ${(((widget.weatherCurrentTemp - 32) * 5) / 9).round()}\u00B0', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'C', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ), ), RichText( // temp_min text: TextSpan( children: [ TextSpan( text: 'Min: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${widget.weatherCurrentTempMin.round()}\u00B0', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'F', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: ', ${(((widget.weatherCurrentTempMin - 32) * 5) / 9).round()}\u00B0', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'C', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ), ), RichText( // temp_max text: TextSpan( children: [ TextSpan( text: 'Max: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${widget.weatherCurrentTempMax.round()}\u00B0', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'F', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: ', ${(((widget.weatherCurrentTempMax - 32) * 5) / 9).round()}\u00B0', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'C', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ), ), ], ); } Column _updateTime() { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( 'Updated at:', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'There: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${updatedAt['There']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'Here: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${updatedAt['Here']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'UTC: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${updatedAt['UTC']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), ], ); } Column _wind() { return Column( children: [ RichText( text: TextSpan( children: [ TextSpan( text: 'Wind: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${widget.weatherCurrentWindSpd.round()}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'mph', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: ',', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: '${(widget.weatherCurrentWindSpd * 1.60934).round()}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'kph', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: ', ${(widget.weatherCurrentWindSpd * 0.868976).round()}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'knots', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'From: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${widget.weatherCurrentWindDir}\u00B0, ', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: '($windDirection)', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ), ), ], ); } Column _sunRise() { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( 'Sunrise:', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'There: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${sunrise['There']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'Here: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${sunrise['Here']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'UTC: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${sunrise['UTC']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), ], ); } Column _sunSet() { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( 'Sunset:', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'There: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${sunset['There']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'Here: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${sunset['Here']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'UTC: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${sunset['UTC']}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), ], ); } Column _pressure() { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ RichText( text: TextSpan( children: [ TextSpan( text: 'Pressure: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: '${widget.weatherCurrentPressure}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'mb', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: ', ', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: '${(widget.weatherCurrentPressure * 29.53).round() / 1000}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'inHg', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), TextSpan( text: ',', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: '${(widget.weatherCurrentPressure * 75.0062).round() / 100}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: 'mmHg', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ), ), ], ); } Column _visibility() { return Column( children: [ Text( 'Visibility:', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), RichText( text: TextSpan( children: [ TextSpan( text: '${widget.weatherCurrentVisibility}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: ' feet,', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: '$visibilityMeters', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), TextSpan( text: ' meters', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'ID: ', style: TextStyle( height: textHeight, color: candyApple, fontSize: 16, ), ), TextSpan( text: '${widget.weatherLocationID}', style: TextStyle( height: textHeight, color: Colors.black, fontSize: 16, ), ), ], ), ), ], ); } Container _icon() { return Container( width: 100, child: CachedNetworkImage( imageUrl: 'https://openweathermap.org/img/w/${widget.weatherConditionsIcon}.png', ), ); } if (widget.weatherConditions == '') { return Container( margin: EdgeInsets.symmetric( vertical: 3, horizontal: 6, ), padding: myBoxPadding, decoration: myBoxDecoration(_stale ? Colors.grey : ivory), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Check Data Connection', style: TextStyle( height: textHeight, fontSize: 16, ), ), Text( 'Probably Offline', style: TextStyle( height: textHeight, fontSize: 16, ), ), ], ), ); } else if (_deviceWidth < 400) { return Container( margin: EdgeInsets.symmetric( vertical: 3, horizontal: 6, ), padding: myBoxPadding, decoration: myBoxDecoration(_stale ? Colors.grey : ivory), child: Column( children: [ Padding( padding: EdgeInsets.only( top: 16.0, bottom: 2.0, right: 25.0, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _refresh(), _staleNotice(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _icon(), _currentConditions(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _updateTime(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _temps(), _wind(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _pressure(), _visibility(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _sunRise(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _sunSet(), ], ), ), ], ), ); } else if ((_deviceWidth >= 400) && (_deviceWidth < 650)) { return Container( margin: EdgeInsets.symmetric( vertical: 3, horizontal: 6, ), padding: myBoxPadding, decoration: myBoxDecoration(_stale ? Colors.grey : ivory), child: Column( children: [ Padding( padding: EdgeInsets.only( top: 16.0, bottom: 2.0, right: 25.0, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _refresh(), _staleNotice(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _icon(), _currentConditions(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _updateTime(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _temps(), _wind(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _sunRise(), _pressure(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _sunSet(), _visibility(), ], ), ), ], ), ); } else if ((_deviceWidth >= 650) && (_deviceWidth < 1000)) { return Container( margin: EdgeInsets.symmetric( vertical: 3, horizontal: 6, ), padding: myBoxPadding, decoration: myBoxDecoration(_stale ? Colors.grey : ivory), child: Column( children: [ Padding( padding: EdgeInsets.only( top: 16.0, bottom: 2.0, right: 25.0, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _refresh(), _staleNotice(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _icon(), _currentConditions(), _temps(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _updateTime(), _visibility(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _sunRise(), _wind(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _sunSet(), _pressure(), ], ), ), ], ), ); } else if (_deviceWidth >= 1000) { return Container( margin: EdgeInsets.symmetric( vertical: 3, horizontal: 6, ), padding: myBoxPadding, decoration: myBoxDecoration(_stale ? Colors.grey : ivory), child: Column( children: [ Padding( padding: EdgeInsets.only( top: 16.0, bottom: 2.0, right: 25.0, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _refresh(), _staleNotice(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _icon(), _currentConditions(), _temps(), _visibility(), _updateTime(), ], ), ), Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _pressure(), _sunRise(), _sunSet(), _wind(), ], ), ), ], ), ); } } Map get3WayTime(int secondsFromEpoch, int timeOffSet) { DateTime utcTime = DateTime.fromMillisecondsSinceEpoch(secondsFromEpoch * 1000).toUtc(); DateTime thereTime = utcTime.add(Duration(minutes: timeOffSet)); DateTime hereTime = utcTime.toLocal(); String there = (timeOffSet != 2000) ? DateFormat('EEEE MMMM d, HH:mm').format(thereTime) : 'UNKNOWN, INVALID'; String here = DateFormat('EEEE MMMM d, HH:mm').format(hereTime); String utc = DateFormat('EEEE MMMM d, HH:mm').format(utcTime); return { 'There': there, 'Here': here, 'UTC': utc, }; } String getElapsedTimeString(int then, int now) { final int elapsedMin = ((now - then) / 60).round(); final int elapsedHours = (elapsedMin / 60).floor(); final int remainingMin = elapsedMin % 60; return (remainingMin < 10) ? elapsedHours.toString() + ':0' + remainingMin.toString() : elapsedHours.toString() + ':' + remainingMin.toString(); } int getMeters(int feet) { return (feet != null) ? (feet * 0.3048).round() : null; } String getDirection(int direction) { if ((direction < 22.5) || (direction >= 337.5)) { return "N"; } else if ((direction >= 22.5) && (direction < 67.5)) { return "NE"; } else if ((direction >= 67.5) && (direction < 112.5)) { return "E"; } else if ((direction >= 112.5) && (direction < 157.5)) { return "SE"; } else if ((direction >= 157.5) && (direction < 202.5)) { return "S"; } else if ((direction >= 202.5) && (direction < 247.5)) { return "SW"; } else if ((direction >= 247.5) && (direction < 292.5)) { return "W"; } else if ((direction >= 292.5) && (direction < 337.5)) { return "NW"; } return "none"; } }