Flutter宏定义参数传递插件args_macro的使用

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

Flutter宏定义参数传递插件args_macro的使用

简介

args_macro 插件用于生成命令行参数解析器,基于你的数据类。它封装了标准的 args 包。

使用

import 'package:args_macro/args_macro.dart';

@Args()
class MyArgs {
  final String requiredString;
  final String? optionalString;
  String stringWithDefault = 'My default string.';

  final int requiredInt;
  final int? optionalInt;
  int intWithDefault = 7;

  final double requiredDouble;
  final double? optionalDouble;
  double doubleWithDefault = 7.77;

  bool boolWithDefaultFalse = false;
  bool boolWithDefaultTrue = true;

  final Fruit requiredEnum;
  final Fruit? optionalEnum;
  Fruit enumWithDefault = Fruit.mango;

  final List<String> stringList;
  List<String> stringListWithDefault = ['Huey', 'Dewey', 'Louie'];

  final List<int> intList;
  List<int> intListWithDefault = [1, 2];

  final List<double> doubleList;
  List<double> doubleListWithDefault = [1, 2.0];

  final List<Fruit> enumList;
  List<Fruit> enumListWithDefault = [Fruit.apple, Fruit.banana];
}

enum Fruit { apple, banana, mango, orange }

void main(List<String> argv) {
  final parser = MyArgsParser(); // 生成的类。
  final MyArgs args = parser.parse(argv);
  // ...
}

这将创建一个包含解析数据的 MyArgs 类实例。

参数类型配置

  • 非空字段:无初始值的非空字段会创建一个必填选项。
  • 可空字段:无初始值的可空字段会创建一个默认为 null 的可选选项。
  • 有初始值的字段:有初始值的字段会创建一个具有默认值的可选选项。此类字段不能为 finalnullable

支持的类型

字符串(String)

字符串选项不会被解释。

@Args()
class MyArgs {
  final String requiredString;
  final String? optionalString;
  String stringWithDefault = 'My default string.';
}
整数(int)

整数通过 int.parse(String) 进行解析。如果无法解析,调用 MyArgsParser.parse() 会显示消息并终止程序。

@Args()
class MyArgs {
  final int requiredInt;
  final int? optionalInt;
  int intWithDefault = 7;
}
双精度浮点数(double)

双精度浮点数通过 double.parse(String) 进行解析。如果无法解析,调用 MyArgsParser.parse() 会显示消息并终止程序。

@Args()
class MyArgs {
  final double requiredDouble;
  final double? optionalDouble;
  double doubleWithDefault = 7.77;
}
布尔值(bool)

布尔字段生成一个标志。布尔字段必须有初始值,因为它们不能被要求,缺少一个标志意味着其不存在。因此,布尔字段也不能为 nullable

通常你希望默认为 false 的布尔值,添加标志会将其更改为 true。对于默认为 true 的字段,会产生一个标志来否定它,并在标志名称前加上 no-

@Args()
class MyArgs {
  bool boolWithDefaultFalse = false; // 使用 --bool-with-default-false 来使其为 true。
  bool boolWithDefaultTrue = true;   // 使用 --no-bool-with-default-true 来使其为 false。
}
枚举(enum)

枚举通过 MyEnum.values.byName(String) 进行解析。如果值与任何枚举常量不匹配,调用 MyArgsParser.parse() 会显示消息并终止程序。

@Args()
class MyArgs {
  final Fruit requiredEnum;
  final Fruit? optionalEnum;
  Fruit enumWithDefault = Fruit.mango;
}

enum Fruit { apple, banana, mango, orange }
列表和集合(List, Set)

ListSet 字段生成的选项可以在命令行中多次传递。每次传递都会向集合添加一个项目。

可以使用默认值,如果选项未传递零次。

如果不在乎值的顺序且希望去重,请使用 Set。否则,使用 List

这些集合支持 Stringintdoubleenum

@Args()
class MyArgs {
  final List<String> stringList;
  List<String> stringListWithDefault = ['Huey', 'Dewey', 'Louie'];

  final List<int> intList;
  List<int> intListWithDefault = [1, 2];

  final List<double> doubleList;
  List<double> doubleListWithDefault = [1, 2.0];

  final List<Fruit> enumList;
  List<Fruit> enumListWithDefault = [Fruit.apple, Fruit.banana];

  final Set<String> stringSet;
  Set<String> stringSetWithDefault = {'Huey', 'Dewey', 'Louie'};

  final Set<int> intSet;
  Set<int> intSetWithDefault = {3, 4};

  final Set<double> doubleSet;
  Set<double> doubleSetWithDefault = {3, 4.0};

  final Set<Fruit> enumSet;
  Set<Fruit> enumSetWithDefault = {Fruit.orange, Fruit.banana};
}

enum Fruit { apple, banana, mango, orange }

帮助信息

解析器自动添加 --help 选项。它打印使用信息并终止程序 (parse() 方法永远不会返回)。

添加帮助信息

要为选项添加帮助信息,请定义一个静态变量,前面加上下划线并追加 Help

@Args()
class MyArgs {
  final String requiredString;
  static const _requiredStringHelp = 'Help for the required string.';
  // ...
}
可执行文件名和描述

要将帮助文本前缀为描述和命令示例,请将以下参数传递给宏:

@Args(
  description: 'The command description.',
  executableName: 'executable_name',
)
class MyArgs { /* ... */ }
示例帮助输出

这是完整的示例文件的输出:

The command description.

Usage: executable_name [arguments]
    --required-string (mandatory)    Help for the required string.
    --optional-string                
    --string-with-default            (defaults to "My default string.")
    --required-int (mandatory)       
    --optional-int                   Help for the optional int.
    --int-with-default               (defaults to "7")
    --required-double (mandatory)    Help for the required double.
    --optional-double                
    --double-with-default            (defaults to "7.77")
    --bool-with-default-false        Help for the flag.
    --no-bool-with-default-true      
    --required-enum (mandatory)      [apple, banana, mango, orange]
    --optional-enum                  Help for the optional Enum.
                                     [apple, banana, mango, orange]
    --enum-with-default              [apple, banana, mango (default), orange]
    --string-list                    
    --string-list-with-default       Help for String[] with default.
                                     (defaults to "Huey", "Dewey", "Louie")
    --int-list                       Help for int[].
    --int-list-with-default          (defaults to "1", "2")
    --double-list                    Help for double[].
    --double-list-with-default       (defaults to "1.0", "2.0")
    --enum-list                      Help for Enum[].
                                     [apple, banana, mango, orange]
    --enum-list-with-default         [apple (default), banana (default), mango, orange]
    --string-set                     Help for String{}.
    --string-set-with-default        (defaults to "Huey", "Dewey", "Louie")
    --int-set                        Help for int{}.
    --int-set-with-default           (defaults to "3", "4")
    --double-set                     Help for double{}.
    --double-set-with-default        (defaults to "3.0", "4.0")
    --enum-set                       Help for Enum{}.
                                     [apple, banana, mango, orange]
    --enum-set-with-default          [apple, banana, mango (default), orange]
-h, --help                           Print this usage information.

错误处理

除非传递了 --help,所有数据都会根据你的数据类结构进行验证。发生任何错误时,parse() 方法会将第一个错误和使用信息打印到 stderr 并终止程序,退出代码为 64。

要忽略所有错误但仍然让解析器填充由你的数据类派生的选项(并跳过 --help 的自动处理),请使用标准的 ArgParser 实例,该解析器包装了此生成的解析器。它将返回普通的 ArgResults 而没有任何错误处理:

void main(List<String> argv) {
  final result = MyArgsParser().parser.parse(argv);
  print(result.option('required-string'));
}

更多关于Flutter宏定义参数传递插件args_macro的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter宏定义参数传递插件args_macro的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中,如果你需要利用宏定义参数传递插件(如args_macro,尽管这不是一个官方的Flutter插件,但假设它提供类似功能),你可以通过创建和使用Flutter平台通道(Platform Channel)结合Dart宏定义来实现参数的传递。不过,由于Dart本身并不支持C/C++风格的宏定义,我们通常通过常量或配置来实现类似效果。

以下是一个简化的示例,展示了如何通过平台通道在Flutter和原生代码之间传递参数,并结合Dart中的常量定义来模拟宏定义的效果。

1. 设置Flutter项目

首先,确保你的Flutter项目已经创建。如果还没有,可以使用以下命令创建:

flutter create my_flutter_app
cd my_flutter_app

2. 定义Dart常量(模拟宏定义)

lib目录下创建一个新的Dart文件,比如config.dart,用于定义常量:

// lib/config.dart
class Config {
  static const String apiEndpoint = "https://api.example.com";
  static const bool enableLogging = true;
}

3. 创建Flutter平台通道

lib/main.dart中,设置平台通道以从原生代码接收参数(虽然这个例子没有直接传递宏定义,但展示了参数传递的过程):

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Macro Args Example'),
        ),
        body: Center(
          child: FutureBuilder<String?>(
            future: _getPlatformVersion(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                if (snapshot.hasError) {
                  return Text('Failed to get platform version: ${snapshot.error!}');
                } else {
                  // 这里可以处理从原生代码传递过来的参数
                  // 例如,假设原生代码传递了一个名为"nativeParam"的参数
                  String? nativeParam = snapshot.data;
                  return Text('Received from native: $nativeParam\nConfig API Endpoint: ${Config.apiEndpoint}');
                }
              } else {
                return CircularProgressIndicator();
              }
            },
          ),
        ),
      ),
    );
  }

  Future<String?> _getPlatformVersion() async {
    try {
      final String? version = await platform.invokeMethod('getPlatformVersion');
      return version;
    } on PlatformException catch (e) {
      return "Failed to get platform version: '${e.message}'".trim();
    }
  }
}

4. 在原生代码中实现平台通道

iOS (Swift)

ios/Runner/AppDelegate.swift中,添加平台通道的实现:

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.flutterapp/channel", binaryMessenger: controller.binaryMessenger)
    channel.setMethodCallHandler {
      (call: FlutterMethodCall, result: @escaping FlutterResult) in
      if call.method == "getPlatformVersion" {
        let version = UIDevice.current.systemVersion
        result(version)
      } else {
        result(FlutterMethodNotImplemented)
      }
    }
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Android (Kotlin)

android/app/src/main/kotlin/.../MainActivity.kt中,添加平台通道的实现:

package com.example.flutterapp

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.flutterapp/channel"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "getPlatformVersion") {
                result.success(android.os.Build.VERSION.RELEASE)
            } else {
                result.notImplemented()
            }
        }
    }
}

总结

虽然Dart不支持传统意义上的宏定义,但你可以通过常量配置和平台通道在Flutter和原生代码之间传递参数。上述示例展示了如何在Flutter应用中设置平台通道,并从原生代码中获取参数,同时利用Dart常量来模拟一些配置参数。根据你的需求,你可以进一步扩展这些代码来处理更复杂的参数传递逻辑。

回到顶部