first commit

This commit is contained in:
kicap1992
2022-08-22 00:03:17 +08:00
commit 73afb01a19
139 changed files with 5382 additions and 0 deletions

72
lib/main.dart Normal file
View File

@ -0,0 +1,72 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:provider/provider.dart';
import 'package:tanah_longsor_app/src/provider/maps_provider.dart';
import 'package:tanah_longsor_app/src/service/notification_api.dart';
import 'package:tanah_longsor_app/src/service/socket_io.dart';
// import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'src/config/routes.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
MySocketIO socket = MySocketIO();
await socket.init();
await _configureLocalTimeZone();
await dotenv.load(fileName: ".env");
HttpOverrides.global = MyHttpOverrides();
// if (defaultTargetPlatform == TargetPlatform.android) {
// AndroidGoogleMapsFlutter.useAndroidViewSurface = true;
// }
NotificationApi.init(initScheduled: true);
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => MapsProvider()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: MyRoutes.getRoutes(),
initialRoute: '/',
);
}
}
class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
}
}
Future<void> _configureLocalTimeZone() async {
if (kIsWeb || Platform.isLinux) {
return;
}
tz.initializeTimeZones();
final String timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName));
}

View File

@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
import '../page/index.dart';
class MyRoutes {
static getRoutes() {
return {
'/': (BuildContext context) => const IndexPage(),
};
}
}

View File

@ -0,0 +1,34 @@
// To parse this JSON data, do
//
// final baseResponse = baseResponseFromJson(jsonString);
import 'dart:convert';
BaseResponse baseResponseFromJson(String str) =>
BaseResponse.fromJson(json.decode(str));
String baseResponseToJson(BaseResponse data) => json.encode(data.toJson());
class BaseResponse {
BaseResponse({
required this.status,
required this.message,
this.data,
});
bool status;
String message;
dynamic data;
factory BaseResponse.fromJson(Map<String, dynamic> json) => BaseResponse(
status: json["status"],
message: json["message"],
data: json["data"],
);
Map<String, dynamic> toJson() => {
"status": status,
"message": message,
"data": data,
};
}

View File

@ -0,0 +1,40 @@
class MarkerModel {
String? sId;
String? lat;
String? lng;
String? status;
String? createdAt;
String? updatedAt;
int? iV;
MarkerModel(
{this.sId,
this.lat,
this.lng,
this.status,
this.createdAt,
this.updatedAt,
this.iV});
MarkerModel.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
lat = json['lat'];
lng = json['lng'];
status = json['status'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
iV = json['__v'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['_id'] = sId;
data['lat'] = lat;
data['lng'] = lng;
data['status'] = status;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
data['__v'] = iV;
return data;
}
}

104
lib/src/page/index.dart Normal file
View File

@ -0,0 +1,104 @@
// ignore_for_file: unnecessary_const
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
import 'package:provider/provider.dart';
import '../provider/maps_provider.dart';
import '../service/notification_api.dart';
import '../service/socket_io.dart';
import 'list.dart';
import 'maps2.dart';
class IndexPage extends StatefulWidget {
const IndexPage({Key? key}) : super(key: key);
@override
State<IndexPage> createState() => _IndexPageState();
}
class _IndexPageState extends State<IndexPage> {
// final logger = Logger();
int currentIndex = 0;
final socketIO = MySocketIO();
final logger = Logger();
@override
void initState() {
super.initState();
// context.read<MapsProvider>().getMarkers();
socketIO.on('notif_to_phone', (data) {
logger.i(data);
context.read<MapsProvider>().changeMarkerStyle(
data['id'], data['status'], data['lat'], data['lng']);
if (data['status'] != 'Normal') {
NotificationApi.showNotification(
id: 1,
title: 'Info',
body: data['message'],
payload: "Info Kemungkinan Bencana Tanah Longsor",
);
}
});
socketIO.on('reload_calibation', (data) {
logger.i(data);
context.watch<MapsProvider>().getMarkers();
// if (data['status'] != 'Normal') {
NotificationApi.showNotification(
id: 2,
title: 'Info',
body: "Alat Baru Berhasil Di Calibrasi",
payload: "Alat Baru Berhasil Di Calibrasi",
);
// }
});
}
@override
void dispose() {
super.dispose();
socketIO.disconnect();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: myWidget(context),
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: const Color.fromARGB(255, 234, 230, 230),
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.location_on),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.map_outlined),
label: 'List Lokasi',
),
],
currentIndex: currentIndex,
selectedItemColor: Colors.blue,
onTap: (int index) {
// logger.i('index: $index');
setState(() {
currentIndex = index;
});
},
),
);
}
myWidget(BuildContext context) {
switch (currentIndex) {
case 0:
// return const MapPage();
return const Map2Page();
case 1:
return const ListAlatPage();
}
}
}

