termui_flutter 0.3.0 copy "termui_flutter: ^0.3.0" to clipboard
termui_flutter: ^0.3.0 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 'window_manager_interactive_demo.dart';
import 'scenario_a_demo.dart';

enum TuiDemo {
  widgetBook('Widget Book'),
  windowManager('Window Manager'),
  scenarioA('Scenario A Dashboard');

  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();
    } catch (_) {}
  }
  _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.widgetBook;
  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.windowManager) {
          _log('main.dart: Running WindowManagerInteractive');
          await runWindowManagerInteractive(
            _terminal,
            onFrameRedrawn: onDrawFrame,
          );
          _log('main.dart: WindowManagerInteractive returned');
        } else if (_currentDemo == TuiDemo.scenarioA) {
          _log('main.dart: Running ScenarioADemo');
          await runScenarioADemo(_terminal, onFrameRedrawn: onDrawFrame);
          _log('main.dart: ScenarioADemo 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(
          '\x03',
          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.windowManager);
            },
            child: const Text('Window Manager'),
          ),
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              _switchDemo(TuiDemo.scenarioA);
            },
            child: const Text('Scenario A Dashboard'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TermUI ${_currentDemo.label} - Direct Canvas'),
        backgroundColor: Colors.black87,
        actions: [
          IconButton(
            icon: const Icon(Icons.swap_horiz),
            tooltip: 'Switch TUI App',
            onPressed: () => _showSwitchDemoDialog(),
          ),
        ],
      ),
      body: Terminal(
        terminal: _terminal,
        fontSize: 13,
        fontFamily: 'Cascadia Mono',
        onRun: (terminal, drawFrame) async {
          _runTUI(drawFrame);
        },
      ),
    );
  }
}
2
likes
160
points
120
downloads

Documentation

API reference

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

MIT (license)

Dependencies

characters, flutter, termui

More

Packages that depend on termui_flutter