main.dart 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:flutter_acrylic/flutter_acrylic.dart';
  7. import 'package:flutter_pty/flutter_pty.dart';
  8. import 'package:xterm/xterm.dart';
  9. void main() {
  10. WidgetsFlutterBinding.ensureInitialized();
  11. if (isDesktop) {
  12. setupAcrylic();
  13. }
  14. runApp(MyApp());
  15. }
  16. bool get isDesktop {
  17. if (kIsWeb) return false;
  18. return [
  19. TargetPlatform.windows,
  20. TargetPlatform.linux,
  21. TargetPlatform.macOS,
  22. ].contains(defaultTargetPlatform);
  23. }
  24. Future<void> setupAcrylic() async {
  25. await Window.initialize();
  26. await Window.makeTitlebarTransparent();
  27. await Window.setEffect(effect: WindowEffect.aero, color: Color(0xFFFFFFFF));
  28. await Window.setBlurViewState(MacOSBlurViewState.active);
  29. }
  30. class MyApp extends StatelessWidget {
  31. @override
  32. Widget build(BuildContext context) {
  33. return MaterialApp(
  34. title: 'xterm.dart demo',
  35. debugShowCheckedModeBanner: false,
  36. home: Home(),
  37. // shortcuts: ,
  38. );
  39. }
  40. }
  41. class Home extends StatefulWidget {
  42. Home({Key? key}) : super(key: key);
  43. @override
  44. // ignore: library_private_types_in_public_api
  45. _HomeState createState() => _HomeState();
  46. }
  47. class _HomeState extends State<Home> {
  48. final terminal = Terminal(
  49. maxLines: 10000,
  50. );
  51. final terminalController = TerminalController();
  52. late final Pty pty;
  53. @override
  54. void initState() {
  55. super.initState();
  56. WidgetsBinding.instance.endOfFrame.then(
  57. (_) {
  58. if (mounted) _startPty();
  59. },
  60. );
  61. }
  62. void _startPty() {
  63. pty = Pty.start(
  64. shell,
  65. columns: terminal.viewWidth,
  66. rows: terminal.viewHeight,
  67. );
  68. pty.output
  69. .cast<List<int>>()
  70. .transform(Utf8Decoder())
  71. .listen(terminal.write);
  72. pty.exitCode.then((code) {
  73. terminal.write('the process exited with exit code $code');
  74. });
  75. terminal.onOutput = (data) {
  76. pty.write(const Utf8Encoder().convert(data));
  77. };
  78. terminal.onResize = (w, h, pw, ph) {
  79. pty.resize(h, w);
  80. };
  81. }
  82. @override
  83. Widget build(BuildContext context) {
  84. return Scaffold(
  85. backgroundColor: Colors.transparent,
  86. body: SafeArea(
  87. child: TerminalView(
  88. terminal,
  89. controller: terminalController,
  90. autofocus: true,
  91. backgroundOpacity: 0.7,
  92. onSecondaryTapDown: (details, offset) async {
  93. final selection = terminalController.selection;
  94. if (selection != null) {
  95. final text = terminal.buffer.getText(selection);
  96. terminalController.clearSelection();
  97. await Clipboard.setData(ClipboardData(text: text));
  98. } else {
  99. final data = await Clipboard.getData('text/plain');
  100. final text = data?.text;
  101. if (text != null) {
  102. terminal.paste(text);
  103. }
  104. }
  105. },
  106. ),
  107. ),
  108. );
  109. }
  110. }
  111. String get shell {
  112. if (Platform.isMacOS || Platform.isLinux) {
  113. return Platform.environment['SHELL'] ?? 'bash';
  114. }
  115. if (Platform.isWindows) {
  116. return 'cmd.exe';
  117. }
  118. return 'sh';
  119. }