Flutter系统交互插件shell的使用

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

Flutter系统交互插件shell的使用

简介

shell 是一个 Dart 包,它为 dart:io 中的 Process API 提供了一个封装,支持环境管理、用户切换等功能。这个包对于编写 Dart 的 shell 工具脚本或在应用程序中执行系统管理任务非常有用。

安装

pubspec.yaml 文件中添加依赖:

dependencies:
  shell: ^latest_version
  file: ^latest_version

示例代码

以下是一个完整的示例代码,展示了如何使用 shell 插件与系统进行交互。该示例包括将命令输出写入文件、检查退出码、导航目录以及以管理员身份运行命令。

import 'dart:io';
import 'package:file/local.dart';
import 'package:shell/shell.dart';

void main() async {
  // 创建本地文件系统实例
  var fs = const LocalFileSystem();
  
  // 创建 Shell 实例
  var shell = Shell();
  
  // 获取环境变量中的密码
  var password = Platform.environment['PASSWORD'];
  print('Password: $password');

  // 运行 echo 命令并将结果写入文件
  var echo = await shell.start('echo', arguments: ['hello world']);
  await echo.stdout.writeToFile(fs.file('hello.txt')); // 将标准输出写入文件
  await echo.stderr.drain(); // 清空标准错误

  // 运行 find 命令并检查退出码
  var find = await shell.start('find', arguments: ['.']);
  await find.expectExitCode([0]); // 检查退出码是否为 0,也可以使用 find.expectZeroExit()
  
  // 打印 find 命令的输出
  print(await find.stdout.readAsString());

  // 运行 pwd 命令并立即获取字符串结果
  var pwd = await shell.startAndReadAsString('pwd', arguments: []);
  print('当前工作目录: $pwd');

  // 导航到指定目录
  shell.navigate('./lib/src');
  pwd = await shell.startAndReadAsString('pwd', arguments: []);
  print('当前工作目录: $pwd');

  // 创建一个新的 Shell 实例,并设置为管理员模式
  var forked = Shell.copy(shell)
    ..sudo = true
    ..password = password;

  // 以管理员身份运行 echo 命令
  var superEcho = await forked.start('echo', arguments: ['hello, admin!']);
  await superEcho.expectExitCode([0, 1]); // 允许退出码为 0 或 1
  await superEcho.stdout.writeToFile(fs.file('hello_sudo.txt')); // 将标准输出写入文件
}

更多关于Flutter系统交互插件shell的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter系统交互插件shell的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,platform_channels 是实现与原生平台(Android 和 iOS)交互的一种常见方式。shell 通常不是直接用于描述这种交互的术语,但如果你指的是通过某种方式(例如,执行原生命令或脚本)与系统交互,你可以使用 Method Channels 或 Platform Channels 来实现。

以下是一个使用 Method Channel 在 Flutter 和原生平台(Android 和 iOS)之间通信的示例。这个示例展示了如何从 Flutter 调用原生平台的方法,并接收结果。

1. 设置 Flutter 项目

首先,确保你已经设置了一个 Flutter 项目。如果还没有,可以通过以下命令创建一个新的 Flutter 项目:

flutter create my_flutter_app
cd my_flutter_app

2. 在 Flutter 中定义 Method Channel

lib 目录下,打开 main.dart 文件,并定义一个 Method Channel:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  static const platform = const MethodChannel('com.example.my_flutter_app/channel');

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Platform Channel Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('Press the button to call the native platform code.'),
              ElevatedButton(
                onPressed: _invokeNativeFunction,
                child: Text('Invoke Native Function'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _invokeNativeFunction() async {
    try {
      final String result = await platform.invokeMethod('runShellCommand', 'echo Hello from Flutter');
      print(result); // This will print the result from the native code.
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Result: $result')));
    } on PlatformException catch (e) {
      print("Failed to invoke: '${e.message}'.");
    }
  }
}

3. 在 Android 中实现 Method Channel

android/app/src/main/kotlin/.../MainActivity.kt(或 MainActivity.java,如果你使用的是 Java)中,添加对 Method Channel 的处理:

Kotlin:

package com.example.my_flutter_app

import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.example.my_flutter_app/channel"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "runShellCommand") {
                val command = call.argument<String>("command") ?: ""
                // Here you would run the shell command. For simplicity, we'll just return the command.
                result.success("Command executed: $command")
            } else {
                result.notImplemented()
            }
        }
    }
}

Java:

package com.example.my_flutter_app;

import android.os.Bundle;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "com.example.my_flutter_app/channel";

    @Override
    public void configureFlutterEngine(FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
                .setMethodCallHandler(
                        (call, result) -> {
                            if (call.method.equals("runShellCommand")) {
                                String command = call.argument("command");
                                if (command == null) {
                                    command = "";
                                }
                                // Here you would run the shell command. For simplicity, we'll just return the command.
                                result.success("Command executed: " + command);
                            } else {
                                result.notImplemented();
                            }
                        }
                );
    }
}

4. 在 iOS 中实现 Method Channel

ios/Runner/AppDelegate.swift 中,添加对 Method Channel 的处理:

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "com.example.my_flutter_app/channel", binaryMessenger: controller)
    channel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) in
      if call.method == "runShellCommand" {
        if let command = call.arguments as? String {
          // Here you would run the shell command. For simplicity, we'll just return the command.
          result("Command executed: \(command)")
        } else {
          result(FlutterError(code: "INVALID_ARGUMENT", message: "Expected a string argument for 'command'.", details: nil))
        }
      } else {
        result(FlutterMethodNotImplementedError(methodName: call.method))
      }
    })
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

总结

以上代码展示了如何在 Flutter 中通过 Method Channel 与原生平台(Android 和 iOS)进行交互。在这个例子中,Flutter 调用了一个名为 runShellCommand 的原生方法,并传递了一个简单的字符串参数。原生平台接收到调用后,执行了一些操作(在这个例子中是简单地返回了传递的命令),并将结果返回给 Flutter。

请注意,实际运行 shell 命令(特别是在 iOS 上)需要额外的权限和配置,并且出于安全考虑,通常不建议在移动应用中直接运行不受信任的 shell 命令。

回到顶部