Fakultas Ilmu Komputer UI

dashboard.dart 18 KB
Newer Older
Yoga Pratama's avatar
Yoga Pratama committed
1
import 'package:bisaGo/bloc/cloud_messaging_bloc.dart';
2
import 'package:bisaGo/bloc/lokasi_response_bloc.dart';
3
import 'package:bisaGo/model/komentar.dart';
4
import 'package:bisaGo/model/lokasi.dart';
5
6
import 'package:bisaGo/bloc/kegiatan_terdekat_bloc.dart';
import 'package:bisaGo/model/kegiatan.dart';
7
8
import 'package:bisaGo/page/filter_fasilitas/postingan/detail_post_kegiatan.dart';
import 'package:bisaGo/repository/kegiatan_repository.dart';
9
import 'package:bisaGo/utils/custom_kegiatan_terdekat_button.dart';
10
import 'package:bisaGo/network/data/network_model.dart';
11
12
import 'package:bisaGo/page/filter_fasilitas/postingan/detail_post.dart';
import 'package:bisaGo/repository/komentar_repository.dart';
13
import 'package:bisaGo/utils/custom_dashboard_location_button.dart';
14
import 'package:bisaGo/utils/location_turn_on_dialog.dart';
15
import 'package:firebase_core/firebase_core.dart';
16
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
17
import 'package:firebase_messaging/firebase_messaging.dart';
18
import 'package:flushbar/flushbar.dart';
19
import 'package:flutter/material.dart';
20
import 'package:flutter_dotenv/flutter_dotenv.dart';
21
import 'package:geolocator/geolocator.dart';
22
import 'package:google_maps_flutter/google_maps_flutter.dart';
23
24
25
26
import 'package:bisaGo/component/bisago_appbar.dart';
import 'package:bisaGo/component/bisago_drawer.dart';
import 'package:bisaGo/config/styles.dart';
import 'package:bisaGo/page/pencarian/pencarian.dart';
27
import 'package:google_maps_webservice/places.dart';
Yoga Pratama's avatar
Yoga Pratama committed
28
import 'package:shared_preferences/shared_preferences.dart';
29

30
31
import '../filter_fasilitas/postingan/detail_post.dart';

32
class Dashboard extends StatefulWidget {
33
  const Dashboard({Key key}) : super(key: key);
34
  @override
35
36
37
38
  DashboardState createState() => DashboardState();
}

class DashboardState extends State<Dashboard> {
Fakhira Devina's avatar
Fakhira Devina committed
39
  final double cameraZoom = 15;
40
41
42
  GoogleMapController mapController;
  Geolocator geolocator;
  LatLng currentLocation;
43
  Set<Marker> markers = {};
44

45
  LokasiResponseBloc bloc = LokasiResponseBloc();
46
  KegiatanTerdekatBloc blocKegiatanTerdekat = KegiatanTerdekatBloc();
Yoga Pratama's avatar
Yoga Pratama committed
47
  CloudMessagingBloc cloudMessagingBloc = CloudMessagingBloc();
48

49
50
51
52
  FirebaseMessaging _firebaseMessaging;

  bool _initialized = false;

53
54
  DateTime _lastNotification;

55
56
57
  @override
  void initState() {
    super.initState();
58
    _showTurnOnLocationDialog(context);
59
    geolocator = Geolocator()..forceAndroidLocationManager;
60
    initDynamicLinks();
61
    setInitialLocation();
62
63
64
65
66
67
68
69
70
71
72
73

    if (!_initialized) {
      _initialized = true;
      _setupFirebase();
    }
  }

  void _setupFirebase() async {
    await Firebase.initializeApp();
    _firebaseMessaging = FirebaseMessaging.instance;

    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
74
75
76
77
78
79
80
81
      final now = DateTime.now();

      if (_lastNotification != null &&
          _lastNotification.add(Duration(seconds: 10)).isAfter(now)) return;

      _lastNotification = now;
      final data = message.data;
      final String msg = data['message'];
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
      final String title = data['title'];
      Flushbar(
        title: title,
        message: '"$msg"',
        duration: Duration(seconds: 8),
        backgroundColor: Color(0xFF003566),
        onTap: (_) {
          if (data['type'] == 'fasilitas') {
            final String placeId = data['place_id'];
            final id = int.parse(data['id']);
            _navigateToDetailFasilitasPage(context, placeId, id);
          } else if (data['type'] == 'kegiatan') {
            final String placeId = data['place_id'];
            final id = int.parse(data['id']);
            _navigateToDetailKegiatanPage(context, placeId, id);
          }
        },
        flushbarStyle: FlushbarStyle.GROUNDED,
        flushbarPosition: FlushbarPosition.TOP,
      ).show(context);
102
    });
103

104
105
106
107
108
109
110
111
112
113
    FirebaseMessaging.onMessageOpenedApp.listen((message) {
      final data = message.data;
      if (data['type'] == 'fasilitas') {
        final String placeId = data['place_id'];
        final id = int.parse(data['id']);
        _navigateToDetailFasilitasPage(context, placeId, id);
      } else if (data['type'] == 'kegiatan') {
        final String placeId = data['place_id'];
        final id = int.parse(data['id']);
        _navigateToDetailKegiatanPage(context, placeId, id);
114
115
      }
    });
116

117
118
119
120
    _requestFCMToken();
  }

  void _requestFCMToken() async {
Yoga Pratama's avatar
Yoga Pratama committed
121
122
123
124
125
126
    final fcmToken = await _firebaseMessaging.getToken();
    final sharedPreferences = await SharedPreferences.getInstance();
    final token = sharedPreferences.getString('token');
    if (token != null) {
      await cloudMessagingBloc.sendFCMToken(fcmToken, token);
    }
127
128
  }

129
  void _navigateToPencarianPage(BuildContext context) {
130
    final route = MaterialPageRoute(builder: (_) => const Pencarian());
131
132
133
    Navigator.of(context).push(route);
  }

134
135
136
137
  // void _navigateToInformasiLayananDisabilitasPage(BuildContext context) {
  //   final route = MaterialPageRoute(builder: (_) => ListSekolah());
  //   Navigator.of(context).push(route);
  // }
138

139
  static const textFieldKey = Key('Text Field Mau Kemana');
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

  void _showTurnOnLocationDialog(BuildContext context) {
    bloc.isLocationServiceTurnedOn().then(
      (value) {
        if (!value) {
          showDialog(
            context: context,
            builder: (BuildContext context) {
              return LocationTurnOnDialog(
                message: 'Mohon Menyalakan Lokasi',
                title: 'Lokasi',
                onPressed: () {
                  bloc.requestLocationService().then((loc) {
                    if (loc) {
                      Navigator.pop(context);
                    }
                  });
                },
              );
            },
          );
        }
      },
    );
  }

