asset tracking list view & map view added.

main_production_asset_tracking
Sikander Saleem 3 weeks ago
parent 2f3bb5ba7d
commit 2a560abcb3

@ -3,13 +3,13 @@ class URLs {
static const String appReleaseBuildNumber = "53";
static const host1 = "https://atomsm.hmg.com"; // production url
// static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
// static const host1 = "https://atomsm.hmg.com"; // production url
static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
// static const host1 = "https://atomsmuat.hmg.com"; // local UAT url
// static final String _baseUrl = "$_host/mobile"; // host local UAT
static final String _baseUrl = "$_host/mobile"; // host local UAT
// static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis
static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM
// static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM
// static final String _baseUrl = "$_host/v4/mobile"; // v4 for Demo module
// static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation
// static final String _baseUrl = "$_host/v6/mobile"; // for asset delivery module
@ -55,11 +55,17 @@ class URLs {
// SignUp APIs
static get signUpGetEmployeeFromERP => "$_baseUrl/SignUpRequest/GetEmployeeFromERP";
static get createSignUpRequest => "$_baseUrl/SignUpRequest/CreateSignUpRequest";
static get signUpGetAssetGroups => "$_baseUrl/AssetGroup/GetAssetGroupsAutoComplete";
static get signUpGetCompaniesByAssetGroup => "$_baseUrl/Company/GetCompaniesByAssetGroup";
static get signUpGetSiteList => "$_baseUrl/Customer/GetSiteList";
static get signUpGetDepartmentAutoComplete => "$_baseUrl/Department/GetDepartmentAutoCompleteByAssetGroup";
static get signUpGetDepartmentsBySitesAndAssetGroup => "$_baseUrl/Department/GetDepartmentsBySitesAndAssetGroup";
// Site Request APIs
@ -606,6 +612,7 @@ class URLs {
// Notification Preference Center
static String get getNotificationPreferences => "$_baseUrl/NotificationPreferenceCenter/GetTemplateApplyOnCurrentUser";
static String get saveNotificationPreferences => "$_baseUrl/NotificationPreferenceCenter/SaveNotificationPreferenceCenter";
//4104 attachment type lookup
@ -621,4 +628,9 @@ class URLs {
static get submitApplicationDemoRequest => "$_baseUrl/DemoUser/CreateDemoUser"; // post submit application demo request// get application demo request history
static get sendDemoUserOtp => "$_baseUrl/DemoUser/EmailVerification"; // post send OTP to user
static get verifyDemoUserOtp => "$_baseUrl/DemoUser/VerifyOtp"; // post verify OTP
// Asset Tracking APIs
static get getAssetsFromPenguin => "$_baseUrl/PenguinIN/GetAssetsFromPenguin";
static get getFloorMap => "$_baseUrl/PenguinIN/floors/";
}

@ -110,6 +110,18 @@ extension WidgetExtensions on Widget {
).toShadowContainer(context)
: this;
Widget toAssetListShimmer(BuildContext context, bool isShow) => isShow
? Column(
crossAxisAlignment: CrossAxisAlignment.start,spacing: 8,
children: [
const SizedBox(width: 50, height: 16).toShimmer(isShow: isShow, context: context).toShimmer(isShow: isShow, context: context),
const SizedBox(width: 250, height: 20).toShimmer(isShow: isShow, context: context),
const SizedBox(width: 160, height: 14).toShimmer(isShow: isShow, context: context),
const SizedBox(width: 120, height: 14).toShimmer(isShow: isShow, context: context).toShimmer(isShow: isShow, context: context),
],
).toShadowContainer(context)
: this;
Widget toShadowContainer(
BuildContext context, {
bool showShadow = true,

@ -136,6 +136,7 @@ import 'controllers/providers/api/user_provider.dart';
import 'controllers/providers/settings/setting_provider.dart';
import 'dashboard_latest/dashboard_provider.dart';
import 'l10n/app_localizations.dart';
import 'modules/asset_tracking/asset_tracking_provider.dart';
import 'modules/demo_module/demo_document_lookup_provider.dart';
import 'modules/incident_module/create_incident_request_page.dart';
import 'modules/incident_module/incident_lookup_provider.dart';
@ -403,6 +404,7 @@ class MyApp extends StatelessWidget {
// UTILITY PROVIDERS (1)
ChangeNotifierProvider(create: (_) => OracleCodeProvider(), lazy: true),
ChangeNotifierProvider(create: (_) => AssetTrackingProvider(), lazy: true),
],
child: GestureDetector(
onTap: () {

@ -3,7 +3,22 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_geojson2/flutter_map_geojson2.dart';
import 'package:latlong2/latlong.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/asset_tracking/asset_tracking_provider.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/item_views/info_header_widget.dart';
import 'package:test_sa/views/widgets/item_views/info_text_widget.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
import 'map_tracking_page.dart';
class AssetTrackingPage extends StatefulWidget {
AssetTrackingPage({Key? key}) : super(key: key);
@ -14,42 +29,163 @@ class AssetTrackingPage extends StatefulWidget {
}
}
class _AssetTrackingPageState extends State<AssetTrackingPage> {
// Bounds computed from assets/floor.geojson so the floor plan is visible immediately.
static final LatLngBounds _floorBounds = LatLngBounds(
const LatLng(24.81155194782686, 46.62383802969581),
const LatLng(24.81245295363478, 46.62470917292456),
);
class _AssetTrackingPageState extends State<AssetTrackingPage> with SingleTickerProviderStateMixin {
int selectedIndex = 1;
late AnimationController _animationController;
late AssetTrackingProvider assetTrackingProvider;
@override
void initState() {
super.initState();
assetTrackingProvider = Provider.of<AssetTrackingProvider>(context, listen: false);
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
WidgetsBinding.instance.addPostFrameCallback((_) {
assetTrackingProvider.reset();
assetTrackingProvider.getAssetsFromPenguin();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(title: "Live Asset Tracking"),
body: FlutterMap(
options: MapOptions(
initialCenter: const LatLng(24.81200245073082, 46.624273601310186),
initialZoom: 16.0,
initialCameraFit: CameraFit.bounds(bounds: _floorBounds, padding: const EdgeInsets.all(24)),
),
body: Column(
// crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.hmg.atoms',
),
GeoJsonLayer.asset(
'assets/floor.geojson',
styleDefaults: GeoJsonStyleDefaults.leaflet,
),
RichAttributionWidget(
attributions: [
TextSourceAttribution(
'OpenStreetMap contributors',
onTap: () {}, // (external)
SizedBox(
width: double.infinity,
child: SegmentedButton<int>(
multiSelectionEnabled: false,
emptySelectionAllowed: false,
expandedInsets: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 8),
style: SegmentedButton.styleFrom(
side: BorderSide(color: AppColor.borderColor(context)),
textStyle: AppTextStyles.bodyText,
backgroundColor: AppColor.background(context),
foregroundColor: context.isDark ? AppColor.neutral30 : AppColor.black20,
selectedForegroundColor: AppColor.addItemButtonColor(context),
selectedBackgroundColor: AppColor.primary10,
),
],
segments: <ButtonSegment<int>>[
ButtonSegment<int>(
value: 1,
label: const Text("List"),
icon: Icon(Icons.list_rounded, color: AppColor.icon2Color(context)),
),
ButtonSegment<int>(
value: 2,
label: const Text("Grid"),
icon: Icon(Icons.grid_view_rounded, color: AppColor.icon2Color(context)),
),
ButtonSegment<int>(
value: 3,
label: const Text("Map"),
icon: Icon(Icons.map, color: AppColor.icon2Color(context)),
),
],
selected: <int>{selectedIndex},
onSelectionChanged: (Set<int> newSelection) {
setState(() {
selectedIndex = newSelection.first;
});
},
),
),
SafeArea(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: selectedIndex == 3 ? MapTrackingPage(inAppView: true) : listView(),
),
).expanded,
],
),
);
}
Widget listView() {
return Consumer<AssetTrackingProvider>(builder: (context, provider, _) {
return (provider.assetsList.isEmpty && !provider.isLoading)
? NoDataFound(message: context.translation.noDataFound).center
: ListView.separated(
key: const ValueKey('asset-list_view'),
padding: const EdgeInsets.all(16),
itemCount: provider.isLoading ? 6 : provider.assetsList.length,
separatorBuilder: (listContext, itemIndex) => 12.height,
itemBuilder: (listContext, itemIndex) {
if (provider.isLoading) return const SizedBox().toAssetListShimmer(listContext, provider.isLoading);
// Calculate staggered animation delay
final delay = itemIndex * 0.08;
final animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.7),
(delay + 0.4).clamp(0.0, 1.0),
curve: Curves.easeOut,
),
),
);
final slideAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.15),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.7),
(delay + 0.4).clamp(0.0, 1.0),
curve: Curves.easeOutCubic,
),
),
);
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: slideAnimation,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: (provider.assetsList[itemIndex].location?.isLive ?? false) ? "Live" : "Not Live",
radius: 4,
textColor: AppColor.getPriorityStatusTextColor(context, (provider.assetsList[itemIndex].location?.isLive ?? false) ? 0 : 1),
backgroundColor: AppColor.getPriorityStatusColor(context, (provider.assetsList[itemIndex].location?.isLive ?? false) ? 0 : 1),
),
4.height,
InfoHeader16Widget(provider.assetsList[itemIndex].label ?? ""),
InfoTextWidget(label: "Serial No", value: provider.assetsList[itemIndex].label, showEmptyValue: true),
InfoTextWidget(label: "Type", value: provider.assetsList[itemIndex].type?.name, showEmptyValue: true),
InfoTextWidget(label: "Status", value: provider.assetsList[itemIndex].status?.name, showEmptyValue: true),
InfoTextWidget(label: "Department", value: provider.assetsList[itemIndex].department?.name, showEmptyValue: true),
InfoTextWidget(
label: "Last seen",
value: provider.assetsList[itemIndex].location?.lastSeen?.toServiceRequestDetailsFormat,
showEmptyValue: true,
showArrow: true,
arrowMsg: "View on map",
),
],
).toShadowContainer(context)).onPress((){
Navigator.push(context, MaterialPageRoute(builder: (context) => MapTrackingPage(assetTrackingData: provider.assetsList[itemIndex])));
}),
);
},
);
});
// Selector<AssetTrackingPage, bool>(
// selector: (_, myModel) => myModel, // Selects only the userName
// builder: (_, isLoading, __) {
//
// });
}
}

