Flutter终端模拟插件terminal_library的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter终端模拟插件terminal_library的使用

演示

文档

  1. 文档
  2. YouTube
  3. Telegram支持群组
  4. 联系开发者(检查社交媒体或README文件中的GitHub个人资料)

功能

  1. ✅ 📱️ 跨平台 支持(设备、边缘服务器无函数)
  2. ✅ 📜️ 标准化 样式代码
  3. ✅ ⌨️ CLI(终端帮助你使用此库或创建项目)
  4. ✅ 🔥️ API(如果你开发机器人/用户机器人,可以使用此库而无需交互CLI,只需添加库并使用🚀️)
  5. ❌ 🧩️ 可定制扩展(如果你想添加扩展,可以加快开发速度)
  6. ❌ ✨️ 漂亮信息(对新手友好)

有趣的小知识

此库在所有平台(CLI、服务器、Web、GUI)上运行,忽略flutter标志,仅限pub.dev

安装库

  1. Dart
    dart pub add terminal_library
    
  2. Flutter
    flutter pub add terminal_library
    

使用此项目/示例项目的应用

  1. 通用工作室开发者应用
  2. 通用机器人应用/通用自动化应用
  3. 通用应用程序

快速开始

以下是一个快速入门脚本的最小示例,旨在为你提供灵感,或者使你更容易使用此库,因为它非常简单。

// ignore_for_file: empty_catches, non_constant_identifier_names
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:general_lib/general_lib.dart';
import 'package:general_lib_flutter/extension/build_context.dart';
import 'package:general_lib_flutter/widget/widget.dart';
import 'package:terminal_library/pty_library/pty_library.dart';
import 'package:terminal_library/xterm_library/xterm.dart';

void main(List<String> args) {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const App());
}

class App extends StatelessWidget {
  static GeneralLibFlutterApp generalLibFlutterApp = GeneralLibFlutterApp();
  const App({super.key});
  [@override](/user/override)
  Widget build(BuildContext context) {
    return GeneralLibFlutterAppMain(
      generalLibFlutterApp: generalLibFlutterApp,
      builder: (themeMode, lightTheme, darkTheme, widget) {
        Widget child = MaterialApp(
          theme: lightTheme,
          darkTheme: darkTheme,
          themeMode: themeMode,
          debugShowCheckedModeBanner: false,
          home: const MyApp(),
        );
        return child;
      },
    );
  }
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  TerminalLibraryFlutterController terminalLibraryFlutterController = TerminalLibraryFlutterController();
  final TerminalLibraryFlutter terminalLibraryFlutter = TerminalLibraryFlutter(
    maxLines: 1000,
  );
  late final PtyLibrary ptyLibrary;

  [@override](/user/override)
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      await refresh();
    });
  }

  [@override](/user/override)
  void dispose() {
    ptyLibrary.kill();
    terminalLibraryFlutterController.dispose();
    super.dispose();
  }

  bool is_loading = false;

  Future<void> refresh() async {
    if (is_loading) {
      return;
    }
    setState(() {
      is_loading = true;
    });

    await Future(() async {
      await Future.delayed(Duration(milliseconds: 500));

      ptyLibrary = PtyLibrary.start(
        shell,
        columns: terminalLibraryFlutter.viewWidth,
        rows: terminalLibraryFlutter.viewHeight,
      );

      ptyLibrary.output.listen((event) {
        if (event.isNotEmpty) {
          try {
            terminalLibraryFlutter.write(utf8.decode(event, allowMalformed: true));
          } catch (e) {}
        }
      });

      terminalLibraryFlutter.onOutput = (String value) {
        if (value.isNotEmpty) {
          try {
            ptyLibrary.write(utf8.encode(value));
          } catch (e) {}
        }
      };

      terminalLibraryFlutter.onResize = (w, h, pw, ph) {
        ptyLibrary.resize(h, w);
      };

      terminalLibraryFlutter.buffer.clear();
      terminalLibraryFlutter.buffer.setCursor(0, 0);
      terminalLibraryFlutter.textInput("clear");
      terminalLibraryFlutter.keyInput(TerminalLibraryFlutterKey.enter);

      setState(() {});
    });

    setState(() {
      is_loading = false;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    if (is_loading) {
      return Scaffold(
        body: Center(
          child: CircularProgressIndicator(
            color: context.theme.indicatorColor,
          ),
        ),
      );
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text("终端库:"),
      ),
      body: TerminalLibraryFlutterViewWidget(
        terminalLibraryFlutter,
        controller: terminalLibraryFlutterController,
        autofocus: true,
        backgroundOpacity: 0,
        simulateScroll: true,
        padding: const EdgeInsets.all(5),
        alwaysShowCursor: true,
        deleteDetection: Dart.isMobile,
      ),
    );
  }

  static String get shell {
    if (Platform.isMacOS || Platform.isLinux) {
      return Platform.environment['SHELL'] ?? 'bash';
    }
    if (Platform.isWindows) {
      return 'cmd.exe';
    }
    return 'sh';
  }
}

示例代码

// 忽略文件: empty_catches, non_constant_identifier_names
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:general_lib/general_lib.dart';
import 'package:general_lib_flutter/extension/build_context.dart';
import 'package:general_lib_flutter/widget/widget.dart';
import 'package:terminal_library/pty_library/pty_library.dart';
import 'package:terminal_library/xterm_library/xterm.dart';

void main(List<String> args) {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const App());
}

class App extends StatelessWidget {
  static GeneralLibFlutterApp generalLibFlutterApp = GeneralLibFlutterApp();
  const App({super.key});
  [@override](/user/override)
  Widget build(BuildContext context) {
    return GeneralLibFlutterAppMain(
      generalLibFlutterApp: generalLibFlutterApp,
      builder: (themeMode, lightTheme, darkTheme, widget) {
        Widget child = MaterialApp(
          theme: lightTheme,
          darkTheme: darkTheme,
          themeMode: themeMode,
          debugShowCheckedModeBanner: false,
          home: const MyApp(),
        );
        return child;
      },
    );
  }
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  TerminalLibraryFlutterController terminalLibraryFlutterController = TerminalLibraryFlutterController();
  final TerminalLibraryFlutter terminalLibraryFlutter = TerminalLibraryFlutter(
    maxLines: 1000,
  );
  late final TerminalPtyLibrary ptyLibrary;

  [@override](/user/override)
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      await refresh();
    });
  }

  [@override](/user/override)
  void dispose() {
    ptyLibrary.kill();
    terminalLibraryFlutterController.dispose();
    super.dispose();
  }

  bool is_loading = false;

  Future<void> refresh() async {
    if (is_loading) {
      return;
    }
    setState(() {
      is_loading = true;
    });

    await Future(() async {
      await Future.delayed(Duration(milliseconds: 500));

      ptyLibrary = TerminalPtyLibrary(
        executable: TerminalPtyLibraryBase.defaultShell,
        columns: terminalLibraryFlutter.viewWidth,
        rows: terminalLibraryFlutter.viewHeight,
      );

      ptyLibrary.on(
        eventName: ptyLibrary.event_output,
        onCallback: onCallback,
      );

      terminalLibraryFlutter.onOutput = (String value) {
        if (value.isNotEmpty) {
          try {
            ptyLibrary.write(utf8.encode(value));
          } catch (e) {}
        }
      };

      terminalLibraryFlutter.onResize = (w, h, pw, ph) {
        ptyLibrary.resize(h, w);
      };

      terminalLibraryFlutter.buffer.clear();
      terminalLibraryFlutter.buffer.setCursor(0, 0);
      terminalLibraryFlutter.textInput("clear");
      terminalLibraryFlutter.keyInput(TerminalLibraryFlutterKey.enter);
      terminalLibraryFlutter.write("Hello World");

      setState(() {});
    });

    setState(() {
      is_loading = false;
    });
  }

  FutureOr<dynamic> onCallback(dynamic update, TerminalPtyLibraryBase te) {
    if (update is Uint8List) {
      try {
        terminalLibraryFlutter.write(utf8.decode(update, allowMalformed: true));
      } catch (e) {}
    } else if (update is String) {
      terminalLibraryFlutter.write(update);
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    if (is_loading) {
      return Scaffold(
        body: Center(
          child: CircularProgressIndicator(
            color: context.theme.indicatorColor,
          ),
        ),
      );
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text("终端库:"),
      ),
      body: TerminalLibraryFlutterViewWidget(
        terminalLibraryFlutter,
        controller: terminalLibraryFlutterController,
        autofocus: true,
        backgroundOpacity: 0,
        simulateScroll: true,
        padding: const EdgeInsets.all(5),
        alwaysShowCursor: true,
        deleteDetection: Dart.isMobile,
      ),
    );
  }
}

更多关于Flutter终端模拟插件terminal_library的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter终端模拟插件terminal_library的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用terminal_library(假设这是一个提供终端模拟功能的插件)的基本示例代码。请注意,由于terminal_library可能不是一个真实存在的Flutter插件(Flutter社区中的插件通常会有更具体的命名,如terminal_viewflutter_term等),以下代码将基于一个假设的API进行说明。如果terminal_library确实存在,你可能需要根据其官方文档进行调整。

