Flutter Hacker Virtual Machine Controller — Cyber Security Dashboard UI

 Demo :


Click Video πŸ‘‡πŸ‘‡πŸ‘‡





























⭐ Features :

  • Modern Hacker Dashboard UI

  • Virtual Machine Control

  • Matrix Rain Effect

  • Fake Cyber Simulation

  • Flutter Desktop App

  • Open Source Project


Code :


import 'dart:async';

import 'dart:convert';

import 'dart:io';

import 'dart:math';

import 'dart:ui';

import 'package:flutter/foundation.dart';

import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

import 'package:fl_chart/fl_chart.dart';

import 'package:google_fonts/google_fonts.dart';


// -----------------------------------------------------------------------------

// CONSTANTS & THEME

// -----------------------------------------------------------------------------


class AppColors {

  static const Color background = Color(0xFF030303); // Deep black

  static const Color surface = Color(0xFF0A0A0A);

  static const Color primary = Color(0xFF00FF41); // Matrix Green

  static const Color secondary = Color(0xFF008F11); // Darker Green

  static const Color accent = Color(0xFF00FFFF); // Cyan

  static const Color error = Color(0xFFFF0033);

  static const Color terminal = Color(0xFF1E1E1E);

}


class AppTheme {

  static ThemeData get darkTheme {

    return ThemeData.dark().copyWith(

      scaffoldBackgroundColor: AppColors.background,

      primaryColor: AppColors.primary,

      colorScheme: const ColorScheme.dark(

        primary: AppColors.primary,

        secondary: AppColors.accent,

        surface: AppColors.surface,

        background: AppColors.background,

      ),

      textTheme: GoogleFonts.firaCodeTextTheme(ThemeData.dark().textTheme).apply(

        bodyColor: AppColors.primary,

        displayColor: AppColors.primary,

      ),

      iconTheme: const IconThemeData(color: AppColors.primary),

    );

  }

}


// -----------------------------------------------------------------------------

// MODELS

// -----------------------------------------------------------------------------


class VirtualMachine {

  final String id;

  final String name;

  final String state; // running, powered off, saved, unknown

  final String osIcon; // simplified for now


  VirtualMachine({

    required this.id,

    required this.name,

    required this.state,

    this.osIcon = 'assets/linux.png',

  });


  factory VirtualMachine.fromJson(Map<String, dynamic> json) {

    return VirtualMachine(

      id: json['id'] ?? '',

      name: json['name'] ?? 'Unknown',

      state: json['state'] ?? 'unknown',

    );

  }

}


class SystemStats {

  final int cpu;

  final int ram;

  final int netIn;

  final int netOut;


  SystemStats({this.cpu = 0, this.ram = 0, this.netIn = 0, this.netOut = 0});


  factory SystemStats.fromJson(Map<String, dynamic> json) {

    return SystemStats(

      cpu: json['cpu'] ?? 0,

      ram: json['ram'] ?? 0,

      netIn: json['network_in'] ?? 0,

      netOut: json['network_out'] ?? 0,

    );

  }

}


// -----------------------------------------------------------------------------

// SERVICE

// -----------------------------------------------------------------------------


class VBoxService extends ChangeNotifier {

  List<VirtualMachine> _vms = [];

  SystemStats _stats = SystemStats();

  List<String> _logs = [];

  bool _isLoading = false;


  List<VirtualMachine> get vms => _vms;

  SystemStats get stats => _stats;

  List<String> get logs => _logs;

  bool get isLoading => _isLoading;


  final String _scriptPath = 'backend/vbox_controller.py';


  // Mock data for Web/Mobile or when Python fails

  bool _useMock = false;

  final List<VirtualMachine> _mockVMs = [

    VirtualMachine(id: 'mock-1', name: 'CyberLinux_2077', state: 'running'),

    VirtualMachine(id: 'mock-2', name: 'WinServer_Black', state: 'powered off'),

    VirtualMachine(id: 'mock-3', name: 'NetRunner_v9', state: 'saved'),

  ];


