kterm 1.0.0
kterm: ^1.0.0 copied to clipboard
kterm is a fast and fully-featured terminal emulator for Flutter applications, with support for mobile and desktop platforms.
example/lib/main.dart
import 'dart:convert';
import 'dart:io';
import 'package:example/src/platform_menu.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_pty/flutter_pty.dart';
import 'package:kterm/xterm.dart';
void main() {
runApp(MyApp());
}
bool get isDesktop {
if (kIsWeb) return false;
return [
TargetPlatform.windows,
TargetPlatform.linux,
TargetPlatform.macOS,
].contains(defaultTargetPlatform);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'kterm demo',
debugShowCheckedModeBanner: false,
home: AppPlatformMenu(child: Home()),
// shortcuts: ,
);
}
}
class Home extends StatefulWidget {
Home({super.key});
@override
// ignore: library_private_types_in_public_api
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final terminal = Terminal(
maxLines: 10000,
);
final terminalController = TerminalController();
bool _kittyModeEnabled = false;
late final Pty pty;
@override
void initState() {
super.initState();
WidgetsBinding.instance.endOfFrame.then(
(_) {
if (mounted) _startPty();
},
);
}
void _toggleKittyMode() {
setState(() {
_kittyModeEnabled = !_kittyModeEnabled;
terminal.setKittyMode(_kittyModeEnabled);
});
}
/// Send a small red PNG image using Kitty Graphics Protocol
void _sendTestImage() {
// Valid 16x16 red PNG (base64 encoded)
final pngData = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGUlEQVR4nGP4z8DwnxLMMGrAqAGjBgwXAwAwxP4QHCfkAAAAAABJRU5ErkJggg==';
// s=4,v=4 means 4x4 cells, image will be stretched to fill
final imageData = '\x1b_Ga=t,f=100,s=4,v=4,i=1,S=C,q=1;$pngData\x1b\\';
terminal.write(imageData);
}
void _startPty() {
pty = Pty.start(
shell,
columns: terminal.viewWidth,
rows: terminal.viewHeight,
);
pty.output
.cast<List<int>>()
.transform(Utf8Decoder())
.listen(terminal.write);
pty.exitCode.then((code) {
terminal.write('the process exited with exit code $code');
});
terminal.onOutput = (data) {
pty.write(const Utf8Encoder().convert(data));
};
terminal.onResize = (w, h, pw, ph) {
pty.resize(h, w);
};
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
child: Stack(
children: [
TerminalView(
terminal,
controller: terminalController,
autofocus: true,
backgroundOpacity: 0.7,
onSecondaryTapDown: (details, offset) async {
final selection = terminalController.selection;
if (selection != null) {
final text = terminal.buffer.getText(selection);
terminalController.clearSelection();
await Clipboard.setData(ClipboardData(text: text));
} else {
final data = await Clipboard.getData('text/plain');
final text = data?.text;
if (text != null) {
terminal.paste(text);
}
}
},
),
// Kitty Mode indicator
Positioned(
top: 8,
right: 8,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _kittyModeEnabled
? Colors.green.withOpacity(0.8)
: Colors.grey.withOpacity(0.8),
borderRadius: BorderRadius.circular(4),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.keyboard,
size: 14,
color: Colors.white,
),
const SizedBox(width: 4),
Text(
'Kitty: ${_kittyModeEnabled ? "ON" : "OFF"}',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
),
floatingActionButton: Row(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton.small(
heroTag: 'kitty',
onPressed: _toggleKittyMode,
backgroundColor: _kittyModeEnabled ? Colors.green : Colors.grey,
child: const Icon(Icons.toggle_on),
),
const SizedBox(width: 8),
FloatingActionButton.small(
heroTag: 'image',
onPressed: _sendTestImage,
backgroundColor: Colors.red,
child: const Icon(Icons.image),
),
],
),
);
}
}
String get shell {
if (Platform.isMacOS || Platform.isLinux) {
return Platform.environment['SHELL'] ?? 'bash';
}
if (Platform.isWindows) {
return 'cmd.exe';
}
return 'sh';
}