166
167
168
  @override
  Widget build(BuildContext context) {
    return Scaffold(
169
      drawer: BisaGoDrawer(),
170
171
      body: Stack(
        key: const Key('Stack'),
172
        //alignment: AlignmentDirectional.bottomStart,
173
174
        children: <Widget>[
          Container(
175
              padding: EdgeInsets.only(top: 35),
Yoga Pratama's avatar
Yoga Pratama committed
176
177
178
179
              child: _buildGoogleMap(context)),
          Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <
              Widget>[
            Container(
180
181
182
183
184
                height: 40,
                alignment: Alignment.center,
                decoration: const BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.only(
Yoga Pratama's avatar
Yoga Pratama committed
185
186
                      bottomLeft: Radius.circular(10.0),
                      bottomRight: Radius.circular(10.0)),
187
188
                ),
                child: Padding(
Yoga Pratama's avatar
Yoga Pratama committed
189
190
191
192
                    padding:
                        const EdgeInsets.symmetric(horizontal: doubleSpace),
                    child: StreamBuilder<NetworkModel>(
                        stream: blocKegiatanTerdekat.kegiatanTerdekatStream,
193
194
195
196
197
                        builder: (context, snapshot) {
                          if (snapshot.hasData) {
                            switch (snapshot.data.status) {
                              case Status.loading:
                                return Container(
Yoga Pratama's avatar
Yoga Pratama committed
198
199
200
201
202
                                    child: const Center(
                                        child: LinearProgressIndicator(
                                  valueColor: AlwaysStoppedAnimation<Color>(
                                      greenPrimary),
                                )));
203
204
                                break;
                              case Status.completed:
Yoga Pratama's avatar
Yoga Pratama committed
205
206
207
                                final kegiatanTerdekat = snapshot.data.data;
                                return _buildKegiatanTerdekatWidget(
                                    kegiatanTerdekat);
208
209
210
                                break;
                              case Status.error:
                                return Container(
Yoga Pratama's avatar
Yoga Pratama committed
211
212
213
214
215
216
217
218
219
                                    child: const Center(
                                  child: Text(
                                      'Tidak ada kegiatan dalam waktu dekat',
                                      style: TextStyle(
                                        color: greenPrimary,
                                        fontSize: 16,
                                        fontFamily: 'Muli',
                                      )),
                                ));
220
221
222
223
224
                                break;
                            }
                            return Container();
                          }
                          return Container();
Yoga Pratama's avatar
Yoga Pratama committed
225
226
227
228
229
230
231
232
                        }))), // Container info kegiatan
            Container(
              height: 285,
              alignment: Alignment.bottomCenter,
              decoration: const BoxDecoration(
                color: white,
                borderRadius: BorderRadius.all(
                  Radius.circular(10.0),
233
                ),
Yoga Pratama's avatar
Yoga Pratama committed
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
              ),
              padding: const EdgeInsets.symmetric(
                  horizontal: doubleSpace, vertical: doubleSpace),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  InkWell(
                    key: const Key('Navigate to Pencarian'),
                    onTap: () => _navigateToPencarianPage(context),
                    child: Container(
                      key: const Key('Container Text Field'),
                      padding: EdgeInsets.zero,
                      decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: regularBorderRadius,
                          border: Border.all(color: greenPrimary),
                          boxShadow: regularShadow),
                      child: TextFormField(
                        enabled: false,
                        key: const Key('Text Field Mau Kemana'),
                        decoration: const InputDecoration(
                          prefixIcon: Icon(
                            Icons.search,
                            color: greenPrimary,
                            size: 25,
                          ),
                          border: InputBorder.none,
                          fillColor: Colors.white,
                          labelText: 'Tekan untuk mencari tempat',
                          labelStyle: TextStyle(
                              color: greenPrimary,
                              fontSize: 20,
                              fontFamily: 'Muli',
                              fontWeight: FontWeight.w700),
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: doubleSpace),
                  const Text(
                    'Paling sering dicari',
                    style: TextStyle(
                      color: Colors.black,
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 10),
                  StreamBuilder<NetworkModel<LokasiListResponse>>(
                      stream: bloc.lokasiListStream,
                      builder: (context, snapshot) {
                        if (snapshot.hasData) {
                          switch (snapshot.data.status) {
                            case Status.loading:
                              return Container(
                                height: 130,
                                child: const Center(
                                  child: CircularProgressIndicator(
                                    valueColor: AlwaysStoppedAnimation<Color>(
                                        greenPrimary),
                                  ),
                                ),
                              );
                              break;
                            case Status.completed:
                              final places = snapshot.data.data.listLokasi;
                              return _buildLokasiWidget(places);
                              break;
                            case Status.error:
                              return Container(
                                height: 130,
                                child: const Center(
                                  child: Text('Gagal untuk mendapatkan tempat'),
                                ),
                              );
                              break;
                          }
                          return Container();
                        }
                        return Container();
                      }),
                ],
              ),
            ),
          ])
320
        ],
321
      ),
322
323
324
325
326
327
328
329
330
331
      // floatingActionButton: FloatingActionButton.extended(
      //   key: const Key('FloatingActionButton'),
      //   onPressed: () {
      //     _navigateToInformasiLayananDisabilitasPage(context);
      //   },
      //   label: Text('Informasi Layanan Disabilitas'),
      //   icon: Icon(Icons.search),
      //   backgroundColor: greenPrimary,
      // ),
      // floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
332
333
334
      appBar: const PreferredSize(
        preferredSize: Size.fromHeight(55),
        key: Key('Scaffold Text Field'),
335
        child: BisaGoAppBar(),
Agnes Handoko's avatar
Agnes Handoko committed
336
      ),
337
338
339
    );
  }

340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  Widget _buildLokasiWidget(List<Lokasi> lokasilistresponse) {
    return Container(
      height: 130,
      child: ListView(
        scrollDirection: Axis.horizontal,
        children: [
          Row(
              children: lokasilistresponse
                  .map<Widget>((k) => LocationIconButton(
                        location: k,
                      ))
                  .toList()),
        ],
      ),
    );
  }

357
  Widget _buildKegiatanTerdekatWidget(KegiatanModel kegiatanModel) {
Yoga Pratama's avatar
Yoga Pratama committed
358
    return Container(child: KegiatanTerdekatButton(kegiatan: kegiatanModel));
359
360
  }

361
  Widget _buildGoogleMap(BuildContext context) {
362
    if (currentLocation == null) {
363
      return const Center(
364
        child: CircularProgressIndicator(),
365
      );
366
367
368
369
    } else {
      return GoogleMap(
        initialCameraPosition:
            CameraPosition(target: currentLocation, zoom: cameraZoom),
370
        onMapCreated: (GoogleMapController controller) {
371
          mapController = controller;
372
        },
373
        myLocationEnabled: true,
374
        markers: markers,
375
376
377
        onCameraMove: _onCameraMove,
      );
    }
378
379
  }