71
lib/src/page/list.dart Normal file
View File

@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tanah_longsor_app/src/provider/maps_provider.dart';
class ListAlatPage extends StatefulWidget {
const ListAlatPage({Key? key}) : super(key: key);
@override
State<ListAlatPage> createState() => _ListAlatPageState();
}
class _ListAlatPageState extends State<ListAlatPage> {
late MapsProvider mapsProvider;
@override
void initState() {
super.initState();
mapsProvider = Provider.of<MapsProvider>(context, listen: false);
mapsProvider.getMarkers();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
const SizedBox(
height: 20,
),
const Align(
alignment: Alignment.center,
child: Text(
'List Alat',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
// Text('List Alat'),
if (mapsProvider.isLoading == false) const Text("Error Loading"),
if (mapsProvider.isLoading == true)
if (mapsProvider.markerModels.isEmpty)
const Text('Tidak ada alat yang tersedia'),
if (mapsProvider.isLoading == true)
if (mapsProvider.markerModels.isNotEmpty)
Expanded(
child: ListView.builder(
itemCount: mapsProvider.markerModels.length,
itemBuilder: (context, index) {
return Card(
elevation: 5,
child: ListTile(
title: Text(
"ID : ${mapsProvider.markerModels[index].sId ?? ''}",
),
subtitle: Text(
"Status : ${mapsProvider.markerModels[index].status ?? ''}",
),
onTap: () {},
),
);
},
),
),
],
),
);
}
}

93
lib/src/page/maps.dart Normal file
View File

@ -0,0 +1,93 @@
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:logger/logger.dart';
import '../model/base_response.dart';
import '../model/marker_model.dart';
import '../service/api.dart';
import '../service/socket_io.dart';
class MapPage extends StatefulWidget {
const MapPage({Key? key}) : super(key: key);
@override
State<MapPage> createState() => _MapPageState();
}
class _MapPageState extends State<MapPage> {
final logger = Logger();
final socket = MySocketIO();
static const _initialCameraPosition = CameraPosition(
target: LatLng(-4.006971153905515, 119.63025053884449),
zoom: 11,
);
final Set<Marker> _markers = HashSet<Marker>();
final Set<Circle> _circles = HashSet<Circle>();
void _getMarkers(BuildContext context) async {
BaseResponse baseResponse = await ApiService.getAllMarker();
// logger.i(baseResponse.data);
if (baseResponse.status) {
if (mounted) {
baseResponse.data.forEach((element) {
MarkerModel markerModel = MarkerModel.fromJson(element);
late double icon;
switch (markerModel.status) {
case "Normal":
icon = BitmapDescriptor.hueBlue;
break;
case "Warning":
icon = BitmapDescriptor.hueYellow;
break;
case "Danger":
icon = BitmapDescriptor.hueOrange;
break;
case "Danger Area":
icon = BitmapDescriptor.hueRed;
break;
}
setState(() {
_markers.add(
Marker(
icon: BitmapDescriptor.defaultMarkerWithHue(icon),
markerId: MarkerId(markerModel.sId!),
position: LatLng(double.parse(markerModel.lat!),
double.parse(markerModel.lng!)),
infoWindow: InfoWindow(
title: markerModel.sId,
snippet: markerModel.status,
),
),
);
});
});
}
} else {
logger.e(baseResponse.message);
}
}
@override
void initState() {
super.initState();
_getMarkers(context);
// socket.on('hehe', (data) {
// logger.i(data);
// });
}
@override
Widget build(BuildContext context) {
return GoogleMap(
initialCameraPosition: _initialCameraPosition,
onMapCreated: _onMapCreated,
markers: _markers,
circles: _circles,
);
}
void _onMapCreated(GoogleMapController controller) {}
}

19
lib/src/page/maps2.dart Normal file
View File