  Future<void> fetchVMs() async {

    _isLoading = true;

    notifyListeners();

    try {

      if (kIsWeb || _useMock) {

        await Future.delayed(const Duration(milliseconds: 800)); // Fake network play

        _vms = List.from(_mockVMs);

        _log("fetched vms (Simulated)");

      } else {

        final result = await _runPython(['list']);

        if (result.containsKey('vms')) {

          _vms = (result['vms'] as List)

              .map((e) => VirtualMachine.fromJson(e))

              .toList();

        }

        _log("fetched vms");

      }

    } catch (e) {

      // If native fetch fails, switch to mock automatically so app is usable

      _log("Connection failed ($e). Switching to Simulation Mode.");

      _useMock = true;

      _vms = List.from(_mockVMs);

    }

    _isLoading = false;

    notifyListeners();

  }


  Future<void> startVM(String id) async {

    _log("initiating launch sequence for $id...");

    notifyListeners();

    if (_useMock || kIsWeb) {

      await Future.delayed(const Duration(seconds: 2));

      final index = _mockVMs.indexWhere((vm) => vm.id == id);

      if (index != -1) {

        _mockVMs[index] = VirtualMachine(id: id, name: _mockVMs[index].name, state: 'running');

        _log("VM $id started successfully (Simulated).");

        fetchVMs();

      }

      return;

    }


    try {

      final result = await _runPython(['start', id]);

      if (result['status'] == 'success') {

        _log("VM $id started successfully.");

        fetchVMs();

      } else {

        _log("Failed to start VM: ${result['message']}");

      }

    } catch (e) {

      _log("Error starting VM: $e");

    }

  }


  Future<void> stopVM(String id) async {

    _log("sending kill signal to $id...");

    notifyListeners();

    if (_useMock || kIsWeb) {

      await Future.delayed(const Duration(seconds: 1));

       final index = _mockVMs.indexWhere((vm) => vm.id == id);

      if (index != -1) {

        _mockVMs[index] = VirtualMachine(id: id, name: _mockVMs[index].name, state: 'powered off');

        _log("VM $id stopped (Simulated).");

        fetchVMs();

      }

      return;

    }


    try {

      final result = await _runPython(['stop', id]);

      if (result['status'] == 'success') {

        _log("VM $id stopped.");

        fetchVMs();

      } else {

        _log("Failed to stop VM: ${result['message']}");

      }

    } catch (e) {

      _log("Error stopping VM: $e");

    }

  }


  Future<void> restartVM(String id) async {

    _log("rebooting system $id...");

    notifyListeners();

    if (_useMock || kIsWeb) {

       await Future.delayed(const Duration(seconds: 3));

       final index = _mockVMs.indexWhere((vm) => vm.id == id);

      if (index != -1) {

        _mockVMs[index] = VirtualMachine(id: id, name: _mockVMs[index].name, state: 'running');

        _log("VM $id restarted (Simulated).");

        fetchVMs();

      }

      return;

    }


    try {

      final result = await _runPython(['restart', id]);

      if (result['status'] == 'success') {

        _log("VM $id restarted.");

        fetchVMs();

      } else {

        _log("Failed to restart VM: ${result['message']}");

      }

    } catch (e) {

      _log("Error restarting VM: $e");

    }

  }


  Future<void> modifyVM(String id, int cpus, int memoryMb) async {

    _log("modifying hardware spec for $id...");

    if (_useMock || kIsWeb) {

        await Future.delayed(const Duration(milliseconds: 500));

        _log("VM $id spec updated: ${cpus}vCPU, ${memoryMb}MB RAM (Simulated).");

        return;

    }


    try {

      final result = await _runPython([

        'modify',

        id,

        '--cpus',

        cpus.toString(),

        '--memory',

        memoryMb.toString()

      ]);

      if (result['status'] == 'success') {

        _log("VM $id spec updated: ${cpus}vCPU, ${memoryMb}MB RAM.");

      } else {

        _log("Failed to update spec: ${result['message']}");

      }

    } catch (e) {

      _log("Error modifying VM: $e");

    }

  }


