如何使用Flutter开发执行操作系统shell命令的工具
简介
Flutter是一种由Google开发的移动应用程序开发框架,它允许开发人员使用单个代码库构建高性能、高质量的移动体验。而Android终端命令行工具则允许用户在Android手机上运行类似于Linux的操作系统命令。本文的目的是介绍如何在Flutter应用中开发一个Android终端命令行工具,包括终端命令行页面的布局设计、与Shell通信的基本原理、输入输出处理的基本技巧、终端样式和输出样式的可配置性,以及如何在具体应用中利用终端命令行工具来执行系统命令和与用户进行交互。
实现终端命令行页面
为了实现一个完整的终端命令行页面,我们可以使用Flutter提供的基础控制台模块,例如TextArea、TextField等。需要注意的是,在Android中Shell是另一个进程,我们需要使用Flutter提供的方法来与Shell进行通信。具体来说,我们可以通过创建一个Future或者Stream来与Shell进行通信。其中Future用于单个结果的返回,而Stream则可以返回多个结果。我们可以在Flutter使用Async函数和这些模块的onChanged()方法来处理输入和输出。以下是最简单的实现方式。
dart复制代码
class TerminalPage extends StatefulWidget {_TerminalPageState createState() => _TerminalPageState();
}class _TerminalPageState extends State<TerminalPage> {TextEditingController _controller;Future<Process> _process;Stream<String> _output;void initState() {super.initState();_controller = TextEditingController();_process = Process.start('/system/bin/sh', []);_controller.addListener(_handleInput);}void _handleInput() {_process.then((process) {process.stdin.writeln(_controller.text);_controller.clear();});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Terminal Command"),),body: Column(children: [Flexible(flex: 1,child: StreamBuilder<String>(stream: _output,builder: (BuildContext context, AsyncSnapshot<String> snapshot) {if (snapshot.hasData) {return Text(snapshot.data);} else {return CircularProgressIndicator();}},),),TextField(controller: _controller,decoration: InputDecoration(border: InputBorder.none, hintText: "Input terminal command"),)],),);}
}
终端样式和输出样式的设计
除了基本的终端命令行页面,我们也需要为终端和输出样式提供更多的可配置性。在Flutter中,我们可以使用TextStyle模块(例如,color、fontSize、fontFamily等)来控制终端和输出的样式。我们可以将TextStyle保存在Flutter的本地存储中,以便在应用中进行修改。以下是一个具体的样例:
dart复制代码
class _TerminalPageState extends State<TerminalPage> {TextEditingController _controller;Future<Process> _process;Stream<String> _output;TextStyle _terminalStyle;TextStyle _outputStyle;void initState() {super.initState();_controller = TextEditingController();_process = Process.start('/system/bin/sh', []);_terminalStyle = TextStyle(color: Colors.white, backgroundColor: Colors.black, fontFamily: 'Monospace');_outputStyle = TextStyle(color: Colors.white, backgroundColor: Colors.black, fontSize: 16);_controller.addListener(_handleInput);}void _handleInput() {_process.then((process) {process.stdin.writeln(_controller.text);_controller.clear();});}Future<File> _getTerminalStyleFile() async {final directory = await getApplicationDocumentsDirectory();final file = File('${directory.path}/terminalStyle');if (file.existsSync()) {return file;} else {await file.create();await file.writeAsString('{"color": "#FFFFFF", "background": "#000000"}');return file;}}Future<File> _getOutputStyleFile() async {final directory = await getApplicationDocumentsDirectory();final file = File('${directory.path}/outputStyle');if (file.existsSync()) {return file;} else {await file.create();await file.writeAsString('{"color": "#FFFFFF", "background": "#000000", "size": 16}');return file;}}Future<void> _loadStyles() async {final terminalFile = await _getTerminalStyleFile();final terminalJson = await terminalFile.readAsString();final terminalStylesMap = jsonDecode(terminalJson);_terminalStyle = TextStyle(color: Color(int.parse(terminalStylesMap['color'].toString(), radix: 16)),backgroundColor: Color(int.parse(terminalStylesMap['background'].toString(), radix: 16)),fontFamily: 'Monospace',);final outputFile = await _getOutputStyleFile();final outputJson = await outputFile.readAsString();final outputStylesMap = jsonDecode(outputJson);_outputStyle = TextStyle(color: Color(int.parse(outputStylesMap['color'].toString(), radix: 16)),backgroundColor: Color(int.parse(outputStylesMap['background'].toString(), radix: 16)),fontSize: outputStylesMap['size'].toDouble(),);}Future<void> _saveTerminalStyle(String color, String background) async {final terminalFile = await _getTerminalStyleFile();final terminalStylesMap = {"color": color, "background": background};final terminalJson = jsonEncode(terminalStylesMap);await terminalFile.writeAsString(terminalJson);}Future<void> _saveOutputStyle(String color, String background, double size) async {final outputFile = await _getOutputStyleFile();final outputStylesMap = {"color": color, "background": background, "size": size.toInt()};final outputJson = jsonEncode(outputStylesMap);await outputFile.writeAsString(outputJson);}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Terminal Command"),),body: FutureBuilder(future: _loadStyles(),builder: (BuildContext context, AsyncSnapshot<void> snapshot) {switch (snapshot.connectionState) {case ConnectionState.done:return Column(children: [Flexible(flex: 1,child: StreamBuilder<String>(stream: _output,builder: (BuildContext context, AsyncSnapshot<String> snapshot) {if (snapshot.hasData) {return Text(snapshot.data, style: _outputStyle);} else {return CircularProgressIndicator();}},),),TextField(style: _terminalStyle,controller: _controller,decoration: InputDecoration(border: InputBorder.none,hintText: "Input terminal command",hintStyle: _terminalStyle,),)],);default:return Center(child: CircularProgressIndicator());}},),);}}
在Flutter应用中使用终端命令行工具的例子
在具体应用中,我们可以使用终端命令行工具来执行一些系统命令,例如ping、df、top等。同时,我们也可以利用终端界面来与用户进行交互,例如读取用户的输入并将其传递给后端服务器。以下是一个简单的应用案例:
dart复制代码
class PingPage extends StatelessWidget {final String ip;PingPage({Key key, this.ip}) : super(key: key);Future<Process> _getPingProcess() async {final process = await Process.start('/system/bin/ping', ['-c', '5', ip]);return process;}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Ping $ip")),body: Container(child: FutureBuilder(future: _getPingProcess(),builder: (BuildContext context, AsyncSnapshot<Process> snapshot) {if (snapshot.hasData) {return Column(children: [Expanded(child: TerminalOutput(process: snapshot.data),),Container(margin: EdgeInsets.all(10),child: TextField(decoration: InputDecoration(border: OutlineInputBorder(),hintText: 'Input Ping Command',),),),Container(margin: EdgeInsets.all(10),child: ElevatedButton(onPressed: () {},child: Text("Send Command"),),)],);} else {return Center(child: CircularProgressIndicator());}},),),);}
}class TerminalOutput extends StatelessWidget {final Process process;TerminalOutput({Key key, this.process}) : super(key: key);Widget build(BuildContext context) {return StreamBuilder<String>(stream: process.stdout.transform(utf8.decoder),builder: (BuildContext context, AsyncSnapshot<String> snapshot) {if (snapshot.hasData) {return SingleChildScrollView(child: Text(snapshot.data,style: TextStyle(color: Colors.white,fontFamily: 'Monospace',backgroundColor: Colors.black,),),);} else {return Center(child: CircularProgressIndicator());}},);}
}
总结
本文介绍了在Flutter应用中开发Android终端命令行工具的方法。主要包括终端命令行页面的布局设计、与Shell通信的基本原理、输入输出处理的基本技巧、终端样式和输出样式的可配置性,以及如何在具体应用中利用终端命令行工具来执行系统命令和与用户进行交互。
本文提供了一个完整的演示案例,包括输出和输入的显示、终端命令行页面的开发、以及关于终端样式为可配置的,输出样式也为可配置的。希望这篇文章对于Flutter开发者有所帮助,能够为他们在实际开发中提供参考。
除了本文所介绍的方法外,还有其他方法可以在Flutter应用中实现终端命令行工具,例如使用Dart的Process类进行进程调用,或者将Flutter应用封装为Docker容器。这些方法也值得开发者进一步探索和试验。未来,随着移动应用越来越复杂,终端命令行工具的需求也会越来越大。因此,掌握Flutter中开发终端命令行工具的技能将会变得越来越重要。
链接:https://juejin.cn/post/7248827630865596474
相关文章:
如何使用Flutter开发执行操作系统shell命令的工具
简介 Flutter是一种由Google开发的移动应用程序开发框架,它允许开发人员使用单个代码库构建高性能、高质量的移动体验。而Android终端命令行工具则允许用户在Android手机上运行类似于Linux的操作系统命令。本文的目的是介绍如何在Flutter应用中开发一个Android终端命…...
西山居 游戏研发工程师实习生 面经
西山居实习面经 面试时长:26min(两个面试官交替问) 1、自我介绍 2、你平常怎么学习的 3、你实习接受加班么 4、说一下Unity的生命周期,Start和Awake哪里不同 5、Unity中Update与FixedUpdate的区别,怎么设置Fixed…...
YOLOv8训练自己的数据集+改进方法复现
yolov8已经出来好几个月了,并且yolov8从刚开始出来之后的小版本也升级好几次,总体变化不大,个别文件存放位置发生了变化,以下以最新版本的YOLOv8来详细学习和使用YOLOv8完成一次目标检测。 一、环境按照 深度学习环境搭建就不再…...
尚硅谷kafka3.0.0
目录 💃概述 ⛹定义 编辑⛹消息队列 🤸♂️消息队列应用场景 编辑🤸♂️两种模式:点对点、发布订阅 编辑⛹基本概念 💃Kafka安装 ⛹ zookeeper安装 ⛹集群规划 编辑⛹流程 ⛹原神启动 🤸♂️…...
【Andriod】Appium的不同版本(Appium GUI、Appium Desktop、Appium Server )的安装教程
文章目录 前言一.Appium GUI二.Appium Desktop三.Appium Server 命令行版本1.安装node.js2.安装Appium Server 前言 Appium 安装提供两2方式:桌面版和命令行版。其中桌面版又分为 Appium GUI 和 Appium Desktop。 建议:使用Appium Desktop 一.Appium …...
leetcode:面试题 17.04. 消失的数字(找单身狗/排序/公式)
一、题目: 函数原型:int missingNumber(int* nums, int numsSize) 二、思路: 思路1 利用“找单身狗”的思路(n^n0;0^nn),数组中有0-n的数字,但缺失了一个数字x。将这些数字按位异或0…...
基于SpringBoot的时间管理系统
基于SpringBoot的时间管理系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringBootMyBatis工具:IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 管理员界面 用户界面 摘要 基于Spring Boot的时间管理系统是一款功能丰富…...
centos搭建elastic集群
1、环境可以在同一台集群上搭建elastic,也可以在三台机器上搭建,这次演示的是在同一台机器搭建机器。 2、下载elastic :https://www.elastic.co/cn/downloads/past-releases#elasticsearch 2、 tar -zxvf elasticsearch-xxx-版…...
CUDA学习笔记(九)Dynamic Parallelism
本篇博文转载于https://www.cnblogs.com/1024incn/tag/CUDA/,仅用于学习。 Dynamic Parallelism 到目前为止,所有kernel都是在host端调用,CUDA Dynamic Parallelism允许GPU kernel在device端创建调用。Dynamic Parallelism使递归更容易实现…...
周记之马上要答辩了
“ 要变得温柔和强大,就算哪天突然孤身一人,也能平静地活下去,不至于崩溃。” 10.16 今天提前写完了一篇六级阅读,积累了一些词组: speak out against 公然反对,印象最深刻的就这个; 先了解…...
git简介和指令
git是一个开源的的分布式版本控制系统,用于高效的管理各种大小项目和文件 用途:防止代码丢失,做备份 项目的版本管理和控制,可以通过设置节点进行跳转 建立各自的开发环境分支,互不影响,方便合并 在多终端开…...
alibaba.fastjson的使用(五)-- Json数组字符串 ==》 JSONArray
目录 1. 使用到的方法 2. 实例演示 1. 使用到的方法 static JSONArray parseArray(String text) 2. 实例演示 /*** 将Json数组字符串转JsonArray*/@Testpublic void test5() {String jsonArrStr = "[{\"name\":\"郭靖\",\"age\":35},{\…...
ts json的中boolean布尔值或者int数字都是字符串,转成对象对应类型
没啥好写的再水一篇 json中都是字符串,转换一下就好,简单来说就是转换一次不行,再转换换一次,整体转换不够,细分的再转换一次 这是vue中 ts写法 ,我这里是拿对象做对比,不好字符和对象做对比,…...
【OpenGL】七、混合
混合 文章目录 混合混合公式glBlendFunc(混合函数)glBlendFuncSeparate渲染半透明纹理 参考链接 混合(Blending)通常是实现物体透明度(Transparency)的一种技术 简而言之:混合就是如何将输出颜色和目标缓冲区颜色结合起来。 混合公式 C_fina…...
JVM——堆内存调优(Jprofiler使用)Jprofile下载和安装很容易,故没有记录,如有需要,在评论区留言)
堆内存调优 当遇到OOM时,可以进行调参 1、尝试扩大堆内存看结果 2、分析内存,看哪个地方出现了问题(专业工具) 调整初始分配内存为1024M,调整最大分配内存为1024M,打印GC细节(如何添加JVM操…...
Android cmdline-tools 版本与其最小JDK关系
关键词:Android cmdline-tools 历史版本、Android cmdline-tools 最小JDK版本、JDK 对应 major version、JDK LTS 信息 由于 JDK8 是一个常用的、较低的版本,因此只需要关注 JDK8 及以上版本的运行情况。 cmdline-tools 版本和最低 JDK 最终结论&…...
基于ARM+FPGA+AD的多通道精密数据采集仪方案
XM 系列具备了数据采集仪应具备的“操作简单、便于携带、满足各种测量需求”等功能的产品。具有超小、超轻量的手掌大小尺寸,支持8 种测量模块,还可进行最多576 Ch的多通道测量。另外,支持省配线系统,可大幅削减配线工时。使用时不…...
【JAVA学习笔记】43 - 枚举类
项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter11/src/com/yinhai/enum_ 〇、创建时自动填入版权 作者等信息 如何在每个文件创建的时候打入自己的信息以及版权呢 菜单栏-File-setting-Editor-File and Code Templaters -Includes-输入信…...
Springcloud介绍
1.基本介绍 Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring …...
LK光流法和LK金字塔光流法(含python和c++代码示例)
0 引言 本文主要记录LK光流算法及LK金字塔光流算法的详细原理,最后还调用OpenCV中的cv2.calcOpticalFlowPyrLK()函数实现LK金字塔光流算法,其中第3部分是python语言实现版本,第4部分是c++语言实现版本。 1 LK光流算法 1.1 简述 LK光流法是一种计算图像序列中物体运动的光…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
