From 151bfa1bd3e7c9e83f99fba50cf5edf982c9254b Mon Sep 17 00:00:00 2001 From: allez_ryan_venga <120065584+MiAmigoRyan@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:52:55 -0600 Subject: [PATCH 1/2] Ryan Arment Interview Project PR --- .vscode/settings.json | 3 + .../reports/problems/problems-report.html | 663 ++++++++++++++++++ devtools_options.yaml | 3 + lib/home.dart | 126 ++++ lib/home_wrapper.dart | 19 + lib/main.dart | 9 +- lib/sensor_bloc.dart | 66 ++ lib/sensor_event.dart | 54 ++ lib/sensor_state.dart | 20 + 9 files changed, 958 insertions(+), 5 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 android/build/reports/problems/problems-report.html create mode 100644 devtools_options.yaml create mode 100644 lib/home.dart create mode 100644 lib/home_wrapper.dart create mode 100644 lib/sensor_bloc.dart create mode 100644 lib/sensor_event.dart create mode 100644 lib/sensor_state.dart 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/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..0c4d3fb --- /dev/null +++ b/lib/home.dart @@ -0,0 +1,126 @@ +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 = ''; + + + + @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), + ); print('pressed'); + + Navigator.pop(context); + }, + child: const Text('Save'), + ), + ], + ), + ); + } + return Scaffold( + appBar: AppBar( + title: const Text('Thermal Sensors'), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(50), + 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: 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(); + + 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 From 832386fe279f4836d6dc6a1d1a259bc584ee9603 Mon Sep 17 00:00:00 2001 From: allez_ryan_venga <120065584+MiAmigoRyan@users.noreply.github.com> Date: Wed, 23 Jul 2025 04:54:11 -0600 Subject: [PATCH 2/2] added ascending/decending and ecolab logo --- assets/images/ecolab.png | Bin 0 -> 13712 bytes lib/home.dart | 204 ++++++++++++++++++++++++--------------- pubspec.yaml | 2 + 3 files changed, 126 insertions(+), 80 deletions(-) create mode 100644 assets/images/ecolab.png diff --git a/assets/images/ecolab.png b/assets/images/ecolab.png new file mode 100644 index 0000000000000000000000000000000000000000..009eede5c7b151d0680caf396145b16a79508c42 GIT binary patch literal 13712 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 literal 0 HcmV?d00001 diff --git a/lib/home.dart b/lib/home.dart index 0c4d3fb..006f1de 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -14,58 +14,67 @@ class HomeScreen extends StatefulWidget { 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(); - 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')), + 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'), + ), ], ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('Cancel'), - ), - ElevatedButton( - onPressed: () { - - - bloc.add( - EditSensorDetails(sensor.id, nameController.text, descController.text), - ); print('pressed'); + ); + } - Navigator.pop(context); - }, - child: const Text('Save'), - ), - ], - ), - ); - } return Scaffold( appBar: AppBar( - title: const Text('Thermal Sensors'), + 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(50), + preferredSize: const Size.fromHeight(80), child: Padding( padding: const EdgeInsets.all(8.0), child: TextField( @@ -79,47 +88,82 @@ class _HomeScreenState extends State { ), ), ), - body: 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(); + 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(); - if (filteredSensors.isEmpty) { - return const Center(child: Text('No sensors found.')); - } + filteredSensors.sort((a, b) => isAscending + ? a.value.compareTo(b.value) + : b.value.compareTo(a.value)); - 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), - ), - ], - ), - ); - }, - ); - }, + 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/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