Completed basic functionality

This commit is contained in:
Matic Ivešić 2026-02-02 20:52:17 +01:00
parent 922f26310a
commit 78f2dfa742
55 changed files with 1399 additions and 101 deletions

View File

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application <application
android:label="quick_ssh" android:label="QuickSSH"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

3
devtools_options.yaml Normal file
View File

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View File

@ -427,7 +427,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@ -484,7 +484,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,37 @@
class ServerCommand {
String name;
String ip;
String username;
String password;
String command;
ServerCommand({
required this.name,
required this.ip,
required this.username,
required this.password,
required this.command,
});
// Convert Object to Map
Map<String, dynamic> toMap() {
return {
'name': name,
'ip': ip,
'username': username,
'password': password,
'command': command,
};
}
// Create Object from Map
factory ServerCommand.fromMap(Map<String, dynamic> map) {
return ServerCommand(
name: map['name'],
ip: map['ip'],
username: map['username'],
password: map['password'],
command: map['command'],
);
}
}

View File

@ -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),
),
);
}

View File

@ -1,36 +1,25 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'screens/home_screen.dart';
import 'package:QuickSSH/classes/theme_provider.dart';
void main() { void main() {
runApp(MyApp()); runApp(MyApp());
} }
class MyApp extends StatefulWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
home: Scaffold( title: 'Quick SSH',
appBar: AppBar(
backgroundColor: const Color(0xFF37364A), theme: MyThemes.lightTheme,
title: const Text("Qucik SSH"), darkTheme: MyThemes.darkTheme,
),
body: const Center( themeMode: ThemeMode.system,
child: Text('Hello, World!', style: TextStyle(color: Colors.white)),
), home: HomeScreen(),
floatingActionButton: FloatingActionButton(
onPressed: null,
backgroundColor: const Color(0xFF6264AE),
shape: const CircleBorder(),
child: const Icon(Icons.add, color: const Color(0xFF22202B)),
),
backgroundColor: const Color(0xFF22202B),
),
); );
} }
} }

234
lib/screens/add_client.dart Normal file
View File

@ -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<AddClient> createState() => _AddClientState();
}
class _AddClientState extends State<AddClient> {
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);
}
}

View File

@ -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<EditClient> createState() => _EditClientState();
}
class _EditClientState extends State<EditClient> {
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);
}
}

View File

@ -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<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<ServerCommand> 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'");
}
}
}

View File

@ -0,0 +1,34 @@
import 'dart:convert';
import 'package:dartssh2/dartssh2.dart';
import 'package:QuickSSH/classes/ServerCommand.dart';
class SSHService {
static Future<String> 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";
}
}
}

View File

@ -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<void> saveCommands(List<ServerCommand> 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<List<ServerCommand>> loadCommands() async {
final String? encryptedData = await _storage.read(key: _key);
if (encryptedData == null) return [];
final List<dynamic> decodedData = jsonDecode(encryptedData);
return decodedData.map((item) => ServerCommand.fromMap(item)).toList();
}
}

View File

@ -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,
),
);
}
}

View File

@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { 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);
} }

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -5,6 +5,12 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import flutter_secure_storage_macos
import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 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"))
} }

View File

@ -1,68 +1,68 @@
{ {
"images" : [ "info": {
{ "version": 1,
"size" : "16x16", "author": "xcode"
"idiom" : "mac",
"filename" : "app_icon_16.png",
"scale" : "1x"
}, },
{ "images": [
"size" : "16x16", {
"idiom" : "mac", "size": "16x16",
"filename" : "app_icon_32.png", "idiom": "mac",
"scale" : "2x" "filename": "app_icon_16.png",
}, "scale": "1x"
{ },
"size" : "32x32", {
"idiom" : "mac", "size": "16x16",
"filename" : "app_icon_32.png", "idiom": "mac",
"scale" : "1x" "filename": "app_icon_32.png",
}, "scale": "2x"
{ },
"size" : "32x32", {
"idiom" : "mac", "size": "32x32",
"filename" : "app_icon_64.png", "idiom": "mac",
"scale" : "2x" "filename": "app_icon_32.png",
}, "scale": "1x"
{ },
"size" : "128x128", {
"idiom" : "mac", "size": "32x32",
"filename" : "app_icon_128.png", "idiom": "mac",
"scale" : "1x" "filename": "app_icon_64.png",
}, "scale": "2x"
{ },
"size" : "128x128", {
"idiom" : "mac", "size": "128x128",
"filename" : "app_icon_256.png", "idiom": "mac",
"scale" : "2x" "filename": "app_icon_128.png",
}, "scale": "1x"
{ },
"size" : "256x256", {
"idiom" : "mac", "size": "128x128",
"filename" : "app_icon_256.png", "idiom": "mac",
"scale" : "1x" "filename": "app_icon_256.png",
}, "scale": "2x"
{ },
"size" : "256x256", {
"idiom" : "mac", "size": "256x256",
"filename" : "app_icon_512.png", "idiom": "mac",
"scale" : "2x" "filename": "app_icon_256.png",
}, "scale": "1x"
{ },
"size" : "512x512", {
"idiom" : "mac", "size": "256x256",
"filename" : "app_icon_512.png", "idiom": "mac",
"scale" : "1x" "filename": "app_icon_512.png",
}, "scale": "2x"
{ },
"size" : "512x512", {
"idiom" : "mac", "size": "512x512",
"filename" : "app_icon_1024.png", "idiom": "mac",
"scale" : "2x" "filename": "app_icon_512.png",
} "scale": "1x"
], },
"info" : { {
"version" : 1, "size": "512x512",
"author" : "xcode" "idiom": "mac",
} "filename": "app_icon_1024.png",
"scale": "2x"
}
]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,6 +1,30 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: 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: async:
dependency: transitive dependency: transitive
description: description:
@ -25,6 +49,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" 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: clock:
dependency: transitive dependency: transitive
description: description:
@ -41,6 +81,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" 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: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -49,6 +105,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" 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: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -57,11 +121,35 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.3" 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: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" 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: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -70,11 +158,88 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" 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: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" 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: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -139,6 +304,158 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" 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: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -192,6 +509,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.6" 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: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -208,6 +533,46 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "15.0.2" 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: sdks:
dart: ">=3.9.2 <4.0.0" dart: ">=3.9.2 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54" flutter: ">=3.35.0"

View File

@ -1,5 +1,5 @@
name: quick_ssh name: QuickSSH
description: "A new Flutter project." description: "SSH command sender"
# The following line prevents the package from being accidentally published to # The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages. # 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 publish_to: 'none' # Remove this line if you wish to publish to pub.dev
@ -30,21 +30,28 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: 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. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
dev_dependencies: dev_dependencies:
flutter_lints: ^5.0.0
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_launcher_icons: ^0.13.1
# The "flutter_lints" package below contains a set of recommended lints to flutter_launcher_icons:
# encourage good coding practices. The lint set provided by the package is image_path: "lib/assets/images/app_icon.png"
# activated in the `analysis_options.yaml` file located at the root of your android: true
# package. See that file for information about deactivating specific lint ios: true
# rules and activating additional ones. windows:
flutter_lints: ^5.0.0 generate: true
macos:
generate: true
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec

View File

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
} }

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_windows
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB