Skip to content

Commit 2105eea

Browse files
author
Tensor-Programming
authored
Stream based command used for location
RxDart added, Stream based Command used to fetch location. UI was reworked for more symmetry and a better look. lastKnownLocation method is not used to get location anymore because it is inconsistent. CurrentLocation method is now used in its place. New Object made to combine the two boolean Obervables so that they can disable both the location and weather commands based on more complex logic.
1 parent 5192818 commit 2105eea

File tree

5 files changed

+96
-27
lines changed

5 files changed

+96
-27
lines changed

lib/const.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
const API_KEY = '[OPEN WEATHER MAP API KEY HERE]';
1+
const API_KEY = '[API KEY HERE]';

lib/main.dart

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import 'package:weather/model/model_command.dart';
55
import 'package:weather/model/model.dart';
66
import 'package:weather/model/model_provider.dart';
77

8+
//unimport if you need the initState function stuff.
9+
10+
// import 'package:geolocation/geolocation.dart';
11+
812
import 'package:rx_widgets/rx_widgets.dart';
913

1014
import 'package:http/http.dart' as http;
@@ -37,8 +41,20 @@ class MyHomePage extends StatefulWidget {
3741
}
3842

3943
class _MyHomePageState extends State<MyHomePage> {
44+
@override
45+
void initState() {
46+
super.initState();
47+
//If Geolocation is unable to get location in emulator, uncomment this and then restart the program.
48+
//This tends to fix the error and you can see if the GPS is actually getting the location.
49+
// var x = Geolocation.locationUpdates(
50+
// accuracy: LocationAccuracy.best, inBackground: false);
51+
// x.listen((d) => print(d.isSuccessful));
52+
}
53+
4054
@override
4155
Widget build(BuildContext context) {
56+
//call to getGpsCommand handler on build. If GPS is active comes back with true, else false.
57+
ModelProvider.of(context).getGpsCommand.call();
4258
return Scaffold(
4359
appBar: AppBar(
4460
title: Text('Weather App'),
@@ -48,20 +64,25 @@ class _MyHomePageState extends State<MyHomePage> {
4864
child: RxLoader<bool>(
4965
radius: 20.0,
5066
commandResults: ModelProvider.of(context).getGpsCommand,
51-
dataBuilder: (context, data) =>
52-
Text(data ? "GPS Active" : "GPS Inactive"),
67+
dataBuilder: (context, data) => Row(
68+
children: <Widget>[
69+
Text(data ? "GPS is Active" : "GPS is Inactive"),
70+
// Added logic to change the Icon when GPS is inactive.
71+
IconButton(
72+
icon: Icon(
73+
data ? Icons.gps_fixed : Icons.gps_not_fixed),
74+
onPressed: ModelProvider.of(context).getGpsCommand,
75+
),
76+
],
77+
),
5378
placeHolderBuilder: (context) => Text("Push the Button"),
5479
errorBuilder: (context, exception) => Text("$exception"),
5580
),
5681
),
5782
),
58-
IconButton(
59-
icon: Icon(Icons.gps_fixed),
60-
onPressed: ModelProvider.of(context).getGpsCommand,
61-
),
6283
PopupMenuButton<int>(
6384
padding: EdgeInsets.all(1.0),
64-
tooltip: "Select how much data you want",
85+
tooltip: "Select how many cities you want",
6586
onSelected: (int item) {
6687
ModelProvider.of(context).addCitiesCommand(item);
6788
},
@@ -70,7 +91,7 @@ class _MyHomePageState extends State<MyHomePage> {
7091
.map((number) => PopupMenuItem(
7192
value: number,
7293
child: Center(
73-
child: Text(number.toString()),
94+
child: Text("$number Cities"),
7495
),
7596
))
7697
.toList();
@@ -95,32 +116,32 @@ class _MyHomePageState extends State<MyHomePage> {
95116
padding: EdgeInsets.all(10.0),
96117
child: Row(
97118
children: <Widget>[
98-
Container(
99-
padding: EdgeInsets.all(5.0),
119+
//changed to expanded for more consistency.
120+
Expanded(
100121
child: WidgetSelector(
101122
buildEvents: ModelProvider
102123
.of(context)
103-
.updateLocationCommand
124+
.updateWeatherCommand
104125
.canExecute,
105126
onTrue: MaterialButton(
106127
elevation: 5.0,
107128
color: Colors.blueGrey,
108129
child: Text("Get the Weather"),
109130
onPressed: ModelProvider
110131
.of(context)
111-
.updateLocationCommand
112-
.execute,
132+
.updateLocationStreamCommand
133+
.call,
113134
),
114135
onFalse: MaterialButton(
115136
elevation: 0.0,
116137
color: Colors.blueGrey,
117138
onPressed: null,
118-
child: Text("Loading..."),
139+
child: Text("Loading Data....."),
119140
),
120141
),
121142
),
122-
Container(
123-
padding: EdgeInsets.only(left: 90.0),
143+
//changed to expanded for more consistency.
144+
Expanded(
124145
child: Column(
125146
children: <Widget>[
126147
SliderItem(

lib/model/model_command.dart

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,90 @@
11
import 'package:rx_command/rx_command.dart';
22
import 'package:geolocation/geolocation.dart';
3+
import 'package:rxdart/rxdart.dart';
4+
import 'package:rxdart/streams.dart';
35

46
import 'package:weather/model/model.dart';
57
import 'package:weather/model/weather_repo.dart';
68

79
class ModelCommand {
810
final WeatherRepo weatherRepo;
911

10-
final RxCommand<Null, LocationResult> updateLocationCommand;
12+
// final RxCommand<Null, LocationResult> updateLocationCommand;
1113
final RxCommand<LocationResult, List<WeatherModel>> updateWeatherCommand;
1214
final RxCommand<Null, bool> getGpsCommand;
1315
final RxCommand<bool, bool> radioCheckedCommand;
1416
final RxCommand<int, Null> addCitiesCommand;
17+
final RxCommand<dynamic, LocationResult> updateLocationStreamCommand;
1518

1619
ModelCommand._(
1720
this.weatherRepo,
18-
this.updateLocationCommand,
21+
// this.updateLocationCommand,
1922
this.updateWeatherCommand,
2023
this.getGpsCommand,
2124
this.radioCheckedCommand,
2225
this.addCitiesCommand,
26+
this.updateLocationStreamCommand,
2327
);
2428

2529
factory ModelCommand(WeatherRepo repo) {
2630
final _getGpsCommand = RxCommand.createAsync2<bool>(repo.getGps);
2731

2832
final _radioCheckedCommand = RxCommand.createSync3<bool, bool>((b) => b);
2933

30-
final _updateLocationCommand = RxCommand.createAsync2<LocationResult>(
31-
repo.updateLocation, _getGpsCommand.results);
34+
//A combined Observable of the GPS and Radio observables using And logic. If one is false then false will be returned.
35+
//This allows us to have a more dynamic set of circumstances for shutting down the buttons.
36+
// final Observable<bool> _boolCombine = Observable
37+
// .combineLatest2(_getGpsCommand.results, _radioCheckedCommand.results,
38+
// (gps, radio) => gps && radio)
39+
// .distinctUnique();
40+
41+
//Two Observables needed because they are only cold observables (single subscription).
42+
final _boolCombineA =
43+
CombineObs(_getGpsCommand.results, _radioCheckedCommand.results)
44+
.combinedObservable;
45+
final _boolCombineB =
46+
CombineObs(_getGpsCommand.results, _radioCheckedCommand.results)
47+
.combinedObservable;
48+
49+
// final _updateLocationCommand =
50+
// RxCommand.createAsync2<LocationResult>(repo.updateLocation);
3251

3352
final _updateWeatherCommand =
3453
RxCommand.createAsync3<LocationResult, List<WeatherModel>>(
35-
repo.updateWeather, _radioCheckedCommand.results);
54+
repo.updateWeather, _boolCombineA);
3655

3756
final _addCitiesCommand = RxCommand.createSync1<int>(repo.addCities);
3857

39-
_updateLocationCommand.results.listen(_updateWeatherCommand);
58+
final _updateLocationStreamCommand =
59+
RxCommand.createFromStream<dynamic, LocationResult>(
60+
repo.updateLocationStream, _boolCombineB);
61+
62+
// _updateLocationCommand.results.listen(_updateWeatherCommand);
63+
//using a stream based command now because lastKnownLocation is not as consistent as [currentLocation]
64+
_updateLocationStreamCommand.results
65+
.listen((data) => _updateWeatherCommand(data));
4066

4167
_updateWeatherCommand(null);
4268

4369
return ModelCommand._(
4470
repo,
45-
_updateLocationCommand,
71+
// _updateLocationCommand,
4672
_updateWeatherCommand,
4773
_getGpsCommand,
4874
_radioCheckedCommand,
4975
_addCitiesCommand,
76+
_updateLocationStreamCommand,
5077
);
5178
}
5279
}
80+
81+
class CombineObs {
82+
final Observable<bool> _combinedObservable;
83+
Observable<bool> get combinedObservable => _combinedObservable;
84+
CombineObs._(this._combinedObservable);
85+
86+
factory CombineObs(a, b) {
87+
Observable<bool> c = Observable.combineLatest2(a, b, (x, y) => x && y);
88+
return CombineObs._(c);
89+
}
90+
}

lib/model/weather_repo.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,18 @@ class WeatherRepo {
4141
return req;
4242
}
4343

44-
Future<LocationResult> updateLocation() async {
45-
Future<LocationResult> result = Geolocation.lastKnownLocation();
46-
return result;
44+
// Future<LocationResult> updateLocation() async {
45+
// Future<LocationResult> result = Geolocation.lastKnownLocation();
46+
// return result;
47+
// }
48+
49+
//uses Stream instead of future. Mainly done because lastKnownLocation isn't as reliable as locationUpdates.
50+
//when geolocation plugin is fixed or better plugin is released, will update code.
51+
52+
Stream<LocationResult> updateLocationStream(dynamic item) {
53+
Stream<LocationResult> stream = Geolocation.currentLocation(
54+
accuracy: LocationAccuracy.best, inBackground: false);
55+
return stream;
4756
}
4857

4958
Future<bool> getGps() async {

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies:
1313
rx_command: "^1.0.7"
1414
rx_widgets: "^1.0.2"
1515
geolocation: "^0.2.1"
16+
rxdart: "^0.16.6"
1617

1718

1819

0 commit comments

Comments
 (0)