Fakultas Ilmu Komputer UI
Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
ppl-fasilkom-ui
PPL Sosial
bisago
bisago-fe
Commits
fefbfb6a
Commit
fefbfb6a
authored
Jun 07, 2021
by
Dzaky Noor Hasyim
Browse files
Merge branch 'PBI-14-agenda_kegiatan' into 'development'
Pbi 14 agenda kegiatan See merge request
!81
parents
92d38a6a
b1d7597c
Pipeline
#82554
passed with stages
in 15 minutes and 56 seconds
Changes
16
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
android/fastlane/metadata/android/id/changelogs/changelogs.txt
View file @
fefbfb6a
3.
5.2
:
3.
6.0
:
- Tersedia nama dan foto profil pada drawer
- Tersedia daftar agenda kegiatan
- Pengguna dapat mengganti foto profil
- Pengguna dapat mengubah informasi kegiatan yang sudah ada
...
...
@@ -11,10 +12,4 @@
- Perbaikan bugs
3.2.0:
- Tersedia fitur membagikan informasi fasilitas/kegiatan disabilitas kepada orang lain
3.1.2:
- Perbaikan masuk dengan Google
- Tersedia fitur pencarian layanan
- Tersedia pilihan kamera ketika menambahkan gambar fasilitas
- Tersedia fitur riwayat pencarian
- Tersedia fitur membagikan informasi fasilitas/kegiatan disabilitas kepada orang lain
\ No newline at end of file
lib/bloc/kegiatan_search_bloc.dart
0 → 100644
View file @
fefbfb6a
import
'dart:async'
;
import
'package:bisaGo/network/data/network_model.dart'
;
import
'package:bisaGo/repository/kegiatan_repository.dart'
;
import
'package:get_it/get_it.dart'
;
class
KegiatanSearchBloc
{
StreamController
_recentSearchController
;
KegiatanRepository
_kegiatanRepository
;
StreamController
_kegiatanSearchListController
;
StreamController
<
NetworkModel
<
List
<
dynamic
>>>
get
recentSearchSink
=>
_recentSearchController
.
sink
;
Stream
<
NetworkModel
<
List
<
dynamic
>>>
get
recentSearchStream
=>
_recentSearchController
.
stream
;
StreamSink
<
NetworkModel
<
List
<
dynamic
>>>
get
kegiatanSearchListSink
=>
_kegiatanSearchListController
.
sink
;
Stream
<
NetworkModel
<
List
<
dynamic
>>>
get
layananListStream
=>
_kegiatanSearchListController
.
stream
;
KegiatanSearchBloc
()
{
_kegiatanSearchListController
=
StreamController
<
NetworkModel
<
List
<
dynamic
>>>();
_recentSearchController
=
StreamController
<
NetworkModel
<
List
<
dynamic
>>>();
_kegiatanRepository
=
GetIt
.
instance
.
get
<
BaseKegiatanRepository
>();
}
Future
<
void
>
fetchKegiatanSearchList
(
String
search
,
String
sortConfig
)
async
{
kegiatanSearchListSink
.
add
(
NetworkModel
.
loading
(
'Getting Kegiatan'
));
try
{
final
kegiatanSearchListResponse
=
await
_kegiatanRepository
.
fetchKegiatanSearch
(
search
,
sortConfig
);
kegiatanSearchListSink
.
add
(
NetworkModel
.
completed
(
kegiatanSearchListResponse
));
}
catch
(
e
)
{
kegiatanSearchListSink
.
add
(
NetworkModel
.
error
(
e
.
toString
()));
}
}
void
dispose
()
{
_recentSearchController
?.
close
();
_kegiatanSearchListController
?.
close
();
}
}
\ No newline at end of file
lib/bloc/lokasi_response_bloc.dart
View file @
fefbfb6a
...
...
@@ -42,6 +42,15 @@ class LokasiResponseBloc {
}
}
Future
<
Lokasi
>
fetchLokasiListById
(
String
placeId
)
async
{
lokasiListSink
.
add
(
NetworkModel
.
loading
(
'Getting Location by Id'
));
try
{
return
await
_lokasiRepository
.
fetchLokasiById
(
placeId
);
}
catch
(
e
)
{
return
Lokasi
();
}
}
Future
<
void
>
fetchRecentSearch
()
async
{
recentSearchSink
.
add
(
NetworkModel
.
loading
(
'Getting Recent Search'
));
try
{
...
...
lib/component/bisago_drawer.dart
View file @
fefbfb6a
import
'dart:convert'
;
import
'package:bisaGo/model/user.dart'
;
import
'package:bisaGo/page/informasi/list_
sekolah
.dart'
;
import
'package:bisaGo/page/informasi/list_
kegiatan
.dart'
;
import
'package:bisaGo/page/profile/profile.dart'
;
import
'package:bisaGo/page/tentang_disabilitas/tentang_disabilitas.dart'
;
import
'package:flutter/material.dart'
;
...
...
@@ -18,7 +18,8 @@ class BisaGoDrawer extends StatelessWidget {
final
List
<
Map
<
String
,
dynamic
>>
drawerList
=
[
{
'title'
:
'Beranda'
,
'icon'
:
Icons
.
home
},
{
'title'
:
'Riwayat Pencarian'
,
'icon'
:
Icons
.
history
},
{
'title'
:
'Layanan Disabilitas'
,
'icon'
:
Icons
.
group
},
// {'title': 'Layanan Disabilitas', 'icon': Icons.group},
{
'title'
:
'Agenda Kegiatan'
,
'icon'
:
Icons
.
calendar_today
},
{
'title'
:
'Tentang Disabilitas'
,
'icon'
:
Icons
.
accessible
},
{
'title'
:
'Tentang Aplikasi'
,
'icon'
:
Icons
.
info
},
{
'title'
:
'Login'
,
'icon'
:
Icons
.
keyboard_backspace
}
...
...
@@ -263,8 +264,10 @@ class BisaGoDrawer extends StatelessWidget {
route
=
MaterialPageRoute
(
builder:
(
_
)
=>
const
AboutUs
());
}
else
if
(
page
==
'Tentang Disabilitas'
)
{
route
=
MaterialPageRoute
(
builder:
(
_
)
=>
TentangDisabilitas
());
}
else
if
(
page
==
'Layanan Disabilitas'
)
{
route
=
MaterialPageRoute
(
builder:
(
_
)
=>
ListSekolah
());
// } else if (page == 'Layanan Disabilitas') {
// route = MaterialPageRoute(builder: (_) => ListSekolah());
}
else
if
(
page
==
'Agenda Kegiatan'
)
{
route
=
MaterialPageRoute
(
builder:
(
_
)
=>
ListKegiatan
());
}
Navigator
.
of
(
context
).
push
(
route
);
}
...
...
lib/config/strings.dart
View file @
fefbfb6a
...
...
@@ -105,6 +105,12 @@ const zonaWaktu = [
'WIT'
,
];
const
sortBy
=
{
'time'
:
'Kegiatan Terdekat'
,
'name'
:
'Nama Kegiatan'
,
'latest-added'
:
'Kegiatan Terbaru'
};
String
getTag
(
String
tag
)
{
return
tags
[
tag
];
}
lib/model/kegiatan.dart
View file @
fefbfb6a
...
...
@@ -56,4 +56,4 @@ class KegiatanModel {
factory
KegiatanModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$KegiatanModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$KegiatanModelToJson
(
this
);
}
}
\ No newline at end of file
lib/page/filter_fasilitas/postingan/detail_post_kegiatan.dart
View file @
fefbfb6a
...
...
@@ -157,6 +157,7 @@ class _DetailPostKegiatanPageState extends State<DetailPostKegiatanPage> {
enlargeCenterPage:
true
,
enableInfiniteScroll:
false
,
initialPage:
0
,
autoPlay:
true
),
items:
widget
.
kegiatan
.
images
.
map
((
item
)
=>
Container
(
...
...
@@ -517,8 +518,7 @@ class _DetailPostKegiatanPageState extends State<DetailPostKegiatanPage> {
),
),
],
),
),
),)
);
}
...
...
lib/page/informasi/list_kegiatan.dart
0 → 100644
View file @
fefbfb6a
import
'package:bisaGo/bloc/kegiatan_search_bloc.dart'
;
import
'package:bisaGo/component/bisago_drawer.dart'
;
import
'package:bisaGo/config/styles.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:bisaGo/model/kegiatan.dart'
;
import
'package:bisaGo/bloc/lokasi_response_bloc.dart'
;
import
'package:bisaGo/network/data/network_model.dart'
;
import
'package:bisaGo/page/filter_fasilitas/postingan/detail_post_kegiatan.dart'
;
import
'package:bisaGo/config/strings.dart'
;
import
'package:bisaGo/utils/custom_dropdown_sort_kegiatan.dart'
;
import
'package:bisaGo/utils/validator.dart'
;
import
'package:intl/intl.dart'
;
class
ListKegiatan
extends
StatefulWidget
{
const
ListKegiatan
({
Key
key
})
:
super
(
key:
key
);
@override
_ListKegiatanState
createState
()
=>
_ListKegiatanState
();
}
class
_ListKegiatanState
extends
State
<
ListKegiatan
>
{
/// Search Icon for textFormField
Icon
searchIcon
=
const
Icon
(
Icons
.
search
);
KegiatanSearchBloc
kegiatan_search_bloc
=
KegiatanSearchBloc
();
LokasiResponseBloc
lokasi_bloc
=
LokasiResponseBloc
();
@override
void
initState
()
{
_sortConfig
=
'time'
;
kegiatan_search_bloc
.
fetchKegiatanSearchList
(
_text
,
_sortConfig
);
super
.
initState
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
backgroundColor:
Colors
.
white
,
drawer:
BisaGoDrawer
(),
appBar:
AppBar
(
backgroundColor:
greenPrimary
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back_ios
,
size:
25
),
key:
const
Key
(
'Back Icon Key'
),
onPressed:
()
=>
Navigator
.
of
(
context
).
pop
()),
title:
Container
(
margin:
const
EdgeInsets
.
only
(
top:
doubleSpace
,
bottom:
doubleSpace
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
doubleBorderRadius
,
boxShadow:
regularShadow
),
child:
TextFormField
(
onFieldSubmitted:
(
text
)
{
_text
=
text
;
kegiatan_search_bloc
.
fetchKegiatanSearchList
(
_text
,
_sortConfig
);
},
key:
const
Key
(
'Text Field Cari Kegiatan'
),
decoration:
InputDecoration
(
prefixIcon:
const
Icon
(
Icons
.
search
,
color:
greenPrimary
,
size:
25
,
),
hintText:
'Cari kegiatan di sini'
,
),
),
),
),
body:
SingleChildScrollView
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
const
Padding
(
padding:
EdgeInsets
.
only
(
top:
doubleSpace
,
left:
doubleSpace
,
right:
doubleSpace
),
child:
Text
(
'Agenda Kegiatan'
??
''
,
style:
TextStyle
(
fontSize:
25
,
fontFamily:
'Muli'
,
fontWeight:
FontWeight
.
w800
),
),
),
Container
(
child:
CustomDropdown
(
title:
'Urutkan berdasarkan:'
,
required
:
true
,
key:
const
Key
(
'Dropdown Sort Kegiatan'
),
validator:
FieldValidator
.
validateDropdown
,
dropdownList:
sortBy
,
hint:
'Kegiatan Terdekat'
,
// hardcoded
onChanged:
(
value
)
{
setState
(()
{
_sortConfig
=
value
;
});
kegiatan_search_bloc
.
fetchKegiatanSearchList
(
_text
,
_sortConfig
);
},
),
),
//BLoC for Kegiatan Search
StreamBuilder
<
NetworkModel
<
List
<
dynamic
>>>(
stream:
kegiatan_search_bloc
.
layananListStream
,
builder:
(
context
,
snapshot
)
{
if
(
snapshot
.
hasData
)
{
switch
(
snapshot
.
data
.
status
)
{
case
Status
.
loading
:
return
const
Center
(
child:
CircularProgressIndicator
(
valueColor:
AlwaysStoppedAnimation
<
Color
>(
greenPrimary
),
),
);
break
;
case
Status
.
completed
:
var
layanan
=
snapshot
.
data
.
data
;
return
Column
(
children:
layanan
.
map
<
Widget
>((
each
)
{
return
makeKegiatanWidget
(
'kegiatan'
,
each
);
}).
toList
(),
);
break
;
case
Status
.
error
:
return
Center
(
child:
Container
(
padding:
const
EdgeInsets
.
only
(
top:
tripleSpace
),
child:
Text
(
'Tidak ada kegaitan ditemukan'
,
style:
const
TextStyle
(
fontSize:
18
,
color:
Colors
.
black
,
fontFamily:
'Muli'
,
),
textAlign:
TextAlign
.
center
,),),);
break
;
}
}
return
Container
();
},
),
],
),
),
);
}
Widget
makeKegiatanWidget
(
String
key
,
KegiatanModel
kegiatan
)
{
return
InkWell
(
key:
Key
(
'
$key
-
${kegiatan.id}
'
),
onTap:
()
{
_navigateToDetailKegiatanPage
(
context
,
kegiatan
);
},
child:
Container
(
decoration:
BoxDecoration
(
color:
Colors
.
transparent
,
border:
Border
(
bottom:
BorderSide
(
color:
Colors
.
grey
[
400
]))),
margin:
const
EdgeInsets
.
only
(
left:
doubleSpace
,
right:
doubleSpace
),
height:
90
,
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
<
Widget
>[
const
CircleAvatar
(
backgroundColor:
greenPrimary
,
child:
Icon
(
Icons
.
school
,
color:
Colors
.
white
,
),
),
Expanded
(
child:
Container
(
padding:
const
EdgeInsets
.
all
(
doubleSpace
),
child:
Row
(
//crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
<
Widget
>[
Flexible
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
Text
(
kegiatan
.
namaKegiatan
,
overflow:
TextOverflow
.
ellipsis
,
textAlign:
TextAlign
.
left
,
style:
const
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
w800
,
color:
Colors
.
black
,
fontFamily:
'Muli'
,
),
),
Text
(
kegiatan
.
penyelenggara
,
overflow:
TextOverflow
.
ellipsis
,
textAlign:
TextAlign
.
left
,
style:
const
TextStyle
(
fontSize:
15
,
//fontWeight: FontWeight.w800,
color:
Colors
.
black
,
fontFamily:
'Muli'
,
),
),
]
),
),
Text
(
DateFormat
(
'dd-MM-yyyy
\n
hh:mm'
).
format
(
kegiatan
.
timeStart
),
overflow:
TextOverflow
.
ellipsis
,
textAlign:
TextAlign
.
right
,
style:
const
TextStyle
(
fontSize:
15
,
color:
Colors
.
black
,
fontFamily:
'Muli'
,
),
),
],
),
),
),
Icon
(
Icons
.
arrow_forward_ios
,
color:
Colors
.
grey
[
400
],
size:
20
,
)
],
),
),
);
}
void
_navigateToDetailKegiatanPage
(
BuildContext
context
,
KegiatanModel
kegiatan
)
async
{
final
lokasi
=
await
lokasi_bloc
.
fetchLokasiListById
(
kegiatan
.
placeId
);
final
route
=
MaterialPageRoute
(
builder:
(
_
)
=>
DetailPostKegiatanPage
(
lokasi:
lokasi
,
kegiatan:
kegiatan
));
await
Navigator
.
of
(
context
).
push
(
route
);
}
String
getTanggalFormatting
(
KegiatanModel
kegiatan
)
{
return
null
;
}
String
_text
;
String
_sortConfig
;
}
\ No newline at end of file
lib/page/informasi/list_sekolah.dart
View file @
fefbfb6a
...
...
@@ -91,7 +91,7 @@ class _ListSekolahState extends State<ListSekolah> {
padding:
EdgeInsets
.
only
(
top:
tripleSpace
,
left:
doubleSpace
,
right:
doubleSpace
),
child:
Text
(
'
Jenis Layan
an'
,
'
Agenda Kegiat
an'
,
style:
TextStyle
(
fontSize:
25
,
fontFamily:
'Muli'
,
...
...
lib/page/profile/profile.dart
View file @
fefbfb6a
...
...
@@ -363,6 +363,7 @@ class _ProfileState extends State<Profile> {
}
Color
_getFontColor
(
String
fieldName
,
{
Color
seenColor
=
seenColor
})
{
if
(
user
==
null
)
return
seenColor
;
if
(
user
.
canSeeHiddenFields
)
return
seenColor
;
final
hiddenFields
=
user
.
hiddenFields
;
if
(
hiddenFields
.
contains
(
fieldName
))
{
...
...
lib/repository/kegiatan_repository.dart
View file @
fefbfb6a
...
...
@@ -9,6 +9,7 @@ abstract class BaseKegiatanRepository {
Future
<
dynamic
>
updateKegiatan
(
Map
<
String
,
dynamic
>
newKegiatanData
,
String
placeId
,
int
kegiatanId
);
Future
<
KegiatanModel
>
fetchDetailKegiatan
(
String
placeId
,
int
kegiatanId
);
Future
<
dynamic
>
fetchKegiatanSearch
(
String
search
,
String
sortConfig
);
}
class
KegiatanRepository
implements
BaseKegiatanRepository
{
...
...
@@ -30,6 +31,20 @@ class KegiatanRepository implements BaseKegiatanRepository {
return
KegiatanList
(
allKegiatan
);
}
@override
Future
<
dynamic
>
fetchKegiatanSearch
(
String
search
,
String
sortConfig
)
async
{
var
url
=
'/informasi-fasilitas/lokasi/list-kegiatan-by-
$sortConfig
'
;
if
(
search
!=
null
&&
search
!=
''
){
url
+=
'/
$search
'
;
}
final
response
=
await
_network
.
get
(
url:
url
,
isLogin:
false
);
var
list
=
[];
for
(
var
each
in
response
.
entries
.
toList
())
{
list
.
add
(
KegiatanModel
.
fromJson
(
each
.
value
));
}
return
list
;
// KegiatanSearchList
}
@override
Future
<
List
<
String
>>
fetchImages
(
String
placeId
,
int
id
)
async
{
final
url
=
'/informasi-fasilitas/lokasi/list-foto-kegiatan/
$placeId
/
$id
'
;
...
...
lib/repository/lokasi_repository.dart
View file @
fefbfb6a
...
...
@@ -11,6 +11,8 @@ import 'package:http/http.dart' as http;
abstract
class
BaseLokasiRepository
{
Future
<
LokasiListResponse
>
fetchLokasi
();
Future
<
Lokasi
>
fetchLokasiById
(
String
placeId
);
Future
<
LokasiListResponse
>
fetchRecentSearch
();
Future
<
void
>
saveRecentSearch
(
Lokasi
recentSearch
);
...
...
@@ -65,6 +67,32 @@ class LokasiRepository implements BaseLokasiRepository {
return
LokasiListResponse
(
freqPlaces
);
}
@override
Future
<
Lokasi
>
fetchLokasiById
(
String
placeId
)
async
{
final
_places
=
GoogleMapsPlaces
(
apiKey:
DotEnv
().
env
[
'API_KEY'
]);
var
details
=
await
_places
.
getDetailsByPlaceId
(
placeId
,
fields:
[
'name'
,
'formatted_address'
,
'formatted_phone_number'
,
'photos'
],
);
final
result
=
details
.
result
;
final
lokasi
=
Lokasi
()
..
placeId
=
result
.
placeId
..
name
=
result
.
name
??
'INVALID'
..
alamat
=
result
.
formattedAddress
??
'LOCATION INVALID'
..
noTelp
=
result
.
formattedPhoneNumber
??
'-'
;
final
image
=
fetchPhoto
(
details
.
result
.
photos
[
0
].
photoReference
,
480
,
480
)
??
''
;
lokasi
.
image
=
image
;
return
lokasi
;
}
@override
Future
<
LokasiListResponse
>
fetchRecentSearch
()
async
{
final
response
=
await
CookiesInterface
().
getSearchHistory
();
...
...
lib/utils/custom_dropdown_sort_kegiatan.dart
0 → 100644
View file @
fefbfb6a
import
'package:bisaGo/config/styles.dart'
;
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
class
CustomDropdown
extends
StatefulWidget
{
const
CustomDropdown
({
this
.
title
,
this
.
hint
,
this
.
required
=
false
,
this
.
key
,
this
.
validator
,
this
.
onSaved
,
this
.
dropdownList
,
this
.
onChanged
,
this
.
value
,
})
:
super
(
key:
key
);
final
String
title
;
final
String
value
;
@override
final
Key
key
;
final
FormFieldSetter
<
String
>
onSaved
;
final
String
hint
;
final
bool
required
;
final
FormFieldValidator
<
String
>
validator
;
final
Map
<
String
,
String
>
dropdownList
;
final
Function
onChanged
;
@override
_CustomDropdownState
createState
()
=>
_CustomDropdownState
();
}
class
_CustomDropdownState
extends
State
<
CustomDropdown
>
{
final
List
<
DropdownMenuItem
<
String
>>
_dropdownMenuItems
=
[];
void
loadDropdownList
()
{
for
(
final
key
in
widget
.
dropdownList
.
keys
)
{
_dropdownMenuItems
.
add
(
DropdownMenuItem
(
key:
Key
(
'Dropdown Item
${widget.dropdownList[key]}
'
),
value:
key
,
child:
Text
(
widget
.
dropdownList
[
key
]),
));
}
}
void
setDefaultValue
()
{
}
@override
void
initState
()
{
super
.
initState
();
loadDropdownList
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
margin:
const
EdgeInsets
.
only
(
top:
tripleSpace
,
left:
doubleSpace
,
right:
doubleSpace
,),
child:
Row
(
//crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
<
Widget
>[
RichText
(
key:
Key
(
widget
.
title
),
text:
TextSpan
(
text:
widget
.
title
,
style:
TextStyle
(
fontSize:
widget
.
title
==
''
?
0
:
15
,
color:
Colors
.
black
,
fontFamily:
'Muli'
),
),
),
// Container width and fontSize are bounded, re-adjust required after any change(s)
Container
(
width:
170
,
child:
DropdownButtonFormField
(
isExpanded:
true
,
onSaved:
widget
.
onSaved
,
validator:
widget
.
validator
,
style:
const
TextStyle
(
color:
Colors
.
black
,
fontSize:
15
,
fontFamily:
'Muli'
,
fontWeight:
FontWeight
.
w800
,
),
decoration:
InputDecoration
.
collapsed
(
hintStyle:
const
TextStyle
(
fontSize:
15
,
fontFamily:
'Muli'
,
fontWeight:
FontWeight
.
w800
,),
hintText:
widget
.
hint
,
),
value:
widget
.
value
,
items:
_dropdownMenuItems
,
onChanged:
widget
.
onChanged
,
),),
],
),
);
}
}
test/bisago_drawer_test.dart