Skip to content

Commit d076422

Browse files
author
Ananthu P Kanive
authored
Merge pull request #10 from LonelyCpp/gps_weather
GPS weather
2 parents 8c4db68 + 463fe8e commit d076422

File tree

13 files changed

+160
-17
lines changed

13 files changed

+160
-17
lines changed

android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.ananthu.flutter_weather">
33

4+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
5+
46
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
57
calls FlutterMain.startInitialization(this); in its onCreate method.
68
In most cases you can leave this as-is, but you if you want to provide

android/gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
org.gradle.jvmargs=-Xmx1536M
2+
android.useAndroidX=true
3+
android.enableJetifier=true

ios/Podfile.lock

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
11
PODS:
22
- Flutter (1.0.0)
3+
- geolocator (5.0.1):
4+
- Flutter
5+
- google_api_availability (2.0.1):
6+
- Flutter
7+
- location_permissions (2.0.2):
8+
- Flutter
9+
- permission_handler (3.1.0):
10+
- Flutter
311
- shared_preferences (0.0.1):
412
- Flutter
513

614
DEPENDENCIES:
715
- Flutter (from `.symlinks/flutter/ios`)
16+
- geolocator (from `.symlinks/plugins/geolocator/ios`)
17+
- google_api_availability (from `.symlinks/plugins/google_api_availability/ios`)
18+
- location_permissions (from `.symlinks/plugins/location_permissions/ios`)
19+
- permission_handler (from `.symlinks/plugins/permission_handler/ios`)
820
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
921

1022
EXTERNAL SOURCES:
1123
Flutter:
1224
:path: ".symlinks/flutter/ios"
25+
geolocator:
26+
:path: ".symlinks/plugins/geolocator/ios"
27+
google_api_availability:
28+
:path: ".symlinks/plugins/google_api_availability/ios"
29+
location_permissions:
30+
:path: ".symlinks/plugins/location_permissions/ios"
31+
permission_handler:
32+
:path: ".symlinks/plugins/permission_handler/ios"
1333
shared_preferences:
1434
:path: ".symlinks/plugins/shared_preferences/ios"
1535

1636
SPEC CHECKSUMS:
1737
Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296
38+
geolocator: ac98a5fcdddd7fcbae404b136ab9c33bf08739c7
39+
google_api_availability: cd29400b9c6eb73b0978f609f0ad66f41dfa90b4
40+
location_permissions: 974d8fbc3ec5fed16908492a5e03bcb3372fcfd9
41+
permission_handler: a1b8c0f8c83b4e7201f9c04b9aef09979cc97f60
1842
shared_preferences: 5a1d487c427ee18fcd3ea1f2a131569481834b53
1943

2044
PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09

ios/Runner/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5+
<key>NSLocationWhenInUseUsageDescription</key>
6+
<string>Your location will be used to determine the current weather</string>
57
<key>CFBundleDevelopmentRegion</key>
68
<string>en</string>
79
<key>CFBundleExecutable</key>

lib/main.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class _AppStateContainerState extends State<AppStateContainer> {
5454
int themeCode = Themes.DARK_THEME_CODE;
5555
TemperatureUnit temperatureUnit = TemperatureUnit.celsius;
5656

57+
5758
@override
5859
initState() {
5960
super.initState();
@@ -63,7 +64,7 @@ class _AppStateContainerState extends State<AppStateContainer> {
6364
Themes.DARK_THEME_CODE;
6465
temperatureUnit = TemperatureUnit.values[
6566
sharedPref.getInt(CONSTANTS.SHARED_PREF_KEY_TEMPERATURE_UNIT) ??
66-
TemperatureUnit.kelvin];
67+
TemperatureUnit.celsius.index];
6768
this._theme = Themes.getTheme(themeCode);
6869
});
6970
});
@@ -95,7 +96,7 @@ class _AppStateContainerState extends State<AppStateContainer> {
9596
this.temperatureUnit = unit;
9697
});
9798
SharedPreferences.getInstance().then((sharedPref) {
98-
sharedPref.setInt(CONSTANTS.SHARED_PREF_KEY_THEME, unit.index);
99+
sharedPref.setInt(CONSTANTS.SHARED_PREF_KEY_TEMPERATURE_UNIT, unit.index);
99100
});
100101
}
101102
}

