Mobile Hacker IDE – Phone Style Coding Tool (Flutter Project)
Demo :
Click Video πππ
Features :
• Mobile phone UI
• Hacker neon theme
• Run preview
• Terminal panel
• File tree
• Theme engine
Code :
main.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'dart:math';
import 'dart:async';
// ==========================================
// MAIN ENTRY POINT
// ==========================================
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ThemeEngine()),
],
child: const HackerIDEApp(),
),
);
}
class HackerIDEApp extends StatelessWidget {
const HackerIDEApp({super.key});
@override
Widget build(BuildContext context) {
var themeEngine = Provider.of<ThemeEngine>(context);
return MaterialApp(
title: 'Mobile Hacker IDE',
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.dark,
scaffoldBackgroundColor: themeEngine.theme.background,
primaryColor: themeEngine.theme.accent,
textTheme: GoogleFonts.firaCodeTextTheme(Theme.of(context).textTheme).apply(
bodyColor: themeEngine.theme.text,
displayColor: themeEngine.theme.text,
),
),
home: const MainScreen(),
);
}
}
// ==========================================
// THEME ENGINE
// ==========================================
class ThemeModel {
final String name;
final Color background;
final Color surface;
final Color accent;
final Color text;
final Color terminalBg;
final Color border;
ThemeModel({
required this.name,
required this.background,
required this.surface,
required this.accent,
required this.text,
required this.terminalBg,
required this.border,
});
factory ThemeModel.neon() => ThemeModel(
name: "Neon Cyber",
background: const Color(0xFF050510),
surface: const Color(0xFF101020),
accent: const Color(0xFF00FF9D),
text: const Color(0xFFE0E0E0),
terminalBg: const Color(0xFF000000),
border: const Color(0xFF00FF9D),
);
factory ThemeModel.matrix() => ThemeModel(
name: "The Matrix",
background: const Color(0xFF000800),
surface: const Color(0xFF001100),
accent: const Color(0xFF00FF00),
text: const Color(0xFFCCFFCC),
terminalBg: const Color(0xFF000500),
border: const Color(0xFF008800),
);
factory ThemeModel.redHacker() => ThemeModel(
name: "Red Ops",
background: const Color(0xFF0F0000),
surface: const Color(0xFF1F0000),
accent: const Color(0xFFFF0000),
text: const Color(0xFFFFCCCC),
terminalBg: const Color(0xFF050000),
border: const Color(0xFF880000),
);
}
class ThemeEngine extends ChangeNotifier {
ThemeModel _currentTheme = ThemeModel.neon();
ThemeModel get theme => _currentTheme;
void cycle() {
if (_currentTheme.name == "Neon Cyber") {
_currentTheme = ThemeModel.matrix();
} else if (_currentTheme.name == "The Matrix") {
_currentTheme = ThemeModel.redHacker();
} else {
_currentTheme = ThemeModel.neon();
}
notifyListeners();
}
}
// ==========================================
// CODE EDITOR WIDGET
// ==========================================
class CodeEditor extends StatelessWidget {
final TextEditingController controller;
const CodeEditor({super.key, required this.controller});
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeEngine>(context).theme;
return Container(
color: theme.surface,
padding: const EdgeInsets.all(10),
child: TextField(
controller: controller,
maxLines: null,
expands: true,
style: GoogleFonts.firaCode(
color: theme.text,
fontSize: 14,
height: 1.4,
),
decoration: InputDecoration(
border: InputBorder.none,
hintText: "// Start hacking...",
hintStyle: TextStyle(color: theme.text.withOpacity(0.3)),
),
cursorColor: theme.accent,
autocorrect: false,
enableSuggestions: false,
),
);
}
}
class HackerCodeController extends TextEditingController {
final Map<String, TextStyle> patternMap;
HackerCodeController({required this.patternMap, String? text}) : super(text: text);
@override
TextSpan buildTextSpan({required BuildContext context, TextStyle? style, required bool withComposing}) {
List<TextSpan> children = [];
String source = text;
// Syntax Highlighting Regex
source.splitMapJoin(
RegExp(r'\b(import|class|def|return|if|else|while|for|in|print|true|false|await|async|var|const|let|function|void|int|double|String)\b|(".*?"|' + "'.*?')|(#.*)|(//.*)"),
onMatch: (Match m) {
String word = m[0]!;
TextStyle? spanStyle;
if (word.startsWith(RegExp(r'["' + "'" + ']'))) {
spanStyle = patternMap['string'];
} else if (word.startsWith("#") || word.startsWith("//")) {
spanStyle = patternMap['comment'];
} else {
spanStyle = patternMap['keyword'];
}
children.add(TextSpan(text: word, style: spanStyle?.merge(style)));
return word;
},
onNonMatch: (String span) {
children.add(TextSpan(text: span, style: style));
return span;
},
);
return TextSpan(style: style, children: children);
}
}
// ==========================================
// TERMINAL WIDGET
// ==========================================
class TerminalWithIO extends StatelessWidget {
final List<String> logs;
final ScrollController scrollController;
const TerminalWithIO({super.key, required this.logs, required this.scrollController});
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeEngine>(context).theme;
return Container(
width: double.infinity,
color: theme.terminalBg,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
color: theme.surface,
child: Row(
children: [
Icon(Icons.terminal, size: 14, color: theme.accent),
const SizedBox(width: 5),
Text("TERMINAL_OUTPUT", style: GoogleFonts.shareTechMono(color: theme.accent, fontSize: 12)),
],
),
),
Expanded(
child: ListView.builder(
controller: scrollController,
padding: const EdgeInsets.all(8),
itemCount: logs.length,
itemBuilder: (context, index) {
return SelectableText(
logs[index],
style: GoogleFonts.firaCode(
color: index == logs.length - 1 ? theme.accent : theme.text.withOpacity(0.8),
fontSize: 12,
),
);
},
),
),
],
),
);
}
}
// ==========================================
// FILE TREE WIDGET
// ==========================================
class FileTree extends StatelessWidget {
final Function(String fileName, String content) onFileSelected;
const FileTree({super.key, required this.onFileSelected});
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeEngine>(context).theme;
final files = {
"PROJECT_ZERO": [
{"name": "main.py", "type": "py", "content": "import os\n\ndef hack_nsa():\n print('Accessing Mainframe...')\n for i in range(5):\n print(f'Decrypting sector {i}...')\n\nhack_nsa()"},
{"name": "brute_force.py", "type": "py", "content": "# Brute Force Module\npassword = 'admin'\nwhile True:\n print('Trying password...')"},
{"name": "index.html", "type": "html", "content": "<html>\n<body>\n <h1>HACKED</h1>\n</body>\n</html>"},
{"name": "payload.js", "type": "js", "content": "console.log('Injecting SQL...');"},
{"name": "config.json", "type": "json", "content": "{\n \"target\": \"192.168.1.100\"\n}"},
],
"ASSETS": [
{"name": "exploit.bin", "type": "bin", "content": "[BINARY DATA CACHED]"},
]
};
return Container(
color: theme.surface,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(10),
child: Text("EXPLORER", style: GoogleFonts.orbitron(color: theme.accent, fontWeight: FontWeight.bold)),
),
Divider(color: theme.border, height: 1),
Expanded(
child: ListView(
children: files.entries.map((entry) {
return ExpansionTile(
title: Text(entry.key, style: GoogleFonts.roboto(color: theme.text, fontWeight: FontWeight.bold)),
leading: Icon(Icons.folder, color: theme.accent),
iconColor: theme.accent,
collapsedIconColor: theme.text,
initiallyExpanded: true,
children: (entry.value).map((file) {
return ListTile(
leading: Icon(_getIcon(file['type']!), color: theme.text.withOpacity(0.7), size: 18),
title: Text(file['name']!, style: GoogleFonts.firaCode(color: theme.text, fontSize: 13)),
dense: true,
visualDensity: VisualDensity.compact,
onTap: () => onFileSelected(file['name']!, file['content']!),
hoverColor: theme.accent.withOpacity(0.1),
);
}).toList(),
);
}).toList(),
),
)
],
),
);
}
IconData _getIcon(String type) {
switch (type) {
case 'py': return Icons.code;
case 'html': return Icons.html;
case 'js': return Icons.javascript;
case 'json': return Icons.data_object;
case 'bin': return Icons.android;
default: return Icons.insert_drive_file;
}
}
}
// ==========================================
// CODE RUNNER LOGIC
// ==========================================
class CodeRunner {
final Function(String) onLog;
final VoidCallback onClear;
CodeRunner({required this.onLog, required this.onClear});
bool _isRunning = false;
void run(String code, String filename) async {
if (_isRunning) return;
_isRunning = true;
onClear();
onLog("[System] Initializing Sandbox Environment...");
await Future.delayed(const Duration(milliseconds: 500));
if (filename.endsWith(".py")) {
await _simulatePython(code);
} else if (filename.endsWith(".js")) {
await _simulateJS(code);
} else {
onLog("[System] No runner configured for this file type.");
}
onLog("\n[System] Process finished with exit code 0");
_isRunning = false;
}
Future<void> _simulatePython(String code) async {
onLog("[Python] Intepreter v3.11.2 loaded.");
await Future.delayed(const Duration(milliseconds: 400));
final lines = code.split("\n");
for (var line in lines) {
line = line.trim();
if (line.startsWith("print(") && line.endsWith(")")) {
String content = line.substring(6, line.length - 1).replaceAll("'", "").replaceAll('"',"");
if (content.contains("f'") || content.contains('f"')) content = content.substring(2);
onLog(content);
await Future.delayed(const Duration(milliseconds: 100));
}
if (line.contains("range") || line.contains("while")) {
onLog("[Loop] Executing iterative logic...");
for(int i=0; i<3; i++) {
onLog(" . step $i");
await Future.delayed(const Duration(milliseconds: 200));
}
}
}
if (code.contains("hack")) {
onLog("[SECURITY] Analyzing exploit chain...");
await Future.delayed(const Duration(milliseconds: 500));
onLog("ACCESS GRANTED.");
}
}
Future<void> _simulateJS(String code) async {
onLog("[NodeJS] v18.0.0 Runtime.");
if (code.contains("console.log")) {
onLog("Output: " + code.split("console.log(")[1].split(")")[0].replaceAll("'", "").replaceAll('"',""));
}
}
}
// ==========================================
// MATRIX RAIN BACKGROUND
// ==========================================
class MatrixRainWidget extends StatefulWidget {
final Color color;
const MatrixRainWidget({super.key, required this.color});
@override
_MatrixRainWidgetState createState() => _MatrixRainWidgetState();
}
class _MatrixRainWidgetState extends State<MatrixRainWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
List<double> drops = [];
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 1))..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (drops.isEmpty || drops.length != (constraints.maxWidth / 20).floor()) {
drops = List.generate((constraints.maxWidth / 20).floor(), (index) => Random().nextDouble() * -100);
}
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
painter: MatrixPainter(drops: drops, color: widget.color),
);
},
);
},
);
}
}
class MatrixPainter extends CustomPainter {
final List<double> drops;
final Color color;
final Random random = Random();
MatrixPainter({required this.drops, required this.color});
@override
void paint(Canvas canvas, Size size) {
final textStyle = TextStyle(color: color, fontSize: 14, fontFamily: 'monospace');
final textPainter = TextPainter(textDirection: TextDirection.ltr);
for (int i = 0; i < drops.length; i++) {
String char = String.fromCharCode(0x30A0 + random.nextInt(96));
textPainter.text = TextSpan(text: char, style: textStyle);
textPainter.layout();
double x = i * 20.0;
double y = drops[i] * 20.0;
textPainter.paint(canvas, Offset(x, y));
if (y > size.height && random.nextDouble() > 0.975) {
drops[i] = -2;
} else {
drops[i] += 0.5;
}
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
// ==========================================
// MAIN SCREEN UI
// ==========================================
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
bool _showFiles = false;
late CodeRunner _runner;
final TextEditingController _codeController = HackerCodeController(patternMap: {
'keyword': const TextStyle(color: Colors.pinkAccent),
'string': const TextStyle(color: Colors.yellowAccent),
'comment': const TextStyle(color: Colors.blueGrey),
}, text: "# Welcome to Hacker IDE v2.0\nimport os\n\ndef init():\n print('System Ready')");
final ScrollController _terminalScroll = ScrollController();
final List<String> _logs = ["[System] Booting kernel...", "[System] UI Loaded."];
String currentFile = "main.py";
@override
void initState() {
super.initState();
_runner = CodeRunner(
onLog: (text) {
setState(() {
_logs.add(text);
if (_terminalScroll.hasClients) {
_terminalScroll.animateTo(
_terminalScroll.position.maxScrollExtent + 50,
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut
);
}
});
},
onClear: () {
setState(() => _logs.clear());
}
);
}
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeEngine>(context).theme;
final size = MediaQuery.of(context).size;
final bool isMobile = size.width < 600;
return Scaffold(
body: Stack(
children: [
Positioned.fill(
child: RepaintBoundary(child: MatrixRainWidget(color: theme.accent.withOpacity(0.15))),
),
Center(
child: Container(
width: isMobile ? size.width : 420,
height: isMobile ? size.height : 750,
decoration: BoxDecoration(
color: theme.background.withOpacity(0.95),
borderRadius: BorderRadius.circular(isMobile ? 0 : 30),
border: Border.all(color: theme.border, width: 2),
boxShadow: [
BoxShadow(color: theme.accent.withOpacity(0.3), blurRadius: 20, spreadRadius: 2)
]
),
clipBehavior: Clip.hardEdge,
child: Column(
children: [
_buildStatusBar(theme),
_buildToolbar(theme, context),
Expanded(
child: Stack(
children: [
Column(
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
color: theme.surface,
child: Text(currentFile, style: TextStyle(color: theme.text, fontSize: 12)),
),
Expanded(flex: 2, child: CodeEditor(controller: _codeController)),
Expanded(flex: 1, child: TerminalWithIO(logs: _logs, scrollController: _terminalScroll)),
],
),
if (_showFiles)
Positioned(
left: 0, top: 0, bottom: 0, width: 250,
child: Container(
decoration: BoxDecoration(
color: theme.surface,
border: Border(right: BorderSide(color: theme.border))
),
child: FileTree(onFileSelected: (name, content) {
setState(() {
currentFile = name;
_codeController.text = content;
_showFiles = false;
});
}),
),
)
],
),
)
],
),
),
)
],
),
);
}
Widget _buildStatusBar(ThemeModel theme) {
return Container(
height: 30,
padding: const EdgeInsets.symmetric(horizontal: 15),
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("5G WARNING_NET", style: TextStyle(color: theme.accent, fontSize: 10, fontWeight: FontWeight.bold)),
Text("13:37", style: TextStyle(color: theme.text, fontWeight: FontWeight.bold)),
Icon(Icons.battery_charging_full, color: theme.accent, size: 14),
],
),
);
}
Widget _buildToolbar(ThemeModel theme, BuildContext context) {
return Container(
height: 50,
color: theme.surface,
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Row(
children: [
IconButton(
icon: Icon(Icons.menu, color: theme.accent),
onPressed: () => setState(() => _showFiles = !_showFiles),
),
Spacer(),
IconButton(
icon: Icon(Icons.style, color: theme.text),
tooltip: "Cycle Theme",
onPressed: () => Provider.of<ThemeEngine>(context, listen: false).cycle(),
),
const SizedBox(width: 10),
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: theme.accent,
foregroundColor: Colors.black,
),
icon: const Icon(Icons.play_arrow, size: 16),
label: const Text("EXECUTE"),
onPressed: () => _runner.run(_codeController.text, currentFile),
)
],
),
);
}
}
editor.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'theme_engine.dart';
import 'package:provider/provider.dart';
class CodeEditor extends StatelessWidget {
final TextEditingController controller;
const CodeEditor({Key? key, required this.controller}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeEngine>(context).theme;
return Container(
color: theme.surface,
padding: EdgeInsets.all(10),
child: TextField(
controller: controller,
maxLines: null,
expands: true,
style: GoogleFonts.firaCode(
color: theme.text,
fontSize: 14,
height: 1.4,
),
decoration: InputDecoration(
border: InputBorder.none,
hintText: "// Start hacking...",
hintStyle: TextStyle(color: theme.text.withOpacity(0.3)),
),
cursorColor: theme.accent,
autocorrect: false,
enableSuggestions: false,
),
);
}
}
class HackerCodeController extends TextEditingController {
final Map<String, TextStyle> patternMap;
HackerCodeController({required this.patternMap, String? text}) : super(text: text);
@override
TextSpan buildTextSpan({required BuildContext context, TextStyle? style, required bool withComposing}) {
List<TextSpan> children = [];
String source = text;
// Simple regex for keywords and strings
source.splitMapJoin(
RegExp(r'\b(import|class|def|return|if|else|while|for|in|print|true|false,await|async|var|const|let|function|void|int|double|String)\b|(".*?"|' + "'.*?')|(#.*)|(//.*)"),
onMatch: (Match m) {
String word = m[0]!;
TextStyle? spanStyle;
if (word.startsWith('"') || word.startsWith("'")) {
spanStyle = patternMap['string'];
} else if (word.startsWith("#") || word.startsWith("//")) {
spanStyle = patternMap['comment'];
} else {
spanStyle = patternMap['keyword'];
}
children.add(TextSpan(text: word, style: spanStyle?.merge(style)));
return word;
},
onNonMatch: (String span) {
children.add(TextSpan(text: span, style: style));
return span;
},
);
return TextSpan(style: style, children: children);
}
}
file_tree.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'theme_engine.dart';
class FileTree extends StatelessWidget {
final Function(String fileName, String content) onFileSelected;
const FileTree({super.key, required this.onFileSelected});
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeEngine>(context).theme;
final files = {
"PROJECT_ZERO": [
{"name": "main.py", "type": "py", "content": "import os\n\ndef hack_nsa():\n print('Accessing Mainframe...')\n for i in range(5):\n print(f'Decrypting sector {i}...')\n\nhack_nsa()"},
{"name": "brute_force.py", "type": "py", "content": "# Brute Force Module\npassword = 'admin'\nwhile True:\n print('Trying password...')"},
{"name": "index.html", "type": "html", "content": "<html>\n<body>\n <h1>HACKED</h1>\n</body>\n</html>"},
{"name": "payload.js", "type": "js", "content": "console.log('Injecting SQL...');"},
{"name": "config.json", "type": "json", "content": "{\n \"target\": \"192.168.1.100\"\n}"},
],
"ASSETS": [
{"name": "exploit.bin", "type": "bin", "content": "[BINARY DATA CACHED]"},
]
};
return Container(
color: theme.surface,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(10),
child: Text("EXPLORER", style: GoogleFonts.orbitron(color: theme.accent, fontWeight: FontWeight.bold)),
),
Divider(color: theme.border, height: 1),
Expanded(
child: ListView(
children: files.entries.map((entry) {
return ExpansionTile(
title: Text(entry.key, style: GoogleFonts.roboto(color: theme.text, fontWeight: FontWeight.bold)),
leading: Icon(Icons.folder, color: theme.accent),
iconColor: theme.accent,
collapsedIconColor: theme.text,
initiallyExpanded: true,
children: (entry.value).map((file) {
return ListTile(
leading: Icon(_getIcon(file['type']!), color: theme.text.withOpacity(0.7), size: 18),
title: Text(file['name']!, style: GoogleFonts.firaCode(color: theme.text, fontSize: 13)),
dense: true,
visualDensity: VisualDensity.compact,
onTap: () => onFileSelected(file['name']!, file['content']!),
hoverColor: theme.accent.withOpacity(0.1),
);
}).toList(),
);
}).toList(),
),
)
],
),
);
}
IconData _getIcon(String type) {
switch (type) {
case 'py': return Icons.code;
case 'html': return Icons.html;
case 'js': return Icons.javascript;
case 'json': return Icons.data_object;
case 'bin': return Icons.android;
default: return Icons.insert_drive_file;
}
}
}
runner.dart
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
class CodeRunner {
final Function(String) onLog;
final VoidCallback onClear; // FIXED: VoidCallback
CodeRunner({required this.onLog, required this.onClear});
bool _isRunning = false;
void run(String code, String filename) async {
if (_isRunning) return;
_isRunning = true;
onClear();
onLog("[System] Initializing Sandbox Environment...");
await Future.delayed(Duration(milliseconds: 500));
if (filename.endsWith(".py")) {
await _simulatePython(code);
} else if (filename.endsWith(".js")) {
await _simulateJS(code);
} else {
onLog("[System] No runner configured for this file type.");
}
onLog("\n[System] Process finished with exit code 0");
_isRunning = false;
}
Future<void> _simulatePython(String code) async {
onLog("[Python] Intepreter v3.11.2 loaded.");
await Future.delayed(Duration(milliseconds: 400));
final lines = code.split("\n");
for (var line in lines) {
line = line.trim();
if (line.startsWith("print(") && line.endsWith(")")) {
String content = line.substring(6, line.length - 1).replaceAll("'", "").replaceAll('"',"");
if (content.contains("f'") || content.contains('f"')) content = content.substring(2);
onLog(content);
await Future.delayed(Duration(milliseconds: 100));
}
if (line.contains("range") || line.contains("while")) {
onLog("[Loop] Executing iterative logic...");
for(int i=0; i<3; i++) {
onLog(" . step $i");
await Future.delayed(Duration(milliseconds: 200));
}
}
}
// Hack check
if (code.contains("hack")) {
onLog("[SECURITY] Analyzing exploit chain...");
await Future.delayed(Duration(milliseconds: 500));
onLog("ACCESS GRANTED.");
}
}
Future<void> _simulateJS(String code) async {
onLog("[NodeJS] v18.0.0 Runtime.");
if (code.contains("console.log")) {
onLog("Output: " + code.split("console.log(")[1].split(")")[0].replaceAll("'", "").replaceAll('"',""));
}
}
}
terminal.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'theme_engine.dart';
class TerminalWithIO extends StatelessWidget {
final List<String> logs;
final ScrollController scrollController;
const TerminalWithIO({super.key, required this.logs, required this.scrollController});
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeEngine>(context).theme;
return Container(
width: double.infinity,
color: theme.terminalBg,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
color: theme.surface,
child: Row(
children: [
Icon(Icons.terminal, size: 14, color: theme.accent),
SizedBox(width: 5),
Text("TERMINAL_OUTPUT", style: GoogleFonts.shareTechMono(color: theme.accent, fontSize: 12)),
],
),
),
Expanded(
child: ListView.builder(
controller: scrollController,
padding: EdgeInsets.all(8),
itemCount: logs.length,
itemBuilder: (context, index) {
return SelectableText(
logs[index],
style: GoogleFonts.firaCode(
color: index == logs.length - 1 ? theme.accent : theme.text.withOpacity(0.8),
fontSize: 12,
),
);
},
),
),
],
),
);
}
}
theme_engine.dart
import 'package:flutter/material.dart';
class ThemeModel {
final String name;
final Color background;
final Color surface;
final Color accent;
final Color text;
final Color terminalBg;
final Color border;
ThemeModel({
required this.name,
required this.background,
required this.surface,
required this.accent,
required this.text,
required this.terminalBg,
required this.border,
});
factory ThemeModel.neon() {
return ThemeModel(
name: "Neon Cyber",
background: Color(0xFF050510),
surface: Color(0xFF101020),
accent: Color(0xFF00FF9D),
text: Color(0xFFE0E0E0),
terminalBg: Color(0xFF000000),
border: Color(0xFF00FF9D),
);
}
factory ThemeModel.matrix() {
return ThemeModel(
name: "The Matrix",
background: Color(0xFF000800),
surface: Color(0xFF001100),
accent: Color(0xFF00FF00),
text: Color(0xFFCCFFCC),
terminalBg: Color(0xFF000500),
border: Color(0xFF008800),
);
}
factory ThemeModel.redHacker() {
return ThemeModel(
name: "Red Ops",
background: Color(0xFF0F0000),
surface: Color(0xFF1F0000),
accent: Color(0xFFFF0000),
text: Color(0xFFFFCCCC),
terminalBg: Color(0xFF050000),
border: Color(0xFF880000),
);
}
}
class ThemeEngine extends ChangeNotifier {
ThemeModel _currentTheme = ThemeModel.neon();
ThemeModel get theme => _currentTheme;
void cycle() {
if (_currentTheme.name == "Neon Cyber") _currentTheme = ThemeModel.matrix();
else if (_currentTheme.name == "The Matrix") _currentTheme = ThemeModel.redHacker();
else _currentTheme = ThemeModel.neon();
notifyListeners();
}
}
Comments
Post a Comment