termui_flutter 0.6.3 copy "termui_flutter: ^0.6.3" to clipboard
termui_flutter: ^0.6.3 copied to clipboard

Flutter integration package for termui, containing the reusable Terminal widget and GPU-accelerated canvas renderer.

example/lib/main.dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:termui/terminal/terminal.dart' as termui;
import 'package:termui/ui/buffer.dart';
import 'package:termui_flutter/termui_flutter.dart';
import 'widget_book.dart';

import 'package:termui_shared_examples/glass_compositing/glass_compositing.dart';
import 'package:termui_shared_examples/glass_compositing/pty_glass_runner.dart';

enum TuiDemo {
  widgetBook('Widget Book'),
  glassCompositing('Glass Compositing'),
  ptyGlassCompositing('PTY Glass Compositing');

  final String label;
  const TuiDemo(this.label);
}

void _log(String message) {
  if (kIsWeb) {
    // ignore: avoid_print
    print('[TUI] $message');
    return;
  }
  try {
    final file = File('tui_diagnostics.log');
    file.writeAsStringSync(
      '[${DateTime.now().toIso8601String()}] $message\n',
      mode: FileMode.append,
    );
  } catch (_) {}
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  if (!kIsWeb) {
    try {
      final file = File('tui_diagnostics.log');
      if (file.existsSync()) {
        file.deleteSync();
      }
    } catch (_) {}
  } else {
    try {
      final fontLoader = FontLoader('Cascadia Mono');
      fontLoader.addFont(rootBundle.load('fonts/CascadiaMonoPL.ttf'));
      await fontLoader.load();
      final fontLoader2 = FontLoader('MesloLGS NF');
      fontLoader2.addFont(rootBundle.load('fonts/MesloLGS_NF_Regular.ttf'));
      await fontLoader2.load();

      final brailleLoader = FontLoader('Noto Sans Braille');
      brailleLoader.addFont(
        rootBundle.load('fonts/NotoSansBraille-Regular.ttf'),
      );
      await brailleLoader.load();
    } catch (e) {
      debugPrint('FONT LOADER ERROR: $e');
    }
  }
  _log('main.dart: main() started');
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TermUI Direct TuiView Demo',
      theme: ThemeData.dark(),
      home: const TermUIWebHome(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class TermUIWebHome extends StatefulWidget {
  const TermUIWebHome({super.key});

  @override
  State<TermUIWebHome> createState() => _TermUIWebHomeState();
}

class _TermUIWebHomeState extends State<TermUIWebHome> {
  late final FlutterTerminal _terminal;

  TuiDemo _currentDemo = TuiDemo.glassCompositing;
  bool _switching = false;
  bool _tuiRunning = false;
  void Function(Buffer)? _onDrawFrame;

  @override
  void initState() {
    super.initState();
    _log('main.dart: initState() started');
    _terminal = FlutterTerminal();
  }

  @override
  void dispose() {
    _terminal.dispose();
    super.dispose();
  }

  void _runTUI(void Function(Buffer) onDrawFrame) async {
    _log('main.dart: _runTUI() started, _tuiRunning: $_tuiRunning');
    _onDrawFrame = onDrawFrame;
    if (_tuiRunning) return;
    _tuiRunning = true;

    try {
      while (mounted) {
        _log('main.dart: Loop iteration, demo: $_currentDemo');
        if (_currentDemo == TuiDemo.widgetBook) {
          _log('main.dart: Running WidgetBook');
          await runWidgetBook(_terminal, onFrameRedrawn: onDrawFrame);
          _log('main.dart: WidgetBook returned');
        } else if (_currentDemo == TuiDemo.glassCompositing) {
          _log('main.dart: Running Glass Compositing');
          await runGlassCompositingShared(
            _terminal,
            isInline: false,
            onFrameRedrawn: onDrawFrame,
          );
          _log('main.dart: Glass Compositing returned');
        } else if (_currentDemo == TuiDemo.ptyGlassCompositing) {
          _log('main.dart: Running PTY Glass Compositing');
          await runPtyGlassDemo(_terminal, onFrameRedrawn: onDrawFrame);
          _log('main.dart: PTY Glass Compositing returned');
        }

        if (!mounted) {
          _log('main.dart: Not mounted, exiting loop');
          break;
        }
        if (!_switching) {
          _log('main.dart: Exited loop without switching, showing dialog');
          _showSwitchDemoDialog(title: 'TUI Application Exited');
          break;
        } else {
          _log('main.dart: Exited loop due to switching');
          _switching = false;
        }
      }
    } catch (e, stack) {
      _log('main.dart: ERROR in _runTUI loop: $e\n$stack');
      rethrow;
    } finally {
      _tuiRunning = false;
      _log('main.dart: _runTUI finished');
    }
  }

  void _switchDemo(TuiDemo target) {
    _log('main.dart: Switch demo requested to: ${target.label}');
    if (_currentDemo == target && _tuiRunning) return;

    _currentDemo = target;
    setState(() {});

    if (_tuiRunning) {
      _switching = true;
      // Inject Ctrl+C to terminate current loop
      _terminal.injectEvent(
        const termui.KeyEvent(
          'c',
          termui.KeyType.character,
          modifiers: {termui.Modifier.control},
        ),
      );
    } else if (_onDrawFrame != null) {
      _runTUI(_onDrawFrame!);
    }
  }

  void _showSwitchDemoDialog({String title = 'Switch TUI Application'}) {
    showDialog(
      context: context,
      barrierDismissible: _tuiRunning,
      builder: (context) => AlertDialog(
        title: Text(title),
        content: const Text(
          'Select which TUI application to run in the terminal emulator:',
        ),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              _switchDemo(TuiDemo.widgetBook);
            },
            child: const Text('Widget Book'),
          ),
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              _switchDemo(TuiDemo.glassCompositing);
            },
            child: const Text('Glass Compositing'),
          ),
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              _switchDemo(TuiDemo.ptyGlassCompositing);
            },
            child: const Text('PTY Glass Compositing'),
          ),
        ],
      ),
    );
  }

  double _baseFontSize = 13.0;
  double _currentFontSize = 13.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Positioned.fill(
            child: GestureDetector(
              onScaleStart: (details) {
                _baseFontSize = _currentFontSize;
              },
              onScaleUpdate: (details) {
                setState(() {
                  _currentFontSize = (_baseFontSize * details.scale).clamp(
                    4.0,
                    72.0,
                  );
                  _terminal.setFontSize(_currentFontSize);
                });
              },
              child: Terminal(
                terminal: _terminal,
                fontSize: _currentFontSize,
                fontFamily: 'MesloLGS NF',
                fontFamilyFallback: const [
                  'Noto Color Emoji',
                  'Noto Sans Braille',
                ],
                onRun: (terminal, drawFrame) async {
                  _runTUI(drawFrame);
                },
              ),
            ),
          ),
          Positioned(
            top: 16.0,
            right: 16.0,
            child: Material(
              color: Colors.transparent,
              child: Container(
                decoration: BoxDecoration(
                  color: Colors.black.withAlpha(150),
                  borderRadius: BorderRadius.circular(8.0),
                ),
                child: IconButton(
                  icon: const Icon(Icons.swap_horiz, color: Colors.white70),
                  tooltip: 'Switch TUI App',
                  onPressed: () => _showSwitchDemoDialog(),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
3
likes
0
points
443
downloads

Publisher

verified publishermcdole.org

Weekly Downloads

Flutter integration package for termui, containing the reusable Terminal widget and GPU-accelerated canvas renderer.

Repository (GitHub)
View/report issues

Topics

#cli #tui #terminal #console

License

unknown (license)

Dependencies

characters, clock, flutter, termui, web

More

Packages that depend on termui_flutter