ssh.dart 2.8 KB

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