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":[]}]}
\ No newline at end of file
{"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
......@@ -287,3 +287,7 @@ modules.xml
.venv/
# End of https://www.gitignore.io/api/linux,django,python,pycharm+all
tests.output
.flutter-plugins-dependencies
......@@ -22,8 +22,8 @@ Lint:
Test:
stage: test
script:
- flutter test --machine > tests.output
- flutter test --coverage
- flutter test --machine > tests.output
- lcov --summary coverage/lcov.info
- genhtml coverage/lcov.info --output=coverage
artifacts:
......
......@@ -50,4 +50,17 @@ MAPS_API_KEY=Bu***************
Run the app using the development flavor
```bash
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 {
Navigator.pop(context);
},
),
decoration: new BoxDecoration(
decoration: BoxDecoration(
color: greenPrimary,
border: new Border(
bottom: new BorderSide(color: Colors.white),
border: Border(
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 {
await getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory;
});
File cookieFile = File("${dir.path}/$fileName");
File cookieFile = File("${dir.path}/$fileName.json");
bool cookiesExist = cookieFile.existsSync();
return cookiesExist;
......@@ -29,7 +29,7 @@ class CookiesInterface {
await getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory;
});
File cookieFile = new File("${dir.path}/usercookies.json");
File cookieFile = File("${dir.path}/usercookies.json");
cookieFile.createSync();
setCookie = responseHeaders["set-cookie"];
......@@ -47,39 +47,52 @@ class CookiesInterface {
return cookieFile;
} on Exception catch (e) {
print(e.toString());
throw e;
rethrow;
}
}
Future<File> createSearchHistoryCookie({
Map<String, dynamic> recentSearch}) async {
print("recent searrch $recentSearch");
Directory dir;
List currentSearchHistory;
try {
await getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory;
});
File cookieFile = new File(dir.path + "/searchhistory.json");
File cookieFile = File(dir.path + "/searchhistory.json");
cookieFile.createSync();
List currentSearchHistory = await getCookieFile(fileName: "searchhistory.json");
if (currentSearchHistory.length == 0) {
currentSearchHistory = [];
}
currentSearchHistory.add(recentSearch);
cookieFile.writeAsString(json.encode(currentSearchHistory));
await checkCookieFileAvailability(fileName: "searchhistory").then((available) async {
if (available) {
await getCookieFile(fileName: "searchhistory").then((cookie) {
bool test = cookie == null;
print("cookie is null? $test");
if (cookie == null) {
currentSearchHistory = [];
} else {
currentSearchHistory = json.decode(cookie);
}
currentSearchHistory.add(recentSearch);
});
} else {
currentSearchHistory = [];
}
await cookieFile.writeAsString(json.encode(currentSearchHistory));
});
return cookieFile;
} on Exception catch (e) {
print(e.toString());
throw e;
rethrow;
}
}
Future<List<dynamic>> getCookieFile({String fileName}) async {
Future<dynamic> getCookieFile({String fileName}) async {
Directory dir;
await getApplicationDocumentsDirectory().then((Directory directory) {
dir = directory;
});
File file = File("${dir.path}/fileName");
File file = File("${dir.path}/$fileName.json");
dynamic res = file.readAsStringSync();
return json.decode(res);
return res;
}
}
class NetworkModel {
var response;
int statusCode;
String errorMessage;
class NetworkModel<T> {
Status status;
T data;
String message;
NetworkModel({
this.response,
this.statusCode,
this.errorMessage,
});
}
\ No newline at end of file
NetworkModel.loading(this.message) : status = Status.LOADING;
NetworkModel.completed(this.data) : status = Status.COMPLETED;
NetworkModel.error(this.message) : status = Status.ERROR;
@override
String toString() {
return "Status : $status \n Message : $message \n Data : $data";
}
}
enum Status { LOADING, COMPLETED, ERROR }
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 'dart:io';
class NetworkInterface {
//String key = KEY;
// POST request
Future<NetworkModel> post({
String url, //url nya apa
Future<dynamic> post({
String url, //url nya apa
dynamic bodyParams, //data apa yang mau dikasih
bool isLogin, //dia login apa ngga
}) async {
Map<String, String> headersJson = await _buildRequestHeader(isLogin); //butuh header apa ngga
NetworkModel model;
var responseJson;
Map<String, String> headersJson =
await _buildRequestHeader(isLogin); //butuh header apa ngga
try {
model = await http.post(
final response = await http.post(
"$url",
body: json.encode(bodyParams),
headers: headersJson,
).then((response) async {
Map<String, dynamic> responseBody = json.decode(response.body);
print(responseBody.toString());
//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;
);
responseJson = _response(response);
} on SocketException {
throw FetchDataException("No Internet Connection");
}
return model;
return responseJson;
}
// GET request
Future<NetworkModel> get({
Future<dynamic> get({
String url,
bool isLogin,
}) async {
var responseJson;
Map<String, dynamic> headersJson = await _buildRequestHeader(isLogin);
NetworkModel model;
try {
model = await http
.get(
final response = await http.get(
"$url",
headers: headersJson,
)
.then((response) {
print("masuk sini");
dynamic responseBody = json.decode(response.body);
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;
);
responseJson = _response(response);
} on SocketException {
throw FetchDataException("No Internet Connection");
}
return model;
return responseJson;
}
// buildRequestHeader: untuk nentuin pake header apa aja berdasarkan login apa ngga
Future<Map<String, dynamic>> _buildRequestHeader(bool isLogin) async {
Map<String, String> headers = Map<String, String>();
headers.putIfAbsent("Content-Type", () => "application/json");
//if (isLogin) {
//List<dynamic> cookieFile = await CookiesInterface.getCookieFile(); //ngambil data dari yg udh disimpen di cookie
//print("cookieFile list --> ${cookieFile.toString()}");
//print("check key here >>> $key");
//setKey(cookieFile[2]);
//key = cookieFile[2];
//headers.putIfAbsent("Authorization", () => '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}");
//List<dynamic> cookieFile = await CookiesInterface().getCookieFile(
// fileName:
// "userCookies"); //ngambil data dari yg udh disimpen di cookie
//print("cookieFile list --> ${cookieFile.toString()}");
//print("check key here >>> $key");
//setKey(cookieFile[2]);
//key = cookieFile[2];
//headers.putIfAbsent(
// "Authorization",
// () =>
// '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;
}
}
\ 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> {
@override
void initState() {
super.initState();
location = new Location();
location = Location();
enableLocationService();
location.onLocationChanged().listen((LocationData cLoc) {
currentLocation = cLoc;
......@@ -46,6 +46,7 @@ class DashboardState extends State<Dashboard> {
}
void enableLocationService() async {
await location.changeSettings(accuracy: LocationAccuracy.HIGH);
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
......@@ -72,7 +73,7 @@ class DashboardState extends State<Dashboard> {
Widget build(BuildContext context) {
return Scaffold(
drawer: BisaGoDrawer(),
body: Stack(children: <Widget>[
body: Stack(key: Key("Stack"),children: <Widget>[
_buildGoogleMap(context),
InkWell(
key: Key("Navigate to Pencarian"),
......@@ -161,7 +162,7 @@ class DashboardState extends State<Dashboard> {
target: LatLng(currentLocation.latitude, currentLocation.longitude),
);
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(cPosition));
await controller.animateCamera(CameraUpdate.newCameraPosition(cPosition));
setState(() {
var pinPosition =
LatLng(currentLocation.latitude, currentLocation.longitude);
......
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:ppl_disabilitas/bloc/LokasiResponseBloc.dart';
import 'package:ppl_disabilitas/config/styles.dart';
import 'package:ppl_disabilitas/model/lokasi.dart';
import 'package:ppl_disabilitas/network/data/network_model.dart';
import 'package:ppl_disabilitas/network/network_interface.dart';
/// Create Pencarian page widget with a state
class Pencarian extends StatefulWidget {
......
import 'dart:convert';
import 'package:ppl_disabilitas/model/lokasi.dart';
import 'package:ppl_disabilitas/network/cookies_interface.dart';
import 'package:ppl_disabilitas/network/network_interface.dart';
class LokasiRepository {
NetworkInterface _network = NetworkInterface();
Future<LokasiListResponse> fetchLokasi() async {
final response = await _network.get(
url: 'https://my.api.mockaroo.com/mall.json?key=dbcde960',
isLogin: false);
return LokasiListResponse.fromJson(response);
}
Future<LokasiListResponse> fetchRecentSearch() async {
var response;
await CookiesInterface().checkCookieFileAvailability(fileName: "searchhistory").then((boolean) async {
if (!boolean) {
response = [];
} else {
await CookiesInterface().getCookieFile(fileName: "searchhistory").then((cookie) {
response = json.decode(cookie);
print("response type: ${response.runtimeType}");
});
}
});
return LokasiListResponse.fromJson(response);
}
Future<void> saveRecentSearch(Lokasi recentSearch) async {
Map<String, dynamic> searchToMap = recentSearch.toJson();
await CookiesInterface()
.createSearchHistoryCookie(recentSearch: searchToMap);
}
}
......@@ -29,12 +29,15 @@ dependencies:
cupertino_icons: ^0.1.2
google_maps_flutter: ^0.5.24+1
flutter_dotenv: ^2.1.0
flutter_typeahead: ^1.8.0
json_serializable: