// tools/advanced_ide.dart import 'package:flutter/material.dart'; import 'package:flutter_code_editor/flutter_code_editor.dart'; import 'package:flutter_js/flutter_js.dart'; import 'package:flutter/services.dart'; import 'package:highlight/languages/javascript.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; import 'package:file_picker/file_picker.dart'; import 'dart:io'; import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_highlight/themes/github.dart'; // 确保这个导入存在 import 'package:flutter_highlight/themes/vs.dart'; // 添加更多主题选择 import 'package:flutter_highlight/themes/darcula.dart'; class AdvancedIdePage extends StatefulWidget { @override _AdvancedIdePageState createState() => _AdvancedIdePageState(); } class _AdvancedIdePageState extends State { late CodeController _codeController; JavascriptRuntime? _jsRuntime; String _output = ''; bool _isExecuting = false; bool _isInitialized = false; Map _currentTheme = githubTheme; // 默认主题 @override void initState() { super.initState(); _initializeCodeEditor(); } Future _initializeCodeEditor() async { try { _codeController = CodeController( text: _getExampleCode(), language: javascript, ); setState(() { _isInitialized = true; }); } catch (e) { print('初始化代码编辑器失败: $e'); setState(() { _output = '初始化失败: $e'; _isInitialized = true; }); } } // 切换主题的方法 void _changeTheme(String themeName) { setState(() { switch (themeName) { case 'github': _currentTheme = githubTheme; break; case 'vs': _currentTheme = vsTheme; break; case 'darcula': _currentTheme = darculaTheme; break; default: _currentTheme = githubTheme; } }); } // 在 build 方法中,修改代码编辑器部分: @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('JavaScript IDE', style: TextStyle(color: Colors.black)), backgroundColor: Colors.white, elevation: 1, actions: [ // 主题切换按钮 PopupMenuButton( icon: Icon(Icons.palette, color: Colors.purple, size: 20), itemBuilder: (context) => [ PopupMenuItem( value: 'github', child: Row( children: [ Icon(Icons.brightness_5, color: Colors.blue), SizedBox(width: 8), Text('GitHub 主题'), ], ), ), PopupMenuItem( value: 'vs', child: Row( children: [ Icon(Icons.brightness_5, color: Colors.grey), SizedBox(width: 8), Text('VS 主题'), ], ), ), PopupMenuItem( value: 'darcula', child: Row( children: [ Icon(Icons.brightness_2, color: Colors.green), SizedBox(width: 8), Text('Darcula 主题'), ], ), ), ], onSelected: _changeTheme, tooltip: '切换主题', ), IconButton( icon: Icon(Icons.save_as, color: Colors.blue, size: 20), onPressed: _saveAs, tooltip: '另存为', ), IconButton( icon: Icon(Icons.folder_open, color: Colors.blue, size: 20), onPressed: _loadFromFile, tooltip: '从文件加载', ), IconButton( icon: Icon(Icons.play_arrow, color: Colors.green, size: 20), onPressed: _isInitialized ? _executeCode : null, tooltip: '运行代码(全新环境)', ), IconButton( icon: Icon(Icons.clear, color: Colors.red, size: 20), onPressed: _clearOutput, tooltip: '清空输出', ), IconButton( icon: Icon(Icons.refresh, color: Colors.blue, size: 20), onPressed: _resetCode, tooltip: '重置代码', ), ], ), body: !_isInitialized ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('正在初始化代码编辑器...'), ], ), ) : Column( children: [ Expanded( flex: 3, child: Container( margin: EdgeInsets.all(8), decoration: BoxDecoration( border: Border.all(color: Colors.blue[300]!), borderRadius: BorderRadius.circular(8), ), child: Column( children: [ Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.blue[50], borderRadius: BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), ), ), child: Row( children: [ Icon(Icons.code, size: 16, color: Colors.blue[700]!), SizedBox(width: 8), Text( 'JavaScript 代码编辑器', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.blue[700]!, ), ), Spacer(), // 显示当前主题 Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.blue[300]!), ), child: Text( _getCurrentThemeName(), style: TextStyle(fontSize: 12, color: Colors.blue[700]!), ), ), SizedBox(width: 8), IconButton( icon: Icon(Icons.info_outline, size: 16), onPressed: () { showDialog( context: context, builder: (context) => AlertDialog( title: Text('使用说明'), content: Text( '• 使用本功能需要了解js基本语法\n' '• math.js的版本是12.4.3\n' '• 每次运行代码都会创建全新的JavaScript环境\n' '• 之前的变量和状态不会保留\n' '• math.js库会在每次运行时重新加载\n' '• 执行速度可能稍慢(因为要重新初始化)\n' '• 使用console.log()输出结果\n' '• 点击调色板图标可以切换语法高亮主题' ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('确定'), ), ], ), ); }, tooltip: '使用说明', ), ], ), ), Expanded( child: CodeTheme( // 添加 CodeTheme 包装 data: CodeThemeData(styles: _currentTheme), child: SingleChildScrollView( child: CodeField( controller: _codeController, textStyle: TextStyle(fontFamily: 'Monospace', fontSize: 14), gutterStyle: GutterStyle( showLineNumbers: true, showErrors: true, showFoldingHandles: true, ), ), ), ), ), ], ), ), ), SizedBox(height: 8), // 输出区域 // 输出区域 Expanded( flex: 2, child: Container( margin: EdgeInsets.all(8), decoration: BoxDecoration( border: Border.all(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(8), ), child: Column( children: [ Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), ), ), child: Row( children: [ Icon(Icons.output, size: 16, color: Colors.grey[600]!), SizedBox(width: 8), Text( '执行输出', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey[600]!, ), ), Spacer(), if (_isExecuting) Row( children: [ SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ), SizedBox(width: 8), Text( '执行中...', style: TextStyle( fontSize: 12, color: Colors.orange, ), ), ], ), ], ), ), Expanded( child: SingleChildScrollView( padding: EdgeInsets.all(16), child: SelectableText( // 修改这里:Text -> SelectableText _output, style: TextStyle( fontFamily: 'Monospace', fontSize: 12, ), ), ), ), ], ), ), ), ], ), ); } // 获取当前主题名称 String _getCurrentThemeName() { if (_currentTheme == githubTheme) return 'GitHub'; if (_currentTheme == vsTheme) return 'VS'; if (_currentTheme == darculaTheme) return 'Darcula'; return '默认'; } // 其他方法保持不变... Future _initializeJsRuntime() async { try { if (_jsRuntime != null) { _jsRuntime!.dispose(); _jsRuntime = null; } _jsRuntime = getJavascriptRuntime(); // 设置非浏览器环境的console重写 _setupConsoleOverride(); // 加载math.js库 final String mathJsCode = await rootBundle.loadString('assets/javascripts/math.js'); final JsEvalResult mathResult = _jsRuntime!.evaluate(mathJsCode); if (mathResult.isError) { print('加载math.js失败: ${mathResult.stringResult}'); throw Exception('加载math.js失败: ${mathResult.stringResult}'); } else { print('math.js加载成功'); } } catch (e) { print('初始化JavaScript运行时失败: $e'); if (_jsRuntime != null) { _jsRuntime!.dispose(); _jsRuntime = null; } throw e; } } void _setupConsoleOverride() { if (_jsRuntime == null) return; const String consoleOverride = ''' // 创建全局输出数组 var __consoleOutput = []; // 保存原始console方法(如果存在) var originalConsole = { log: typeof console !== 'undefined' ? console.log : function() {}, warn: typeof console !== 'undefined' ? console.warn : function() {}, error: typeof console !== 'undefined' ? console.error : function() {}, info: typeof console !== 'undefined' ? console.info : function() {} }; // 重写console方法 if (typeof console !== 'undefined') { console.log = function(...args) { originalConsole.log.apply(console, args); const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) ).join(' '); __consoleOutput.push('[LOG] ' + message); }; console.warn = function(...args) { originalConsole.warn.apply(console, args); const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) ).join(' '); __consoleOutput.push('[WARN] ' + message); }; console.error = function(...args) { originalConsole.error.apply(console, args); const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) ).join(' '); __consoleOutput.push('[ERROR] ' + message); }; console.info = function(...args) { originalConsole.info.apply(console, args); const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) ).join(' '); __consoleOutput.push('[INFO] ' + message); }; } else { // 如果console不存在,创建它 var console = { log: function(...args) { const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) ).join(' '); __consoleOutput.push('[LOG] ' + message); }, warn: function(...args) { const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) ).join(' '); __consoleOutput.push('[WARN] ' + message); }, error: function(...args) { const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) ).join(' '); __consoleOutput.push('[ERROR] ' + message); }, info: function(...args) { const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) ).join(' '); __consoleOutput.push('[INFO] ' + message); } }; } // 清空输出数组的方法 var __clearConsoleOutput = function() { __consoleOutput = []; }; // 获取输出内容的方法(修复换行问题) var __getConsoleOutput = function() { return __consoleOutput.join('\\n'); // 关键修复:使用单个换行符 }; '''; _jsRuntime!.evaluate(consoleOverride); } @override void dispose() { _codeController.dispose(); if (_jsRuntime != null) { _jsRuntime!.dispose(); _jsRuntime = null; } super.dispose(); } String _getExampleCode() { return '''// JavaScript IDE // math.js 库已自动加载,可以使用所有数学函数 // 创建共享的作用域对象 const scope = {}; // 1. 基本算术运算 console.log('1. 基本算术运算:'); console.log('2 + 3 =', math.evaluate('2 + 3')); console.log('10 - 4 =', math.evaluate('10 - 4')); console.log('6 * 7 =', math.evaluate('6 * 7')); console.log('15 / 3 =', math.evaluate('15 / 3')); console.log('2^8 =', math.evaluate('2^8')); console.log('10 % 3 =', math.evaluate('10 % 3')); console.log(''); // 2. 复杂表达式和运算符优先级 console.log('2. 复杂表达式和运算符优先级:'); console.log('2 + 3 * 4 =', math.evaluate('2 + 3 * 4')); console.log('(2 + 3) * 4 =', math.evaluate('(2 + 3) * 4')); console.log('3 + 4 * 2 / (1 - 5)^2 =', math.evaluate('3 + 4 * 2 / (1 - 5)^2')); console.log(''); // 3. 数学常数 console.log('3. 数学常数:'); console.log('pi =', math.evaluate('pi')); console.log('e =', math.evaluate('e')); console.log('Infinity =', math.evaluate('Infinity')); console.log(''); // 4. 内置函数 console.log('4. 内置函数:'); console.log('sqrt(25) =', math.evaluate('sqrt(25)')); console.log('sin(pi/2) =', math.evaluate('sin(pi/2)')); console.log('cos(pi) =', math.evaluate('cos(pi)')); console.log('tan(0) =', math.evaluate('tan(0)')); console.log('log(100) =', math.evaluate('log(100)')); console.log('log(100, 10) =', math.evaluate('log(100, 10)')); console.log('exp(2) =', math.evaluate('exp(2)')); console.log('abs(-5.5) =', math.evaluate('abs(-5.5)')); console.log('round(3.7) =', math.evaluate('round(3.7)')); console.log('floor(4.9) =', math.evaluate('floor(4.9)')); console.log('ceil(4.1) =', math.evaluate('ceil(4.1)')); console.log('max(1, 5, 3) =', math.evaluate('max(1, 5, 3)')); console.log('min(1, 5, 3) =', math.evaluate('min(1, 5, 3)')); console.log(''); // 5. 变量和赋值 console.log('5. 变量和赋值:'); console.log('x = 5:', math.evaluate('x = 5', scope)); console.log('y = x + 3:', math.evaluate('y = x + 3', scope)); console.log('x * y =', math.evaluate('x * y', scope)); console.log(''); // 6. 矩阵和数组运算 console.log('6. 矩阵和数组运算:'); console.log('[1, 2, 3] =', math.evaluate('[1, 2, 3]')); console.log('[[1,2],[3,4]] =', math.evaluate('[[1,2],[3,4]]')); console.log('size([1,2,3,4]) =', math.evaluate('size([1,2,3,4])')); console.log('[1,2,3] + [4,5,6] =', math.evaluate('[1,2,3] + [4,5,6]')); console.log('2 * [1,2,3] =', math.evaluate('2 * [1,2,3]')); console.log(''); // 7. 复数运算 console.log('7. 复数运算:'); console.log('2 + 3i =', math.evaluate('2 + 3i')); console.log('re(2+3i) =', math.evaluate('re(2+3i)')); console.log('im(2+3i) =', math.evaluate('im(2+3i)')); console.log(''); // 8. 单位换算 console.log('8. 单位换算:'); console.log('5 inch to cm =', math.evaluate('5 inch to cm')); console.log('100 km/hour to m/s =', math.evaluate('100 km/hour to m/s')); console.log(''); // 9. 布尔运算和比较 console.log('9. 布尔运算和比较:'); console.log('2 > 3 =', math.evaluate('2 > 3')); console.log('5 == 5 =', math.evaluate('5 == 5')); console.log('3 != 4 =', math.evaluate('3 != 4')); console.log('true and false =', math.evaluate('true and false')); console.log('true or false =', math.evaluate('true or false')); console.log('not true =', math.evaluate('not true')); console.log(''); // 10. 条件表达式 console.log('10. 条件表达式:'); console.log('2 > 3 ? "yes" : "no" =', math.evaluate('2 > 3 ? "yes" : "no"')); console.log('x > 0 ? "positive" : "non-positive" =', math.evaluate('x > 0 ? "positive" : "non-positive"', scope)); console.log(''); // 11. 字符串操作 console.log('11. 字符串操作:'); console.log('concat("hello", " ", "world") =', math.evaluate('concat("hello", " ", "world")')); console.log(''); // 12. 自定义函数 console.log('12. 自定义函数:'); math.evaluate('f(x) = x^2 + 2*x + 1', scope); console.log('f(3) =', math.evaluate('f(3)', scope)); console.log('f(5) =', math.evaluate('f(5)', scope)); console.log(''); // 13. 统计函数 console.log('13. 统计函数:'); console.log('mean([1, 2, 3, 4, 5]) =', math.evaluate('mean([1, 2, 3, 4, 5])')); console.log('median([1, 2, 3, 4, 5]) =', math.evaluate('median([1, 2, 3, 4, 5])')); console.log('std([1, 2, 3, 4, 5]) =', math.evaluate('std([1, 2, 3, 4, 5])')); console.log('sum([1, 2, 3, 4, 5]) =', math.evaluate('sum([1, 2, 3, 4, 5])')); console.log(''); // 14. 大数运算 console.log('14. 大数运算:'); console.log('123456789012345678901234567890 + 1 =', math.evaluate('123456789012345678901234567890 + 1')); console.log('2^100 =', math.evaluate('2^100')); console.log(''); // 15. 链式操作示例 console.log('15. 链式操作示例:'); try { const result = math.chain(5) .add(3) .multiply(2) .divide(4) .done(); console.log('(5 + 3) * 2 / 4 =', result); } catch (error) { console.log('链式操作出错:', error.message); } console.log(''); // 16. 多表达式执行 console.log('16. 多表达式执行:'); const multiResult = math.evaluate(` a = 10; b = 20; c = a + b; c * 2 `); console.log('多表达式结果:', multiResult); console.log(''); // 17. 数值格式和精度 console.log('17. 数值格式和精度:'); console.log('format(1/3, 14) =', math.evaluate('format(1/3, 14)')); console.log(''); // 18. 角度和弧度转换 console.log('18. 角度和弧度转换:'); console.log('sin(90 deg) =', math.evaluate('sin(90 deg)')); console.log('cos(180 deg) =', math.evaluate('cos(180 deg)')); console.log(''); // 19. 随机数生成 console.log('19. 随机数生成:'); console.log('random() =', math.evaluate('random()')); console.log('random(10) =', math.evaluate('random(10)')); console.log('randomInt(10) =', math.evaluate('randomInt(10)')); console.log('random(5, 10) =', math.evaluate('random(5, 10)')); console.log(''); // 20. 位运算 console.log('20. 位运算:'); console.log('5 & 3 =', math.evaluate('5 & 3')); console.log('5 | 3 =', math.evaluate('5 | 3')); console.log(''); // 21. 阶乘和组合 console.log('21. 阶乘和组合:'); console.log('5! =', math.evaluate('5!')); console.log('combinations(5, 2) =', math.evaluate('combinations(5, 2)')); console.log(''); // 22. 进制转换 console.log('22. 进制转换:'); console.log('0b1010 =', math.evaluate('0b1010')); console.log('0o777 =', math.evaluate('0o777')); console.log('0xFF =', math.evaluate('0xFF')); console.log(''); // 23. 关系运算 console.log('23. 关系运算:'); console.log('2 < 3 =', math.evaluate('2 < 3')); console.log('2 <= 2 =', math.evaluate('2 <= 2')); console.log('3 >= 4 =', math.evaluate('3 >= 4')); console.log(''); // 24. 分数运算 console.log('24. 分数运算:'); console.log("4/8 + 1 -1/3 = ", math.simplify('4/8 + 1 -1/3').toString()); '''; } Future _saveAs() async { try { final String codeText = _codeController.text; final Uint8List codeBytes = Uint8List.fromList(utf8.encode(codeText)); String? outputPath = await FilePicker.platform.saveFile( dialogTitle: '另存为JavaScript文件', fileName: 'javascript_code.js', allowedExtensions: ['js'], bytes: codeBytes, ); if (outputPath != null) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('文件已保存'), backgroundColor: Colors.green, ), ); } } } catch (e) { print('保存文件错误: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('保存文件失败: $e'), backgroundColor: Colors.red, ), ); } } } Future _loadFromFile() async { try { FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['js'], dialogTitle: '选择JavaScript文件', ); if (result != null && result.files.isNotEmpty) { final PlatformFile platformFile = result.files.single; if (platformFile.path == null && platformFile.bytes == null) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('无法获取文件内容'), backgroundColor: Colors.red, ), ); } return; } // 读取文件内容 String contents; if (platformFile.bytes != null) { // 直接从字节数据读取 contents = utf8.decode(platformFile.bytes!); } else { // 从文件路径读取 final file = File(platformFile.path!); contents = await file.readAsString(); } setState(() { _codeController.text = contents; _output = '文件已加载: ${platformFile.name}'; }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('文件加载成功: ${platformFile.name}'), backgroundColor: Colors.green, ), ); } } } catch (e) { print('加载文件错误: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('加载文件失败: $e'), backgroundColor: Colors.red, ), ); } } } Future _executeCode() async { if (_isExecuting || !_isInitialized) return; setState(() { _isExecuting = true; _output = '正在初始化JavaScript环境...\\n'; }); try { await _initializeJsRuntime(); final String code = _codeController.text; setState(() { _output += '执行代码中...\\n'; }); _jsRuntime!.evaluate('__clearConsoleOutput();'); final JsEvalResult result = _jsRuntime!.evaluate(code); final JsEvalResult consoleOutputResult = _jsRuntime!.evaluate('__getConsoleOutput();'); final String consoleOutput = consoleOutputResult.stringResult; setState(() { _output = '执行完成!\n'; if (result.isError) { _output += '❌ 执行错误: ${result.stringResult}\n'; } else { _output += '✅ 执行成功!\n'; if (result.stringResult != 'null' && result.stringResult.isNotEmpty) { _output += '📊 返回值: ${result.stringResult}\n'; } else { _output += '\n'; } } if (consoleOutput.isNotEmpty) { _output += '--- 控制台输出 ---\n$consoleOutput'; } else { _output += 'ℹ️ 没有控制台输出'; } }); } catch (e) { setState(() { _output = '执行出错: $e'; }); } finally { setState(() { _isExecuting = false; }); } } void _clearOutput() { setState(() { _output = ''; }); } void _resetCode() { setState(() { _codeController.text = _getExampleCode(); _output = ''; }); } void _injectMathJs() async { try { await _initializeJsRuntime(); setState(() { _output = 'math.js重新加载成功! JavaScript环境已重置。'; }); } catch (e) { setState(() { _output = '重新加载math.js失败: $e'; }); } } }