380
  void _onCameraMove(CameraPosition position) {
381
    setState(() {
382
      currentLocation = position.target;
383
384
385
    });
  }

386
  void setInitialLocation() async {
387
    final position = await geolocator.getCurrentPosition(
388
389
390
391
392
        desiredAccuracy: LocationAccuracy.high);

    setState(() {
      currentLocation = LatLng(position.latitude, position.longitude);
    });
393
  }
394
395
396
397
398
399
400
401
402

  Future<String> getLokasiName(String placeId) async {
    final _places = GoogleMapsPlaces(apiKey: DotEnv().env['API_KEY']);
    var details = await _places.getDetailsByPlaceId(
      placeId,
      fields: [
        'name',
      ],
    );
403
404
    final result = details.result;
    return result == null ? 'INVALID' : result.name;
405
406
  }

407
  void _navigateToDetailFasilitasPage(
408
      BuildContext context, String placeId, int fasilitasId) async {
409
410
411
412
413
414
415
416
417
    final fetches = await Future.wait([
      KomentarRepository().fetchDetailFasilitas(placeId, fasilitasId),
      getLokasiName(placeId),
    ]);
    final KomentarModel fasilitas = fetches[0];
    final String namaLokasi = fetches[1] ?? 'INVALID';
    final lokasi = Lokasi()
      ..placeId = placeId
      ..name = namaLokasi;
418
    final fasilitasRoute = MaterialPageRoute(
419
        builder: (BuildContext context) => DetailPostPage(
420
421
422
423
424
425
426
427
428
429
430
431
432
433
              lokasi: lokasi,
              komentar: KomentarModel(
                creator: fasilitas.creator,
                dateTime: fasilitas.dateTime,
                deskripsi: fasilitas.deskripsi,
                id: fasilitas.id,
                image: fasilitas.image,
                isVerified: fasilitas.isVerified,
                namaLokasi: lokasi.name,
                tag: fasilitas.tag,
                disabilitas: fasilitas.disabilitas,
                jumlah: fasilitas.jumlah,
              ),
            ));
434
    await Navigator.of(context).push(fasilitasRoute);
435
  }
436

437
438
439
440
441
442
443
444
445
446
447
448
449
  void _navigateToDetailKegiatanPage(
      BuildContext context, String placeId, int fasilitasId) async {
    final fetches = await Future.wait([
      KegiatanRepository().fetchDetailKegiatan(placeId, fasilitasId),
      getLokasiName(placeId),
    ]);
    final KegiatanModel kegiatan = fetches[0];
    final String namaLokasi = fetches[1] ?? 'INVALID';
    final lokasi = Lokasi()
      ..placeId = placeId
      ..name = namaLokasi;
    final fasilitasRoute = MaterialPageRoute(
        builder: (BuildContext context) => DetailPostKegiatanPage(
450
451
452
453
454
455
456
457
458
459
460
461
462
463
              lokasi: lokasi,
              kegiatan: KegiatanModel(
                id: kegiatan.id,
                placeId: lokasi.placeId,
                creator: kegiatan.creator,
                namaKegiatan: kegiatan.namaKegiatan,
                penyelenggara: kegiatan.penyelenggara,
                narahubung: kegiatan.narahubung,
                deskripsi: kegiatan.deskripsi,
                timeStart: kegiatan.timeStart,
                timeEnd: kegiatan.timeEnd,
                image: kegiatan.image,
              ),
            ));
464
465
466
    await Navigator.of(context).push(fasilitasRoute);
  }

467
468
  void initDynamicLinks() async {
    final data = await FirebaseDynamicLinks.instance.getInitialLink();
469

470
471
472
    _handleDeepLink(data);
    FirebaseDynamicLinks.instance.onLink(
        onSuccess: (PendingDynamicLinkData dynamicLink) async {
473
474
475
      _handleDeepLink(dynamicLink);
    }, onError: (OnLinkErrorException e) async {
      print('Link Failed: ${e.message}');
476
477
    });
  }
478
479
480
481
482
483
484

  void _handleDeepLink(PendingDynamicLinkData data) async {
    final deepLink = data?.link;
    if (deepLink != null) {
      final params = deepLink.queryParameters;
      final path = deepLink.pathSegments;
      final placeId = params['place_id'];
485
      final id = int.parse(params['id']);
486
487
488
489
490
      final type = params['type'];
      if (path[0] == 'link') {
        if (type == 'fasilitas') {
          _navigateToDetailFasilitasPage(context, placeId, id);
        } else if (type == 'kegiatan') {
491
          _navigateToDetailKegiatanPage(context, placeId, id);
492
        }
493
494
495
      }
    }
  }
496
}