diff --git a/.vscode/launch.json b/.vscode/launch.json index 86d6e4f..6edd267 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,15 +5,16 @@ "version": "0.2.0", "configurations": [ { - "name": "wetter", - "request": "launch", - "type": "dart" - }, - { - "name": "wetter (profile mode)", + "name": "wetter-simulator", "request": "launch", "type": "dart", - "flutterMode": "profile" + "deviceId": "emulator-5554" + }, + { + "name": "wetter-handy", + "request": "launch", + "type": "dart", + "deviceId": "20113d66", }, { "name": "wetter (release mode)", diff --git a/lib/components/compact_weather_data.dart b/lib/components/compact_weather_data.dart index aca713f..2331bf8 100644 --- a/lib/components/compact_weather_data.dart +++ b/lib/components/compact_weather_data.dart @@ -26,16 +26,7 @@ class _CompactWeatherDataState extends State { style: Theme.of(context).textTheme.headline5, ), const SizedBox(height: 10), - Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _weatherData(), - Expanded( - child: Padding( - padding: const EdgeInsets.all(35), - child: FittedBox( - fit: BoxFit.fill, - child: Icon(weatherIcon( - widget.dwm.prevalentWeatherIcon()))))), - ]), + _weatherData(), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -63,56 +54,65 @@ class _CompactWeatherDataState extends State { } Widget _weatherData() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - "${widget.dwm.dailyMaxTemp()} °C ", - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(fontStyle: FontStyle.normal), - ), - const Icon( - Icons.circle, - size: 10, - color: Colors.grey, - ), - Text( - " ${widget.dwm.dailyMinTemp()} °C", - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(fontStyle: FontStyle.normal, color: Colors.grey), - ) - ], - ), - Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ - const Icon(WeatherIcons.barometer), - const SizedBox(width: 10), - Text("${widget.dwm.dailyAveragePressure().toString()} hPa") - ]), - const SizedBox(height: 5), - Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ - const Icon(WeatherIcons.raindrop), - const SizedBox(width: 10), - Text("${widget.dwm.dailyPrecipitation().toString()} mm") - ]), - const SizedBox(height: 5), - Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ - const Icon(WeatherIcons.cloud), - const SizedBox(width: 10), - Text("${widget.dwm.dailyAverageCloudCover().toString()} %") - ]), - const SizedBox(height: 5), - Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ - const Icon(WeatherIcons.windy), - const SizedBox(width: 10), - Text("${widget.dwm.dailyAverageWindSpeed().toString()} km/h") - ]), - ], - ); + return Row(children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + "${widget.dwm.dailyMaxTemp()} °C ", + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(fontStyle: FontStyle.normal), + ), + const Icon( + Icons.circle, + size: 10, + color: Colors.grey, + ), + Text( + " ${widget.dwm.dailyMinTemp()} °C", + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(fontStyle: FontStyle.normal, color: Colors.grey), + ) + ], + ), + Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ + const Icon(WeatherIcons.barometer), + const SizedBox(width: 10), + Text("${widget.dwm.dailyAveragePressure().toString()} hPa") + ]), + const SizedBox(height: 5), + Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ + const Icon(WeatherIcons.raindrop), + const SizedBox(width: 10), + Text("${widget.dwm.dailyPrecipitation().toString()} mm") + ]), + const SizedBox(height: 5), + Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ + const Icon(WeatherIcons.cloud), + const SizedBox(width: 10), + Text("${widget.dwm.dailyAverageCloudCover().toString()} %") + ]), + const SizedBox(height: 5), + Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ + const Icon(WeatherIcons.windy), + const SizedBox(width: 10), + Text("${widget.dwm.dailyAverageWindSpeed().toString()} km/h") + ]), + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 35), + child: FittedBox( + fit: BoxFit.contain, + child: + Icon(weatherIcon(widget.dwm.prevalentWeatherIcon()))))), + ]); } } diff --git a/lib/main.dart b/lib/main.dart index 2ed6092..f9c0d6b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:wetter/components/compact_weather_data.dart'; import 'package:wetter/components/main_menu.dart'; -import 'package:wetter/models/daily_weather_model.dart'; -import 'package:wetter/services/brightsky_api_service.dart'; +import 'package:wetter/screens/main_weather_view.dart'; void main() => runApp(const MyApp()); @@ -15,25 +12,6 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - SharedPreferences? prefs; - int _daysToLoad = 7; - late Future> dailyWeather; - - @override - void initState() { - super.initState(); - initializePreferences(); - dailyWeather = - BrightSkyAPI.fetchForecastForTimeRange(DateTime.now(), _daysToLoad); - } - - Future initializePreferences() async { - prefs = await SharedPreferences.getInstance(); - setState(() { - _daysToLoad = prefs!.getInt("daysToLoad") ?? 7; - }); - } - @override Widget build(BuildContext context) { return MaterialApp( @@ -46,43 +24,29 @@ class _MyAppState extends State { bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'), ), ), - home: Scaffold( + home: DefaultTabController( + length: 3, + child: Scaffold( appBar: AppBar( - title: const Text('Wetter in Harburg'), + title: const Text('Wetter'), + bottom: const TabBar( + tabs: [ + Tab(icon: Text("Hamburg")), + Tab(icon: Text("Frankfurt")), + Tab(icon: Text("Freiburg")), + ], + ), ), drawer: const NavBar(), - body: RefreshIndicator( - onRefresh: () { - return Future(() { - setState(() { - _daysToLoad = prefs?.getInt("daysToLoad") ?? 7; - dailyWeather = BrightSkyAPI.fetchForecastForTimeRange( - DateTime.now(), _daysToLoad); - }); - }); - }, - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: Center( - child: FutureBuilder>( - future: BrightSkyAPI.fetchForecastForTimeRange( - DateTime.now(), _daysToLoad), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Column( - children: snapshot.data! - .map((e) => CompactWeatherData(e)) - .toList()); - } else if (snapshot.hasError) { - return Text('${snapshot.error}'); - } - - // By default, show a loading spinner. - return const CircularProgressIndicator(); - }, - ), - ), - ))), + body: const TabBarView( + children: [ + MainWeatherView(53.46, 9.98), + MainWeatherView(50.15, 8.70), + MainWeatherView(47.99, 7.85) + ], + ), + ), + ), ); } } diff --git a/lib/screens/main_weather_view.dart b/lib/screens/main_weather_view.dart new file mode 100644 index 0000000..e94efd3 --- /dev/null +++ b/lib/screens/main_weather_view.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../components/compact_weather_data.dart'; +import '../models/daily_weather_model.dart'; +import '../services/brightsky_api_service.dart'; + +class MainWeatherView extends StatefulWidget { + final double lat; + final double lon; + + const MainWeatherView(this.lat, this.lon, {Key? key}) : super(key: key); + + @override + State createState() => _MainWeatherViewState(); +} + +class _MainWeatherViewState extends State + with AutomaticKeepAliveClientMixin { + SharedPreferences? prefs; + int _daysToLoad = 7; + late Future> dailyWeather; + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + initializePreferences(); + dailyWeather = BrightSkyAPI.fetchForecastForTimeRange( + DateTime.now(), _daysToLoad, widget.lat, widget.lon); + } + + Future initializePreferences() async { + prefs = await SharedPreferences.getInstance(); + setState(() { + _daysToLoad = prefs!.getInt("daysToLoad") ?? 7; + }); + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + onRefresh: () { + return Future(() { + setState(() { + _daysToLoad = prefs?.getInt("daysToLoad") ?? 7; + dailyWeather = BrightSkyAPI.fetchForecastForTimeRange( + DateTime.now(), _daysToLoad, widget.lat, widget.lon); + }); + }); + }, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Center( + child: FutureBuilder>( + future: BrightSkyAPI.fetchForecastForTimeRange( + DateTime.now(), _daysToLoad, widget.lat, widget.lon), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Column( + children: snapshot.data! + .map((e) => Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: CompactWeatherData(e))) + .toList()); + } else if (snapshot.hasError) { + return Text('${snapshot.error}'); + } + + // By default, show a loading spinner. + return const CircularProgressIndicator(); + }, + ), + ), + )); + } +} diff --git a/lib/services/brightsky_api_service.dart b/lib/services/brightsky_api_service.dart index 07344d4..c3b20bd 100644 --- a/lib/services/brightsky_api_service.dart +++ b/lib/services/brightsky_api_service.dart @@ -3,11 +3,12 @@ import 'package:http/http.dart' as http; import 'package:wetter/models/daily_weather_model.dart'; class BrightSkyAPI { - static Future fetchForecastOfSingleDay(DateTime d) async { + static Future fetchForecastOfSingleDay( + DateTime d, double lat, double lon) async { String dt = "${d.year}-${d.month}-${d.day}"; final response = await http.get( Uri.parse( - 'https://api.brightsky.dev/weather?lat=53.43&lon=10&date=$dt&tz=Europe/Berlin'), + 'https://api.brightsky.dev/weather?lat=$lat&lon=$lon&date=$dt&tz=Europe/Berlin'), headers: {"Accept": "application/json"}); if (response.statusCode == 200) { return DailyWeatherModel.fromJson(jsonDecode(response.body)); @@ -17,13 +18,13 @@ class BrightSkyAPI { } static Future> fetchForecastForTimeRange( - DateTime from, int noOfDays) async { + DateTime from, int noOfDays, double lat, double lon) async { DateTime until = from.add(Duration(days: noOfDays)); String fromDay = "${from.year}-${from.month}-${from.day}"; String untilDay = "${until.year}-${until.month}-${until.day}"; final response = await http.get( Uri.parse( - 'https://api.brightsky.dev/weather?lat=53.43&lon=10&date=$fromDay&last_date=$untilDay&tz=Europe/Berlin'), + 'https://api.brightsky.dev/weather?lat=$lat&lon=$lon&date=$fromDay&last_date=$untilDay&tz=Europe/Berlin'), headers: {"Accept": "application/json"}); final Map responseAsJson = jsonDecode(response.body); if (response.statusCode == 200) {