  Future<void> updateStats() async {

    if (_useMock || kIsWeb) {

      // Mock stats

      _stats = SystemStats(

        cpu: (Random().nextInt(30) + 10),

        ram: (Random().nextInt(40) + 20),

        netIn: Random().nextInt(1000),

        netOut: Random().nextInt(500),

      );

      notifyListeners();

      return;

    }


    try {

      final result = await _runPython(['stats']);

      _stats = SystemStats.fromJson(result);

      notifyListeners();

    } catch (e) {

      // Switch to mock if stats fail repeatedly? 

      // For now, silent fail or maybe switch if we haven't already

      if (!_useMock) {

         // Don't auto switch just on stats fail to avoid jitter, but maybe safe to do so

      }

    }

  }


  Future<Map<String, dynamic>> _runPython(List<String> args) async {

    // If on web, throw immediately to trigger mock fallback

    if (kIsWeb) throw Exception("Web does not support Process.run");

    

    try {

      final processResult = await Process.run(

        'python',

        [_scriptPath, ...args],

        runInShell: true,

      );


      if (processResult.exitCode != 0) {

        throw Exception("Python Error: ${processResult.stderr}");

      }


      final output = processResult.stdout.toString().trim();

      return jsonDecode(output);

    } catch (e) {

      throw Exception("Process Error: $e");

    }

  }


  void _log(String message) {

    String timestamp = DateTime.now().toIso8601String().substring(11, 19);

    _logs.add("[$timestamp] $message");

    if (_logs.length > 50) _logs.removeAt(0);

    notifyListeners();

  }

}


// -----------------------------------------------------------------------------

// WIDGETS

// -----------------------------------------------------------------------------


class MatrixRainEffect extends StatefulWidget {

  final Widget child;

  const MatrixRainEffect({super.key, required this.child});


  @override

  State<MatrixRainEffect> createState() => _MatrixRainEffectState();

}


class _MatrixRainEffectState extends State<MatrixRainEffect>

    with SingleTickerProviderStateMixin {

  late AnimationController _controller;

  List<double> _drops = [];

  final Random _random = Random();


  @override

  void initState() {

    super.initState();

    _controller = AnimationController(

        vsync: this, duration: const Duration(seconds: 1))

      ..repeat();

  }


  void _initDrops(double width) {

    int cols = (width / 15).floor();

    if (_drops.length != cols) {

      _drops = List.generate(cols, (i) => _random.nextDouble() * -1000);

    }

  }


  @override

  void dispose() {

    _controller.dispose();

    super.dispose();

  }


  @override

  Widget build(BuildContext context) {

    return Stack(

      children: [

        LayoutBuilder(

          builder: (context, constraints) {

            _initDrops(constraints.maxWidth);

            return AnimatedBuilder(

              animation: _controller,

              builder: (context, child) {

                return CustomPaint(

                  painter: _MatrixRainPainter(_drops, _random),

                  size: Size(constraints.maxWidth, constraints.maxHeight),

                );

              },

            );

          },

        ),

        widget.child,

      ],

    );

  }

}


class _MatrixRainPainter extends CustomPainter {

  final List<double> drops;

  final Random random;


  _MatrixRainPainter(this.drops, this.random);


  @override

  void paint(Canvas canvas, Size size) {

    final textPainter = TextPainter(textDirection: TextDirection.ltr);


    for (int i = 0; i < drops.length; i++) {

      drops[i] += random.nextInt(10) + 5;


      if (drops[i] > size.height) {

        drops[i] = random.nextDouble() * -100;

      }


      final char = String.fromCharCode(0x30A0 + random.nextInt(96));

      textPainter.text = TextSpan(

          text: char,

          style: TextStyle(

              color: AppColors.primary.withOpacity(0.3),

              fontSize: 14,

              fontFamily: 'monospace'));

      textPainter.layout();

      textPainter.paint(canvas, Offset(i * 15.0, drops[i]));


      for (int j = 1; j < 5; j++) {

        textPainter.text = TextSpan(

            text: String.fromCharCode(0x30A0 + random.nextInt(96)),

            style: TextStyle(

              color: AppColors.primary.withOpacity(0.3 - (j * 0.05)),

              fontSize: 14,

            ));

        textPainter.layout();

        textPainter.paint(canvas, Offset(i * 15.0, drops[i] - (j * 15)));

      }

    }

  }


