Complete refactoring SFM App Source Code

This commit is contained in:
anhtunz
2024-12-15 00:59:02 +07:00
parent caa73ca43c
commit 2e27d59278
247 changed files with 18390 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:sfm_app/product/services/api_services.dart';
import 'package:sfm_app/product/utils/device_utils.dart';
import '../device_model.dart';
import '../../../product/base/bloc/base_bloc.dart';
class DetailDeviceBloc extends BlocBase {
APIServices apiServices = APIServices();
final deviceInfo = StreamController<Device>.broadcast();
StreamSink<Device> get sinkDeviceInfo => deviceInfo.sink;
Stream<Device> get streamDeviceInfo => deviceInfo.stream;
final deviceSensor = StreamController<Map<String, dynamic>>.broadcast();
StreamSink<Map<String, dynamic>> get sinkDeviceSensor => deviceSensor.sink;
Stream<Map<String, dynamic>> get streamDeviceSensor => deviceSensor.stream;
final deviceLocation = StreamController<String>.broadcast();
StreamSink<String> get sinkDeviceLocation => deviceLocation.sink;
Stream<String> get streamDeviceLocation => deviceLocation.stream;
@override
void dispose() {}
void getDeviceDetail(
BuildContext context,
String thingID,
Completer<GoogleMapController> controller,
) async {
String body = await apiServices.getDeviceInfomation(thingID);
if (body != "") {
final data = jsonDecode(body);
Device device = Device.fromJson(data);
sinkDeviceInfo.add(device);
if (device.areaPath != null) {
String fullLocation = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!);
log("Location: $fullLocation");
sinkDeviceLocation.add(fullLocation);
}
Map<String, dynamic> sensorMap = {};
if (device.status!.sensors != null) {
sensorMap = DeviceUtils.instance
.getDeviceSensors(context, device.status!.sensors!);
} else {
sensorMap = DeviceUtils.instance.getDeviceSensors(context, []);
}
sinkDeviceSensor.add(sensorMap);
if (device.settings!.latitude! != "" &&
device.settings!.longitude! != "") {
final CameraPosition cameraPosition = CameraPosition(
target: LatLng(
double.parse(device.settings!.latitude!),
double.parse(device.settings!.longitude!),
),
zoom: 13,
);
final GoogleMapController mapController = await controller.future;
mapController
.animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
}
}
}
void findLocation(BuildContext context, String areaPath) async {
String fullLocation =
await DeviceUtils.instance.getFullDeviceLocation(context, areaPath);
sinkDeviceLocation.add(fullLocation);
}
}

View File

