diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0e14d8e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "disabled" +} \ No newline at end of file diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html new file mode 100644 index 0000000..3993393 --- /dev/null +++ b/android/build/reports/problems/problems-report.html @@ -0,0 +1,663 @@ + + + + + + + + + + + + + Gradle Configuration Cache + + + +
+ +
+ Loading... +
+ + + + + + diff --git a/assets/images/ecolab.png b/assets/images/ecolab.png new file mode 100644 index 0000000..009eede Binary files /dev/null and b/assets/images/ecolab.png differ diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/home.dart b/lib/home.dart new file mode 100644 index 0000000..006f1de --- /dev/null +++ b/lib/home.dart @@ -0,0 +1,170 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'sensor_bloc.dart'; +import 'sensor_state.dart'; +import 'sensor_event.dart'; +import 'model/sensor.dart'; + +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + String searchQuery = ''; + bool isAscending = true; + + @override + Widget build(BuildContext context) { + void _showEditDialog(Sensor sensor) { + final nameController = TextEditingController(text: sensor.name); + final descController = TextEditingController(text: sensor.description); + final bloc = context.read(); + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Edit Sensor ${sensor.id}'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: nameController, + decoration: const InputDecoration(labelText: 'Name'), + ), + TextField( + controller: descController, + decoration: const InputDecoration(labelText: 'Description'), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () { + bloc.add( + EditSensorDetails(sensor.id, nameController.text, descController.text), + ); + Navigator.pop(context); + }, + child: const Text('Save'), + ), + ], + ), + ); + } + + return Scaffold( + appBar: AppBar( + toolbarHeight: 80, + title: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + 'assets/images/ecolab.png', // Ensure this path is correct + height: 40, // Adjust the height as needed + ), + const SizedBox(height: 10), // Add spacing between the image and the title + const Text('Thermal Sensors'), + ], + ), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(80), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + onChanged: (val) => setState(() => searchQuery = val), + decoration: InputDecoration( + hintText: 'Search by name or description...', + prefixIcon: const Icon(Icons.search), + border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + ), + ), + ), + ), + ), + body: Column( + children: [ + // Sorting toggle button + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon(isAscending ? Icons.arrow_upward : Icons.arrow_downward), + tooltip: 'Sort by Temperature', + onPressed: () { + setState(() { + isAscending = !isAscending; // Toggle sorting order + }); + }, + ), + Text( + isAscending ? 'Sort Ascending' : 'Sort Descending', + style: const TextStyle(fontSize: 16), + ), + ], + ), + ), + // Sensor list + Expanded( + child: BlocBuilder( + builder: (context, state) { + final filteredSensors = state.sensors.where((sensor) { + final q = searchQuery.toLowerCase(); + return sensor.name.toLowerCase().contains(q) || + sensor.description.toLowerCase().contains(q); + }).toList(); + + filteredSensors.sort((a, b) => isAscending + ? a.value.compareTo(b.value) + : b.value.compareTo(a.value)); + + if (filteredSensors.isEmpty) { + return const Center(child: Text('No sensors found.')); + } + + return ListView.builder( + itemCount: filteredSensors.length, + itemBuilder: (context, index) { + final sensor = filteredSensors[index]; + return ListTile( + title: Text('${sensor.name} (${sensor.id})'), + subtitle: Text( + 'Value: ${sensor.value.toStringAsFixed(2)}\n${sensor.description}', + ), + isThreeLine: true, + trailing: Wrap( + spacing: 8, + children: [ + IconButton( + icon: const Icon(Icons.refresh), + tooltip: 'Update Sensor', + onPressed: () { + context.read().add(UpdateSensorValue(sensor)); + }, + ), + IconButton( + icon: const Icon(Icons.edit), + tooltip: 'Edit Sensor', + onPressed: () => _showEditDialog(sensor), + ), + ], + ), + ); + }, + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/home_wrapper.dart b/lib/home_wrapper.dart new file mode 100644 index 0000000..ce58129 --- /dev/null +++ b/lib/home_wrapper.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'sensor_bloc.dart'; +import 'repository/sensor_repository.dart'; +import 'sensor_event.dart'; +import 'home.dart'; + +class HomeWrapper extends StatelessWidget { + const HomeWrapper({super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => SensorBloc(SensorRepository()) + ..add(StartSensorUpdates()), + child: const HomeScreen(), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 927a1e0..69e69cb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'home_wrapper.dart'; void main() { runApp(const MyApp()); @@ -10,11 +11,9 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - ), - home: Placeholder(), + title: 'Sensor App', + theme: ThemeData(primarySwatch: Colors.blue), + home: const HomeWrapper(), ); } } diff --git a/lib/sensor_bloc.dart b/lib/sensor_bloc.dart new file mode 100644 index 0000000..a34fe9a --- /dev/null +++ b/lib/sensor_bloc.dart @@ -0,0 +1,66 @@ +import 'dart:async'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'sensor_event.dart'; +import 'sensor_state.dart'; +import 'repository/sensor_repository.dart'; +import 'model/sensor.dart'; +import 'dart:math'; + +class SensorBloc extends Bloc { + + final SensorRepository repository; + late final StreamSubscription _sensorSub; + + SensorBloc(this.repository) : super(const SensorState()) { + on((event, emit) { + repository.initializeSensors(); + _sensorSub = repository.sensorsStream.listen( + (sensorList) => add(SensorsUpdated(sensorList)), + ); + }); + + on((event, emit) { + emit(state.copyWith( + sensors: event.sensors, + searchQuery: state.searchQuery + )); + }); + + on((event, emit) { + repository.updateSensor(event.sensor); + final updatedSensors = List.from(state.sensors); + final index = updatedSensors.indexWhere((s) => s.id == event.sensor.id); + if (index != -1) { + updatedSensors[index] = event.sensor; + emit(state.copyWith( + sensors: updatedSensors, + searchQuery: state.searchQuery, + )); + } + }); + + on((event, emit) { + repository.updateSensor(event.sensor.copyWith(value: 72)); + }); + + + on((event,emit){ + emit(state.copyWith(searchQuery: event.query)); + }); + + on((event, emit) { + final sensor = state.sensors.firstWhere((s) => s.id == event.sensorId); + final updated = sensor.copyWith( + name: event.sensorName, + description: event.sensorDescription + ); + repository.updateSensor(updated); + }); + + @override + Future close() { + _sensorSub.cancel(); + return super.close(); + } +} +} \ No newline at end of file diff --git a/lib/sensor_event.dart b/lib/sensor_event.dart new file mode 100644 index 0000000..f4531f8 --- /dev/null +++ b/lib/sensor_event.dart @@ -0,0 +1,54 @@ +import 'package:equatable/equatable.dart'; +import 'model/sensor.dart'; + +abstract class SensorEvent extends Equatable { + const SensorEvent(); + @override + List get props => []; +} + +class StartSensorUpdates extends SensorEvent {} + +class SensorsUpdated extends SensorEvent { + final List sensors; + const SensorsUpdated(this.sensors); + @override + List get props => [sensors]; +} + +class UpdateSensorEvent extends SensorEvent { + final Sensor sensor; + const UpdateSensorEvent(this.sensor); + + @override + List get props => [sensor]; +} + +class UpdateSensorValue extends SensorEvent { + final Sensor sensor; + const UpdateSensorValue(this.sensor); + + @override + List get props => [sensor]; +} + +class SearchSensorEvent extends SensorEvent { + final String query; + const SearchSensorEvent(this.query); + + @override + List get props => [query]; +} + +class EditSensorDetails extends SensorEvent { + final int sensorId; + final String sensorName; + final String sensorDescription; + + + const EditSensorDetails( + this.sensorId, + this.sensorName, + this.sensorDescription + ); +} \ No newline at end of file diff --git a/lib/sensor_state.dart b/lib/sensor_state.dart new file mode 100644 index 0000000..41f1435 --- /dev/null +++ b/lib/sensor_state.dart @@ -0,0 +1,20 @@ +import 'package:equatable/equatable.dart'; +import 'model/sensor.dart' ; + +class SensorState extends Equatable{ + + final List sensors; + final String searchQuery; + + const SensorState ({this.sensors = const [], this.searchQuery=''}); + + SensorState copyWith({List? sensors, String? searchQuery}){ + return SensorState( + sensors: sensors ?? this.sensors, + searchQuery: searchQuery ?? this.searchQuery + ); + } + + @override + List get props => [sensors]; +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 7dff62e..40ff5a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,5 +23,7 @@ dev_dependencies: flutter_lints: ^5.0.0 flutter: + assets: + - assets/images/ecolab.png uses-material-design: true