  @override

  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;

}


class GlassContainer extends StatelessWidget {

  final Widget child;

  final double opacity;

  final EdgeInsets padding;

  final Color? borderColor;


  const GlassContainer({

    super.key,

    required this.child,

    this.opacity = 0.1,

    this.padding = const EdgeInsets.all(16),

    this.borderColor,

  });


  @override

  Widget build(BuildContext context) {

    return ClipRRect(

      borderRadius: BorderRadius.circular(16),

      child: BackdropFilter(

        filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),

        child: Container(

          padding: padding,

          decoration: BoxDecoration(

              color: AppColors.surface.withOpacity(opacity),

              borderRadius: BorderRadius.circular(16),

              border: Border.all(

                color: borderColor ?? AppColors.primary.withOpacity(0.3),

                width: 1,

              ),

              boxShadow: [

                BoxShadow(

                  color: (borderColor ?? AppColors.primary).withOpacity(0.1),

                  blurRadius: 10,

                  spreadRadius: 1,

                )

              ]),

          child: child,

        ),

      ),

    );

  }

}


class CyberButton extends StatefulWidget {

  final String text;

  final VoidCallback onPressed;

  final Color? color;

  final IconData? icon;


  const CyberButton(

      {super.key,

      required this.text,

      required this.onPressed,

      this.color,

      this.icon});


  @override

  State<CyberButton> createState() => _CyberButtonState();

}


class _CyberButtonState extends State<CyberButton> {

  bool _isHovered = false;


  @override

  Widget build(BuildContext context) {

    final color = widget.color ?? AppColors.primary;

    return MouseRegion(

      onEnter: (_) => setState(() => _isHovered = true),

      onExit: (_) => setState(() => _isHovered = false),

      child: GestureDetector(

        onTap: widget.onPressed,

        child: AnimatedContainer(

          duration: const Duration(milliseconds: 200),

          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),

          decoration: BoxDecoration(

            color: _isHovered ? color.withOpacity(0.2) : Colors.transparent,

            border: Border.all(color: color, width: 2),

            borderRadius: BorderRadius.circular(4),

            boxShadow: _isHovered

                ? [

                    BoxShadow(

                        color: color.withOpacity(0.5),

                        blurRadius: 16,

                        spreadRadius: 2)

                  ]

                : [],

          ),

          child: Row(

            mainAxisSize: MainAxisSize.min,

            children: [

              if (widget.icon != null) ...[

                Icon(widget.icon, color: color, size: 20),

                const SizedBox(width: 8),

              ],

              Text(

                widget.text.toUpperCase(),

                style: TextStyle(

                  color: color,

                  fontWeight: FontWeight.bold,

                  letterSpacing: 2,

                ),

              ),

            ],

          ),

        ),

      ),

    );

  }

}


class TerminalPanel extends StatelessWidget {

  final List<String> logs;


  const TerminalPanel({super.key, required this.logs});


  @override

  Widget build(BuildContext context) {

    return Container(

      width: double.infinity,

      padding: const EdgeInsets.all(12),

      decoration: BoxDecoration(

        color: AppColors.terminal,

        border: Border(

            top: BorderSide(

                color: AppColors.primary.withOpacity(0.5), width: 2)),

      ),

      child: Column(

        crossAxisAlignment: CrossAxisAlignment.start,

        children: [

          const Text("TERMINAL_OUTPUT //",

              style: TextStyle(

                  color: AppColors.primary, fontWeight: FontWeight.bold)),

          const SizedBox(height: 8),

          Expanded(

            child: ListView.builder(

              itemCount: logs.length,

              reverse: true,

              itemBuilder: (context, index) {

                final log = logs[logs.length - 1 - index];

                return Padding(

                  padding: const EdgeInsets.symmetric(vertical: 2),

                  child: Text("> $log",

                      style: const TextStyle(

                          color: Colors.white70,

                          fontFamily: 'Courier',

                          fontSize: 12)),

                );

              },

            ),

          ),

        ],

      ),

    );

  }

}


