Fakultas Ilmu Komputer UI

Commit a10f0035 authored by Adzkia Aisyah Afrah Hardian's avatar Adzkia Aisyah Afrah Hardian
Browse files

Merge branch 'PBI-2-pencarian_lokasi' into 'dev-afrah'

# Conflicts:
#   .flutter-plugins-dependencies
#   lib/page/pencarian/pencarian.dart
#   pubspec.yaml
#   test/pencarian_test.dart
parents 66def408 0fd8667f
Pipeline #38290 failed with stages
in 1 minute and 26 seconds
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_keyboard_visibility","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"google_maps_flutter","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"location","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]}]} {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"google_maps_flutter","path":"D:\\\\Flutter\\\\flutter_windows_v1.9.1+hotfix.2-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_maps_flutter-0.5.24+1\\\\","dependencies":[]},{"name":"location","path":"D:\\\\Flutter\\\\flutter_windows_v1.9.1+hotfix.2-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location-2.5.3\\\\","dependencies":[]},{"name":"path_provider","path":"D:\\\\Flutter\\\\flutter_windows_v1.9.1+hotfix.2-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.5\\\\","dependencies":[]}],"android":[{"name":"flutter_plugin_android_lifecycle","path":"D:\\\\Flutter\\\\flutter_windows_v1.9.1+hotfix.2-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_plugin_android_lifecycle-1.0.6\\\\","dependencies":[]},{"name":"google_maps_flutter","path":"D:\\\\Flutter\\\\flutter_windows_v1.9.1+hotfix.2-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_maps_flutter-0.5.24+1\\\\","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"location","path":"D:\\\\Flutter\\\\flutter_windows_v1.9.1+hotfix.2-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location-2.5.3\\\\","dependencies":[]},{"name":"path_provider","path":"D:\\\\Flutter\\\\flutter_windows_v1.9.1+hotfix.2-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.5\\\\","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"D:\\\\Flutter\\\\flutter_windows_v1.9.1+hotfix.2-stable\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_macos-0.0.4\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"google_maps_flutter","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"location","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]}],"date_created":"2020-03-25 22:33:34.112007","version":"1.15.17"}
\ No newline at end of file \ No newline at end of file
...@@ -287,3 +287,7 @@ modules.xml ...@@ -287,3 +287,7 @@ modules.xml
.venv/ .venv/
# End of https://www.gitignore.io/api/linux,django,python,pycharm+all # End of https://www.gitignore.io/api/linux,django,python,pycharm+all
tests.output
.flutter-plugins-dependencies
...@@ -22,8 +22,8 @@ Lint: ...@@ -22,8 +22,8 @@ Lint:
Test: Test:
stage: test stage: test
script: script:
- flutter test --machine > tests.output
- flutter test --coverage - flutter test --coverage
- flutter test --machine > tests.output
- lcov --summary coverage/lcov.info - lcov --summary coverage/lcov.info
- genhtml coverage/lcov.info --output=coverage - genhtml coverage/lcov.info --output=coverage
artifacts: artifacts:
......
...@@ -50,4 +50,17 @@ MAPS_API_KEY=Bu*************** ...@@ -50,4 +50,17 @@ MAPS_API_KEY=Bu***************
Run the app using the development flavor Run the app using the development flavor
```bash ```bash
flutter run -t lib/main_dev.dart flutter run -t lib/main_dev.dart
``` ```
\ No newline at end of file ## Building Models with JsonSerializable
Jadi abis get dari API, jsonnya di map ke models biar rapih.
1. Tulis ada field apa aja dari jsonnya (bisa liat contoh yang di models/lokasi.dart)
2. bagian 'part of {nama models}.g.dart' itu harus ditulis di model yg mau dibuat. di awal emang merah, tapi biarin aja
3. kalo semua field udah di tulis, run
```bash
flutter pub run build_runner build
```
4. nanti akan ke build file {nama models}.g.dart, yang di nomor 2 merah harusnya udah gak merah lagi
## Passing Data with BLoC
Udah ada contohnya di /bloc (implementasi di screen nya ada di page/pencarian/pencarian.dart)
Bisa baca [disini]https://itnext.io/flutter-handling-your-network-api-calls-like-a-boss-936eef296547 sebagai panduannya
\ No newline at end of file
include: package:pedantic/analysis_options.yaml
analyzer:
exclude:
- "**/*.g.dart"
\ No newline at end of file
import 'dart:async';
import 'package:ppl_disabilitas/model/lokasi.dart';
import 'package:ppl_disabilitas/network/data/network_model.dart';
import 'package:ppl_disabilitas/repository/LokasiRepository.dart';
class LokasiResponseBloc {
StreamController _recentSearchController;
LokasiRepository _lokasiRepository;
StreamController _lokasiListController;
StreamSink<NetworkModel<LokasiListResponse>> get recentSearchSink =>
_recentSearchController.sink;
Stream<NetworkModel<LokasiListResponse>> get recentSearchStream =>
_recentSearchController.stream;
StreamSink<NetworkModel<LokasiListResponse>> get lokasiListSink =>
_lokasiListController.sink;
Stream<NetworkModel<LokasiListResponse>> get lokasiListStream =>
_lokasiListController.stream;
LokasiResponseBloc() {
_lokasiListController =
StreamController<NetworkModel<LokasiListResponse>>();
_recentSearchController = StreamController<NetworkModel<LokasiListResponse>>();
_lokasiRepository = LokasiRepository();
fetchLokasiList();
fetchRecentSearch();
}
fetchLokasiList() async {
lokasiListSink.add(NetworkModel.loading('Getting Locations'));
try {
LokasiListResponse lokasiListResponse =
await _lokasiRepository.fetchLokasi();
print("lokasi list response: $lokasiListResponse");
lokasiListSink.add(NetworkModel.completed(lokasiListResponse));
} catch (e) {
lokasiListSink.add(NetworkModel.error(e.toString()));
print("$e");
}
}
fetchRecentSearch() async {
recentSearchSink.add(NetworkModel.loading('Getting Recent Search'));
try {
LokasiListResponse recentSearchData = await _lokasiRepository.fetchRecentSearch();
print("recentSearchData ${recentSearchData.listLokasi}");
recentSearchSink.add(NetworkModel.completed(recentSearchData));
} catch (e) {
recentSearchSink.add(NetworkModel.error(e.toString()));
print("line 53 ${e.toString()}");
}
}
saveRecentSearch(Lokasi search) async {
await _lokasiRepository.saveRecentSearch(search);
}
dispose() {
_recentSearchController?.close();
_lokasiListController?.close();
}
}
...@@ -82,10 +82,10 @@ class BisaGoDrawer extends StatelessWidget { ...@@ -82,10 +82,10 @@ class BisaGoDrawer extends StatelessWidget {
Navigator.pop(context); Navigator.pop(context);
}, },
), ),
decoration: new BoxDecoration( decoration: BoxDecoration(
color: greenPrimary, color: greenPrimary,
border: new Border( border: Border(
bottom: new BorderSide(color: Colors.white), bottom: BorderSide(color: Colors.white),
), ),
), ),
); );
......
import 'package:json_annotation/json_annotation.dart';
part 'lokasi.g.dart';
@JsonSerializable()
class LokasiListResponse {
List<Lokasi> listLokasi;
LokasiListResponse();
factory LokasiListResponse.fromJson(List json) => _$LokasiListResponseFromJson(json);
Map<String, dynamic> toJson() => _$LokasiListResponseToJson(this);
}
@JsonSerializable(nullable: true)
class Lokasi {
String nama;
double latitude;
double longitude;
String alamat;
String foto;
String telp;
Lokasi();
factory Lokasi.fromJson(Map<String, dynamic> json) => _$LokasiFromJson(json);
Map<String, dynamic> toJson() => _$LokasiToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'lokasi.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
LokasiListResponse _$LokasiListResponseFromJson(List json) {
return LokasiListResponse()
..listLokasi = json
?.map((e) =>
e == null ? null : Lokasi.fromJson(e as Map<String, dynamic>))
?.toList();
}
Map<String, dynamic> _$LokasiListResponseToJson(LokasiListResponse instance) =>
<String, dynamic>{
'listLokasi': instance.listLokasi,
};
Lokasi _$LokasiFromJson(Map<String, dynamic> json) {
return Lokasi()
..nama = json['nama'] as String
..latitude = (json['latitude'] as num)?.toDouble()
..longitude = (json['longitude'] as num)?.toDouble()
..alamat = json['alamat'] as String
..foto = json['foto'] as String
..telp = json['telp'] as String;
}
Map<String, dynamic> _$LokasiToJson(Lokasi instance) => <String, dynamic>{
'nama': instance.nama,
'latitude': instance.latitude,
'longitude': instance.longitude,
'alamat': instance.alamat,
'foto': instance.foto,
'telp': instance.telp,
};
class CustomException implements Exception {
final _message;
final _prefix;
CustomException([this._message, this._prefix]);
String toString() {
return "$_prefix$_message";
}
}
class FetchDataException extends CustomException {
FetchDataException([String message])
: super(message, "Error During Communication: ");
}
class BadRequestException extends CustomException {
BadRequestException([message]) : super(message, "Invalid Request: ");
}
class UnauthorisedException extends CustomException {
UnauthorisedException([message]) : super(message, "Unauthorised: ");
}
class InvalidInputException extends CustomException {
InvalidInputException([String message]) : super(message, "Invalid Input: ");
}
\ No newline at end of file
...@@ -10,7 +10,7 @@ class CookiesInterface { ...@@ -10,7 +10,7 @@ class CookiesInterface {
await getApplicationDocumentsDirectory().then((Directory directory) { await getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory; dir = directory;
}); });
File cookieFile = File("${dir.path}/$fileName"); File cookieFile = File("${dir.path}/$fileName.json");
bool cookiesExist = cookieFile.existsSync(); bool cookiesExist = cookieFile.existsSync();
return cookiesExist; return cookiesExist;
...@@ -29,7 +29,7 @@ class CookiesInterface { ...@@ -29,7 +29,7 @@ class CookiesInterface {
await getApplicationDocumentsDirectory().then((Directory directory) { await getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory; dir = directory;
}); });
File cookieFile = new File("${dir.path}/usercookies.json"); File cookieFile = File("${dir.path}/usercookies.json");
cookieFile.createSync(); cookieFile.createSync();
setCookie = responseHeaders["set-cookie"]; setCookie = responseHeaders["set-cookie"];
...@@ -47,39 +47,52 @@ class CookiesInterface { ...@@ -47,39 +47,52 @@ class CookiesInterface {
return cookieFile; return cookieFile;
} on Exception catch (e) { } on Exception catch (e) {
print(e.toString()); print(e.toString());
throw e; rethrow;
} }
} }
Future<File> createSearchHistoryCookie({ Future<File> createSearchHistoryCookie({
Map<String, dynamic> recentSearch}) async { Map<String, dynamic> recentSearch}) async {
print("recent searrch $recentSearch");
Directory dir; Directory dir;
List currentSearchHistory;
try { try {
await getApplicationDocumentsDirectory().then((Directory directory) { await getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory; dir = directory;
}); });
File cookieFile = new File(dir.path + "/searchhistory.json"); File cookieFile = File(dir.path + "/searchhistory.json");
cookieFile.createSync(); cookieFile.createSync();
List currentSearchHistory = await getCookieFile(fileName: "searchhistory.json"); await checkCookieFileAvailability(fileName: "searchhistory").then((available) async {
if (currentSearchHistory.length == 0) { if (available) {
currentSearchHistory = []; await getCookieFile(fileName: "searchhistory").then((cookie) {
} bool test = cookie == null;
currentSearchHistory.add(recentSearch); print("cookie is null? $test");
cookieFile.writeAsString(json.encode(currentSearchHistory)); if (cookie == null) {
currentSearchHistory = [];
} else {
currentSearchHistory = json.decode(cookie);
}
currentSearchHistory.add(recentSearch);
});
} else {
currentSearchHistory = [];
}
await cookieFile.writeAsString(json.encode(currentSearchHistory));
});
return cookieFile; return cookieFile;
} on Exception catch (e) { } on Exception catch (e) {
print(e.toString()); print(e.toString());
throw e; rethrow;
} }
} }
Future<List<dynamic>> getCookieFile({String fileName}) async { Future<dynamic> getCookieFile({String fileName}) async {
Directory dir; Directory dir;
await getApplicationDocumentsDirectory().then((Directory directory) { await getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory; dir = directory;
}); });
File file = File("${dir.path}/fileName"); File file = File("${dir.path}/$fileName.json");
dynamic res = file.readAsStringSync(); dynamic res = file.readAsStringSync();
return json.decode(res); return res;
} }
} }
class NetworkModel { class NetworkModel<T> {
var response; Status status;
int statusCode; T data;
String errorMessage; String message;
NetworkModel({ NetworkModel.loading(this.message) : status = Status.LOADING;
this.response, NetworkModel.completed(this.data) : status = Status.COMPLETED;
this.statusCode, NetworkModel.error(this.message) : status = Status.ERROR;
this.errorMessage,
}); @override
} String toString() {
\ No newline at end of file return "Status : $status \n Message : $message \n Data : $data";
}
}
enum Status { LOADING, COMPLETED, ERROR }
import 'dart:convert'; import 'dart:convert';
import 'package:ppl_disabilitas/network/data/network_model.dart'; import 'package:ppl_disabilitas/network/CustomException.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'dart:io';
class NetworkInterface { class NetworkInterface {
//String key = KEY; //String key = KEY;
// POST request // POST request
Future<NetworkModel> post({ Future<dynamic> post({
String url, //url nya apa String url, //url nya apa
dynamic bodyParams, //data apa yang mau dikasih dynamic bodyParams, //data apa yang mau dikasih
bool isLogin, //dia login apa ngga bool isLogin, //dia login apa ngga
}) async { }) async {
Map<String, String> headersJson = await _buildRequestHeader(isLogin); //butuh header apa ngga var responseJson;
NetworkModel model; Map<String, String> headersJson =
await _buildRequestHeader(isLogin); //butuh header apa ngga
try { try {
model = await http.post( final response = await http.post(
"$url", "$url",
body: json.encode(bodyParams), body: json.encode(bodyParams),
headers: headersJson, headers: headersJson,
).then((response) async { );
Map<String, dynamic> responseBody = json.decode(response.body); responseJson = _response(response);
print(responseBody.toString()); } on SocketException {
throw FetchDataException("No Internet Connection");
//if (!isLogin) {
//if (responseBody.containsKey("key")) {
//setKey(responseBody["key"]);
//}
//await CookiesInterface.createCookieFile(response.headers);
//}
print(responseBody);
return NetworkModel(statusCode: response.statusCode,
response: responseBody,
);
});
if (model.statusCode >= 400) {
throw Exception();
}
} on Exception catch (e) {
print("status code --> ${model.statusCode}");
NetworkModel errorModel = NetworkModel(
statusCode: model.statusCode,
errorMessage: e.toString(),
response: model.response);
model = errorModel;
} }
return responseJson;
return model;
} }
// GET request // GET request
Future<NetworkModel> get({ Future<dynamic> get({
String url, String url,
bool isLogin, bool isLogin,
}) async { }) async {
var responseJson;
Map<String, dynamic> headersJson = await _buildRequestHeader(isLogin); Map<String, dynamic> headersJson = await _buildRequestHeader(isLogin);
NetworkModel model;
try { try {
model = await http final response = await http.get(
.get(
"$url", "$url",
headers: headersJson, headers: headersJson,
) );
.then((response) { responseJson = _response(response);
print("masuk sini"); } on SocketException {
dynamic responseBody = json.decode(response.body); throw FetchDataException("No Internet Connection");
print(responseBody);
return NetworkModel(
statusCode: response.statusCode,
response: responseBody,
);
});
if (model.statusCode >= 400) {
print("gamasuk sini");
throw Exception();
}
} on Exception catch (e) {
NetworkModel errorModel = NetworkModel(
statusCode: model.statusCode,
errorMessage: e.toString(),
response: model.response);
model = errorModel;
} }
return responseJson;
return model;
} }
// buildRequestHeader: untuk nentuin pake header apa aja berdasarkan login apa ngga // buildRequestHeader: untuk nentuin pake header apa aja berdasarkan login apa ngga
Future<Map<String, dynamic>> _buildRequestHeader(bool isLogin) async { Future<Map<String, dynamic>> _buildRequestHeader(bool isLogin) async {
Map<String, String> headers = Map<String, String>(); Map<String, String> headers = Map<String, String>();
headers.putIfAbsent("Content-Type", () => "application/json"); headers.putIfAbsent("Content-Type", () => "application/json");
//if (isLogin) { //if (isLogin) {
//List<dynamic> cookieFile = await CookiesInterface.getCookieFile(); //ngambil data dari yg udh disimpen di cookie //List<dynamic> cookieFile = await CookiesInterface().getCookieFile(
//print("cookieFile list --> ${cookieFile.toString()}"); // fileName:
//print("check key here >>> $key"); // "userCookies"); //ngambil data dari yg udh disimpen di cookie
//setKey(cookieFile[2]); //print("cookieFile list --> ${cookieFile.toString()}");
//key = cookieFile[2]; //print("check key here >>> $key");
//headers.putIfAbsent("Authorization", () => 'Token $key'); //ini kalau authorization nya ngga ada baru taro token nya //setKey(cookieFile[2]);
//headers.putIfAbsent("X-CSRFToken", () => cookieFile[0]); //csrf token //key = cookieFile[2];
//headers.putIfAbsent("Cookie", //headers.putIfAbsent(
//() => "csrftoken=${cookieFile[0]};sessionid=${cookieFile[1]}"); //cookie file // "Authorization",
//print("headers --> ${headers}"); // () =>
// 'Token $key'); //ini kalau authorization nya ngga ada baru taro token nya
//headers.putIfAbsent("X-CSRFToken", () => cookieFile[0]); //csrf token
//headers.putIfAbsent(
// "Cookie",
// () =>
// "csrftoken=${cookieFile[0]};sessionid=${cookieFile[1]}"); //cookie file
//print("headers --> ${headers}");
//} //}
return headers; return headers;
} }
}
\ No newline at end of file dynamic _response(http.Response response) {
switch (response.statusCode) {
case 200:
var responseJson = json.decode(response.body.toString());
return responseJson;
case 400:
throw BadRequestException(response.body.toString());
case 401:
case 403:
throw UnauthorisedException(response.body.toString());
case 500:
default:
throw FetchDataException(
'Error occured while Communication with Server with status : ${response.statusCode}');
}
}
}
...@@ -34,7 +34,7 @@ class DashboardState extends State<Dashboard> { ...@@ -34,7 +34,7 @@ class DashboardState extends State<Dashboard> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
location = new Location(); location = Location();
enableLocationService(); enableLocationService();
location.onLocationChanged().listen((LocationData cLoc) { location.onLocationChanged().listen((LocationData cLoc) {
currentLocation = cLoc; currentLocation = cLoc;
...@@ -46,6 +46,7 @@ class DashboardState extends State<Dashboard> { ...@@ -46,6 +46,7 @@ class DashboardState extends State<Dashboard> {
} }
void enableLocationService() async { void enableLocationService() async {
await location.changeSettings(accuracy: LocationAccuracy.HIGH);
_serviceEnabled = await location.serviceEnabled(); _serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) { if (!_serviceEnabled) {
_serviceEnabled = await location.requestService(); _serviceEnabled = await location.requestService();
...@@ -72,7 +73,7 @@ class DashboardState extends State<Dashboard> { ...@@ -72,7 +73,7 @@ class DashboardState extends State<Dashboard> {
Widget build(BuildContext context) {