From bce1bb404a1f1f2491c05b554afbedf686ffc18d Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Sat, 24 May 2025 22:25:37 +0530 Subject: [PATCH 1/8] Added a search bar --- lib/view/instruments_screen.dart | 147 ++++++++++++++++++++++++++++--- 1 file changed, 133 insertions(+), 14 deletions(-) diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index 44028d04d..ea6563b91 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -4,6 +4,7 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:pslab/constants.dart'; import 'package:pslab/view/widgets/applications_list_item.dart'; import 'package:pslab/view/widgets/main_scaffold_widget.dart'; +import 'package:pslab/colors.dart'; class InstrumentsScreen extends StatefulWidget { const InstrumentsScreen({super.key}); @@ -13,6 +14,8 @@ class InstrumentsScreen extends StatefulWidget { } class _InstrumentsScreenState extends State { + final TextEditingController _searchController = TextEditingController(); + List _filteredIndices = []; void _onItemTapped(int index) { switch (index) { case 0: @@ -32,8 +35,24 @@ class _InstrumentsScreenState extends State { } } + void _filterInstruments(String query) { + setState(() { + if (query.isEmpty) { + _filteredIndices = List.generate(instrumentHeadings.length, (index) => index); + } else { + _filteredIndices = []; + for (int i = 0; i < instrumentHeadings.length; i++) { + if (instrumentHeadings[i].toLowerCase().contains(query.toLowerCase())) { + _filteredIndices.add(i); + } + } + } + }); + } + @override void initState() { + _filteredIndices = List.generate(instrumentHeadings.length, (index) => index); WidgetsBinding.instance.addPostFrameCallback((_) { _setOrientation(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); @@ -51,6 +70,7 @@ class _InstrumentsScreenState extends State { @override void dispose() { + _searchController.dispose(); super.dispose(); } @@ -60,21 +80,120 @@ class _InstrumentsScreenState extends State { index: 0, title: 'Instruments', body: SafeArea( - child: ScrollConfiguration( - behavior: const ScrollBehavior(), - child: ListView.builder( - itemCount: instrumentHeadings.length, - itemBuilder: (context, index) { - return GestureDetector( - onTap: () => _onItemTapped(index), - child: ApplicationsListItem( - heading: instrumentHeadings[index], - description: instrumentDesc[index], - instrumentIcon: instrumentIcons[index], + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: TextField( + controller: _searchController, + onChanged: _filterInstruments, + decoration: InputDecoration( + hintText: 'Search instruments...', + hintStyle: TextStyle( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + ), + prefixIcon: Icon( + Icons.search, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), + suffixIcon: _searchController.text.isNotEmpty + ? IconButton( + icon: Icon( + Icons.clear, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), + onPressed: () { + _searchController.clear(); + _filterInstruments(''); + }, + ) + : null, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(30.0), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(30.0), + borderSide: BorderSide( + color: primaryRed, + width: 1.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(30.0), + borderSide: BorderSide( + color: primaryRed, + width: 2.5, + ), + ), + filled: true, + fillColor: Theme.of(context).colorScheme.surface, + contentPadding: const EdgeInsets.symmetric( + horizontal: 20.0, + vertical: 16.0, + ), + ), + ), + ), + ), + Expanded( + child: _filteredIndices.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.search_off, + size: 64, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), + ), + const SizedBox(height: 16), + Text( + 'No instruments found', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), + ), + const SizedBox(height: 8), + Text( + 'Try a different search term', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), + ), + ), + ], + ), + ) + : ScrollConfiguration( + behavior: const ScrollBehavior(), + child: ListView.builder( + itemCount: _filteredIndices.length, + itemBuilder: (context, index) { + final originalIndex = _filteredIndices[index]; + return GestureDetector( + onTap: () => _onItemTapped(originalIndex), + child: ApplicationsListItem( + heading: instrumentHeadings[originalIndex], + description: instrumentDesc[originalIndex], + instrumentIcon: instrumentIcons[originalIndex], + ), + ); + }, ), - ); - }, - ), + ), + ), + ], ), ), ); From 3b4d12eef730b0490bc53b6729073f9f7f60c15d Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Sat, 24 May 2025 22:35:38 +0530 Subject: [PATCH 2/8] Added a Search Bar --- lib/view/instruments_screen.dart | 134 +++++++++++++++++++------------ 1 file changed, 81 insertions(+), 53 deletions(-) diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index ea6563b91..630900df9 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -38,11 +38,14 @@ class _InstrumentsScreenState extends State { void _filterInstruments(String query) { setState(() { if (query.isEmpty) { - _filteredIndices = List.generate(instrumentHeadings.length, (index) => index); + _filteredIndices = + List.generate(instrumentHeadings.length, (index) => index); } else { _filteredIndices = []; for (int i = 0; i < instrumentHeadings.length; i++) { - if (instrumentHeadings[i].toLowerCase().contains(query.toLowerCase())) { + if (instrumentHeadings[i] + .toLowerCase() + .contains(query.toLowerCase())) { _filteredIndices.add(i); } } @@ -52,7 +55,8 @@ class _InstrumentsScreenState extends State { @override void initState() { - _filteredIndices = List.generate(instrumentHeadings.length, (index) => index); + _filteredIndices = + List.generate(instrumentHeadings.length, (index) => index); WidgetsBinding.instance.addPostFrameCallback((_) { _setOrientation(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); @@ -101,23 +105,32 @@ class _InstrumentsScreenState extends State { decoration: InputDecoration( hintText: 'Search instruments...', hintStyle: TextStyle( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6), ), prefixIcon: Icon( Icons.search, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.7), ), suffixIcon: _searchController.text.isNotEmpty ? IconButton( - icon: Icon( - Icons.clear, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), - ), - onPressed: () { - _searchController.clear(); - _filterInstruments(''); - }, - ) + icon: Icon( + Icons.clear, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.7), + ), + onPressed: () { + _searchController.clear(); + _filterInstruments(''); + }, + ) : null, border: OutlineInputBorder( borderRadius: BorderRadius.circular(30.0), @@ -150,48 +163,63 @@ class _InstrumentsScreenState extends State { Expanded( child: _filteredIndices.isEmpty ? Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.search_off, - size: 64, - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), - ), - const SizedBox(height: 16), - Text( - 'No instruments found', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.search_off, + size: 64, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.5), + ), + const SizedBox(height: 16), + Text( + 'No instruments found', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.7), + ), + ), + const SizedBox(height: 8), + Text( + 'Try a different search term', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.5), + ), + ), + ], ), - ), - const SizedBox(height: 8), - Text( - 'Try a different search term', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), - ), - ), - ], - ), - ) + ) : ScrollConfiguration( - behavior: const ScrollBehavior(), - child: ListView.builder( - itemCount: _filteredIndices.length, - itemBuilder: (context, index) { - final originalIndex = _filteredIndices[index]; - return GestureDetector( - onTap: () => _onItemTapped(originalIndex), - child: ApplicationsListItem( - heading: instrumentHeadings[originalIndex], - description: instrumentDesc[originalIndex], - instrumentIcon: instrumentIcons[originalIndex], + behavior: const ScrollBehavior(), + child: ListView.builder( + itemCount: _filteredIndices.length, + itemBuilder: (context, index) { + final originalIndex = _filteredIndices[index]; + return GestureDetector( + onTap: () => _onItemTapped(originalIndex), + child: ApplicationsListItem( + heading: instrumentHeadings[originalIndex], + description: instrumentDesc[originalIndex], + instrumentIcon: instrumentIcons[originalIndex], + ), + ); + }, ), - ); - }, - ), - ), + ), ), ], ), From 29af6a1c9b57afbef4134e2bfd947d66d58f3c43 Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Sun, 25 May 2025 16:43:06 +0530 Subject: [PATCH 3/8] Replaced deprecated methods --- lib/view/instruments_screen.dart | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index 630900df9..9b37ee823 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:pslab/colors.dart'; import 'package:pslab/constants.dart'; import 'package:pslab/view/widgets/applications_list_item.dart'; import 'package:pslab/view/widgets/main_scaffold_widget.dart'; -import 'package:pslab/colors.dart'; class InstrumentsScreen extends StatefulWidget { const InstrumentsScreen({super.key}); @@ -15,7 +15,7 @@ class InstrumentsScreen extends StatefulWidget { class _InstrumentsScreenState extends State { final TextEditingController _searchController = TextEditingController(); - List _filteredIndices = []; + List _filteredIndices = []; void _onItemTapped(int index) { switch (index) { case 0: @@ -39,7 +39,7 @@ class _InstrumentsScreenState extends State { setState(() { if (query.isEmpty) { _filteredIndices = - List.generate(instrumentHeadings.length, (index) => index); + List.generate(instrumentHeadings.length, (index) => index); } else { _filteredIndices = []; for (int i = 0; i < instrumentHeadings.length; i++) { @@ -55,18 +55,18 @@ class _InstrumentsScreenState extends State { @override void initState() { + super.initState(); _filteredIndices = - List.generate(instrumentHeadings.length, (index) => index); + List.generate(instrumentHeadings.length, (index) => index); WidgetsBinding.instance.addPostFrameCallback((_) { _setOrientation(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }); - super.initState(); Permission.microphone.request(); } void _setOrientation() { - SystemChrome.setPreferredOrientations([ + SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); @@ -85,17 +85,17 @@ class _InstrumentsScreenState extends State { title: 'Instruments', body: SafeArea( child: Column( - children: [ + children: [ Padding( padding: const EdgeInsets.all(16.0), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(30.0), - boxShadow: [ + boxShadow: const [ BoxShadow( - color: Colors.black.withOpacity(0.1), + color: Color.fromRGBO(0, 0, 0, 0.1), blurRadius: 8, - offset: const Offset(0, 2), + offset: Offset(0, 2), ), ], ), @@ -108,14 +108,14 @@ class _InstrumentsScreenState extends State { color: Theme.of(context) .colorScheme .onSurface - .withOpacity(0.6), + .withAlpha(153), ), prefixIcon: Icon( Icons.search, color: Theme.of(context) .colorScheme .onSurface - .withOpacity(0.7), + .withAlpha(179), ), suffixIcon: _searchController.text.isNotEmpty ? IconButton( @@ -124,7 +124,7 @@ class _InstrumentsScreenState extends State { color: Theme.of(context) .colorScheme .onSurface - .withOpacity(0.7), + .withAlpha(179), ), onPressed: () { _searchController.clear(); @@ -165,14 +165,14 @@ class _InstrumentsScreenState extends State { ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, - children: [ + children: [ Icon( Icons.search_off, size: 64, color: Theme.of(context) .colorScheme .onSurface - .withOpacity(0.5), + .withAlpha(128), ), const SizedBox(height: 16), Text( @@ -184,7 +184,7 @@ class _InstrumentsScreenState extends State { color: Theme.of(context) .colorScheme .onSurface - .withOpacity(0.7), + .withAlpha(179), ), ), const SizedBox(height: 8), @@ -197,7 +197,7 @@ class _InstrumentsScreenState extends State { color: Theme.of(context) .colorScheme .onSurface - .withOpacity(0.5), + .withAlpha(128), ), ), ], @@ -208,7 +208,7 @@ class _InstrumentsScreenState extends State { child: ListView.builder( itemCount: _filteredIndices.length, itemBuilder: (context, index) { - final originalIndex = _filteredIndices[index]; + final int originalIndex = _filteredIndices[index]; return GestureDetector( onTap: () => _onItemTapped(originalIndex), child: ApplicationsListItem( From cea5202d6bf498792c8e94c76faf477b2567fdaf Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Tue, 27 May 2025 12:12:43 +0530 Subject: [PATCH 4/8] Made the suggested chages --- lib/constants.dart | 4 ++++ lib/view/instruments_screen.dart | 23 ++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/constants.dart b/lib/constants.dart index cca96fde6..bf9c18170 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -133,3 +133,7 @@ List connectWithUs = [ 'Developers' ]; String softwareLicenses = 'Software Licenses'; +String tryDifferentSearchSuggestion = 'Try a different search term'; +String noInstrumentsFoundMessage = 'No instruments found'; +String searchInstrumentsHint = 'Search instruments...'; +String instrumentsTitle = 'Instruments'; diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index 9b37ee823..3b5860887 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -41,14 +41,11 @@ class _InstrumentsScreenState extends State { _filteredIndices = List.generate(instrumentHeadings.length, (index) => index); } else { - _filteredIndices = []; - for (int i = 0; i < instrumentHeadings.length; i++) { - if (instrumentHeadings[i] - .toLowerCase() - .contains(query.toLowerCase())) { - _filteredIndices.add(i); - } - } + _filteredIndices = List.generate(instrumentHeadings.length, (i) => i) + .where((i) => instrumentHeadings[i] + .toLowerCase() + .contains(query.toLowerCase())) + .toList(); } }); } @@ -82,12 +79,12 @@ class _InstrumentsScreenState extends State { Widget build(BuildContext context) { return MainScaffold( index: 0, - title: 'Instruments', + title: instrumentsTitle, body: SafeArea( child: Column( children: [ Padding( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.symmetric(vertical: 8.0,horizontal: 16.0), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(30.0), @@ -103,7 +100,7 @@ class _InstrumentsScreenState extends State { controller: _searchController, onChanged: _filterInstruments, decoration: InputDecoration( - hintText: 'Search instruments...', + hintText: searchInstrumentsHint, hintStyle: TextStyle( color: Theme.of(context) .colorScheme @@ -176,7 +173,7 @@ class _InstrumentsScreenState extends State { ), const SizedBox(height: 16), Text( - 'No instruments found', + noInstrumentsFoundMessage, style: Theme.of(context) .textTheme .titleMedium @@ -189,7 +186,7 @@ class _InstrumentsScreenState extends State { ), const SizedBox(height: 8), Text( - 'Try a different search term', + tryDifferentSearchSuggestion, style: Theme.of(context) .textTheme .bodyMedium From 8cf509b468e404c6ebf519328357366f5e997a48 Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Tue, 27 May 2025 13:56:41 +0530 Subject: [PATCH 5/8] formatted code --- lib/view/instruments_screen.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index 3b5860887..d50f88513 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -84,7 +84,8 @@ class _InstrumentsScreenState extends State { child: Column( children: [ Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0,horizontal: 16.0), + padding: + const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(30.0), From 5acb3a3cbcf7894fd71e75159ee9452f35a2e0e0 Mon Sep 17 00:00:00 2001 From: Yugesh Kumar Date: Mon, 16 Jun 2025 21:36:36 +0530 Subject: [PATCH 6/8] Update constants.dart --- lib/constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/constants.dart b/lib/constants.dart index e4e282b4c..7011d4f70 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -169,4 +169,4 @@ String builtIn = 'Built-In'; String lx = 'Lx'; String maxScaleError = 'Max Scale'; String lightSensorError = 'Light sensor error:'; -String lightSensorInitialError = 'Failed to initialize light sensor:'; \ No newline at end of file +String lightSensorInitialError = 'Failed to initialize light sensor:'; From ded4644f358025c944b6335b4bf8515d4a247415 Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Wed, 18 Jun 2025 20:34:21 +0530 Subject: [PATCH 7/8] Added Floating action button --- lib/constants.dart | 1 + lib/view/instruments_screen.dart | 302 +++++++++++++++++-------------- 2 files changed, 166 insertions(+), 137 deletions(-) diff --git a/lib/constants.dart b/lib/constants.dart index 84ef8d140..181c8f718 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -196,6 +196,7 @@ class FAQConstants { static const String compatibleSensorsAnswer = "In our apps we use the industry standard I²C (Wikipedia). You can get the data from sensors that are connected to the device through the USB port using an OTG USB cable (OTG = On the go) which is a USB cable that allows connected devices to switch back and forth between the roles of host and device. For the transfer we use UART (universal asynchronous receiver-transmitter, Wikipedia). Many sensors can be used with specific instruments, e.g. Barometer, Thermometer, Gyroscope etc. You can access the configuration for sensors in the instrument settings on the top right burger menu of each instrument. All sensors using the I²C standard are compatible with the device. There are connection pins for analogue and digital sensors. You find the description of the pins on the back of the device. Even if there is no specific instrument in one of our apps yet, you can still view and store the raw data using the Oscilloscope instrument component. There is a page with a list of recommended sensors on the website."; } + String accelerometer = 'Accelerometer'; String xAxis = 'x'; String yAxis = 'y'; diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index fe647f96c..de157ce57 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -13,6 +13,7 @@ class InstrumentsScreen extends StatefulWidget { } class _InstrumentsScreenState extends State { + bool _showSearch = false; final TextEditingController _searchController = TextEditingController(); List _filteredIndices = []; void _onItemTapped(int index) { @@ -123,151 +124,178 @@ class _InstrumentsScreenState extends State { @override Widget build(BuildContext context) { - return MainScaffold( - index: 0, - title: instrumentsTitle, - body: SafeArea( - child: Column( - children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - boxShadow: const [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.1), - blurRadius: 8, - offset: Offset(0, 2), - ), - ], - ), - child: TextField( - controller: _searchController, - onChanged: _filterInstruments, - decoration: InputDecoration( - hintText: searchInstrumentsHint, - hintStyle: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(153), - ), - prefixIcon: Icon( - Icons.search, - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(179), - ), - suffixIcon: _searchController.text.isNotEmpty - ? IconButton( - icon: Icon( - Icons.clear, - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(179), - ), - onPressed: () { - _searchController.clear(); - _filterInstruments(''); - }, - ) - : null, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(30.0), - borderSide: BorderSide.none, - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(30.0), - borderSide: BorderSide( - color: primaryRed, - width: 1.5, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(30.0), - borderSide: BorderSide( - color: primaryRed, - width: 2.5, - ), - ), - filled: true, - fillColor: Theme.of(context).colorScheme.surface, - contentPadding: const EdgeInsets.symmetric( - horizontal: 20.0, - vertical: 16.0, - ), - ), - ), - ), - ), - Expanded( - child: _filteredIndices.isEmpty - ? Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.search_off, - size: 64, - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(128), + return Scaffold( + body: MainScaffold( + index: 0, + title: instrumentsTitle, + body: SafeArea( + child: Column( + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: _showSearch + ? Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, horizontal: 16.0), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + boxShadow: const [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.1), + blurRadius: 8, + offset: Offset(0, 2), + ), + ], ), - const SizedBox(height: 16), - Text( - noInstrumentsFoundMessage, - style: Theme.of(context) - .textTheme - .titleMedium - ?.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(179), + child: TextField( + controller: _searchController, + onChanged: _filterInstruments, + decoration: InputDecoration( + hintText: searchInstrumentsHint, + hintStyle: TextStyle( + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(153), + ), + prefixIcon: Icon( + Icons.search, + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(179), + ), + suffixIcon: _searchController.text.isNotEmpty + ? IconButton( + icon: Icon( + Icons.clear, + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(179), + ), + onPressed: () { + _searchController.clear(); + _filterInstruments(''); + }, + ) + : null, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(30.0), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(30.0), + borderSide: BorderSide( + color: primaryRed, + width: 1.5, ), - ), - const SizedBox(height: 8), - Text( - tryDifferentSearchSuggestion, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(128), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(30.0), + borderSide: BorderSide( + color: primaryRed, + width: 2.5, ), + ), + filled: true, + fillColor: Theme.of(context).colorScheme.surface, + contentPadding: const EdgeInsets.symmetric( + horizontal: 20.0, + vertical: 16.0, + ), + ), ), - ], - ), - ) - : ScrollConfiguration( - behavior: const ScrollBehavior(), - child: ListView.builder( - itemCount: _filteredIndices.length, - itemBuilder: (context, index) { - final int originalIndex = _filteredIndices[index]; - return GestureDetector( - onTap: () => _onItemTapped(originalIndex), - child: ApplicationsListItem( - heading: instrumentHeadings[originalIndex], - description: instrumentDesc[originalIndex], - instrumentIcon: instrumentIcons[originalIndex], + ), + ) + : const SizedBox.shrink(key: ValueKey('empty_space')), + ), + Expanded( + child: _filteredIndices.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.search_off, + size: 64, + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + ), + const SizedBox(height: 16), + Text( + noInstrumentsFoundMessage, + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(179), + ), ), - ); - }, + const SizedBox(height: 8), + Text( + tryDifferentSearchSuggestion, + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + ), + ), + ], + ), + ) + : ScrollConfiguration( + behavior: const ScrollBehavior(), + child: ListView.builder( + itemCount: _filteredIndices.length, + itemBuilder: (context, index) { + final int originalIndex = _filteredIndices[index]; + return GestureDetector( + onTap: () => _onItemTapped(originalIndex), + child: ApplicationsListItem( + heading: instrumentHeadings[originalIndex], + description: instrumentDesc[originalIndex], + instrumentIcon: instrumentIcons[originalIndex], + ), + ); + }, + ), ), - ), - ), - ], + ), + ], + ), + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + setState(() { + _showSearch = !_showSearch; + if (!_showSearch) { + _searchController.clear(); + _filterInstruments(''); + } + }); + }, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + backgroundColor: Colors.white, + child: Icon( + _showSearch ? Icons.close : Icons.search, + color: primaryRed, ), ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ); } } From 396834d3ff23bcf927a8dc7f11146e40d664b02c Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Wed, 18 Jun 2025 22:25:18 +0530 Subject: [PATCH 8/8] Final implementation of search functionality --- lib/view/instruments_screen.dart | 233 +++++---------------- lib/view/widgets/main_scaffold_widget.dart | 193 +++++++++++++---- 2 files changed, 207 insertions(+), 219 deletions(-) diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index de157ce57..913880bc4 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:pslab/colors.dart'; import 'package:pslab/constants.dart'; import 'package:pslab/view/widgets/applications_list_item.dart'; import 'package:pslab/view/widgets/main_scaffold_widget.dart'; @@ -13,9 +12,8 @@ class InstrumentsScreen extends StatefulWidget { } class _InstrumentsScreenState extends State { - bool _showSearch = false; - final TextEditingController _searchController = TextEditingController(); List _filteredIndices = []; + void _onItemTapped(int index) { switch (index) { case 0: @@ -116,186 +114,69 @@ class _InstrumentsScreenState extends State { ]); } - @override - void dispose() { - _searchController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { - return Scaffold( - body: MainScaffold( - index: 0, - title: instrumentsTitle, - body: SafeArea( - child: Column( - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: _showSearch - ? Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, horizontal: 16.0), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - boxShadow: const [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.1), - blurRadius: 8, - offset: Offset(0, 2), - ), - ], + return MainScaffold( + index: 0, + title: instrumentsTitle, + showSearch: true, + onSearchChanged: _filterInstruments, + searchHint: searchInstrumentsHint, + body: SafeArea( + child: _filteredIndices.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.search_off, + size: 64, + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + ), + const SizedBox(height: 16), + Text( + noInstrumentsFoundMessage, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(179), ), - child: TextField( - controller: _searchController, - onChanged: _filterInstruments, - decoration: InputDecoration( - hintText: searchInstrumentsHint, - hintStyle: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(153), - ), - prefixIcon: Icon( - Icons.search, - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(179), - ), - suffixIcon: _searchController.text.isNotEmpty - ? IconButton( - icon: Icon( - Icons.clear, - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(179), - ), - onPressed: () { - _searchController.clear(); - _filterInstruments(''); - }, - ) - : null, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(30.0), - borderSide: BorderSide.none, - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(30.0), - borderSide: BorderSide( - color: primaryRed, - width: 1.5, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(30.0), - borderSide: BorderSide( - color: primaryRed, - width: 2.5, - ), - ), - filled: true, - fillColor: Theme.of(context).colorScheme.surface, - contentPadding: const EdgeInsets.symmetric( - horizontal: 20.0, - vertical: 16.0, - ), - ), + ), + const SizedBox(height: 8), + Text( + tryDifferentSearchSuggestion, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), ), - ), - ) - : const SizedBox.shrink(key: ValueKey('empty_space')), - ), - Expanded( - child: _filteredIndices.isEmpty - ? Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.search_off, - size: 64, - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(128), - ), - const SizedBox(height: 16), - Text( - noInstrumentsFoundMessage, - style: Theme.of(context) - .textTheme - .titleMedium - ?.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(179), - ), - ), - const SizedBox(height: 8), - Text( - tryDifferentSearchSuggestion, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith( - color: Theme.of(context) - .colorScheme - .onSurface - .withAlpha(128), - ), - ), - ], - ), - ) - : ScrollConfiguration( - behavior: const ScrollBehavior(), - child: ListView.builder( - itemCount: _filteredIndices.length, - itemBuilder: (context, index) { - final int originalIndex = _filteredIndices[index]; - return GestureDetector( - onTap: () => _onItemTapped(originalIndex), - child: ApplicationsListItem( - heading: instrumentHeadings[originalIndex], - description: instrumentDesc[originalIndex], - instrumentIcon: instrumentIcons[originalIndex], - ), - ); - }, - ), + ), + ], + ), + ) + : ScrollConfiguration( + behavior: const ScrollBehavior(), + child: ListView.builder( + itemCount: _filteredIndices.length, + itemBuilder: (context, index) { + final int originalIndex = _filteredIndices[index]; + return GestureDetector( + onTap: () => _onItemTapped(originalIndex), + child: ApplicationsListItem( + heading: instrumentHeadings[originalIndex], + description: instrumentDesc[originalIndex], + instrumentIcon: instrumentIcons[originalIndex], ), + ); + }, + ), ), - ], - ), - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: () { - setState(() { - _showSearch = !_showSearch; - if (!_showSearch) { - _searchController.clear(); - _filterInstruments(''); - } - }); - }, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - backgroundColor: Colors.white, - child: Icon( - _showSearch ? Icons.close : Icons.search, - color: primaryRed, - ), ), - floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ); } } diff --git a/lib/view/widgets/main_scaffold_widget.dart b/lib/view/widgets/main_scaffold_widget.dart index 97252996c..087151f92 100644 --- a/lib/view/widgets/main_scaffold_widget.dart +++ b/lib/view/widgets/main_scaffold_widget.dart @@ -14,20 +14,70 @@ class MainScaffold extends StatefulWidget { final String icUsbDisconnected = 'assets/icons/ic_usb_disconnected.png'; final String icUsbConnected = 'assets/icons/ic_usb_connected.png'; final String icWiFiConnected = 'assets/icons/ic_wifi_connected.png'; + final bool showSearch; + final Function(String)? onSearchChanged; + final String? searchHint; - const MainScaffold( - {super.key, - required this.body, - required this.title, - this.scaffoldKey, - this.actions, - required this.index}); + const MainScaffold({ + super.key, + required this.body, + required this.title, + this.scaffoldKey, + this.actions, + required this.index, + this.showSearch = false, + this.onSearchChanged, + this.searchHint, + }); @override State createState() => _MainScaffoldState(); } -class _MainScaffoldState extends State { +class _MainScaffoldState extends State + with SingleTickerProviderStateMixin { + bool _isSearching = false; + final TextEditingController _searchController = TextEditingController(); + late AnimationController _animationController; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + } + + @override + void dispose() { + _searchController.dispose(); + _animationController.dispose(); + super.dispose(); + } + + void _toggleSearch() { + setState(() { + if (_isSearching) { + _isSearching = false; + _animationController.reverse(); + _searchController.clear(); + if (widget.onSearchChanged != null) { + widget.onSearchChanged!(''); + } + } else { + _isSearching = true; + _animationController.forward(); + } + }); + } + + void _onSearchChanged(String query) { + if (widget.onSearchChanged != null) { + widget.onSearchChanged!(query); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -48,43 +98,100 @@ class _MainScaffoldState extends State { ); }), backgroundColor: const Color(0xFFD32F2F), - title: Text( - key: widget.scaffoldKey, - widget.title, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 0), + transitionBuilder: (Widget child, Animation animation) { + return FadeTransition( + opacity: animation, + child: child, + ); + }, + child: _isSearching + ? TextField( + key: const ValueKey('search_field'), + controller: _searchController, + onChanged: _onSearchChanged, + autofocus: true, + style: const TextStyle( + color: Colors.white, + fontSize: 18, + ), + decoration: InputDecoration( + hintText: widget.searchHint, + hintStyle: const TextStyle( + color: Colors.white70, + fontSize: 18, + ), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: EdgeInsets.zero, + ), + cursorColor: Colors.white, + ) + : Text( + key: ValueKey('title_${widget.title}'), + widget.title, + style: const TextStyle( + color: Colors.white, + fontSize: 18, + ), + ), ), - actions: [ - Consumer( - builder: (context, provider, _) { - return IconButton( - icon: Image.asset( - provider.pslabIsConnected - ? (provider.scienceLabCommon.isWiFiConnected() - ? widget.icWiFiConnected - : widget.icUsbConnected) - : widget.icUsbDisconnected, - width: 24, - height: 24, + actions: _isSearching + ? [ + IconButton( + icon: const Icon( + Icons.clear, + color: Colors.white, + ), + onPressed: () { + if (_searchController.text.isNotEmpty) { + _searchController.clear(); + _onSearchChanged(''); + } else { + _toggleSearch(); + } + }, ), - onPressed: () { - /**/ - }, - ); - }, - ), - IconButton( - icon: const Icon( - Icons.more_vert, - color: Colors.white, - ), - onPressed: () { - /**/ - }, - ), - ], + ] + : [ + if (widget.showSearch) + IconButton( + icon: const Icon( + Icons.search, + color: Colors.white, + ), + onPressed: _toggleSearch, + ), + Consumer( + builder: (context, provider, _) { + return IconButton( + icon: Image.asset( + provider.pslabIsConnected + ? (provider.scienceLabCommon.isWiFiConnected() + ? widget.icWiFiConnected + : widget.icUsbConnected) + : widget.icUsbDisconnected, + width: 24, + height: 24, + ), + onPressed: () { + /**/ + }, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.more_vert, + color: Colors.white, + ), + onPressed: () { + /**/ + }, + ), + ], ), body: widget.body, drawer: NavDrawer(