diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 647bc8c..e875a60 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,7 @@ + + + { }, ), ), - body: Column( - children: [ - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: nameController, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Command name', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Column( + children: [ + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: nameController, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Command name', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: ipController, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Host (IP address)', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: ipController, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Host (IP address)', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: userController, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Login as (user)', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: userController, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Login as (user)', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: passController, - obscureText: true, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Password', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: passController, + obscureText: true, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Password', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: cmdController, - minLines: 3, - maxLines: 100, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Command', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: cmdController, + minLines: 3, + maxLines: 100, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Command', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - Spacer(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - ElevatedButton( - onPressed: () { - _onSaved(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: theme.primary, - fixedSize: const Size(140, 40), - ), - child: Text( - 'Add', - style: TextStyle( - color: theme.onPrimary, - fontWeight: FontWeight.bold, + Spacer(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + onPressed: () { + _onSaved(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: theme.primary, + fixedSize: const Size(140, 40), + ), + child: Text( + 'Add', + style: TextStyle( + color: theme.onPrimary, + fontWeight: FontWeight.bold, + ), ), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ); } void _onSaved() { - // Create the object using the controller text final newCmd = ServerCommand( name: nameController.text, ip: ipController.text, @@ -226,9 +228,6 @@ class _AddClientState extends State { password: passController.text, ); - print("Saving: ${newCmd.name}"); - - // Send the object BACK to the previous screen (Home) Navigator.pop(context, newCmd); } } diff --git a/lib/screens/edit_client.dart b/lib/screens/edit_client.dart index c30e72e..23e8270 100644 --- a/lib/screens/edit_client.dart +++ b/lib/screens/edit_client.dart @@ -61,246 +61,252 @@ class _EditClientState extends State { }, ), ), - body: Column( - children: [ - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: nameController, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Command name', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Column( + children: [ + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: nameController, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Command name', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: ipController, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Host (IP address)', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: ipController, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Host (IP address)', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: userController, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Login as (user)', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: userController, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Login as (user)', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: passController, - obscureText: true, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Password', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: passController, + obscureText: true, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Password', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - const SizedBox(height: 20), - TextField( - style: TextStyle(color: theme.onSurface), - controller: cmdController, - minLines: 3, - maxLines: 100, - decoration: InputDecoration( - filled: true, - fillColor: theme.primaryContainer, - labelText: 'Command', - labelStyle: TextStyle( - color: theme.onSurfaceVariant, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + const SizedBox(height: 20), + TextField( + style: TextStyle(color: theme.onSurface), + controller: cmdController, + minLines: 3, + maxLines: 100, + decoration: InputDecoration( + filled: true, + fillColor: theme.primaryContainer, + labelText: 'Command', + labelStyle: TextStyle( + color: theme.onSurfaceVariant, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + borderSide: BorderSide(color: Color.fromARGB(0, 0, 0, 0)), + ), ), ), - ), - Spacer(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ElevatedButton( - onPressed: () { - showModalBottomSheet( - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(25.0), - ), - ), - builder: (BuildContext context) { - return SizedBox( - height: 400, - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Remove this command?', - style: TextStyle(fontSize: 20), - ), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - ElevatedButton( - onPressed: () { - Navigator.pop(context); - Navigator.pop(context, "DELETE"); - }, - style: ElevatedButton.styleFrom( - backgroundColor: theme.error, - fixedSize: const Size(140, 40), - ), - child: Text( - 'Yes, remove', - style: TextStyle( - color: theme.surface, - fontWeight: FontWeight.bold, - ), - ), - ), - ElevatedButton( - onPressed: () => Navigator.pop(context), - style: ElevatedButton.styleFrom( - backgroundColor: theme.onSurface, - fixedSize: const Size(140, 40), - ), - child: Text( - 'No', - style: TextStyle( - color: theme.surface, - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ), - ], - ), + Spacer(), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20.0, + vertical: 20, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ElevatedButton( + onPressed: () { + showModalBottomSheet( + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(25.0), ), - ); - }, - ); - }, - style: ElevatedButton.styleFrom( - backgroundColor: theme.error, - fixedSize: const Size(140, 40), - ), - child: Text( - 'Remove', - style: TextStyle( - color: const Color.fromARGB(255, 0, 0, 0), - fontWeight: FontWeight.bold, + ), + builder: (BuildContext context) { + return SizedBox( + height: 400, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'Remove this command?', + style: TextStyle(fontSize: 20), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () { + Navigator.pop(context); + Navigator.pop(context, "DELETE"); + }, + style: ElevatedButton.styleFrom( + backgroundColor: theme.error, + fixedSize: const Size(140, 40), + ), + child: Text( + 'Yes, remove', + style: TextStyle( + color: theme.surface, + fontWeight: FontWeight.bold, + ), + ), + ), + ElevatedButton( + onPressed: () => Navigator.pop(context), + style: ElevatedButton.styleFrom( + backgroundColor: theme.onSurface, + fixedSize: const Size(140, 40), + ), + child: Text( + 'No', + style: TextStyle( + color: theme.surface, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: theme.error, + fixedSize: const Size(140, 40), + ), + child: Text( + 'Remove', + style: TextStyle( + color: const Color.fromARGB(255, 0, 0, 0), + fontWeight: FontWeight.bold, + ), ), ), - ), - ElevatedButton( - onPressed: _save, - style: ElevatedButton.styleFrom( - backgroundColor: theme.onSurface, - fixedSize: const Size(140, 40), - ), - child: Text( - 'Save', - style: TextStyle( - color: theme.surface, - fontWeight: FontWeight.bold, + ElevatedButton( + onPressed: _save, + style: ElevatedButton.styleFrom( + backgroundColor: theme.onSurface, + fixedSize: const Size(140, 40), + ), + child: Text( + 'Save', + style: TextStyle( + color: theme.surface, + fontWeight: FontWeight.bold, + ), ), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 6df777c..4c58853 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -4,6 +4,7 @@ import '../widgets/server_status_tile.dart'; import 'add_client.dart'; import 'package:QuickSSH/classes/ServerCommand.dart'; import 'package:QuickSSH/services/storage_service.dart'; +import 'package:QuickSSH/screens/settings.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @@ -45,29 +46,37 @@ class _HomeScreenState extends State { ), drawer: Drawer( backgroundColor: theme.primaryContainer, - child: ListView( - children: [ - ListTile( - title: Text( - "Settings", - style: TextStyle( - color: theme.onSurface, - fontWeight: FontWeight.bold, - fontSize: 20, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: ListView( + children: [ + InkWell( + borderRadius: BorderRadius.circular(16), + onTap: _openSettings, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Settings", + style: TextStyle( + color: theme.onSurface, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), ), ), - ), - ListTile( - title: Text( - "Help", - style: TextStyle( - color: theme.onSurface, - fontWeight: FontWeight.bold, - fontSize: 20, + ListTile( + title: Text( + "Help", + style: TextStyle( + color: theme.onSurface, + fontWeight: FontWeight.bold, + fontSize: 20, + ), ), ), - ), - ], + ], + ), ), ), body: ListView.builder( @@ -102,24 +111,16 @@ class _HomeScreenState extends State { } void _addCommand() async { - // 1. Must be async - print("Opening Add Screen..."); - final result = await Navigator.push( - // 2. Must have await context, MaterialPageRoute(builder: (context) => const AddClient()), ); - // This part only runs AFTER you come back from AddClient if (result != null) { - print("Received data: ${result.name}"); setState(() { allCommands.add(result); }); SecureStorageService.saveCommands(allCommands); - } else { - print("Result was null - user might have just hit 'Back'"); } } @@ -140,13 +141,17 @@ class _HomeScreenState extends State { return; } - print("Received edited data: ${result.name}"); setState(() { allCommands[index] = result; }); SecureStorageService.saveCommands(allCommands); - } else { - print("Edit result was null - user might have just hit 'Back'"); } } + + _openSettings() { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const Settings()), + ); + } } diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart new file mode 100644 index 0000000..9ea9838 --- /dev/null +++ b/lib/screens/settings.dart @@ -0,0 +1,117 @@ +import 'package:QuickSSH/main.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class Settings extends StatefulWidget { + const Settings({super.key}); + + @override + State createState() => _SettingsState(); +} + +class _SettingsState extends State { + @override + Widget build(BuildContext context) { + final theme = Theme.of(context).colorScheme; + return Scaffold( + appBar: AppBar( + backgroundColor: theme.surface, + title: Text("Settings", style: TextStyle(color: theme.onSurface)), + leading: Builder( + builder: (context) { + return IconButton( + icon: Icon(Icons.arrow_back_ios_rounded, color: theme.onSurface), + onPressed: () { + Navigator.pop(context); + }, + ); + }, + ), + ), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Text( + "Appearance", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, + color: theme.onSurface, + ), + ), + ), + Expanded( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 4, + ), + decoration: BoxDecoration( + color: theme.primaryContainer, + borderRadius: BorderRadius.circular(16), + ), + child: DropdownButton( + value: themeController.themeMode, + underline: Container(), + items: const [ + DropdownMenuItem( + value: ThemeMode.system, + child: Text("System Default"), + ), + DropdownMenuItem( + value: ThemeMode.dark, + child: Text("Dark"), + ), + DropdownMenuItem( + value: ThemeMode.light, + child: Text("Light"), + ), + ], + onChanged: (ThemeMode? newMode) { + themeController.updateTheme(newMode); + setState(() {}); + }, + ), + ), + ), + ], + ), + SizedBox(height: 20), + Row( + children: [ + Expanded( + child: Text( + "Vibrations", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, + color: theme.onSurface, + ), + ), + ), + Expanded( + child: Switch( + activeTrackColor: theme.primary, + activeThumbColor: theme.onPrimary, + inactiveThumbColor: theme.onSurface, + inactiveTrackColor: theme.surface, + value: settingsController.vibrationEnabled, + onChanged: (bool value) { + settingsController.toggleVibration(value); + if (value) HapticFeedback.mediumImpact(); + setState(() {}); + }, + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/services/SSHService.dart b/lib/services/SSHService.dart index 0d24e70..cca7492 100644 --- a/lib/services/SSHService.dart +++ b/lib/services/SSHService.dart @@ -5,27 +5,23 @@ import 'package:QuickSSH/classes/ServerCommand.dart'; class SSHService { static Future execute(ServerCommand server) async { try { - // 1. Connect to the IP and Port final socket = await SSHSocket.connect( server.ip, 22, timeout: const Duration(seconds: 10), ); - // 2. Authenticate final client = SSHClient( socket, username: server.username, onPasswordRequest: () => server.password, ); - // 3. Run the specific command final result = await client.run(server.command); client.close(); await client.done; - // 4. Return the server's response return utf8.decode(result); } catch (e) { return "Error: $e"; diff --git a/lib/services/settings_controller.dart b/lib/services/settings_controller.dart new file mode 100644 index 0000000..b73f232 --- /dev/null +++ b/lib/services/settings_controller.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class SettingsController extends ChangeNotifier { + bool _vibrationEnabled = true; + bool get vibrationEnabled => _vibrationEnabled; + + SettingsController() { + _loadSettings(); + } + + Future _loadSettings() async { + final prefs = await SharedPreferences.getInstance(); + _vibrationEnabled = prefs.getBool('vibration_enabled') ?? true; + notifyListeners(); + } + + Future toggleVibration(bool value) async { + _vibrationEnabled = value; + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('vibration_enabled', value); + notifyListeners(); + } +} diff --git a/lib/services/theme_controller.dart b/lib/services/theme_controller.dart new file mode 100644 index 0000000..60ec6b4 --- /dev/null +++ b/lib/services/theme_controller.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ThemeController extends ChangeNotifier { + ThemeMode _themeMode = ThemeMode.system; + + ThemeMode get themeMode => _themeMode; + + ThemeController() { + _loadTheme(); + } + + // Load saved theme from disk + Future _loadTheme() async { + final prefs = await SharedPreferences.getInstance(); + final themeIndex = prefs.getInt('theme_mode') ?? 0; + _themeMode = ThemeMode.values[themeIndex]; + notifyListeners(); + } + + // Update and save theme + Future updateTheme(ThemeMode? newThemeMode) async { + if (newThemeMode == null || newThemeMode == _themeMode) return; + + _themeMode = newThemeMode; + notifyListeners(); + + final prefs = await SharedPreferences.getInstance(); + await prefs.setInt('theme_mode', newThemeMode.index); + } +} diff --git a/lib/widgets/server_status_tile.dart b/lib/widgets/server_status_tile.dart index 829068d..b470035 100644 --- a/lib/widgets/server_status_tile.dart +++ b/lib/widgets/server_status_tile.dart @@ -1,3 +1,4 @@ +import 'package:QuickSSH/main.dart'; import 'package:flutter/material.dart'; import 'package:QuickSSH/classes/ServerCommand.dart'; import 'package:QuickSSH/services/SSHService.dart'; @@ -21,21 +22,16 @@ class ServerStatusTile extends StatelessWidget { clipBehavior: Clip.antiAlias, color: theme.primaryContainer, child: InkWell( - splashColor: const Color.fromARGB( - 47, - 255, - 255, - 255, - ), // Custom ripple color! + splashColor: const Color.fromARGB(47, 255, 255, 255), highlightColor: Colors.transparent, onDoubleTap: () { - HapticFeedback.heavyImpact(); + if (settingsController.vibrationEnabled) HapticFeedback.heavyImpact(); _handleCommandTap(context, command); }, onLongPress: () { - HapticFeedback.heavyImpact(); + if (settingsController.vibrationEnabled) HapticFeedback.heavyImpact(); onEdit(); }, diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 0fe463a..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:quick_ssh/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}