@ -0,0 +1,80 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'models/asset_tracking_model.dart';
import 'models/floor_map_model.dart';
class AssetTrackingProvider extends ChangeNotifier {
bool isLoading = false;
bool isNextPageLoading = false;
final pageItemNumber = 12;
int pageNo = 1;
List<AssetTrackingModel> assetsList = [];
reset() {
isLoading = false;
notifyListeners();
}
Future<int> getAssetsFromPenguin({bool loadMore = false}) async {
if (isLoading || isNextPageLoading) return -2;
if (loadMore) {
isNextPageLoading = true;
pageNo += 1;
} else {
isLoading = true;
pageNo = 1; // reset pagination
}
notifyListeners();
try {
final payload = {"pageSize": pageItemNumber, "pageNumber": pageNo};
final response = await ApiManager.instance.post(URLs.getAssetsFromPenguin, body: payload);
if (response.statusCode >= 200 && response.statusCode < 300) {
final Map<String, dynamic> jsonData = json.decode(response.body);
List list = jsonData["data"];
assetsList.addAll(list.map((item) => AssetTrackingModel.fromJson(item)).toList());
if (jsonData["totalRows"] <= assetsList.length) {
// nextPage = true;
}
print("assetsList:${assetsList.length}");
// if (loadMore) {
// assetInventoryResponse?.assetList?.addAll(newResponse.assetList ?? []);
// assetInventoryResponse?.totalRows = newResponse.totalRows;
// } else {
// assetInventoryResponse = newResponse;
// }
// nextPage = (assetInventoryResponse?.assetList?.length ?? 0) < (assetInventoryResponse?.totalRows ?? 0);
} else {
// nextPage = false;
}
isLoading = false;
isNextPageLoading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
isLoading = false;
isNextPageLoading = false;
// stateCode = -1;
// nextPage = false;
notifyListeners();
return -1;
}
}
Future<FloorMapModel?> getFloorMap(int floorId) async {
FloorMapModel? floorMapModel;
// try {
final response = await ApiManager.instance.get(URLs.getFloorMap + "$floorId/maps");
if (response.statusCode >= 200 && response.statusCode < 300) {
floorMapModel = FloorMapModel.fromJson(json.decode(response.body)["data"]);
}
return floorMapModel;
// } catch (error) {
// return floorMapModel;
// }
}
}

