Skip to content

Commit

Permalink
#15: Setup http and json serialization and parse api from network
Browse files Browse the repository at this point in the history
  • Loading branch information
burhanrashid52 committed Nov 5, 2019
1 parent e20dcc7 commit 2106d26
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 35 deletions.
3 changes: 2 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter_app/pages/home/home.dart';
import 'package:flutter_app/pages/home/home_bloc.dart';
import 'package:flutter_app/pages/map/map_bloc.dart';
import 'package:flutter_app/pages/map/map_page.dart';
import 'package:flutter_app/pages/places/places_api_service.dart';

void main() => runApp(MyApp());

Expand All @@ -15,7 +16,7 @@ class MyApp extends StatelessWidget {
theme: ThemeData(
accentColor: Colors.orange, primaryColor: const Color(0xFFDE4435)),
home: BlocProvider(
bloc: MapBloc(),
bloc: MapBloc(PlacesApiService()),
child: MapPage(),
));
}
Expand Down
33 changes: 33 additions & 0 deletions lib/pages/map/map_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,53 @@
import 'dart:async';

import 'package:flutter_app/bloc/bloc_provider.dart';
import 'package:flutter_app/pages/places/models.dart';
import 'package:flutter_app/pages/places/places_api_service.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapBloc extends BlocBase {
MapBloc(this.placesApiService) {
_fetchLocations();
}

final PlacesApiService placesApiService;

StreamController<MapType> _mapTypeController =
StreamController<MapType>.broadcast();

Stream<MapType> get mayType => _mapTypeController.stream;

StreamController<List<Office>> _officesController =
StreamController<List<Office>>.broadcast();

Stream<List<Office>> get offices => _officesController.stream;

@override
void dispose() {
_mapTypeController.close();
_officesController.close();
}

void setMayType(MapType mayType) {
_mapTypeController.sink.add(mayType);
}

void _fetchLocations() {
placesApiService.getGoogleOffices().then((value) {
_officesController.sink.add(value.offices);
});
}
}

extension MyOffices on Office {
Marker toMarker() {
return Marker(
markerId: MarkerId(name),
position: LatLng(lat, lng),
infoWindow: InfoWindow(
title: name,
snippet: address,
),
);
}
}
85 changes: 55 additions & 30 deletions lib/pages/map/map_page.dart
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_app/bloc/bloc_provider.dart';
import 'package:flutter_app/pages/map/map_bloc.dart';
import 'package:flutter_app/pages/places/models.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapPage extends StatelessWidget {
final Completer<GoogleMapController> _controller = Completer();

static final CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);

static final CameraPosition _kLake = CameraPosition(
bearing: 192.8334901395799,
target: LatLng(37.43296265331129, -122.08832357078792),
tilt: 59.440717697143555,
zoom: 19.151926040649414);

@override
Widget build(BuildContext context) {
final MapBloc mapBloc = BlocProvider.of(context);
return new Scaffold(
return Scaffold(
appBar: AppBar(
title: Text("Maps"),
actions: <Widget>[
Expand All @@ -40,27 +31,61 @@ class MapPage extends StatelessWidget {
),
],
),
body: StreamBuilder<MapType>(
stream: mapBloc.mayType,
initialData: MapType.hybrid,
builder: (context, snapshot) {
return GoogleMap(
mapType: snapshot.data,
initialCameraPosition: _kGooglePlex,
zoomGesturesEnabled: true,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
);
}),
floatingActionButton: FloatingActionButton(
onPressed: _goToTheLake,
body: Stack(
children: <Widget>[
StreamBuilder<MapType>(
stream: mapBloc.mayType,
initialData: MapType.hybrid,
builder: (context, snapshotType) {
return GoogleMap(
mapType: snapshotType.data,
initialCameraPosition: buildInitialCamera(),
zoomGesturesEnabled: true,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
);
},
),
//TODO: Fix the scroll and item are not visible
StreamBuilder<List<Office>>(
stream: mapBloc.offices,
builder: (context, snapshot) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
var item = snapshot.data[index];
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.red,
child: Text("P"),
),
title: Text(item.name),
subtitle: Text(item.address),
);
//return Text(item.name);
},
);
},
)
],
),
);
}