@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:provider/provider.dart';
import 'package:tanah_longsor_app/src/provider/maps_provider.dart';
class Map2Page extends StatelessWidget {
const Map2Page({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GoogleMap(
initialCameraPosition:
context.watch<MapsProvider>().initialCameraPosition,
onMapCreated: context.watch<MapsProvider>().onMapCreated,
markers: context.watch<MapsProvider>().markers,
circles: context.watch<MapsProvider>().circles,
);
}
}

View File

@ -0,0 +1,134 @@
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:logger/logger.dart';
import 'package:tanah_longsor_app/src/service/socket_io.dart';
import '../model/base_response.dart';
import '../model/marker_model.dart';
import '../service/api.dart';
class MapsProvider extends ChangeNotifier {
MapsProvider() {
// socketIO.on('notif_to_phone', (data) {
// // logger.i(data);
// changeMarkerStyle(data['id']);
// });
getMarkers();
// notifyListeners();
}
final socketIO = MySocketIO();
final logger = Logger();
final _initialCameraPosition = const CameraPosition(
target: LatLng(-4.006971153905515, 119.63025053884449),
zoom: 11,
);
get initialCameraPosition => _initialCameraPosition;
final Set<Marker> _markers = HashSet<Marker>();
final Set<Circle> _circles = HashSet<Circle>();
bool _isLoading = false;
bool get isLoading => _isLoading;
final List<MarkerModel> _markerModels = [];
List<MarkerModel> get markerModels => _markerModels;
Set<Marker> get markers => _markers;
Set<Circle> get circles => _circles;
void getMarkers() async {
BaseResponse baseResponse = await ApiService.getAllMarker();
logger.i(baseResponse.data);
_markerModels.clear();
_markers.clear();
if (baseResponse.status) {
// _markers
// .removeWhere((element) => element.markerId.value == '8C3C4CBD9E7C');
baseResponse.data.forEach((element) {
_markerModels.add(MarkerModel.fromJson(element));
MarkerModel markerModel = MarkerModel.fromJson(element);
late double icon;
switch (markerModel.status) {
case "Normal":
icon = BitmapDescriptor.hueBlue;
break;
case "Warning":
icon = BitmapDescriptor.hueYellow;
break;
case "Danger":
icon = BitmapDescriptor.hueOrange;
break;
case "Danger Area":
icon = BitmapDescriptor.hueRed;
break;
case "calibration":
icon = BitmapDescriptor.hueBlue;
break;
}
_markers.add(
Marker(
icon: BitmapDescriptor.defaultMarkerWithHue(icon),
markerId: MarkerId(markerModel.sId!),
position: LatLng(
double.parse(markerModel.lat!), double.parse(markerModel.lng!)),
infoWindow: InfoWindow(
title: markerModel.sId,
snippet: markerModel.status,
),
),
);
});
_isLoading = true;
logger.i(_markers);
} else {
logger.e(baseResponse.message);
_isLoading = false;
}
// notifyListeners();merah hitam coklat ungu
notifyListeners();
}
void changeMarkerStyle(
String id, String status, String lat, String lng) async {
logger.i("ini berlaku");
late double icon;
switch (status) {
case "Normal":
icon = BitmapDescriptor.hueBlue;
break;
case "Warning":
icon = BitmapDescriptor.hueYellow;
break;
case "Danger":
icon = BitmapDescriptor.hueOrange;
break;
case "Danger Area":
icon = BitmapDescriptor.hueRed;
break;
}
// search in _markers with markerId= '8C3C4CBD9E7C' and delete it
_markers.removeWhere((element) => element.markerId.value == id);
_markers.add(
Marker(
icon: BitmapDescriptor.defaultMarkerWithHue(icon),
markerId: MarkerId(id),
position: LatLng(double.parse(lat), double.parse(lng)),
infoWindow: InfoWindow(
title: id,
snippet: status,
),
),
);
logger.i(_markers);
notifyListeners();
}
void onMapCreated(GoogleMapController controller) {}
}

41
lib/src/service/api.dart Normal file
View File

@ -0,0 +1,41 @@
import 'package:dio/dio.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:logger/logger.dart';
import '../model/base_response.dart';
class ApiService {
static final log = Logger();
static final url = dotenv.env['DEV_URL'];
static final options = BaseOptions(
baseUrl: url!,
connectTimeout: 5000,
receiveTimeout: 3000,
);
static Dio dio = Dio(options);
static Future<BaseResponse> getAllMarker() async {
// log.i(url);
try {
String endpoint = 'device_list';
var response = await dio.get(endpoint);
var data = response.data;
return BaseResponse.fromJson(data);
} on DioError catch (e) {
log.e(e.toString());
return BaseResponse(
status: false,
message: e.message,
);
} catch (e) {
log.e(e.toString());
return BaseResponse(
status: false,
message: e.toString(),
);
}
}
}

View File

@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:rxdart/rxdart.dart';
import 'package:timezone/timezone.dart' as tz;
// import 'package:timezone/tzdata.dart' as tz;
// ignore: avoid_classes_with_only_static_members
class NotificationApi {
// Below is the code for initializing the plugin using var _notificationPlugin
static final _notifications = FlutterLocalNotificationsPlugin();
static final onNotifications = BehaviorSubject<String?>();
static Future showNotification({
required int id,
String? title,
String? body,
String? payload,
}) async =>
_notifications.show(
id,
title,
body,
await _notificanDetails(),
payload: payload,
);
// static Future showScheduleNotification() async =>
// _notifications.zonedSchedule(
// 0,
// 'Cek Laporan',
// "Laporan Baru Mungkin Ada, Sila Cek Di Aplikasi",
// // tz.TZDateTime.from(scheduledDate, tz.local),
// _scheduleDaily(const Time(18)),
// await _notificanDetails(),
// payload: "Laporan Baru Mungkin Ada, Sila Cek Di Aplikasi",
// androidAllowWhileIdle: true,
// uiLocalNotificationDateInterpretation:
// UILocalNotificationDateInterpretation.absoluteTime,
// matchDateTimeComponents: DateTimeComponents.time,
// );
static Future _notificanDetails() async {
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'your channel id',
'your channel name',
channelDescription: 'your channel description',
importance: Importance.max,
// priority: Priority.high,
// ticker: 'ticker',
);
return const NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: IOSNotificationDetails(),
);
}
static Future init(
{bool initScheduled = false, BuildContext? context}) async {
const android = AndroidInitializationSettings('@mipmap/ic_launcher');
// const android = AndroidInitializationSettings('app_icon');
const iOS = IOSInitializationSettings();
const settings = InitializationSettings(android: android, iOS: iOS);
final details = await _notifications.getNotificationAppLaunchDetails();
if (details != null && details.didNotificationLaunchApp) {
onNotifications.add(details.payload);
}
await _notifications.initialize(
settings,
onSelectNotification: (payload) async {
onNotifications.add(payload);
},
);
if (initScheduled) {
final locationName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(locationName));
}
}
// static tz.TZDateTime _scheduleDaily(Time time) {
// final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
// final scheduledDate = tz.TZDateTime(tz.local, now.year, now.month, now.day,
// time.hour, time.minute, time.second);
// // if (scheduledDate.isBefore(now)) {
// // scheduledDate = scheduledDate.add(const Duration(days: 1));
// // }
// return scheduledDate.isBefore(now)
// ? scheduledDate.add(const Duration(days: 1))
// : scheduledDate;
// }
}

View File

@ -0,0 +1,47 @@
import 'package:logger/logger.dart';
import 'package:socket_io_client/socket_io_client.dart';
class MySocketIO {
static final dev = Logger();
static final MySocketIO _singleton = MySocketIO._internal();
factory MySocketIO() {
return _singleton;
}
MySocketIO._internal();
late Socket _socket;
// static const String _url = 'http://192.168.43.125:3004';
static const String _url = 'https://tanah-longosor-be.herokuapp.com';
init() {
_socket = io(_url, <String, dynamic>{
'transports': ['websocket'],
'autoConnect': true,
});
_socket.on('connect', (_) {
dev.i('connect : ${_socket.id}');
});
}
Future<void> emit(String event, dynamic data) async {
_socket.emit(event, data);
}
Future<void> on(String event, Function(dynamic) callback) async {
_socket.on(event, callback);
}
Future<void> off(String event) async {
_socket.off(event);
}
Future<void> disconnect() async {
_socket.disconnect();
}
Future<void> connect() async {
dev.i('connect : ${_socket.id}');
_socket.connect();
}
}