@@ -0,0 +1,361 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:simple_ripple_animation/simple_ripple_animation.dart';
import 'dart:math' as math;
import '../device_model.dart';
import '../../../product/base/bloc/base_bloc.dart';
import '../../../product/extention/context_extention.dart';
import '../../../product/services/language_services.dart';
import '../../../product/utils/device_utils.dart';
import '../../../product/constant/icon/icon_constants.dart';
import 'device_detail_bloc.dart';
class DetailDeviceScreen extends StatefulWidget {
const DetailDeviceScreen({super.key, required this.thingID});
final String thingID;
@override
State<DetailDeviceScreen> createState() => _DetailDeviceScreenState();
}
class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
List<String> imageAssets = [
IconConstants.instance.getIcon("normal_icon"),
IconConstants.instance.getIcon("offline_icon"),
IconConstants.instance.getIcon("flame_icon"),
];
String stateImgAssets(int state) {
String imgStringAsset;
if (state == 0) {
imgStringAsset = imageAssets[0];
} else if (state == 1) {
imgStringAsset = imageAssets[1];
} else {
imgStringAsset = imageAssets[2];
}
return imgStringAsset;
}
late DetailDeviceBloc detailDeviceBloc;
Completer<GoogleMapController> controller = Completer();
CameraPosition initialCamera = const CameraPosition(
target: LatLng(20.966048511844402, 105.74977710843086), zoom: 15);
@override
void initState() {
super.initState();
detailDeviceBloc = BlocProvider.of(context);
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return StreamBuilder<Device>(
stream: detailDeviceBloc.streamDeviceInfo,
builder: (context, deviceSnapshot) {
if (deviceSnapshot.data?.extId == null) {
detailDeviceBloc.getDeviceDetail(context, widget.thingID, controller);
return const Center(
child: CircularProgressIndicator(),
);
} else {
return StreamBuilder<Map<String, dynamic>>(
stream: detailDeviceBloc.streamDeviceSensor,
builder: (context, sensorSnapshot) {
if (sensorSnapshot.data != null) {
return Scaffold(
appBar: AppBar(
title: Text(appLocalization(context).detail_message),
centerTitle: true,
),
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
// device Name
Card(
child: Container(
width: context.dynamicWidth(1),
height: context.highValue,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(12),
),
),
child: Center(
child: Text(
'${appLocalization(context).device_title}: ${deviceSnapshot.data!.name}',
style: const TextStyle(
fontSize: 20,
),
),
),
),
),
// Tinh trang va nhiet do
Row(
children: [
Card(
child: Container(
width: (screenWidth - 20) / 2,
height: context.highValue,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(12),
),
),
padding:
const EdgeInsets.fromLTRB(5, 5, 0, 5),
alignment: Alignment.centerLeft,
child: Row(
children: [
SizedBox(
height: 25,
width: 25,
child: RippleAnimation(
color: DeviceUtils.instance
.getColorRiple(
deviceSnapshot.data!.state!),
delay:
const Duration(milliseconds: 800),
repeat: true,
minRadius: 40,
ripplesCount: 6,
duration: const Duration(
milliseconds: 6 * 300),
child: CircleAvatar(
minRadius: 20,
maxRadius: 20,
backgroundImage: AssetImage(
stateImgAssets(
deviceSnapshot.data!.state!,
),
),
),
),
),
SizedBox(
width: context.lowValue,
),
Text(
DeviceUtils.instance.checkStateDevice(
context,
deviceSnapshot.data!.state!,
),
style: const TextStyle(
fontSize: 15,
),
),
],
),
),
),
Card(
child: SizedBox(
width: (screenWidth - 20) / 2,
height: context.highValue,
child: Container(
alignment: Alignment.centerLeft,
padding:
const EdgeInsets.fromLTRB(5, 5, 0, 5),
child: Row(
children: [
const Icon(
Icons.thermostat,
color: Colors.blue,
size: 30,
),
const SizedBox(
width: 10,
),
Text(
"${appLocalization(context).paginated_data_table_column_deviceTemperature}: ${sensorSnapshot.data?['sensorTemp'] ?? 100}",
style: const TextStyle(
fontSize: 15,
),
),
],
),
),
),
),
],
),
Row(
children: [
Card(
child: Container(
width: (screenWidth - 20) / 2,
height: context.highValue,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(12),
),
),
alignment: Alignment.centerLeft,
padding:
const EdgeInsets.fromLTRB(10, 5, 0, 5),
child: Row(
children: [
Transform.rotate(
angle: 90 * math.pi / 180,
child: Icon(
DeviceUtils.instance.getBatteryIcon(
int.parse(
sensorSnapshot
.data!['sensorBattery'],
),
),
color: Colors.blue,
size: 30,
),
),
SizedBox(
width: context.lowValue,
),
Text(
"${appLocalization(context).paginated_data_table_column_deviceBaterry}: ${sensorSnapshot.data!['sensorBattery']}%",
style: const TextStyle(
fontSize: 15,
),
),
],
),
),
),
Card(
child: Container(
width: (screenWidth - 20) / 2,
height: context.highValue,
alignment: Alignment.centerLeft,
padding:
const EdgeInsets.fromLTRB(10, 5, 0, 5),
child: Row(
children: [
Icon(
DeviceUtils.instance.getSignalIcon(
context,
sensorSnapshot.data!['sensorCsq'],
),
color: Colors.blue,
size: 30,
),
SizedBox(
width: context.lowValue,
),
Text(
"${appLocalization(context).paginated_data_table_column_deviceSignal}: ${sensorSnapshot.data!['sensorCsq']}",
style: const TextStyle(fontSize: 15),
maxLines: 2,
overflow: TextOverflow.ellipsis,
softWrap: true,
),
],
),
),
),
],
),
Card(
child: Container(
padding: const EdgeInsets.all(10.0),
height: context.highValue,
child: Row(
children: [
const Icon(
Icons.location_on,
color: Colors.blue,
size: 30,
),
SizedBox(
width: context.lowValue,
),
Expanded(
child: StreamBuilder<String>(
stream:
detailDeviceBloc.streamDeviceLocation,
builder: (context, locationSnapshot) {
if (locationSnapshot.data != null) {
return Text(
locationSnapshot.data ?? "",
style:
const TextStyle(fontSize: 13),
maxLines: 3,
overflow: TextOverflow.ellipsis,
softWrap: true,
);
} else {
detailDeviceBloc.findLocation(context,
deviceSnapshot.data!.areaPath!);
return Text(appLocalization(context)
.undefine_message);
}
},
),
),
],
),
),
),
Card(
child: Container(
height: 300,
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
child: deviceSnapshot.data!.settings!.latitude !=
""
? GoogleMap(
initialCameraPosition: initialCamera,
mapType: MapType.normal,
markers: {
Marker(
markerId: MarkerId(
deviceSnapshot.data!.thingId!),
position: LatLng(
double.parse(deviceSnapshot
.data!.settings!.latitude!),
double.parse(deviceSnapshot
.data!.settings!.longitude!),
),
),
},
onMapCreated: (mapcontroller) {
controller.complete(mapcontroller);
},
mapToolbarEnabled: false,
zoomControlsEnabled: false,
liteModeEnabled: true,
)
: Center(
child: Text(
appLocalization(context)
.detail_device_dont_has_location_message,
),
),
),
),
],
),
),
),
);
} else {
return Scaffold(
appBar: AppBar(),
body: const Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
},
);
}
}