Future<void> _goToTheLake() async {
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));
Set<Marker> buildMarkerSet(List<Office> offices) {
Set<Marker> markers = Set<Marker>();
for (var office in offices) {
markers.add(office.toMarker());
}
return markers;
}

CameraPosition buildInitialCamera() {
return CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
}
}
136 changes: 136 additions & 0 deletions lib/pages/places/models.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import 'package:json_annotation/json_annotation.dart';

@JsonSerializable()
class Locations {
Locations({this.offices, this.regions});

factory Locations.fromJson(Map<String, dynamic> json) {
return Locations(
offices: json['offices'] != null
? (json['offices'] as List).map((i) => Office.fromJson(i)).toList()
: null,
regions: json['regions'] != null
? (json['regions'] as List).map((i) => Region.fromJson(i)).toList()
: null,
);
}

List<Office> offices;

List<Region> regions;

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
if (this.offices != null) {
data['offices'] = this.offices.map((v) => v.toJson()).toList();
}
if (this.regions != null) {
data['regions'] = this.regions.map((v) => v.toJson()).toList();
}
return data;
}
}

@JsonSerializable()
class Region {
Region({this.coords, this.id, this.name, this.zoom});

factory Region.fromJson(Map<String, dynamic> json) {
return Region(
coords: json['coords'] != null ? Coords.fromJson(json['coords']) : null,
id: json['id'],
name: json['name'],
zoom: json['zoom'],
);
}

Coords coords;
String id;

String name;

double zoom;

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['zoom'] = this.zoom;
if (this.coords != null) {
data['coords'] = this.coords.toJson();
}
return data;
}
}

@JsonSerializable()
class Coords {
Coords({this.lat, this.lng});

factory Coords.fromJson(Map<String, dynamic> json) {
return Coords(
lat: json['lat'],
lng: json['lng'],
);
}

double lat;

double lng;

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['lat'] = this.lat;
data['lng'] = this.lng;
return data;
}
}

@JsonSerializable()
class Office {
Office(
{this.address,
this.id,
this.image,
this.lat,
this.lng,
this.name,
this.phone,
this.region});

factory Office.fromJson(Map<String, dynamic> json) {
return Office(
address: json['address'],
id: json['id'],
image: json['image'],
lat: json['lat'],
lng: json['lng'],
name: json['name'],
phone: json['phone'],
region: json['region'],
);
}

String address;
String id;
String image;
double lat;
double lng;
String name;
String phone;

String region;

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['address'] = this.address;
data['id'] = this.id;
data['image'] = this.image;
data['lat'] = this.lat;
data['lng'] = this.lng;
data['name'] = this.name;
data['phone'] = this.phone;
data['region'] = this.region;
return data;
}
}
23 changes: 23 additions & 0 deletions lib/pages/places/places_api_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;

import 'package:flutter_app/pages/places/models.dart';

class PlacesApiService {
Future<Locations> getGoogleOffices() async {
const googleLocationsURL =
'https://about.google/static/data/locations.json';

// Retrieve the locations of Google offices
final response = await http.get(googleLocationsURL);
if (response.statusCode == 200) {
return Locations.fromJson(json.decode(response.body));
} else {
throw HttpException(
'Unexpected status code ${response.statusCode}:'
' ${response.reasonPhrase}',
uri: Uri.parse(googleLocationsURL));
}
}
}
10 changes: 6 additions & 4 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.0
sqflite: any
url_launcher : ^4.0.0
url_launcher: ^4.0.0
path_provider: ^0.4.1
rxdart: ^0.19.0
google_maps_flutter: ^0.5.21+8
http: ^0.12.0+1
json_serializable: ^2.0.2

dev_dependencies:
pedantic: ^1.4.0
build_runner: ^1.2.7
flutter_test:
sdk: flutter

Expand All @@ -36,9 +38,9 @@ flutter:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
assets:
- assets/profile_pic.jpg
- assets/twitter_logo.png
- assets/facebook_logo.png
- assets/profile_pic.jpg
- assets/twitter_logo.png
- assets/facebook_logo.png

# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
Expand Down

0 comments on commit 2106d26

Please sign in to comment.