diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 20570b2..647bc8c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ toMap() { + return { + 'name': name, + 'ip': ip, + 'username': username, + 'password': password, + 'command': command, + }; + } + + // Create Object from Map + factory ServerCommand.fromMap(Map map) { + return ServerCommand( + name: map['name'], + ip: map['ip'], + username: map['username'], + password: map['password'], + command: map['command'], + ); + } +} diff --git a/lib/classes/theme_provider.dart b/lib/classes/theme_provider.dart new file mode 100644 index 0000000..36b394c --- /dev/null +++ b/lib/classes/theme_provider.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +// 2. THE MAIN THEME DEFINITIONS +class MyThemes { + static final darkTheme = ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + colorScheme: const ColorScheme.dark( + primary: Color.fromARGB(255, 30, 90, 173), + onPrimary: Color.fromARGB(255, 255, 255, 255), + surface: Color.fromARGB(255, 24, 24, 24), // <--- Use this for Card BG + onSurface: Color.fromARGB(255, 255, 255, 255), // Card Text + onSurfaceVariant: Color.fromARGB(255, 155, 155, 155), + primaryContainer: Color.fromARGB(255, 30, 30, 30), + error: Color.fromARGB(255, 255, 0, 0), + ), + ); + + static final lightTheme = ThemeData( + useMaterial3: true, + brightness: Brightness.light, + colorScheme: const ColorScheme.light( + primary: Color.fromARGB(255, 30, 90, 173), + onPrimary: Color.fromARGB(255, 15, 15, 15), + surface: Color.fromARGB(255, 245, 245, 245), // <--- Use this for Card BG + onSurface: Color.fromARGB(255, 19, 19, 19), // Card Text + onSurfaceVariant: Color.fromARGB(255, 61, 61, 61), + primaryContainer: Color.fromARGB(255, 236, 236, 236), + error: Color.fromARGB(255, 255, 0, 0), + ), + ); +} diff --git a/lib/main.dart b/lib/main.dart index 0f14116..d2135f4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,36 +1,25 @@ import 'package:flutter/material.dart'; +import 'screens/home_screen.dart'; +import 'package:QuickSSH/classes/theme_provider.dart'; void main() { runApp(MyApp()); } -class MyApp extends StatefulWidget { +class MyApp extends StatelessWidget { const MyApp({super.key}); - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { @override Widget build(BuildContext context) { return MaterialApp( - home: Scaffold( - appBar: AppBar( - backgroundColor: const Color(0xFF37364A), - title: const Text("Qucik SSH"), - ), - body: const Center( - child: Text('Hello, World!', style: TextStyle(color: Colors.white)), - ), - floatingActionButton: FloatingActionButton( - onPressed: null, - backgroundColor: const Color(0xFF6264AE), - shape: const CircleBorder(), - child: const Icon(Icons.add, color: const Color(0xFF22202B)), - ), - backgroundColor: const Color(0xFF22202B), - ), + title: 'Quick SSH', + + theme: MyThemes.lightTheme, + darkTheme: MyThemes.darkTheme, + + themeMode: ThemeMode.system, + + home: HomeScreen(), ); } } diff --git a/lib/screens/add_client.dart b/lib/screens/add_client.dart new file mode 100644 index 0000000..646a255 --- /dev/null +++ b/lib/screens/add_client.dart @@ -0,0 +1,234 @@ +import 'package:flutter/material.dart'; +import 'package:QuickSSH/classes/ServerCommand.dart'; + +class AddClient extends StatefulWidget { + const AddClient({super.key}); + + @override + State createState() => _AddClientState(); +} + +class _AddClientState extends State { + final nameController = TextEditingController(); + final ipController = TextEditingController(); + final userController = TextEditingController(); + final cmdController = TextEditingController(); + final passController = TextEditingController(); + + @override + void dispose() { + nameController.dispose(); + ipController.dispose(); + userController.dispose(); + cmdController.dispose(); + passController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context).colorScheme; + + return Scaffold( + resizeToAvoidBottomInset: false, + backgroundColor: theme.surface, + appBar: AppBar( + backgroundColor: theme.surface, + title: Text("Add command", 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: 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: 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: 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, + ), + ), + ), + ], + ), + ), + ], + ), + ); + } + + void _onSaved() { + // Create the object using the controller text + final newCmd = ServerCommand( + name: nameController.text, + ip: ipController.text, + username: userController.text, + command: cmdController.text, + 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 new file mode 100644 index 0000000..c30e72e --- /dev/null +++ b/lib/screens/edit_client.dart @@ -0,0 +1,319 @@ +import 'package:flutter/material.dart'; +import 'package:QuickSSH/classes/ServerCommand.dart'; + +class EditClient extends StatefulWidget { + final ServerCommand? existingCommand; // Null if adding, not null if editing + final int? index; + + const EditClient({super.key, this.existingCommand, this.index}); + + @override + State createState() => _EditClientState(); +} + +class _EditClientState extends State { + final nameController = TextEditingController(); + final ipController = TextEditingController(); + final userController = TextEditingController(); + final cmdController = TextEditingController(); + final passController = TextEditingController(); + + @override + void dispose() { + nameController.dispose(); + ipController.dispose(); + userController.dispose(); + cmdController.dispose(); + passController.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + if (widget.existingCommand != null) { + nameController.text = widget.existingCommand!.name; + ipController.text = widget.existingCommand!.ip; + userController.text = widget.existingCommand!.username; + passController.text = widget.existingCommand!.password; + cmdController.text = widget.existingCommand!.command; + } + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context).colorScheme; + + return Scaffold( + backgroundColor: theme.surface, + resizeToAvoidBottomInset: false, + appBar: AppBar( + backgroundColor: theme.surface, + title: Text("Edit", 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: 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: 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: 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, + ), + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + }, + 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, + ), + ), + ), + ], + ), + ), + ], + ), + ); + } + + void _save() { + final updatedCommand = ServerCommand( + name: nameController.text, + ip: ipController.text, + username: userController.text, + command: cmdController.text, + password: passController.text, + ); + + Navigator.pop(context, updatedCommand); + } +} diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart new file mode 100644 index 0000000..6df777c --- /dev/null +++ b/lib/screens/home_screen.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:QuickSSH/screens/edit_client.dart'; +import '../widgets/server_status_tile.dart'; +import 'add_client.dart'; +import 'package:QuickSSH/classes/ServerCommand.dart'; +import 'package:QuickSSH/services/storage_service.dart'; + +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + List allCommands = []; + + @override + void initState() { + super.initState(); + _loadData(); + } + + void _loadData() async { + allCommands = await SecureStorageService.loadCommands(); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context).colorScheme; + return Scaffold( + appBar: AppBar( + backgroundColor: theme.surface, + leading: Builder( + builder: (context) { + return IconButton( + icon: Icon(Icons.menu, color: theme.onSurface), + onPressed: () { + Scaffold.of(context).openDrawer(); + }, + ); + }, + ), + ), + drawer: Drawer( + backgroundColor: theme.primaryContainer, + child: ListView( + children: [ + ListTile( + title: 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, + ), + ), + ), + ], + ), + ), + body: ListView.builder( + itemCount: allCommands.length, + itemBuilder: (context, index) { + // You can keep this as 'context' or rename it + return ServerStatusTile( + command: allCommands[index], + onEdit: () => _editCommand(allCommands[index], index), + ); + }, + ), + floatingActionButton: Builder( + builder: (context) { + return FloatingActionButton( + backgroundColor: theme.primary, + shape: const CircleBorder(), + child: const Icon( + Icons.add, + color: Color.fromARGB(255, 0, 0, 0), + size: 32, + weight: 50, + ), + onPressed: () { + _addCommand(); + }, + ); + }, + ), + backgroundColor: theme.surface, + ); + } + + 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'"); + } + } + + _editCommand(ServerCommand cmdToEdit, int index) async { + final result = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => EditClient(existingCommand: cmdToEdit), + ), + ); + + if (result != null) { + if (result == "DELETE") { + setState(() { + allCommands.removeAt(index); + }); + SecureStorageService.saveCommands(allCommands); + 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'"); + } + } +} diff --git a/lib/services/SSHService.dart b/lib/services/SSHService.dart new file mode 100644 index 0000000..0d24e70 --- /dev/null +++ b/lib/services/SSHService.dart @@ -0,0 +1,34 @@ +import 'dart:convert'; +import 'package:dartssh2/dartssh2.dart'; +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/storage_service.dart b/lib/services/storage_service.dart new file mode 100644 index 0000000..36f28b8 --- /dev/null +++ b/lib/services/storage_service.dart @@ -0,0 +1,24 @@ +import 'dart:convert'; +import 'package:QuickSSH/classes/ServerCommand.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +class SecureStorageService { + static const _storage = FlutterSecureStorage(); + static const _key = 'mc_commands_list'; + + // SAVE: Converts list to JSON, then encrypts and stores it + static Future saveCommands(List commands) async { + final String jsonData = jsonEncode(commands.map((c) => c.toMap()).toList()); + await _storage.write(key: _key, value: jsonData); + } + + // LOAD: Decrypts data, then converts JSON back to Objects + static Future> loadCommands() async { + final String? encryptedData = await _storage.read(key: _key); + + if (encryptedData == null) return []; + + final List decodedData = jsonDecode(encryptedData); + return decodedData.map((item) => ServerCommand.fromMap(item)).toList(); + } +} diff --git a/lib/widgets/server_status_tile.dart b/lib/widgets/server_status_tile.dart new file mode 100644 index 0000000..829068d --- /dev/null +++ b/lib/widgets/server_status_tile.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; +import 'package:QuickSSH/classes/ServerCommand.dart'; +import 'package:QuickSSH/services/SSHService.dart'; +import 'package:flutter/services.dart'; + +class ServerStatusTile extends StatelessWidget { + final ServerCommand command; + final VoidCallback onEdit; + + const ServerStatusTile({ + super.key, + required this.command, + required this.onEdit, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context).colorScheme; + + return Card( + clipBehavior: Clip.antiAlias, + color: theme.primaryContainer, + child: InkWell( + splashColor: const Color.fromARGB( + 47, + 255, + 255, + 255, + ), // Custom ripple color! + highlightColor: Colors.transparent, + + onDoubleTap: () { + HapticFeedback.heavyImpact(); + _handleCommandTap(context, command); + }, + + onLongPress: () { + HapticFeedback.heavyImpact(); + onEdit(); + }, + + child: IgnorePointer( + child: ListTile( + title: Text( + command.name, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, + color: theme.onSurface, + ), + ), + subtitle: Text( + "${command.username}@${command.ip}", + style: TextStyle(color: theme.onSurfaceVariant), + ), + ), + ), + ), + ); + } + + void _handleCommandTap(BuildContext context, ServerCommand cmd) async { + // Show a "Loading" snackbar + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + behavior: SnackBarBehavior.floating, + content: Text("Connecting to ${cmd.ip} as ${cmd.username}..."), + duration: Duration(seconds: 2), + backgroundColor: const Color.fromARGB(255, 56, 159, 243), + ), + ); + + // Run the SSH command + String output = await SSHService.execute(cmd); + + // Show the final result from the server + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + behavior: SnackBarBehavior.floating, + content: Text(output.isEmpty ? "Command executed (no output)" : output), + backgroundColor: output.contains("Error") ? Colors.red : Colors.green, + ), + ); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..d0e7f79 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..b29e9ba 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..37af1fe 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,12 @@ import FlutterMacOS import Foundation +import flutter_secure_storage_macos +import path_provider_foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index a2ec33f..96d3fee 100644 --- a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,68 +1,68 @@ { - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" + "info": { + "version": 1, + "author": "xcode" }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} + "images": [ + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_16.png", + "scale": "1x" + }, + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "2x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "1x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_64.png", + "scale": "2x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_128.png", + "scale": "1x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "2x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "1x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "2x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "1x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_1024.png", + "scale": "2x" + } + ] +} \ No newline at end of file diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png index 82b6f9d..a7b8a8a 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png index 13b35eb..1bd3296 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png index 0a3f5fa..15e4934 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png index bdb5722..71d6358 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png index f083318..c3260f4 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png index 326c0e7..1122aa7 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png index 2f1632c..43be2ae 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/pubspec.lock b/pubspec.lock index c1b51d3..d3470dc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,30 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + asn1lib: + dependency: transitive + description: + name: asn1lib + sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024" + url: "https://pub.dev" + source: hosted + version: "1.6.5" async: dependency: transitive description: @@ -25,6 +49,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" clock: dependency: transitive description: @@ -41,6 +81,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" cupertino_icons: dependency: "direct main" description: @@ -49,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dartssh2: + dependency: "direct main" + description: + name: dartssh2 + sha256: bb242396c1d90f05c261b5eb8338cc67167e803c0948b0207029339cc568a66c + url: "https://pub.dev" + source: hosted + version: "2.13.0" fake_async: dependency: transitive description: @@ -57,11 +121,35 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c + url: "https://pub.dev" + source: hosted + version: "2.1.5" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" + source: hosted + version: "0.13.1" flutter_lints: dependency: "direct dev" description: @@ -70,11 +158,88 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + image: + dependency: transitive + description: + name: image + sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c" + url: "https://pub.dev" + source: hosted + version: "4.7.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "805fa86df56383000f640384b282ce0cb8431f1a7a2396de92fb66186d8c57df" + url: "https://pub.dev" + source: hosted + version: "4.10.0" leak_tracker: dependency: transitive description: @@ -139,6 +304,158 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + url: "https://pub.dev" + source: hosted + version: "2.2.22" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" + url: "https://pub.dev" + source: hosted + version: "2.5.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" + url: "https://pub.dev" + source: hosted + version: "7.0.1" + pinenacl: + dependency: transitive + description: + name: pinenacl + sha256: "57e907beaacbc3c024a098910b6240758e899674de07d6949a67b52fd984cbdf" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: cbc40be9be1c5af4dab4d6e0de4d5d3729e6f3d65b89d21e1815d57705644a6f + url: "https://pub.dev" + source: hosted + version: "2.4.20" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter @@ -192,6 +509,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.6" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -208,6 +533,46 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" sdks: dart: ">=3.9.2 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0ab410d..5317cf3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ -name: quick_ssh -description: "A new Flutter project." +name: QuickSSH +description: "SSH command sender" # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -30,21 +30,28 @@ environment: dependencies: flutter: sdk: flutter + shared_preferences: ^2.5.0 + dartssh2: ^2.0.0 + flutter_secure_storage: ^9.2.2 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter + flutter_launcher_icons: ^0.13.1 - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^5.0.0 +flutter_launcher_icons: + image_path: "lib/assets/images/app_icon.png" + android: true + ios: true + windows: + generate: true + macos: + generate: true # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -86,4 +93,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/to/font-from-package + # see https://flutter.dev/to/font-from-package \ No newline at end of file diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..0c50753 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..4fc759c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index c04e20c..acf166a 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