ssh.dart 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:typed_data';
  4. import 'package:dartssh2/dartssh2.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:xterm/flutter.dart';
  7. import 'package:xterm/xterm.dart';
  8. const host = 'ssh://localhost:22';
  9. const username = '<your username>';
  10. const password = '<your password>';
  11. void main() {
  12. runApp(MyApp());
  13. }
  14. class MyApp extends StatelessWidget {
  15. @override
  16. Widget build(BuildContext context) {
  17. return MaterialApp(
  18. title: 'xterm.dart demo',
  19. theme: ThemeData(
  20. primarySwatch: Colors.blue,
  21. visualDensity: VisualDensity.adaptivePlatformDensity,
  22. ),
  23. home: MyHomePage(),
  24. );
  25. }
  26. }
  27. class MyHomePage extends StatefulWidget {
  28. MyHomePage({Key? key}) : super(key: key);
  29. @override
  30. _MyHomePageState createState() => _MyHomePageState();
  31. }
  32. class SSHTerminalBackend extends TerminalBackend {
  33. late SSHClient client;
  34. String _host;
  35. String _username;
  36. String _password;
  37. final _exitCodeCompleter = Completer<int>();
  38. final _outStream = StreamController<String>();
  39. SSHTerminalBackend(this._host, this._username, this._password);
  40. void onWrite(String data) {
  41. _outStream.sink.add(data);
  42. }
  43. @override
  44. bool get isReady => true;
  45. @override
  46. Future<int> get exitCode => _exitCodeCompleter.future;
  47. @override
  48. void init() {
  49. // Use utf8.decoder to handle broken utf8 chunks
  50. final _sshOutput = StreamController<List<int>>();
  51. _sshOutput.stream.transform(utf8.decoder).listen(onWrite);
  52. onWrite('connecting $_host...');
  53. client = SSHClient(
  54. hostport: Uri.parse(_host),
  55. username: _username,
  56. print: print,
  57. termWidth: 80,
  58. termHeight: 25,
  59. termvar: 'xterm-256color',
  60. onPasswordRequest: () => _password,
  61. response: (data) {
  62. _sshOutput.add(data);
  63. },
  64. success: () {
  65. onWrite('connected.\n');
  66. },
  67. disconnected: () {
  68. onWrite('disconnected.');
  69. _outStream.close();
  70. },
  71. );
  72. }
  73. @override
  74. Stream<String> get out => _outStream.stream;
  75. @override
  76. void resize(int width, int height, int pixelWidth, int pixelHeight) {
  77. client.setTerminalWindowSize(width, height);
  78. }
  79. @override
  80. void write(String input) {
  81. client.sendChannelData(Uint8List.fromList(utf8.encode(input)));
  82. }
  83. @override
  84. void terminate() {
  85. client.disconnect('terminate');
  86. }
  87. @override
  88. void ackProcessed() {
  89. // NOOP
  90. }
  91. }
  92. class _MyHomePageState extends State<MyHomePage> {
  93. late Terminal terminal;
  94. late SSHTerminalBackend backend;
  95. @override
  96. void initState() {
  97. super.initState();
  98. backend = SSHTerminalBackend(host, username, password);
  99. terminal = Terminal(backend: backend, maxLines: 10000);
  100. }
  101. @override
  102. Widget build(BuildContext context) {
  103. return Scaffold(
  104. body: SafeArea(
  105. child: TerminalView(
  106. terminal: terminal,
  107. ),
  108. ),
  109. );
  110. }
  111. }