@ -0,0 +1,159 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_geojson2/flutter_map_geojson2.dart';
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/helper/utils.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'asset_tracking_provider.dart';
import 'models/asset_tracking_model.dart';
import 'models/floor_map_model.dart';
class MapTrackingPage extends StatefulWidget {
final AssetTrackingModel? assetTrackingData;
final bool inAppView;
MapTrackingPage({Key? key, this.inAppView = false, this.assetTrackingData}) : super(key: key);
@override
_MapTrackingPageState createState() {
return _MapTrackingPageState();
}
}
class _MapTrackingPageState extends State<MapTrackingPage> {
final MapController mapController = MapController();
late AssetTrackingProvider assetTrackingProvider;
FloorMapModel? floorMapModel;
Map<String, dynamic>? _geojson;
List<Marker> markers = [];
LatLngBounds? _computedBounds;
// Fallback shown only until the real floor plan bounds are computed.
static const LatLng _fallbackCenter = LatLng(24.81200245073082, 46.624273601310186);
@override
void initState() {
super.initState();
assetTrackingProvider = Provider.of<AssetTrackingProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.assetTrackingData != null) {
print('widget.assetTrackingData!.location!.floor!.id!: ${widget.assetTrackingData!.toJson()}');
Utils.showLoading(context);
assetTrackingProvider.getFloorMap(widget.assetTrackingData!.location!.floor!.id!).then((floorMap) {
Utils.hideLoading(context);
_onFloorMapLoaded(floorMap);
}).catchError((error) {
print('Failed to load floor map: $error');
Utils.hideLoading(context);
});
}
});
}
void _onFloorMapLoaded(FloorMapModel? loadedFloorMap) {
floorMapModel = loadedFloorMap;
if (floorMapModel?.geojsonData == null) {
setState(() {});
return;
}
try {
final decoded = floorMapModel!.geojsonData!;
final bounds = _computeBoundsFromGeoJson(decoded);
setState(() {
_geojson = decoded;
_computedBounds = bounds;
markers = [
Marker(
point: LatLng(widget.assetTrackingData!.location!.latitude!, widget.assetTrackingData!.location!.longitude!),
child: const Icon(Icons.location_pin, color: AppColor.red30),
),
];
});
WidgetsBinding.instance.addPostFrameCallback((_) {
mapController.fitCamera(
CameraFit.bounds(bounds: bounds, padding: const EdgeInsets.all(24)),
);
});
} catch (e) {
print('Failed to parse/compute bounds for floor geojson: $e');
setState(() {}); // still show the map with whatever we have
}
}
LatLngBounds _computeBoundsFromGeoJson(Map<String, dynamic> geojson) {
final features = (geojson['features'] as List<dynamic>? ?? []);
double? minLng, minLat, maxLng, maxLat;
void visit(dynamic node) {
if (node is List && node.isNotEmpty && node.first is num) {
final lng = (node[0] as num).toDouble();
final lat = (node[1] as num).toDouble();
minLng = (minLng == null || lng < minLng!) ? lng : minLng;
maxLng = (maxLng == null || lng > maxLng!) ? lng : maxLng;
minLat = (minLat == null || lat < minLat!) ? lat : minLat;
maxLat = (maxLat == null || lat > maxLat!) ? lat : maxLat;
} else if (node is List) {
for (final child in node) {
visit(child);
}
}
}
for (final feature in features) {
final geometry = feature['geometry'] as Map<String, dynamic>?;
final coordinates = geometry?['coordinates'];
if (coordinates == null) continue;
visit(coordinates);
}
if (minLng == null || minLat == null || maxLng == null || maxLat == null) {
throw StateError('No coordinates found in floor geojson — cannot compute bounds.');
}
return LatLngBounds(
LatLng(minLat!, minLng!),
LatLng(maxLat!, maxLng!),
);
}
@override
Widget build(BuildContext context) {
Widget mapWidget = FlutterMap(
mapController: mapController,
options: MapOptions(initialCenter: _fallbackCenter, initialZoom: 16.0),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.hmg.atoms',
),
if (_geojson != null) ...[
GeoJsonLayer.memory(
_geojson!,
styleDefaults: GeoJsonStyleDefaults.leaflet,
),
MarkerLayer(markers: markers),
]
],
);
return widget.inAppView
? mapWidget
: Scaffold(
appBar: const DefaultAppBar(title: "Live Asset Tracking"),
body: Stack(
children: [mapWidget],
),
);
}
}