首先,确保在pubspec.yaml文件中添加了对该插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  terminal_library: ^x.y.z  # 替换为实际版本号

然后,运行flutter pub get来安装依赖。

接下来,在你的Flutter应用中,你可以按照以下方式使用terminal_library

import 'package:flutter/material.dart';
import 'package:terminal_library/terminal_library.dart';  // 假设这是插件的导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TerminalScreen(),
    );
  }
}

class TerminalScreen extends StatefulWidget {
  @override
  _TerminalScreenState createState() => _TerminalScreenState();
}

class _TerminalScreenState extends State<TerminalScreen> {
  TerminalController _terminalController;

  @override
  void initState() {
    super.initState();
    _terminalController = TerminalController(
      onCommandReceived: (command) {
        // 处理接收到的命令
        print('Received command: $command');
        // 这里可以添加自定义的逻辑,比如执行命令、显示输出等
        _terminalController.writeLine('Command executed successfully.');
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Terminal Emulator'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: TerminalView(
          controller: _terminalController,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 打开一个键盘输入框,用于向终端发送命令
          showCupertinoModalPopup<void>(
            context: context,
            builder: (BuildContext context) {
              return BottomSheet(
                onClosing: () => Navigator.pop(context),
                builder: (BuildContext context) {
                  return Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        TextField(
                          decoration: InputDecoration(
                            hintText: 'Enter command...',
                          ),
                          onEditingComplete: () {
                            _terminalController.sendCommand(
                              _textFieldController.text,
                            );
                            Navigator.pop(context);
                          },
                          controller: _createTextFieldController(),
                        ),
                      ],
                    ),
                  );
                },
              );
            },
          ),
        },
        tooltip: 'Send Command',
        child: Icon(Icons.send),
      ),
    );
  }

  TextFieldController _createTextFieldController() {
    final TextFieldController textFieldController = TextFieldController();
    // 注意:这里我们需要在某处保存这个controller的引用,以便在onEditingComplete中使用
    // 但由于Flutter的State管理,我们不能直接在这里保存它作为实例变量
    // 因此,这里我们假设有一个机制(比如闭包、全局变量等)来确保我们可以访问到这个controller
    // 在这个示例中,为了简化,我们省略了这个机制,实际使用时需要自行实现
    // 例如,你可以使用State的某个变量来保存这个controller的引用,并在onEditingComplete中访问它
    return textFieldController;
  }

  // 注意:上面的_createTextFieldController方法存在一个问题,即TextFieldController的引用管理
  // 在实际应用中,你应该在State类中维护一个TextFieldController的实例变量,并在initState中初始化它
  // 然后直接在TextField的controller属性中使用这个实例变量
  // 下面的代码展示了如何正确管理TextFieldController的引用
  TextFieldController _textFieldController;

  @override
  void initState() {
    super.initState();
    _textFieldController = TextFieldController();
    _terminalController = TerminalController(
      onCommandReceived: (command) {
        print('Received command: $command');
        _terminalController.writeLine('Command executed successfully.');
      },
    );
  }

  @override
  void dispose() {
    _textFieldController.dispose();  // 不要忘记在dispose中释放资源
    super.dispose();
  }
}

// 假设的TerminalController类,用于管理终端的行为
class TerminalController {
  final ValueChanged<String> onCommandReceived;
  List<String> _history = [];

  TerminalController({required this.onCommandReceived});

  void sendCommand(String command) {
    _history.add(command);
    onCommandReceived(command);
  }

  void writeLine(String line) {
    // 这里可以添加逻辑来在UI中显示新行
    // 例如,通过调用某个状态更新函数来更新UI
    print('Terminal Output: $line');  // 假设的输出方式
  }
}

// 假设的TerminalView组件,用于显示终端内容
class TerminalView extends StatelessWidget {
  final TerminalController controller;

  TerminalView({required this.controller});

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: controller._history.map((line) => Text(line)).toList(),
    );
  }
}

请注意,上面的代码是一个高度简化和假设的示例,用于说明如何在Flutter应用中集成一个终端模拟插件。在实际应用中,你可能需要处理更多的细节,比如输入验证、命令执行结果的实时显示、错误处理等。此外,TerminalControllerTerminalView类的实现也会根据插件的实际API有所不同。因此,建议查阅terminal_library(或你实际使用的插件)的官方文档,以获取准确的API信息和用法示例。

回到顶部