lib/src/api/weather_api_client.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ class WeatherApiClient {
1313
: assert(httpClient != null),
1414
assert(apiKey != null);
1515

16+
Future<String> getCityNameFromLocation(
17+
{double latitude, double longitude}) async {
18+
final url =
19+
'$baseUrl/data/2.5/weather?lat=$latitude&lon=$longitude&appid=$apiKey';
20+
print('fetching $url');
21+
final res = await this.httpClient.get(url);
22+
if (res.statusCode != 200) {
23+
throw HTTPException(res.statusCode, "unable to fetch weather data");
24+
}
25+
final weatherJson = json.decode(res.body);
26+
return weatherJson['name'];
27+
}
28+
1629
Future<Weather> getWeatherData(String cityName) async {
1730
final url = '$baseUrl/data/2.5/weather?q=$cityName&appid=$apiKey';
1831
print('fetching $url');

lib/src/bloc/weather_bloc.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class WeatherBloc extends Bloc<WeatherEvent, WeatherState> {
2424
yield WeatherLoading();
2525
try {
2626
final Weather weather =
27-
await weatherRepository.getWeather(event.cityName);
27+
await weatherRepository.getWeather(event.cityName, latitude: event.latitude, longitude: event.longitude);
2828
yield WeatherLoaded(weather: weather);
2929
} catch (exception) {
3030
print(exception);

lib/src/bloc/weather_event.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import 'package:equatable/equatable.dart';
2-
import 'package:meta/meta.dart';
32

43
abstract class WeatherEvent extends Equatable {
54
WeatherEvent([List props = const []]) : super(props);
65
}
76

87
class FetchWeather extends WeatherEvent {
98
final String cityName;
9+
final double longitude;
10+
final double latitude;
1011

11-
FetchWeather({@required this.cityName})
12-
: assert(cityName != null),
13-
super([cityName]);
12+
FetchWeather({this.cityName, this.longitude, this.latitude})
13+
: assert(cityName != null || longitude != null || latitude != null),
14+
super([cityName, longitude, latitude]);
1415
}
16+

lib/src/repository/weather_repository.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ class WeatherRepository {
77
WeatherRepository({@required this.weatherApiClient})
88
: assert(weatherApiClient != null);
99

10-
Future<Weather> getWeather(String cityName) async {
10+
Future<Weather> getWeather(String cityName, {double latitude, double longitude}) async {
11+
if(cityName == null){
12+
cityName = await weatherApiClient.getCityNameFromLocation(latitude: latitude, longitude: longitude);
13+
}
1114
var weather = await weatherApiClient.getWeatherData(cityName);
1215
var weathers = await weatherApiClient.getForecast(cityName);
1316
weather.forecast = weathers;

lib/src/screens/weather_screen.dart

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
1010
import 'package:flutter_weather/src/widgets/weather_widget.dart';
1111
import 'package:http/http.dart' as http;
1212
import 'package:intl/intl.dart';
13+
import 'package:geolocator/geolocator.dart';
14+
import 'package:permission_handler/permission_handler.dart';
1315

1416
enum OptionsMenu { changeCity, settings }
1517

@@ -32,7 +34,9 @@ class _WeatherScreenState extends State<WeatherScreen>
3234
void initState() {
3335
super.initState();
3436
_weatherBloc = WeatherBloc(weatherRepository: widget.weatherRepository);
35-
_weatherBloc.dispatch(FetchWeather(cityName: _cityName));
37+
_fetchWeatherWithLocation().catchError((error) {
38+
_fetchWeatherWithCity();
39+
});
3640
_fadeController = AnimationController(
3741
duration: const Duration(milliseconds: 1000), vsync: this);
3842
_fadeAnimation =
@@ -90,6 +94,7 @@ class _WeatherScreenState extends State<WeatherScreen>
9094
bloc: _weatherBloc,
9195
builder: (_, WeatherState weatherState) {
9296
if (weatherState is WeatherLoaded) {
97+
this._cityName = weatherState.weather.cityName;
9398
_fadeController.reset();
9499
_fadeController.forward();
95100
return WeatherWidget(
@@ -131,7 +136,7 @@ class _WeatherScreenState extends State<WeatherScreen>
131136
.theme
132137
.accentColor),
133138
),
134-
onPressed: _fetchWeather,
139+
onPressed: _fetchWeatherWithCity,
135140
)
136141
],
137142
);
@@ -158,16 +163,28 @@ class _WeatherScreenState extends State<WeatherScreen>
158163
backgroundColor: Colors.white,
159164
title: Text('Change city', style: TextStyle(color: Colors.black)),
160165
actions: <Widget>[
166+
FlatButton(
167+
child: Icon(
168+
Icons.my_location,
169+
color: Colors.black,
170+
size: 16,
171+
),
172+
onPressed: () {
173+
_fetchWeatherWithLocation().catchError((error) {
174+
_fetchWeatherWithCity();
175+
});
176+
Navigator.of(context).pop();
177+
}),
161178
FlatButton(
162179
child: Text(
163180
'ok',
164-
style: TextStyle(color: Colors.black),
181+
style: TextStyle(color: Colors.black, fontSize: 16),
165182
),
166183
onPressed: () {
167-
_fetchWeather();
184+
_fetchWeatherWithCity();
168185
Navigator.of(context).pop();
169186
},
170-
)
187+
),
171188
],
172189
content: TextField(
173190
autofocus: true,
@@ -195,7 +212,52 @@ class _WeatherScreenState extends State<WeatherScreen>
195212
}
196213
}
197214

198-
_fetchWeather() {
215+
_fetchWeatherWithCity() {
199216
_weatherBloc.dispatch(FetchWeather(cityName: _cityName));
200217
}
218+
219+
_fetchWeatherWithLocation() async {
220+
var permissionHandler = PermissionHandler();
221+
var permissionResult = await permissionHandler
222+
.requestPermissions([PermissionGroup.locationWhenInUse]);
223+
224+
switch (permissionResult[PermissionGroup.locationWhenInUse]) {
225+
case PermissionStatus.denied:
226+
case PermissionStatus.unknown:
227+
print('location permission denied');
228+
_showLocationDeniedDialog(permissionHandler);
229+
throw Error();
230+
}
231+
232+
Position position = await Geolocator()
233+
.getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
234+
print(position);
235+
_weatherBloc.dispatch(FetchWeather(
236+
longitude: position.longitude, latitude: position.latitude));
237+
}
238+
239+
void _showLocationDeniedDialog(PermissionHandler permissionHandler) {
240+
showDialog(
241+
context: context,
242+
barrierDismissible: true,
243+
builder: (BuildContext context) {
244+
return AlertDialog(
245+
backgroundColor: Colors.white,
246+
title: Text('Location is disabled :(',
247+
style: TextStyle(color: Colors.black)),
248+
actions: <Widget>[
249+
FlatButton(
250+
child: Text(
251+
'Enable!',
252+
style: TextStyle(color: Colors.green, fontSize: 16),
253+
),
254+
onPressed: () {
255+
permissionHandler.openAppSettings().then((val) => print(val));
256+
Navigator.of(context).pop();
257+
},
258+
),
259+
],
260+
);
261+
});
262+
}
201263
}

0 commit comments

Comments
 (0)