@ -0,0 +1,218 @@
class AssetTrackingModel {
int? id;
String? label;
String? serialNumber;
int? group;
Type? type;
Type? department;
Type? status;
Location? location;
Tag? tag;
Alerts? alerts;
AssetTrackingModel({this.id, this.label, this.serialNumber, this.group, this.type, this.department, this.status, this.location, this.tag, this.alerts});
AssetTrackingModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
label = json['label'];
serialNumber = json['serialNumber'];
group = json['group'];
type = json['type'] != null ? new Type.fromJson(json['type']) : null;
department = json['department'] != null ? new Type.fromJson(json['department']) : null;
status = json['status'] != null ? new Type.fromJson(json['status']) : null;
location = json['location'] != null ? new Location.fromJson(json['location']) : null;
tag = json['tag'] != null ? new Tag.fromJson(json['tag']) : null;
alerts = json['alerts'] != null ? new Alerts.fromJson(json['alerts']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['label'] = this.label;
data['serialNumber'] = this.serialNumber;
data['group'] = this.group;
if (this.type != null) {
data['type'] = this.type!.toJson();
}
if (this.department != null) {
data['department'] = this.department!.toJson();
}
if (this.status != null) {
data['status'] = this.status!.toJson();
}
if (this.location != null) {
data['location'] = this.location!.toJson();
}
if (this.tag != null) {
data['tag'] = this.tag!.toJson();
}
if (this.alerts != null) {
data['alerts'] = this.alerts!.toJson();
}
return data;
}
}
class Type {
int? id;
String? name;
Type({this.id, this.name});
Type.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
return data;
}
}
class Location {
bool? isLive;
String? lastSeen;
Type? venue;
Floor? floor;
Zone? zone;
double? longitude;
double? latitude;
double? x;
double? y;
Location({this.isLive, this.lastSeen, this.venue, this.floor, this.zone, this.longitude, this.latitude, this.x, this.y});
Location.fromJson(Map<String, dynamic> json) {
isLive = json['isLive'];
lastSeen = json['lastSeen'];
venue = json['venue'] != null ? new Type.fromJson(json['venue']) : null;
floor = json['floor'] != null ? new Floor.fromJson(json['floor']) : null;
zone = json['zone'] != null ? new Zone.fromJson(json['zone']) : null;
longitude = json['longitude'];
latitude = json['latitude'];
x = json['x'];
y = json['y'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['isLive'] = this.isLive;
data['lastSeen'] = this.lastSeen;
if (this.venue != null) {
data['venue'] = this.venue!.toJson();
}
if (this.floor != null) {
data['floor'] = this.floor!.toJson();
}
if (this.zone != null) {
data['zone'] = this.zone!.toJson();
}
data['longitude'] = this.longitude;
data['latitude'] = this.latitude;
data['x'] = this.x;
data['y'] = this.y;
return data;
}
}
class Floor {
int? id;
String? name;
String? label;
Floor({this.id, this.name, this.label});
Floor.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
label = json['label'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['label'] = this.label;
return data;
}
}
class Zone {
int? id;
String? name;
Type? zoneType;
Zone({this.id, this.name, this.zoneType});
Zone.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
zoneType = json['zoneType'] != null ? new Type.fromJson(json['zoneType']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
if (this.zoneType != null) {
data['zoneType'] = this.zoneType!.toJson();
}
return data;
}
}
class Tag {
int? id;
String? macAddress;
int? batteryPercentage;
Tag({this.id, this.macAddress, this.batteryPercentage});
Tag.fromJson(Map<String, dynamic> json) {
id = json['id'];
macAddress = json['macAddress'];
batteryPercentage = json['batteryPercentage'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['macAddress'] = this.macAddress;
data['batteryPercentage'] = this.batteryPercentage;
return data;
}
}
class Alerts {
int? count;
String? lastCreationDate;
String? mostSevereCreationDate;
String? mostSevereTriggerType;
String? mostSeverity;
String? mostSevereRuleName;
Alerts({this.count, this.lastCreationDate, this.mostSevereCreationDate, this.mostSevereTriggerType, this.mostSeverity, this.mostSevereRuleName});
Alerts.fromJson(Map<String, dynamic> json) {
count = json['count'];
lastCreationDate = json['lastCreationDate'];
mostSevereCreationDate = json['mostSevereCreationDate'];
mostSevereTriggerType = json['mostSevereTriggerType'];
mostSeverity = json['mostSeverity'];
mostSevereRuleName = json['mostSevereRuleName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['count'] = this.count;
data['lastCreationDate'] = this.lastCreationDate;
data['mostSevereCreationDate'] = this.mostSevereCreationDate;
data['mostSevereTriggerType'] = this.mostSevereTriggerType;
data['mostSeverity'] = this.mostSeverity;
data['mostSevereRuleName'] = this.mostSevereRuleName;
return data;
}
}

@ -0,0 +1,151 @@
class FloorMapModel {
int? id;
String? mapUrl;
String? geojsonUrl;
String? mapBase64;
Map<String, dynamic>? geojsonData;
FloorMapModel({this.id, this.mapUrl, this.geojsonUrl, this.mapBase64, this.geojsonData});
FloorMapModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
mapUrl = json['mapUrl'];
geojsonUrl = json['geojsonUrl'];
mapBase64 = json['mapBase64'];
geojsonData = json['geojsonData'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['mapUrl'] = this.mapUrl;
data['geojsonUrl'] = this.geojsonUrl;
data['mapBase64'] = this.mapBase64;
if (this.geojsonData != null) {
// data['geojsonData'] = this.geojsonData!.toJson();
}
return data;
}
}
// class GeojsonData {
// String? type;
// List<Features>? features;
//
// GeojsonData({this.type, this.features});
//
// GeojsonData.fromJson(Map<String, dynamic> json) {
// type = json['type'];
// if (json['features'] != null) {
// features = <Features>[];
// json['features'].forEach((v) { features!.add(new Features.fromJson(v)); });
// }
// }
//
// Map<String, dynamic> toJson() {
// final Map<String, dynamic> data = new Map<String, dynamic>();
// data['type'] = this.type;
// if (this.features != null) {
// data['features'] = this.features!.map((v) => v.toJson()).toList();
// }
// return data;
// }
// }
// class Features {
// String? type;
// Properties? properties;
// Geometry? geometry;
//
// Features({this.type, this.properties, this.geometry});
//
// Features.fromJson(Map<String, dynamic> json) {
// type = json['type'];
// properties = json['properties'] != null ? new Properties.fromJson(json['properties']) : null;
// geometry = json['geometry'] != null ? new Geometry.fromJson(json['geometry']) : null;
// }
//
// Map<String, dynamic> toJson() {
// final Map<String, dynamic> data = new Map<String, dynamic>();
// data['type'] = this.type;
// if (this.properties != null) {
// data['properties'] = this.properties!.toJson();
// }
// if (this.geometry != null) {
// data['geometry'] = this.geometry!.toJson();
// }
// return data;
// }
// }
//
// class Properties {
// int? ff;
// int? id;
// String? fill;
// int? fillOpacity;
// String? stroke;
// int? strokeOpacity;
// int? strokeWidth;
//
// Properties({this.ff, this.id, this.fill, this.fillOpacity, this.stroke, this.strokeOpacity, this.strokeWidth});
//
// Properties.fromJson(Map<String, dynamic> json) {
// ff = json['ff'];
// id = json['id'];
// fill = json['fill'];
// fillOpacity = json['fillOpacity'];
// stroke = json['stroke'];
// strokeOpacity = json['strokeOpacity'];
// strokeWidth = json['strokeWidth'];
// }
//
// Map<String, dynamic> toJson() {
// final Map<String, dynamic> data = new Map<String, dynamic>();
// data['ff'] = this.ff;
// data['id'] = this.id;
// data['fill'] = this.fill;
// data['fillOpacity'] = this.fillOpacity;
// data['stroke'] = this.stroke;
// data['strokeOpacity'] = this.strokeOpacity;
// data['strokeWidth'] = this.strokeWidth;
// return data;
// }
// }
// class Geometry {
// String? type;
// List<List>? coordinates;
//
// Geometry({this.type, this.coordinates});
//
// Geometry.fromJson(Map<String, dynamic> json) {
// type = json['type'];
// if (json['coordinates'] != null) {
// coordinates = <List>[];
// json['coordinates'].forEach((v) { coordinates!.add(new List.fromJson(v)); });
// }
// }
//
// Map<String, dynamic> toJson() {
// final Map<String, dynamic> data = new Map<String, dynamic>();
// data['type'] = this.type;
// if (this.coordinates != null) {
// data['coordinates'] = this.coordinates!.map((v) => v.toJson()).toList();
// }
// return data;
// }
// }
//
// class Coordinates {
//
//
// Coordinates({});
//
// Coordinates.fromJson(Map<String, dynamic> json) {
// }
//
// Map<String, dynamic> toJson() {
// final Map<String, dynamic> data = new Map<String, dynamic>();
// return data;
// }
// }

@ -5,6 +5,7 @@ import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/asset_tracking/asset_tracking_page.dart';
import 'package:test_sa/modules/notification_setting/view/notification_setting_page.dart';
import 'package:test_sa/modules/site_request_module/screens/site_request_history_screen.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
@ -74,6 +75,8 @@ class AppDrawer extends StatelessWidget {
// 18.height,
// drawerItem("rate_us", context.translation.rateUs, context),
// 18.height,
drawerItem("setting", "Asset Tracking", context).onPress(() => Navigator.push(context, MaterialPageRoute(builder: (context) => AssetTrackingPage()))),
18.height,
drawerItem("setting", context.translation.settings, context).onPress(() => Navigator.of(context).pushNamed(SettingsPage.id)),
18.height,
drawerItem("site_request_icon", "Site Request", context).onPress(() => Navigator.of(context).pushNamed(SiteRequestHistoryScreen.routeName)),

@ -12,11 +12,12 @@ class InfoTextWidget extends StatelessWidget {
String? value;
final String label;
final bool showEmptyValue;
final String arrowMsg;
final bool showArrow;
final bool copyValue;
final bool isMsg;
InfoTextWidget({required this.label, this.value, this.showEmptyValue = false, this.copyValue = false, this.showArrow = false, this.isMsg = false, Key? key}) : super(key: key);
InfoTextWidget({required this.label, this.value, this.arrowMsg = "", this.showEmptyValue = false, this.copyValue = false, this.showArrow = false, this.isMsg = false, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -33,9 +34,10 @@ class InfoTextWidget extends StatelessWidget {
text = Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
spacing: 4,
children: [
text.expanded,
const SizedBox(width: 4),
if (arrowMsg.isNotEmpty) Text(arrowMsg, style: AppTextStyles.tinyFont2.copyWith(color: AppColor.textStyleColor(context))),
"arrow_right".toSvgAsset(width: 17, color: AppColor.primary10),
],
);

@ -121,6 +121,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
benchmark:
dependency: transitive
description:
name: benchmark
sha256: cb3eeea01e3f054df76ee9775ca680f3afa5f19f39b2bb426ba78ba27654493b
url: "https://pub.dev"
source: hosted
version: "0.3.0"
boolean_selector:
dependency: transitive
description:
@ -233,6 +241,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
dart_earcut:
dependency: transitive
description:
name: dart_earcut
sha256: e485001bfc05dcbc437d7bfb666316182e3522d4c3f9668048e004d0eb2ce43b
url: "https://pub.dev"
source: hosted
version: "1.2.0"
dart_polylabel2:
dependency: transitive
description:
name: dart_polylabel2
sha256: "7eeab15ce72894e4bdba6a8765712231fc81be0bd95247de4ad9966abc57adc6"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
dart_sort_queue:
dependency: transitive
description:
name: dart_sort_queue
sha256: f3353ba8b4850e072d3368757f62edb79af34a9703c3e3df9c59342721f5f5b1
url: "https://pub.dev"
source: hosted
version: "0.0.2+3"
date_picker_timeline:
dependency: "direct main"
description:
@ -524,6 +556,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_map:
dependency: "direct main"
description:
name: flutter_map
sha256: "9bbe5472f66ae648d2ca2efbce97fcaa04e71261fabd3e062f26728222d7f767"
url: "https://pub.dev"
source: hosted
version: "8.3.2"
flutter_map_geojson2:
dependency: "direct main"
description:
name: flutter_map_geojson2
sha256: "7e05ffbf87cc777d872ec7de21142e5aca5ae65949265943d0e1bc704b81011e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@ -654,6 +702,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.3"
geotypes:
dependency: transitive
description:
name: geotypes
sha256: "5bedf57de92283133dd221e363812ef50eaaba414f0823b1974ef7d84b86991f"
url: "https://pub.dev"
source: hosted
version: "0.0.2"
get:
dependency: transitive
description:
@ -894,6 +950,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.4.16"
latlong2:
dependency: "direct main"
description:
name: latlong2
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
url: "https://pub.dev"
source: hosted
version: "0.9.1"
leak_tracker:
dependency: transitive
description:
@ -998,6 +1062,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.3.2"
mapbox_maps_flutter:
dependency: "direct main"
description:
name: mapbox_maps_flutter
sha256: c9888c9fed527a3ad7c65d2b34dc5a401ee3ddc1cb2c368899dfb40ba924f894
url: "https://pub.dev"
source: hosted
version: "2.30.1"
matcher:
dependency: transitive
description:
@ -1006,6 +1078,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.12.19"
material_color_names:
dependency: transitive
description:
name: material_color_names
sha256: "12bf9cdbe60002908730605854172d878ad726fd4f2dfdc665aea1b32895fc8e"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
material_color_utilities:
dependency: transitive
description:
@ -1030,6 +1110,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.17.0"
mgrs_dart:
dependency: transitive
description:
name: mgrs_dart
sha256: "385e7168ecc77eb545220223c49eef8ab249da7bf57f22781c40a04d23fb196f"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
mime:
dependency: transitive
description:
@ -1390,6 +1478,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.5.0"
proj4dart:
dependency: transitive
description:
name: proj4dart
sha256: ddcedc1f7876e62717de43ab3491e2829bdad0b028261805f94aa080967e5859
url: "https://pub.dev"
source: hosted
version: "3.0.0"
provider:
dependency: "direct main"
description:
@ -1414,6 +1510,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
rbush:
dependency: transitive
description:
name: rbush
sha256: "48b683421b4afb43a642f82c6aa31911e54f3069143d31c7d33cbe329df13403"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
record_mp3_plus:
dependency: "direct main"
description:
@ -1566,6 +1670,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.1"
simple_sparse_list:
dependency: transitive
description:
name: simple_sparse_list
sha256: aa648fd240fa39b49dcd11c19c266990006006de6699a412de485695910fbc1f
url: "https://pub.dev"
source: hosted
version: "0.1.4"
sky_engine:
dependency: transitive
description: flutter
@ -1699,6 +1811,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
sweepline_intersections:
dependency: transitive
description:
name: sweepline_intersections
sha256: a665c707200a4f07140a4029b41a7c4883beb3f04322cd8e08ebf650f69e1176
url: "https://pub.dev"
source: hosted
version: "0.0.4"
synchronized:
dependency: transitive
description:
@ -1763,6 +1883,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.2"
turf:
dependency: transitive
description:
name: turf
sha256: "7684654c561e751332bc2fa6e9bb7bcf0c71bad5315acf126206e7ecfef67256"
url: "https://pub.dev"
source: hosted
version: "0.0.12"
turf_equality:
dependency: transitive
description:
name: turf_equality
sha256: f0f44ffe389547941358e0d3d4a747db2bd56115b32ff1cede5e5bdf3126a3e2
url: "https://pub.dev"
source: hosted
version: "0.1.0"
turf_pip:
dependency: transitive
description:
name: turf_pip
sha256: ba4fd414baffd5d7b30880658ad6db82461c49ec023f8ffd0c23d398ad8b14be
url: "https://pub.dev"
source: hosted
version: "0.0.2"
typed_data:
dependency: transitive
description:
@ -1771,6 +1915,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
unicode:
dependency: transitive
description:
name: unicode
sha256: a6f7bcfc8ea1d5ce1f6c0b1c39117a9919f4953edd9fd7a64090a9796c499b57
url: "https://pub.dev"
source: hosted
version: "1.1.9"
url_launcher:
dependency: "direct main"
description:
@ -1923,6 +2075,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.15.0"
wkt_parser:
dependency: transitive
description:
name: wkt_parser
sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
xdg_directories:
dependency: transitive
description:

@ -67,6 +67,7 @@ dependencies:
flutter_map: ^8.3.2
latlong2: ^0.9.0
flutter_map_geojson2: ^1.0.2
mapbox_maps_flutter: ^2.30.1
flare_flutter:
git:

Loading…
Cancel
Save