diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index e49cab89bc9eaab918130ecfdcd141b49c1b2043..c058ba48e61e18367eacbe9c3483fe6d2d1500fc 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"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":[]}],"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":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"google_maps_flutter","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"location","dependencies":[]}],"date_created":"2020-03-25 22:29:07.715265","version":"1.15.17"} \ 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"} diff --git a/.gitignore b/.gitignore index 93baea49f73ca9c807d41f71ac6cba1aa443ff43..a1b47e53bf30b0552bf6204186dc275a0899c63b 100644 --- a/.gitignore +++ b/.gitignore @@ -289,3 +289,5 @@ modules.xml # End of https://www.gitignore.io/api/linux,django,python,pycharm+all tests.output + +.flutter-plugins-dependencies diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c73806b74dce2b8ad3336235acda0c95f4e4e742..da9a47c7190313948d82edb935b54ebcdf895416 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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: diff --git a/README.md b/README.md index 67bdfefd39a8e5db3902558252c432f5f09598a4..6c0f425d264c9e29e46a7929166d7d3cb77bf6c3 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/assets/images/disabletoilet.jpg b/assets/images/disabletoilet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..74ce1ac58fd215c325a41b25404e6b3e05712ea3 Binary files /dev/null and b/assets/images/disabletoilet.jpg differ diff --git a/assets/images/margocity.jpg b/assets/images/margocity.jpg new file mode 100644 index 0000000000000000000000000000000000000000..af9bc7e0b559cb8d63d8a12e60cd03b01783e315 Binary files /dev/null and b/assets/images/margocity.jpg differ diff --git a/lib/app.dart b/lib/app.dart index f5a00dfb9b5822c8be96289a04ace48f9c1df934..f113fe88a70914aec0e9f1176f7ddb2f2df22978 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:ppl_disabilitas/page/dashboard/dashboard.dart'; +//import 'package:ppl_disabilitas/page/filter & fasilitas/fasilitas.dart'; class BisaGo extends StatelessWidget { @override diff --git a/lib/bloc/LokasiResponseBloc.dart b/lib/bloc/LokasiResponseBloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..e9266e95fe9a66acc34ec1d22e631772bf9c51a6 --- /dev/null +++ b/lib/bloc/LokasiResponseBloc.dart @@ -0,0 +1,64 @@ +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> get recentSearchSink => + _recentSearchController.sink; + Stream> get recentSearchStream => + _recentSearchController.stream; + + StreamSink> get lokasiListSink => + _lokasiListController.sink; + Stream> get lokasiListStream => + _lokasiListController.stream; + + LokasiResponseBloc() { + _lokasiListController = + StreamController>(); + _recentSearchController = StreamController>(); + _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(); + } +} diff --git a/lib/config/strings.dart b/lib/config/strings.dart index f680bdbc8eb6ac533121eaa2f9de0da036523dac..0162847de9bfef0804b5b982a287419cc9838126 100644 --- a/lib/config/strings.dart +++ b/lib/config/strings.dart @@ -5,12 +5,12 @@ final String devBaseURL = "poipole.herokuapp.com"; final String baseURL = "poipole.herokuapp.com"; String key = ""; String csrf = ""; -String sessionID = ""; +String sessionId = ""; -setKey(String key) { - key = key; +setKey(String newKey) { + key = newKey; } -setSessionId(String sessionId) { - sessionID = sessionId; +setSessionId(String newSessionId) { + sessionId = newSessionId; } \ No newline at end of file diff --git a/lib/config/styles.dart b/lib/config/styles.dart index 3b140b67428cd2b3e08642963582324ae7d0420e..aa7838fdcbe5056cd0fd90dba217b58fcbfd947f 100644 --- a/lib/config/styles.dart +++ b/lib/config/styles.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; final Color greenPrimary = Color(0xff3A903A); final Color redPrimary = Color(0xffC60000); -final Color bluePrimary = Color(0xff537AC6); final double smallSpace = 4.0; final double regularSpace = 8.0; @@ -13,9 +12,25 @@ final double spaceFourtyEight = 48.0; final List regularShadow = [ BoxShadow( - blurRadius: 4, - color: Colors.black.withOpacity(0.25), - offset: Offset(0, 0)) + color: Colors.black.withOpacity(0.25), + blurRadius: 7.0, // has the effect of softening the shadow + offset: Offset( + 5.0, // horizontal, move right 10 + 5.0, // vertical, movesn down 10 + ), + ) ]; + +final List smallShadow = [ + BoxShadow( + color: Colors.black.withOpacity(0.25), + blurRadius: 2.0, // has the effect of softening the shadow + offset: Offset( + 1.5, // horizontal, move right 10 + 1.5, // vertical, move down 10 + ), + ) +]; + final BorderRadius regularBorderRadius = BorderRadius.circular(10); final BorderRadius doubleBorderRadius = BorderRadius.circular(20); diff --git a/lib/model/lokasi.dart b/lib/model/lokasi.dart new file mode 100644 index 0000000000000000000000000000000000000000..7981aec880c098aa96785a6515d9084cb99127b6 --- /dev/null +++ b/lib/model/lokasi.dart @@ -0,0 +1,25 @@ +import 'package:json_annotation/json_annotation.dart'; +part 'lokasi.g.dart'; +@JsonSerializable() +class LokasiListResponse { + List listLokasi; + + LokasiListResponse(); + factory LokasiListResponse.fromJson(List json) => _$LokasiListResponseFromJson(json); + Map 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 json) => _$LokasiFromJson(json); + Map toJson() => _$LokasiToJson(this); +} diff --git a/lib/model/lokasi.g.dart b/lib/model/lokasi.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..a5ec83050b8de86fb48eeeb2c8dd560f82f1567d --- /dev/null +++ b/lib/model/lokasi.g.dart @@ -0,0 +1,40 @@ +// 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)) + ?.toList(); +} + +Map _$LokasiListResponseToJson(LokasiListResponse instance) => + { + 'listLokasi': instance.listLokasi, + }; + + +Lokasi _$LokasiFromJson(Map 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 _$LokasiToJson(Lokasi instance) => { + 'nama': instance.nama, + 'latitude': instance.latitude, + 'longitude': instance.longitude, + 'alamat': instance.alamat, + 'foto': instance.foto, + 'telp': instance.telp, +}; diff --git a/lib/network/CustomException.dart b/lib/network/CustomException.dart new file mode 100644 index 0000000000000000000000000000000000000000..7d533760addfda8e9f174f86d4caef0ac0f8c9b6 --- /dev/null +++ b/lib/network/CustomException.dart @@ -0,0 +1,27 @@ +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 diff --git a/lib/network/cookies_interface.dart b/lib/network/cookies_interface.dart new file mode 100644 index 0000000000000000000000000000000000000000..6a6a079ff8212cee11a02f13dc6be5080228ac8f --- /dev/null +++ b/lib/network/cookies_interface.dart @@ -0,0 +1,98 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:path_provider/path_provider.dart'; +import 'package:ppl_disabilitas/config/strings.dart'; + +class CookiesInterface { + Future checkCookieFileAvailability({String fileName}) async { + Directory dir; + await getApplicationDocumentsDirectory().then((Directory directory) { + dir = directory; + }); + File cookieFile = File("${dir.path}/$fileName.json"); + bool cookiesExist = cookieFile.existsSync(); + + return cookiesExist; + } + + Future createSignInCookie({ + Map responseHeaders}) async { + try { + String setCookie; + String csrfToken; + String sessionId; + String userKey; + List cookiesList; + Directory dir; + + await getApplicationDocumentsDirectory().then((Directory directory) { + dir = directory; + }); + File cookieFile = File("${dir.path}/usercookies.json"); + cookieFile.createSync(); + + setCookie = responseHeaders["set-cookie"]; + if (setCookie != null) { + csrfToken = setCookie.split(";")[0].split("=")[1]; + sessionId = setCookie.split(";")[4].split(",")[1].split("=")[1]; + userKey = key; + } + cookiesList = [ + csrfToken, + sessionId, + userKey, + ]; + cookieFile.writeAsStringSync(json.encode(cookiesList)); + return cookieFile; + } on Exception catch (e) { + print(e.toString()); + rethrow; + } + } + + Future createSearchHistoryCookie({ + Map recentSearch}) async { + print("recent searrch $recentSearch"); + Directory dir; + List currentSearchHistory; + try { + await getApplicationDocumentsDirectory().then((Directory directory) { + dir = directory; + }); + File cookieFile = File(dir.path + "/searchhistory.json"); + cookieFile.createSync(); + 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()); + rethrow; + } + } + + Future getCookieFile({String fileName}) async { + Directory dir; + await getApplicationDocumentsDirectory().then((Directory directory) { + dir = directory; + }); + File file = File("${dir.path}/$fileName.json"); + dynamic res = file.readAsStringSync(); + return res; + } +} diff --git a/lib/network/data/network_model.dart b/lib/network/data/network_model.dart new file mode 100644 index 0000000000000000000000000000000000000000..f42bffafabfe8b772622147ab6b0e697dddfc463 --- /dev/null +++ b/lib/network/data/network_model.dart @@ -0,0 +1,16 @@ +class NetworkModel { + Status status; + T data; + String message; + + 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 } diff --git a/lib/network/dummy.dart b/lib/network/dummy.dart new file mode 100644 index 0000000000000000000000000000000000000000..af243edff6d51ee244328a97c28eda75d7f526d3 --- /dev/null +++ b/lib/network/dummy.dart @@ -0,0 +1,165 @@ +final mall = [ + { + "nama": "Garrison", + "latitude": -29.7127463, + "longitude": -51.2422395, + "alamat": "39 Florence Crossing", + "telepon": "+55 427 384 8575" + }, + { + "nama": "Merchant", + "latitude": 59.3528754, + "longitude": 24.0551606, + "alamat": "479 Tennessee Alley", + "telepon": "+372 472 370 4597" + }, + { + "nama": "Sachs", + "latitude": 53.7109905, + "longitude": 20.6879247, + "alamat": "238 Hermina Park", + "telepon": "+48 574 907 6433" + }, + { + "nama": "Mccormick", + "latitude": -40.1929865, + "longitude": 175.2929384, + "alamat": "70127 Warrior Pass", + "telepon": "+64 280 260 5446" + }, + { + "nama": "Columbus", + "latitude": 38.627216, + "longitude": -9.1035863, + "alamat": "502 Carberry Park", + "telepon": "+351 321 303 5722" + }, + { + "nama": "Homewood", + "latitude": 55.6849184, + "longitude": 12.5506988, + "alamat": "95032 Stephen Crossing", + "telepon": "+45 542 945 6476" + }, + { + "nama": "Fallview", + "latitude": -8.4824984, + "longitude": 118.9586867, + "alamat": "75544 Farragut Center", + "telepon": "+62 436 950 7980" + }, + { + "nama": "Sugar", + "latitude": 40.09864, + "longitude": 119.949545, + "alamat": "1251 Logan Hill", + "telepon": "+86 212 470 4195" + }, + { + "nama": "Holy Cross", + "latitude": 16.6482598, + "longitude": 101.0118776, + "alamat": "7 Corry Drive", + "telepon": "+66 136 457 5719" + }, + { + "nama": "Bashford", + "latitude": 12.3730419, + "longitude": 14.2076222, + "alamat": "92 Rockefeller Road", + "telepon": "+234 119 536 3649" + } +]; + +final postingan = [ + { + "nama_orang": "Barny Folkerd", + "suka": 1, + "tidak_suka": 1, + "diverifikasi": true, + "foto": "http://dummyimage.com/128x141.png/ff4444/ffffff", + "date": "12/22/2019", + "time": "3:57 AM" + }, + { + "nama_orang": "Erhart Cicchillo", + "suka": 2, + "tidak_suka": 2, + "diverifikasi": true, + "foto": "http://dummyimage.com/128x141.png/5fa2dd/ffffff", + "date": "1/8/2020", + "time": "6:38 PM" + }, + { + "nama_orang": "Jori Biaggioli", + "suka": 3, + "tidak_suka": 3, + "diverifikasi": true, + "foto": "http://dummyimage.com/128x141.png/cc0000/ffffff", + "date": "7/23/2019", + "time": "5:59 AM" + }, + { + "nama_orang": "Giacinta Mirando", + "suka": 4, + "tidak_suka": 4, + "diverifikasi": true, + "foto": "http://dummyimage.com/128x141.png/dddddd/000000", + "date": "12/3/2019", + "time": "11:14 PM" + }, + { + "nama_orang": "Reece Seals", + "suka": 5, + "tidak_suka": 5, + "diverifikasi": false, + "foto": "http://dummyimage.com/128x141.png/cc0000/ffffff", + "date": "11/6/2019", + "time": "11:27 PM" + }, + { + "nama_orang": "Lark McReidy", + "suka": 6, + "tidak_suka": 6, + "diverifikasi": true, + "foto": "http://dummyimage.com/128x141.png/dddddd/000000", + "date": "1/15/2020", + "time": "11:05 AM" + }, + { + "nama_orang": "Helli Gentsch", + "suka": 7, + "tidak_suka": 7, + "diverifikasi": false, + "foto": "http://dummyimage.com/128x141.png/cc0000/ffffff", + "date": "1/24/2020", + "time": "9:17 PM" + }, + { + "nama_orang": "Beniamino Dadd", + "suka": 8, + "tidak_suka": 8, + "diverifikasi": true, + "foto": "http://dummyimage.com/128x141.png/dddddd/000000", + "date": "1/17/2020", + "time": "12:34 PM" + }, + { + "nama_orang": "Mar Outridge", + "suka": 9, + "tidak_suka": 9, + "diverifikasi": false, + "foto": "http://dummyimage.com/128x141.png/5fa2dd/ffffff", + "date": "9/16/2019", + "time": "12:17 AM" + }, + { + "nama_orang": "Domenic Pennetta", + "suka": 10, + "tidak_suka": 10, + "diverifikasi": false, + "foto": "http://dummyimage.com/128x141.png/dddddd/000000", + "date": "4/4/2019", + "time": "9:46 PM" + } +]; diff --git a/lib/network/network_interface.dart b/lib/network/network_interface.dart new file mode 100644 index 0000000000000000000000000000000000000000..a451cf3934eafb869984372427c71a5aad9b6e95 --- /dev/null +++ b/lib/network/network_interface.dart @@ -0,0 +1,94 @@ +import 'dart:convert'; +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 post({ + String url, //url nya apa + dynamic bodyParams, //data apa yang mau dikasih + bool isLogin, //dia login apa ngga + }) async { + var responseJson; + Map headersJson = + await _buildRequestHeader(isLogin); //butuh header apa ngga + try { + final response = await http.post( + "$url", + body: json.encode(bodyParams), + headers: headersJson, + ); + responseJson = _response(response); + } on SocketException { + throw FetchDataException("No Internet Connection"); + } + return responseJson; + } + + // GET request + Future get({ + String url, + bool isLogin, + }) async { + var responseJson; + Map headersJson = await _buildRequestHeader(isLogin); + try { + final response = await http.get( + "$url", + headers: headersJson, + ); + responseJson = _response(response); + } on SocketException { + throw FetchDataException("No Internet Connection"); + } + return responseJson; + } + + // buildRequestHeader: untuk nentuin pake header apa aja berdasarkan login apa ngga + Future> _buildRequestHeader(bool isLogin) async { + Map headers = Map(); + headers.putIfAbsent("Content-Type", () => "application/json"); + //if (isLogin) { + //List 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; + } + + 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}'); + } + } +} diff --git a/lib/page/dashboard/dashboard.dart b/lib/page/dashboard/dashboard.dart index 1f4dcea180ad2772a05bc7cb2d5088c89bf6729d..a8c24ec04d86ccf7a26ad49651e23816c808e6e3 100644 --- a/lib/page/dashboard/dashboard.dart +++ b/lib/page/dashboard/dashboard.dart @@ -46,6 +46,7 @@ class DashboardState extends State { } 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 { Widget build(BuildContext context) { return Scaffold( drawer: BisaGoDrawer(), - body: Stack(children: [ + body: Stack(key: Key("Stack"),children: [ _buildGoogleMap(context), InkWell( key: Key("Navigate to Pencarian"), diff --git a/lib/page/filter & fasilitas/fasilitas.dart b/lib/page/filter & fasilitas/fasilitas.dart new file mode 100644 index 0000000000000000000000000000000000000000..858c0944d4cd54875fce53de2fc5b984bbe6fca4 --- /dev/null +++ b/lib/page/filter & fasilitas/fasilitas.dart @@ -0,0 +1,299 @@ +import 'package:flutter/material.dart'; +import 'package:ppl_disabilitas/component/bisago_appbar.dart'; +import 'package:ppl_disabilitas/component/bisago_drawer.dart'; +import 'package:ppl_disabilitas/config/styles.dart'; + +class Fasilitas extends StatefulWidget { + @override + _FasilitasState createState() => _FasilitasState(); +} + +class _FasilitasState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + drawer: BisaGoDrawer(), + body: ListView(children: [ + Container( + child: Image.asset('assets/images/margocity.jpg'), + ), + Container( + padding: EdgeInsets.all(doubleSpace), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Margo City', + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.w800, + color: Colors.black, + fontFamily: 'Muli', + ), + ), + Container( + margin: EdgeInsets.only( + top: regularSpace, bottom: regularSpace, left: smallSpace), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.place, color: Colors.black, size: 20), + Flexible( + child: Text( + 'Jl. Margonda Raya No.358, Kemiri Muka, Kecamatan Beji, Kota Depok, Jawa Barat 16423', + softWrap: true, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 15, + color: Colors.black, + fontFamily: 'Muli', + ), + ), + ), + ], + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + Icons.local_phone, + color: Colors.black, + size: 20, + ), + Flexible( + child: Text( + '+6289535205205', + softWrap: true, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 15, + color: Colors.black, + fontFamily: 'Muli', + ), + ), + ), + ], + ), + Container( + margin: EdgeInsets.only( + top: doubleSpace, bottom: doubleSpace, left: smallSpace), + decoration: BoxDecoration(boxShadow: regularShadow), + child: SizedBox( + width: double.infinity, + child: FlatButton( + color: Colors.green[700], + textColor: Colors.white, + disabledColor: Colors.grey, + disabledTextColor: Colors.black, + padding: EdgeInsets.all(8), + shape: RoundedRectangleBorder( + borderRadius: regularBorderRadius, + side: BorderSide(color: Colors.transparent)), + splashColor: Colors.lightGreen, + onPressed: () { + /*...*/ + }, + child: Text( + "Tambah Informasi", + style: TextStyle(fontSize: 20), + ), + ), + ), + ), + Container( + margin: EdgeInsets.only(bottom: doubleSpace), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Fasilitas', + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.w800, + color: Colors.black, + fontFamily: 'Muli', + ), + ), + Container( + decoration: BoxDecoration(boxShadow: regularShadow), + child: FlatButton( + color: Colors.green[700], + textColor: Colors.white, + disabledColor: Colors.grey, + disabledTextColor: Colors.black, + padding: EdgeInsets.all(8), + shape: RoundedRectangleBorder( + borderRadius: regularBorderRadius, + side: BorderSide(color: Colors.transparent)), + splashColor: Colors.lightGreen, + onPressed: () { + /*...*/ + }, + child: Row( + children: [ + Icon(Icons.filter_list, + color: Colors.white, size: 20), + Text( + "Filter Informasi", + style: TextStyle(fontSize: 13), + ), + ], + ), + ), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(bottom: regularSpace), + padding: EdgeInsets.all(doubleSpace), + decoration: BoxDecoration( + boxShadow: regularShadow, + border: Border.all( + width: 2, color: greenPrimary.withOpacity(0.4)), + borderRadius: BorderRadius.all( + Radius.circular(10) // <--- border radius here + ), + color: Colors.white, + ), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(bottom: regularSpace), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Afrah Hardian', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w800, + color: Colors.black, + fontFamily: 'Muli', + ), + ), + Row( + children: [ + Container( + margin: + EdgeInsets.only(right: regularSpace), + child: Row( + children: [ + Icon(Icons.thumb_up, + color: Colors.green[800], size: 20), + Text( + '20 suka', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.green[800], + ), + ), + ], + )), + Icon(Icons.thumb_down, + color: redPrimary, size: 20), + Text( + '1 tidak suka', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: redPrimary, + ), + ), + ], + ) + ], + ), + ), + Container( + margin: EdgeInsets.only(bottom: regularSpace), + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: + Image.asset('assets/images/disabletoilet.jpg'), + )), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: Text( + 'Ada toilet khusus disabilitas terletak di lantai 2 dekat kintan, kondisinya bagus dan bersih layak pak.', + softWrap: true, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 15, + color: Colors.black, + fontFamily: 'Muli', + ), + ), + ), + ], + ), + Row( + children: [ + Container( + margin: EdgeInsets.only( + top: regularSpace, right: regularSpace), + padding: EdgeInsets.all(6), + decoration: BoxDecoration( + boxShadow: smallShadow, + border: Border.all( + width: 2, + color: greenPrimary.withOpacity(0.4)), + borderRadius: BorderRadius.all(Radius.circular( + 10) // <--- border radius here + ), + color: Colors.white, + ), + child: Text( + '#toiletdisabilitas', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w800, + color: greenPrimary, + fontFamily: 'Muli', + ), + ), + ), + Container( + margin: EdgeInsets.only(top: regularSpace), + padding: EdgeInsets.all(6), + decoration: BoxDecoration( + boxShadow: smallShadow, + border: Border.all( + width: 2, + color: greenPrimary.withOpacity(0.4)), + borderRadius: BorderRadius.all(Radius.circular( + 10) // <--- border radius here + ), + color: Colors.white, + ), + child: Text( + '#kursiroda', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w800, + color: greenPrimary, + fontFamily: 'Muli', + ), + ), + ), + ], + ), + ], + )), + ], + ), + ), + ]), + appBar: PreferredSize( + preferredSize: Size.fromHeight(55), + child: BisaGoAppBar(), + key: Key('Scaffold Text Field'), + ), + ); + } +} diff --git a/lib/page/pencarian/pencarian.dart b/lib/page/pencarian/pencarian.dart index 5e4a680c2eed5bdd1f02e93cdf49c03cefadda00..20aad672ac0a2174716f9d4aedeeb68b47196732 100644 --- a/lib/page/pencarian/pencarian.dart +++ b/lib/page/pencarian/pencarian.dart @@ -1,5 +1,8 @@ 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'; class Pencarian extends StatefulWidget { @override @@ -9,6 +12,13 @@ class Pencarian extends StatefulWidget { class PencarianState extends State { Icon searchIcon = Icon(Icons.search); Widget appBarText = Text("Pencarian Lokasi"); + LokasiResponseBloc _bloc = LokasiResponseBloc(); + LokasiListResponse lokasiFromApi; + LokasiListResponse recentSearch; + @override + void initState() { + super.initState(); + } @override Widget build(BuildContext context) { @@ -53,118 +63,154 @@ class PencarianState extends State { ), ), ), - body: ListView( - padding: const EdgeInsets.all(8), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: EdgeInsets.only(left: doubleSpace, top: 10), - child: Text( - 'Hasil Pencarian', - style: TextStyle( - fontSize: 15, - color: Colors.black, - fontFamily: 'Muli', - ), - ), - ), - Container( - height: 90, - color: Colors.transparent, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircleAvatar( - backgroundColor: greenPrimary, - child: Text('Test'), - ), - Padding( - padding: EdgeInsets.all(doubleSpace), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Margo City', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w800, - color: Colors.black, - fontFamily: 'Muli', - ), + StreamBuilder>( + stream: _bloc.recentSearchStream, + builder: (context, snapshot) { + if (snapshot.hasData) { + switch (snapshot.data.status) { + case Status.LOADING: + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(greenPrimary), ), - Text( - 'Jl. Margonda Raya No.358, Kemir...', - style: TextStyle( - fontSize: 15, - color: Colors.black, - fontFamily: 'Muli', - ), - ), - ], - ), - ), - Icon( - Icons.arrow_forward_ios, - color: Colors.grey[400], - size: 20, - ) - ], - ), + ); + break; + case Status.COMPLETED: + recentSearch = snapshot.data.data; + Widget displayWidget; + if (recentSearch.listLokasi.isEmpty) { + displayWidget = Center( + child: Text("Anda belum pernah melakukan pencarian")); + } else { + displayWidget = makeLokasiWidget("history",recentSearch); + } + return Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.all(doubleSpace), + child: Text( + "Pencarian terdahulu", + style: TextStyle( + fontFamily: 'Muli', fontSize: 15), + )), + Flexible(child: displayWidget), + ], + )); + break; + case Status.ERROR: + return Center( + child: Text("${snapshot.data.status}"), + ); + break; + } + } + return Container(); + }, ), Container( - decoration: BoxDecoration( - border: Border(top: BorderSide(color: Colors.grey[400]))), + margin: EdgeInsets.only(left: doubleSpace, top: regularSpace, bottom: smallSpace), + child: Text("Hasil Pencarian"), ), - Container( - height: 90, - color: Colors.transparent, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircleAvatar( - backgroundColor: greenPrimary, - child: Text('Test'), - ), - Padding( - padding: EdgeInsets.all(doubleSpace), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + StreamBuilder>( + stream: _bloc.lokasiListStream, + builder: (context, snapshot) { + if (snapshot.hasData) { + switch (snapshot.data.status) { + case Status.LOADING: + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(greenPrimary), + ), + ); + break; + case Status.COMPLETED: + lokasiFromApi = snapshot.data.data; + return Expanded( + flex: 2, child: makeLokasiWidget("api",lokasiFromApi)); + break; + case Status.ERROR: + return Center( + child: Text(snapshot.data.data.toString()), + ); + break; + } + } + return Container(); + }, + ), + ], + ), + ); + } + + Widget makeLokasiWidget(String key, LokasiListResponse places) { + return ListView.builder( + shrinkWrap: true, + itemCount: places.listLokasi.length, + itemBuilder: (context, index) { + return InkWell( + key: Key("$key-${places.listLokasi[index].nama}"), + onTap: () { + _bloc.saveRecentSearch(places.listLokasi[index]); + }, + child: Container( + decoration: BoxDecoration( + color: Colors.transparent, + border: Border(bottom: BorderSide(color: Colors.grey[400]))), + margin: EdgeInsets.only(left: doubleSpace, right: doubleSpace), + height: 90, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( children: [ - Text( - 'Margo City', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w800, - color: Colors.black, - fontFamily: 'Muli', - ), + CircleAvatar( + backgroundColor: greenPrimary, + child: Text('Test'), ), - Text( - 'Jl. Margonda Raya No.358, Kemir...', - style: TextStyle( - fontSize: 15, - color: Colors.black, - fontFamily: 'Muli', + Container( + padding: EdgeInsets.all(doubleSpace), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + places.listLokasi[index].nama, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w800, + color: Colors.black, + fontFamily: 'Muli', + ), + ), + Text( + places.listLokasi[index].alamat, + style: TextStyle( + fontSize: 15, + color: Colors.black, + fontFamily: 'Muli', + ), + ), + ], ), ), ], ), - ), - Icon( - Icons.arrow_forward_ios, - color: Colors.grey[400], - size: 20, - ) - ], + Icon( + Icons.arrow_forward_ios, + color: Colors.grey[400], + size: 20, + ) + ], + ), ), - ), - Container( - decoration: BoxDecoration( - border: Border(top: BorderSide(color: Colors.grey[400]))), - ), - ], - ), - ); + ); + }); } } diff --git a/lib/repository/LokasiRepository.dart b/lib/repository/LokasiRepository.dart new file mode 100644 index 0000000000000000000000000000000000000000..c838e73314e65d3f4f834419af97339fcc154f48 --- /dev/null +++ b/lib/repository/LokasiRepository.dart @@ -0,0 +1,37 @@ +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 fetchLokasi() async { + final response = await _network.get( + url: 'https://my.api.mockaroo.com/mall.json?key=dbcde960', + isLogin: false); + return LokasiListResponse.fromJson(response); + } + + Future 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 saveRecentSearch(Lokasi recentSearch) async { + Map searchToMap = recentSearch.toJson(); + await CookiesInterface() + .createSearchHistoryCookie(recentSearch: searchToMap); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 60b1bcba2fbd8c886733d270bf276ab7df6ee22a..9ff4dfd9e39a40d3d208fc94cd1296b70534e0be 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,11 +25,11 @@ dependencies: location: ^2.5.3 flutter_plugin_android_lifecycle: ^1.0.6 flutter_polyline_points: ^0.1.0 - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. + path_provider: ^1.6.5 cupertino_icons: ^0.1.2 google_maps_flutter: ^0.5.24+1 flutter_dotenv: ^2.1.0 + json_serializable: ^3.2.5 dev_dependencies: flutter_test: @@ -37,6 +37,7 @@ dev_dependencies: flutter_launcher_icons: ^0.7.4 # Linter dependency pedantic: ^1.8.0 # The default Linter package used in Google + build_runner: ^1.8.0 flutter_icons: android: "launcher_icon" @@ -60,6 +61,8 @@ flutter: - assets/icon/loc.png - assets/icon/current_loc.png - assets/icon/icon_launcher.png + - assets/images/margocity.jpg + - assets/images/disabletoilet.jpg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. diff --git a/test/cookie_test.dart b/test/cookie_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..7a35a8c56f354eaaf63900337eba435e49f43118 --- /dev/null +++ b/test/cookie_test.dart @@ -0,0 +1,63 @@ +import 'dart:io'; +import 'package:flutter/services.dart'; +import 'package:mockito/mockito.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:ppl_disabilitas/network/cookies_interface.dart'; + +class MockCookiesInterface extends Mock implements CookiesInterface {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + MethodChannel channel = + const MethodChannel('plugins.flutter.io/path_provider'); + setUpAll(() async { + // Create a temporary directory. + final directory = await Directory.systemTemp.createTemp(); + + // Mock out the MethodChannel for the path_provider plugin. + channel.setMockMethodCallHandler((MethodCall methodCall) async { + // If you're getting the apps documents directory, return the path to the + // temp directory on the test environment instead. + if (methodCall.method == 'getApplicationDocumentsDirectory') { + return directory.path; + } + return null; + }); + }); + CookiesInterface mockHttpClient; + test('Creates cookie file for sign in session', () async { + final responseHeaderFromSignIn = { + "set-cookie": + "csrftoken=v4E6UNpTMUMAoDxMoSZUBVPuAh7mkIb96DfRcakdivghb0d57yvCZxbbya7L3kFv; expires=Fri, 05 Mar 2021 03:33:39 GMT; Max-Age=31449600; Path=/; SameSite=Lax;sessionid=vrarp9pga02bwr97duemf6ym94gjgepn; expires=Fri, 20 Mar 2020 03:33:39 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax", + }; + mockHttpClient = MockCookiesInterface(); + String rootDir = + await channel.invokeMethod('getApplicationDocumentsDirectory'); + when(mockHttpClient.createSignInCookie( + responseHeaders: responseHeaderFromSignIn)) + .thenAnswer((_) async { + await Future.delayed(Duration(milliseconds: 50)); + return Future.value(File("$rootDir/usercookies.json")); + }); + // combine with sign in test here + }); + test('Creates cookie file after search', () async { + final recentSearch = { + "nama": "Johnson", + "latitude": -2.9062039, + "longitude": 114.6905436, + "alamat": "2460 Comanche Crossing", + "telepon": "+62 805 612 4225" + }; + String rootDir = + await channel.invokeMethod("getApplicationDocumentsDirectory"); + when(mockHttpClient.createSearchHistoryCookie( + recentSearch: recentSearch + )) + .thenAnswer((_) async { + await Future.delayed(Duration(milliseconds: 50)); + return Future.value(File("$rootDir/usercookies.json")); + }); + // combine with sign in test here + }); +} diff --git a/test/fasilitas_test.dart b/test/fasilitas_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..71d2c313f480a86ca47f927cbb865e6da56c75b7 --- /dev/null +++ b/test/fasilitas_test.dart @@ -0,0 +1,29 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:ppl_disabilitas/page/filter & fasilitas/fasilitas.dart'; + + +void main() { + testWidgets('find fasilitas page', (WidgetTester tester) async { + // Provide the childWidget to the Container. + await tester.pumpWidget(MaterialApp(home: Fasilitas())); + // Search for the childWidget in the tree and verify it exists. + expect(find.byType(Scaffold), findsOneWidget); + expect(find.byType(Container), findsNWidgets(14)); + expect(find.byType(Icon), findsNWidgets(6)); + expect(find.byType(Column), findsNWidgets(2)); + expect(find.byType(Row), findsNWidgets(10)); + expect(find.byType(Text), findsNWidgets(13)); + expect(find.byType(Flexible), findsNWidgets(3)); + expect(find.byType(FlatButton), findsNWidgets(2)); + }); + +} diff --git a/test/mock_test.dart b/test/mock_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..537257fa5a6978b2f3df1af8853a28e8a10733c0 --- /dev/null +++ b/test/mock_test.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; +import 'package:mockito/mockito.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:ppl_disabilitas/network/network_interface.dart'; +import 'package:ppl_disabilitas/page/dashboard/dashboard.dart'; +import 'package:http/http.dart' as http; +import 'package:pedantic/pedantic.dart'; + +class MockNavigatorObserver extends Mock implements NavigatorObserver {} + +class MockNetwork extends Mock implements NetworkInterface {} + +class MockHttp extends Mock implements http.Client {} + +void main() { + group('Dashboard navigation tests', () { + NavigatorObserver mockObserver; + NetworkInterface mockNetwork; + MockHttp mockHttp; + setUp(() { + mockObserver = MockNavigatorObserver(); + mockNetwork = MockNetwork(); + mockHttp = MockHttp(); + when(mockHttp.get('http://wwww.google.com')) + .thenAnswer((_) async => http.Response('{"title": "Test"}', 200)); + when(mockNetwork.get(isLogin: false, url: anyNamed('url'))) + .thenAnswer((_) async { + await Future.delayed(Duration(milliseconds: 50)); + return Future.value([ + { + "nama": "Coolidge", + "latitude": -23.7169139, + "longitude": -46.8498038, + "alamat": "74809 Hooker Drive", + "telepon": "+55 956 836 5799" + } + ]); + }); + }); + + Future _buildDashboardPage(WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: Dashboard(), + + /// This mocked observer will now receive all navigation events + /// that happen in our app. + navigatorObservers: [mockObserver], + )); + + /// The tester.pumpWidget() call above just built our app widget + /// and triggered the pushObserver method on the mockObserver once. + verify(mockObserver.didPush(any, any)); + } + + Future _navigateToPencarianPage(WidgetTester tester) async { + final textFieldKey = Key("Text Field Mau Kemana"); + await tester.tap(find.byKey(textFieldKey)); + await tester.pump(); + } + + testWidgets( + 'when tapping text form field, should navigate to pencarian page', + (WidgetTester tester) async { + final textFieldKeyPencarian = Key("Text Field Mau Kemana"); + await _buildDashboardPage(tester); + await _navigateToPencarianPage(tester); + + verify(mockObserver.didPush(any, any)); + expect(find.byKey(textFieldKeyPencarian), findsOneWidget); + }); + + testWidgets('tapping the back button should navigate back to the dashboard', + (WidgetTester tester) async { + final backIconKey = Key("Back Icon Key"); + await _buildDashboardPage(tester); + await _navigateToPencarianPage(tester); + await tester.pump(); + final Route pushedRoute = + verify(mockObserver.didPush(captureAny, any)).captured.single; + String popResult; + unawaited(pushedRoute.popped.then((result) => popResult = result)); + await tester.tap(find.byKey(backIconKey)); + await tester.pumpAndSettle(); + expect(popResult, 'Take me back'); + }); + }); +} diff --git a/test/navigation_test.dart b/test/navigation_test.dart deleted file mode 100644 index d8952af10976e7f65cee572985b8f078949ec8b7..0000000000000000000000000000000000000000 --- a/test/navigation_test.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mockito/mockito.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:ppl_disabilitas/page/dashboard/dashboard.dart'; -import 'package:pedantic/pedantic.dart'; - - -class MockNavigatorObserver extends Mock implements NavigatorObserver {} - -void main() { - group('Dashboard navigation tests', () { - NavigatorObserver mockObserver; - - setUp(() { - mockObserver = MockNavigatorObserver(); - }); - - Future _buildDashboardPage(WidgetTester tester) async { - await tester.pumpWidget(MaterialApp( - home: Dashboard(), - - /// This mocked observer will now receive all navigation events - /// that happen in our app. - navigatorObservers: [mockObserver], - )); - - /// The tester.pumpWidget() call above just built our app widget - /// and triggered the pushObserver method on the mockObserver once. - verify(mockObserver.didPush(any, any)); - } - - Future _navigateToPencarianPage(WidgetTester tester) async { - final textFieldKey = Key("Text Field Mau Kemana"); - await tester.tap(find.byKey(textFieldKey)); - await tester.pumpAndSettle(); - } - - testWidgets( - 'when tapping text form field, should navigate to pencarina page', - (WidgetTester tester) async { - final textFieldKeyPencarian = Key("Text Field Mau Kemana"); - await _buildDashboardPage(tester); - await _navigateToPencarianPage(tester); - verify(mockObserver.didPush(any, any)); - expect(find.byKey(textFieldKeyPencarian), findsOneWidget); - - }); - - testWidgets('tapping the back button should navigate back to the dashboard', - (WidgetTester tester) async { - final backIconKey = Key("Back Icon Key"); - await _buildDashboardPage(tester); - await _navigateToPencarianPage(tester); - final Route pushedRoute =verify(mockObserver.didPush(captureAny, any)).captured.single; - String popResult; - unawaited(pushedRoute.popped.then((result) => popResult = result)); - await tester.tap(find.byKey(backIconKey)); - await tester.pumpAndSettle(); - expect(popResult, 'Take me back'); - - - - }); - }); -} \ No newline at end of file diff --git a/test/pencarian_test.dart b/test/pencarian_test.dart index 43546747e61eccf3c9b60b39e43965a131a56741..1d4f32f7d64818771b5db9b028bc288ee65f8a4e 100644 --- a/test/pencarian_test.dart +++ b/test/pencarian_test.dart @@ -6,19 +6,45 @@ // tree, read text, and verify that the values of widget properties are correct. +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; import 'package:ppl_disabilitas/page/pencarian/pencarian.dart'; +import 'package:ppl_disabilitas/network/network_interface.dart'; +class MockNetwork extends Mock implements NetworkInterface {} void main() { + MockNetwork mockNetwork; + setUp(() { + mockNetwork = MockNetwork(); + when(mockNetwork.get(isLogin: false, url: anyNamed('url'))).thenAnswer((_) async { + await Future.delayed(Duration(milliseconds: 50)); + return Future.value( + [ + { + "nama": "Coolidge", + "latitude": -23.7169139, + "longitude": -46.8498038, + "alamat": "74809 Hooker Drive", + "telepon": "+55 956 836 5799" + } + ] + ); + }); + }); testWidgets('display list view in pencarian', (WidgetTester tester) async { // Provide the childWidget to the Container. await tester.pumpWidget(MaterialApp(home: Pencarian())); + // [TODO] pencet textfieldnya, isi textfieldnya pake Coolidge + // [TODO] expect nya keluar satu item namanya coolidge, expectnya pake key aja + // [TODO] tiap item itu punya key unik // Search for the childWidget in the tree and verify it exists. - expect(find.byType(ListView), findsOneWidget); - expect(find.byType(Container), findsNWidgets(7)); - expect(find.byType(Icon), findsNWidgets(5)); + //expect(find.byType(ListView), findsNWidgets); + //expect(find.byType(Container), findsWidgets); + //expect(find.byType(Icon), findsWidgets); }); testWidgets('finds a text field in pencarian', (WidgetTester tester) async { diff --git a/test/widget_test.dart b/test/widget_test.dart index 0337d3091bf1d658deecd094f759046644658ace..0a223972d3cab637dbed4c8c3d3ed006175fa98c 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -14,7 +14,7 @@ void main() { await tester.pumpWidget(MaterialApp(home: Dashboard())); // Search for the childWidget in the tree and verify it exists. expect(find.byType(Scaffold), findsOneWidget); - expect(find.byType(Stack), findsNWidgets(2)); + expect(find.byKey(Key("Stack")), findsOneWidget); expect(find.byType(TextFormField), findsOneWidget); expect(find.byType(Icon), findsNWidgets(3)); expect(find.text('Kamu mau kemana?'), findsOneWidget);