isolate.dart 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:xterm/flutter.dart';
  4. import 'package:xterm/isolate.dart';
  5. import 'package:xterm/theme/terminal_theme.dart';
  6. import 'package:xterm/theme/terminal_themes.dart';
  7. import 'package:xterm/xterm.dart';
  8. void main() {
  9. runApp(MyApp());
  10. }
  11. class MyApp extends StatelessWidget {
  12. @override
  13. Widget build(BuildContext context) {
  14. return MaterialApp(
  15. title: 'xterm.dart demo',
  16. theme: ThemeData(
  17. primarySwatch: Colors.blue,
  18. visualDensity: VisualDensity.adaptivePlatformDensity,
  19. ),
  20. home: MyHomePage(
  21. theme: TerminalThemes.defaultTheme,
  22. terminalOpacity: 0.8,
  23. ),
  24. );
  25. }
  26. }
  27. class MyHomePage extends StatefulWidget {
  28. MyHomePage({
  29. Key? key,
  30. required this.theme,
  31. required this.terminalOpacity,
  32. }) : super(key: key);
  33. final TerminalTheme theme;
  34. final double terminalOpacity;
  35. @override
  36. _MyHomePageState createState() => _MyHomePageState();
  37. }
  38. class FakeTerminalBackend extends TerminalBackend {
  39. // we do a late initialization of those backend members as the backend gets
  40. // transferred into the Isolate.
  41. // It is not allowed to e.g. transfer closures which we can not guarantee
  42. // to not exist in our member types.
  43. // The Isolate will call init() once it starts (from its context) and that is
  44. // the place where we initialize those members
  45. late final _exitCodeCompleter;
  46. // ignore: close_sinks
  47. late final _outStream;
  48. @override
  49. bool get isReady => true;
  50. @override
  51. Future<int> get exitCode => _exitCodeCompleter.future;
  52. @override
  53. void init() {
  54. _exitCodeCompleter = Completer<int>();
  55. _outStream = StreamController<String>();
  56. _outStream.sink.add('xterm.dart demo');
  57. _outStream.sink.add('\r\n');
  58. _outStream.sink.add('\$ ');
  59. }
  60. @override
  61. Stream<String> get out => _outStream.stream;
  62. @override
  63. void resize(int width, int height, int pixelWidth, int pixelHeight) {
  64. // NOOP
  65. }
  66. @override
  67. void write(String input) {
  68. if (input.length <= 0) {
  69. return;
  70. }
  71. // in a "real" terminal emulation you would connect onInput to the backend
  72. // (like a pty or ssh connection) that then handles the changes in the
  73. // terminal.
  74. // As we don't have a connected backend here we simulate the changes by
  75. // directly writing to the terminal.
  76. if (input == '\r') {
  77. _outStream.sink.add('\r\n');
  78. _outStream.sink.add('\$ ');
  79. } else if (input.codeUnitAt(0) == 127) {
  80. // Backspace handling
  81. _outStream.sink.add('\b \b');
  82. } else {
  83. _outStream.sink.add(input);
  84. }
  85. }
  86. @override
  87. void terminate() {
  88. //NOOP
  89. }
  90. @override
  91. void ackProcessed() {
  92. //NOOP
  93. }
  94. }
  95. class _MyHomePageState extends State<MyHomePage> {
  96. TerminalIsolate? terminal;
  97. Future<TerminalIsolate> _ensureTerminalStarted() async {
  98. if (terminal == null) {
  99. terminal = TerminalIsolate(
  100. backend: FakeTerminalBackend(),
  101. maxLines: 10000,
  102. theme: widget.theme,
  103. );
  104. }
  105. if (!terminal!.isReady) {
  106. await terminal!.start();
  107. }
  108. return terminal!;
  109. }
  110. void onInput(String input) {}
  111. @override
  112. Widget build(BuildContext context) {
  113. return Scaffold(
  114. body: FutureBuilder(
  115. future: _ensureTerminalStarted(),
  116. builder: (context, snapshot) {
  117. return SafeArea(
  118. child: snapshot.hasData
  119. ? TerminalView(terminal: snapshot.data as TerminalIsolate)
  120. : Container(
  121. constraints: const BoxConstraints.expand(),
  122. color: Color(widget.theme.background)
  123. .withOpacity(widget.terminalOpacity),
  124. ),
  125. );
  126. },
  127. ),
  128. );
  129. }
  130. }