class ResourceGraph extends StatelessWidget {

  final String label;

  final int value;

  final Color color;

  final List<FlSpot> history;


  const ResourceGraph(

      {super.key,

      required this.label,

      required this.value,

      required this.color,

      required this.history});


  @override

  Widget build(BuildContext context) {

    return Column(

      crossAxisAlignment: CrossAxisAlignment.start,

      children: [

        Row(

          mainAxisAlignment: MainAxisAlignment.spaceBetween,

          children: [

            Text(label,

                style: TextStyle(color: color, fontWeight: FontWeight.bold)),

            Text("$value%",

                style: TextStyle(

                    color: color, fontWeight: FontWeight.bold, fontSize: 18)),

          ],

        ),

        const SizedBox(height: 8),

        SizedBox(

          height: 100,

          child: LineChart(

            LineChartData(

              gridData: FlGridData(show: false),

              titlesData: FlTitlesData(show: false),

              borderData: FlBorderData(show: false),

              lineBarsData: [

                LineChartBarData(

                  spots: history,

                  isCurved: true,

                  color: color,

                  barWidth: 2,

                  isStrokeCapRound: true,

                  dotData: FlDotData(show: false),

                  belowBarData:

                      BarAreaData(show: true, color: color.withOpacity(0.1)),

                ),

              ],

              minX: 0,

              maxX: 20,

              minY: 0,

              maxY: 100,

            ),

          ),

        ),

      ],

    );

  }

}


// -----------------------------------------------------------------------------

// SCREENS

// -----------------------------------------------------------------------------


class DashboardScreen extends StatefulWidget {

  const DashboardScreen({super.key});


  @override

  State<DashboardScreen> createState() => _DashboardScreenState();

}


class _DashboardScreenState extends State<DashboardScreen> {

  // History for graphs

  final List<FlSpot> cpuHistory = [];

  final List<FlSpot> ramHistory = [];

  Timer? _timer;


  @override

  void initState() {

    super.initState();

    // Start fetching

    WidgetsBinding.instance.addPostFrameCallback((_) {

      context.read<VBoxService>().fetchVMs();

    });


    // Update stats loop

    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {

      final service = context.read<VBoxService>();

      service.updateStats();

      setState(() {

        if (cpuHistory.length > 20) {

          cpuHistory.removeAt(0);

          ramHistory.removeAt(0);

        }

        // Shift x values

        for (int i = 0; i < cpuHistory.length; i++) {

          cpuHistory[i] = FlSpot(cpuHistory[i].x - 1, cpuHistory[i].y);

          ramHistory[i] = FlSpot(ramHistory[i].x - 1, ramHistory[i].y);

        }

        cpuHistory.add(FlSpot(20, service.stats.cpu.toDouble()));

        ramHistory.add(FlSpot(20, service.stats.ram.toDouble()));

      });

    });

  }


  @override

  void dispose() {

    _timer?.cancel();

    super.dispose();

  }


  Widget _buildSidebar(BuildContext context) {

    return Column(

      crossAxisAlignment: CrossAxisAlignment.start,

      children: [

        if (MediaQuery.of(context).size.width < 900) ...[

             const SizedBox(height: 50), // Spacing for drawer

        ],

        const Text("MINI_VM_CONTROLLER",

            style: TextStyle(

                fontSize: 20,

                fontWeight: FontWeight.bold,

                color: AppColors.primary)),

        const SizedBox(height: 40),

        const Text("SYSTEM_RESOURCES",

            style: TextStyle(color: Colors.grey, fontSize: 12)),

        const SizedBox(height: 20),

        ResourceGraph(

            label: "CPU_CORE_0",

            value: context.watch<VBoxService>().stats.cpu,

            color: AppColors.primary,

            history: cpuHistory),

        const SizedBox(height: 20),

        ResourceGraph(

            label: "RAM_ALLOCATION",

            value: context.watch<VBoxService>().stats.ram,

            color: AppColors.accent,

            history: ramHistory),

        const Spacer(),

        GlassContainer(

          child: Column(

            crossAxisAlignment: CrossAxisAlignment.start,

            children: const [

              Text("STATUS: ONLINE",

                  style: TextStyle(color: AppColors.primary)),

              SizedBox(height: 5),

              Text("SECURE CONNECTION",

                  style: TextStyle(color: AppColors.primary)),

              SizedBox(height: 5),

              Text("ENCRYPTION: AES-256",

                  style: TextStyle(color: AppColors.primary)),

            ],

          ),

        )

      ],

    );

  }


  Widget _buildMainContent(BuildContext context, bool isMobile) {

    return Column(

      children: [

        // Header

        Container(

          height: 80,

          padding: const EdgeInsets.symmetric(horizontal: 30),

          alignment: Alignment.centerLeft,

          child: Row(

            mainAxisAlignment: MainAxisAlignment.spaceBetween,

            children: [

              Text(

                isMobile ? "DASHBOARD" : "DASHBOARD // VIRTUAL_MACHINES",

                style: const TextStyle(fontSize: 24, letterSpacing: 2),

              ),

              IconButton(

                  onPressed: () => context.read<VBoxService>().fetchVMs(),

                  icon: const Icon(Icons.refresh))

            ],

          ),

        ),


        // VM Grid

        Expanded(

          child: Consumer<VBoxService>(

            builder: (context, service, _) {

              if (service.isLoading && service.vms.isEmpty) {

                return const Center(child: CircularProgressIndicator());

              }

              if (service.vms.isEmpty) {

                return Center(

                    child: Text("NO VMs DETECTED",

                        style: TextStyle(

                            color: AppColors.error.withOpacity(0.7),

                            fontSize: 20)));

              }

              return GridView.builder(

                padding: const EdgeInsets.all(30),

                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(

                  crossAxisCount: isMobile ? 1 : 3,

                  childAspectRatio: isMobile ? 1.5 : 1.1,

                  crossAxisSpacing: 20,

                  mainAxisSpacing: 20,

                ),

                itemCount: service.vms.length,

                itemBuilder: (context, index) {

                  final vm = service.vms[index];

                  return VMCard(vm: vm);

                },

              );

            },

          ),

        ),


        // Terminal

        SizedBox(

          height: 200,

          child: Consumer<VBoxService>(

            builder: (context, service, _) => TerminalPanel(logs: service.logs),

          ),

        ),

      ],

    );

  }


  @override

  Widget build(BuildContext context) {

    return LayoutBuilder(

      builder: (context, constraints) {

        final bool isMobile = constraints.maxWidth < 900;


        return Scaffold(

          extendBodyBehindAppBar: true,

          appBar: isMobile

              ? AppBar(

                  backgroundColor: Colors.transparent,

                  elevation: 0,

                  leading: Builder(

                    builder: (context) => IconButton(

                      icon: const Icon(Icons.menu, color: AppColors.primary),

                      onPressed: () => Scaffold.of(context).openDrawer(),

                    ),

                  ),

                  title: const Text("MINI_VM",

                      style: TextStyle(color: AppColors.primary)),

                  centerTitle: true,

                )

              : null,

          drawer: isMobile

              ? Drawer(

                  backgroundColor: Colors.black.withOpacity(0.9),

                  child: Padding(

                    padding: const EdgeInsets.all(20),

                    child: _buildSidebar(context),

                  ),

                )

              : null,

          body: Stack(

            children: [

              // Background

              const MatrixRainEffect(child: SizedBox.expand()),


              // Gradient Overlay

              Container(

                decoration: BoxDecoration(

                  gradient: LinearGradient(

                    colors: [

                      AppColors.background.withOpacity(0.9),

                      AppColors.background.withOpacity(0.7),

                    ],

                    begin: Alignment.centerLeft,

                    end: Alignment.centerRight,

                  ),

                ),

              ),


              // Responsive Layout

              isMobile

                  ? Column(

                      children: [

                        const SizedBox(

                            height: kToolbarHeight + 20),

                        Expanded(child: _buildMainContent(context, true)),

                      ],

                    )

                  : Row(

                      children: [

                        Container(

                          width: 280,

                          padding: const EdgeInsets.all(20),

                          decoration: BoxDecoration(

                            border: Border(

                                right: BorderSide(

                                    color: AppColors.primary.withOpacity(0.2))),

                            color: Colors.black.withOpacity(0.5),

                          ),

                          child: _buildSidebar(context),

                        ),

                        Expanded(child: _buildMainContent(context, false)),

                      ],

                    ),

            ],

          ),

        );

      },

    );

  }

}


class VMCard extends StatelessWidget {

  final VirtualMachine vm;

  const VMCard({super.key, required this.vm});


  @override

  Widget build(BuildContext context) {

    Color statusColor = AppColors.error;

    if (vm.state.toLowerCase().contains("running"))

      statusColor = AppColors.primary;

    if (vm.state.toLowerCase().contains("saved")) statusColor = Colors.orange;


    return GlassContainer(

      borderColor: statusColor.withOpacity(0.5),

      child: Column(

        mainAxisAlignment: MainAxisAlignment.spaceBetween,

        crossAxisAlignment: CrossAxisAlignment.start,

        children: [

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceBetween,

            children: [

              Icon(Icons.computer, color: statusColor, size: 40),

              Container(

                padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),

                decoration: BoxDecoration(

                    color: statusColor.withOpacity(0.2),

                    borderRadius: BorderRadius.circular(4),

                    border: Border.all(color: statusColor.withOpacity(0.5))),

                child: Text(vm.state.toUpperCase(),

                    style: TextStyle(

                        color: statusColor,

                        fontSize: 10,

                        fontWeight: FontWeight.bold)),

              )

            ],

          ),

          Text(vm.name,

              style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),

              maxLines: 1,

              overflow: TextOverflow.ellipsis),

          Text(vm.id,

              style: TextStyle(fontSize: 10, color: Colors.grey[500]),

              maxLines: 1,

              overflow: TextOverflow.ellipsis),

          const SizedBox(height: 10),

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _ActionButton(

                  icon: Icons.play_arrow,

                  color: AppColors.primary,

                  onPressed: () => context.read<VBoxService>().startVM(vm.id)),

              _ActionButton(

                  icon: Icons.stop,

                  color: AppColors.error,

                  onPressed: () => context.read<VBoxService>().stopVM(vm.id)),

              _ActionButton(

                  icon: Icons.refresh,

                  color: Colors.orange,

                  onPressed: () =>

                      context.read<VBoxService>().restartVM(vm.id)),

              _ActionButton(

                  icon: Icons.settings,

                  color: Colors.cyan,

                  onPressed: () => _showSettingsDialog(context, vm)),

            ],

          )

        ],

      ),

    );

  }


  void _showSettingsDialog(BuildContext context, VirtualMachine vm) {

    showDialog(

        context: context, builder: (context) => _ResourceSettingsDialog(vm: vm));

  }

}


class _ResourceSettingsDialog extends StatefulWidget {

  final VirtualMachine vm;

  const _ResourceSettingsDialog({required this.vm});


  @override

  State<_ResourceSettingsDialog> createState() =>

      _ResourceSettingsDialogState();

}


class _ResourceSettingsDialogState extends State<_ResourceSettingsDialog> {

  int _selectedPreset = 1; // 0: Low, 1: Medium, 2: High


  final List<Map<String, dynamic>> presets = [

    {"name": "LOW", "cpu": 1, "ram": 2048},

    {"name": "MEDIUM", "cpu": 2, "ram": 4096},

    {"name": "HIGH", "cpu": 4, "ram": 8192},

  ];


  @override

  Widget build(BuildContext context) {

    return AlertDialog(

      backgroundColor: AppColors.surface,

      shape: RoundedRectangleBorder(

          side: const BorderSide(color: AppColors.primary),

          borderRadius: BorderRadius.circular(16)),

      title: Text("MODIFY RESOURCES // ${widget.vm.name}",

          style: const TextStyle(color: AppColors.primary)),

      content: Column(

        mainAxisSize: MainAxisSize.min,

        children: [

          const Text("Select Performance Preset:",

              style: TextStyle(color: Colors.white70)),

          const SizedBox(height: 20),

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: List.generate(presets.length, (index) {

              final isSelected = _selectedPreset == index;

              return GestureDetector(

                onTap: () => setState(() => _selectedPreset = index),

                child: Container(

                  padding:

                      const EdgeInsets.symmetric(vertical: 16, horizontal: 24),

                  decoration: BoxDecoration(

                      color: isSelected

                          ? AppColors.primary.withOpacity(0.2)

                          : Colors.transparent,

                      border: Border.all(

                          color: isSelected ? AppColors.primary : Colors.grey),

                      borderRadius: BorderRadius.circular(8)),

                  child: Column(

                    children: [

                      Text(presets[index]["name"],

                          style: TextStyle(

                              color:

                                  isSelected ? AppColors.primary : Colors.grey,

                              fontWeight: FontWeight.bold)),

                      const SizedBox(height: 8),

                      Text("${presets[index]["cpu"]} vCPU",

                          style: const TextStyle(

                              color: Colors.white70, fontSize: 12)),

                      Text("${presets[index]["ram"]} MB",

                          style: const TextStyle(

                              color: Colors.white70, fontSize: 12)),

                    ],

                  ),

                ),

              );

            }),

          ),

          const SizedBox(height: 20),

          const Text("Warning: VM must be stopped to apply changes.",

              style: TextStyle(color: Colors.orange, fontSize: 10)),

        ],

      ),

      actions: [

        TextButton(

            onPressed: () => Navigator.pop(context),

            child: const Text("CANCEL", style: TextStyle(color: Colors.grey))),

        CyberButton(

            text: "APPLY PERMUTATIONS",

            onPressed: () {

              final p = presets[_selectedPreset];

              context

                  .read<VBoxService>()

                  .modifyVM(widget.vm.id, p["cpu"], p["ram"]);

              Navigator.pop(context);

            })

      ],

    );

  }

}


class _ActionButton extends StatelessWidget {

  final IconData icon;

  final Color color;

  final VoidCallback onPressed;


  const _ActionButton(

      {required this.icon, required this.color, required this.onPressed});


  @override

  Widget build(BuildContext context) {

    return IconButton(

      onPressed: onPressed,

      icon: Icon(icon, color: color),

      hoverColor: color.withOpacity(0.2),

      tooltip: "Action",

    );

  }

}


// -----------------------------------------------------------------------------

// MAIN ENTRY POINT

// -----------------------------------------------------------------------------


void main() async {

  WidgetsFlutterBinding.ensureInitialized();

  runApp(

    MultiProvider(

      providers: [

        ChangeNotifierProvider(create: (_) => VBoxService()),

      ],

      child: const MiniVMControllerApp(),

    ),

  );

}


class MiniVMControllerApp extends StatelessWidget {

  const MiniVMControllerApp({super.key});


  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Mini VM Controller',

      debugShowCheckedModeBanner: false,

      theme: AppTheme.darkTheme,

      home: const DashboardScreen(),

    );

  }

}


Comments

Popular posts from this blog

Educational File Encryptor GUI (Python AES Project) | FuzzuTech

Is This News Real or Fake? πŸ€– AI Exposes the Truth | FuzzuTech Python App Demo

🚨 Python Intrusion Detection System (IDS) – Real-Time ML + Tkinter GUI Project | FuzzuTech