Flutter自定义用户命令执行插件user_command的使用
Flutter自定义用户命令执行插件user_command的使用
Command 类
Command 类是一个统一定义的用户命令。一个 Command 包含以下动态属性:
name
:命令名称。action
:当用户点击命令时要执行的代码。- (可选)
icon
:命令图标。 - (可选)
visibility
:命令可见性。
注意,Command 类没有禁用状态,因为禁用按钮体验不好。
Command 小部件
Command 可以在以下小部件中使用:
CommandTextButton
CommandElevatedButton
CommandOutlinedButton
CommandPopupMenuItem
(例如,在CommandPopupMenu
中)CommandPopupMenuButton
CommandPopupMenuWrapper
CommandToolbarButton
(例如,在CommandToolbar
中)CommandTile
(例如,在CommandListView
中)- 等等…
Command 小部件样式
这些 Command 小部件都有一个单一的样式类,该类:
- 在没有提供样式参数时使用合理的默认格式。
- 包含所有格式的样式参数:
- 大小
- 颜色
- 字体
- 填充
- 对齐
- 高度
- 等等
安装和使用
请参阅相关文档了解安装和使用方法。
示例
以下是完整的示例 demo:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:user_command/user_command.dart';
void main() {
return runApp(const App());
}
final ThemeData darkTheme = ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
);
final ThemeData lightTheme = ThemeData(
brightness: Brightness.light,
primarySwatch: Colors.blue,
);
const List<IconData> numberedIcons = [
Icons.looks_one,
Icons.looks_two,
Icons.looks_3,
Icons.looks_4,
Icons.looks_5,
Icons.looks_6
];
// 创建示例命令列表
List<Command> createExampleCommands(BuildContext context) => [
Command(
name: "Example without icon",
action: () {
showSnackBar(context, 'You selected: Example without icon');
}),
Command.dynamic(
name: () => "Sometimes visible example",
icon: () => Icons.casino,
visible: () => Random().nextBool(),
action: () {
showSnackBar(context, 'You selected: Sometimes visible example');
}),
for (int index = 0; index < 6; index++)
Command(
name: "Example ${index + 1}",
icon: numberedIcons[index],
action: () {
showSnackBar(context, 'You selected: Example ${index + 1}');
}),
];
class App extends StatefulWidget {
const App({super.key});
[@override](/user/override)
AppState createState() => AppState();
}
class AppState extends State<App> {
Widget _page = const WelcomePage();
set page(Widget newPage) {
setState(() {
_page = newPage;
});
}
ThemeData _theme = lightTheme;
ThemeData get theme => _theme;
set theme(ThemeData newTheme) {
setState(() {
_theme = newTheme;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
theme: theme,
home: Scaffold(
appBar: AppBar(title: Text(pageTitle(_page.runtimeType))),
drawer: DrawerMenu(this),
body: _page),
);
}
}
class WelcomePage extends StatelessWidget {
const WelcomePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) => const Center(
child: Text(
'Welcome the user_command package examples.\n\n'
'Please select an example from the menu...',
style: TextStyle(fontSize: 20),
));
}
class TextButtonExamplePage extends StatelessWidget {
const TextButtonExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) => Center(
child: CommandTextButton(
Command(
name: "Text Button",
icon: Icons.thumb_up,
action: () {
showSnackBar(context, 'You have clicked on the text button');
}),
));
}
class ElevatedButtonExamplePage extends StatelessWidget {
const ElevatedButtonExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) => Center(
child: CommandElevatedButton(
Command(
name: "Elevated Button",
icon: Icons.thumb_up,
action: () {
showSnackBar(context, 'You have clicked on the elevated button');
}),
));
}
class OutlinedButtonExamplePage extends StatelessWidget {
const OutlinedButtonExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) => Center(
child: CommandOutlinedButton(
Command(
name: "Outlined Button",
icon: Icons.thumb_up,
action: () {
showSnackBar(context, 'You have clicked on the outlined button');
}),
));
}
class PopupMenuExamplePage extends StatelessWidget {
const PopupMenuExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
child: const Text('Click me to open the popup menu'),
onPressed: () {
CommandPopupMenu(context, createExampleCommands(context),
title: "Popup Menu");
},
));
}
}
class PopupMenuButtonExamplePage extends StatelessWidget {
const PopupMenuButtonExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
CommandPopupMenuButton(
iconData: Icons.more_vert,
commands: createExampleCommands(context)),
const SizedBox(height: 48),
CommandPopupMenuButton(
iconData: Icons.more_vert,
commands: createExampleCommands(context),
anchorPosition: AnchorPosition.left,
),
],
));
}
}
class PopupMenuWidgetForContainerExamplePage extends StatelessWidget {
const PopupMenuWidgetForContainerExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return Center(
child: CommandPopupMenuWrapper(
commands: createExampleCommands(context),
//Important: Inkwell will not be visible when the child has a background color.
// Use CommandPopupMenuWrapperStyle.backgroundColor instead
style: CommandPopupMenuWrapperStyle(
backgroundColor: Theme.of(context).colorScheme.primary),
child: const SizedBox(
width: 200,
height: 200,
child: Text(
'Click me anywhere',
textAlign: TextAlign.center,
),
),
),
);
}
}
class PopupMenuWidgetForListViewExamplePage extends StatelessWidget {
const PopupMenuWidgetForListViewExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return ListView(
children: [
for (int rowNr = 1; rowNr <= 20; rowNr++)
CommandPopupMenuWrapper(
popupMenuTitle: 'Row:$rowNr',
commands: [
for (int commandNr = 1; commandNr <= 5; commandNr++)
Command(
name: 'Row:$rowNr, Command:$commandNr',
icon: numberedIcons[commandNr - 1],
action: () {
showSnackBar(context,
'You selected Row:$rowNr, Command:$commandNr');
})
],
child: ListTile(title: Text('Row:$rowNr')))
],
);
}
}
class PopupMenuButtonInsideTextFieldExamplePage extends StatelessWidget {
final TextEditingController nameController = TextEditingController();
final textFieldKey = GlobalKey();
PopupMenuButtonInsideTextFieldExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 300),
child: TextField(
key: textFieldKey,
controller: nameController,
decoration: InputDecoration(
labelText: 'Please enter your name',
suffixIcon: CommandPopupMenuButton(
// important: we want the popup menu to position relative to
// the TextField by providing its global key.
anchorWidgetKey: textFieldKey,
// limiting the button height because its inside a TextField.
style: const CommandPopupMenuButtonStyle(
iconButtonStyle: CommandPopupMenuIconButtonStyle(
constraints: BoxConstraints(minHeight: 10))),
iconData: Icons.more_vert,
commands: [
Command(
name: 'Clear',
icon: Icons.clear,
action: () {
nameController.text = '';
}),
Command(
name: 'Say hallo',
icon: Icons.chat_bubble_outlined,
action: () {
showSnackBar(context, 'Hello ${nameController.text}!');
})
],
)),
),
),
);
}
}
class ToolbarExamplePage extends StatelessWidget {
const ToolbarExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) =>
Center(child: CommandToolbar(createExampleCommands(context)));
}
class ListViewExamplePage extends StatelessWidget {
const ListViewExamplePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return CommandListView(createExampleCommands(context));
}
}
class DrawerMenu extends StatelessWidget {
final AppState _appState;
const DrawerMenu(this._appState, {super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return Drawer(
child: Scaffold(
appBar: AppBar(title: const Text('Menu')),
body: CommandListView([
Command.dynamic(
name: () => 'Switch to dark theme',
icon: () => Icons.dark_mode,
visible: () => _appState.theme == lightTheme,
action: () {
_appState.theme = darkTheme;
closeMenu(context);
},
),
Command.dynamic(
name: () => 'Switch to light theme',
icon: () => Icons.light_mode,
visible: () => _appState.theme == darkTheme,
action: () {
_appState.theme = lightTheme;
closeMenu(context);
},
),
Command(
name: 'Text Button',
action: () {
_appState.page = const TextButtonExamplePage();
closeMenu(context);
},
),
Command(
name: 'Elevated Button',
action: () {
_appState.page = const ElevatedButtonExamplePage();
closeMenu(context);
},
),
Command(
name: 'Outlined Button',
action: () {
_appState.page = const OutlinedButtonExamplePage();
closeMenu(context);
},
),
Command(
name: 'PopUp Menu',
action: () {
_appState.page = const PopupMenuExamplePage();
closeMenu(context);
},
),
Command(
name: 'Popup Menu Button',
action: () {
_appState.page = const PopupMenuButtonExamplePage();
closeMenu(context);
},
),
Command(
name: 'Nested Popup Menu Button',
action: () {
_appState.page = PopupMenuButtonInsideTextFieldExamplePage();
closeMenu(context);
},
),
Command(
name: 'Popup Menu Widget 1',
action: () {
_appState.page = const PopupMenuWidgetForContainerExamplePage();
closeMenu(context);
},
),
Command(
name: 'Popup Menu Widget 2',
action: () {
_appState.page = const PopupMenuWidgetForListViewExamplePage();
closeMenu(context);
},
),
Command(
name: 'Toolbar',
action: () {
_appState.page = const ToolbarExamplePage();
closeMenu(context);
},
),
Command(
name: 'List View and List Tile',
action: () {
_appState.page = const ListViewExamplePage();
closeMenu(context);
},
),
]),
));
}
}
String pageTitle(Type pageType) {
final pageSuffix = RegExp(r"Page$");
final beforeCapitalLetter = RegExp(r"(?=[A-Z])");
String titleWithoutSpaces = pageType.toString().replaceAll(pageSuffix, '');
var titleWords = titleWithoutSpaces.split(beforeCapitalLetter);
return 'user_command ${titleWords.join(' ')}';
}
closeMenu(BuildContext context) {
try {
Navigator.pop(context);
} catch (e) {
// failed: not the end of the world.
}
}
showSnackBar(BuildContext context, String message) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
}
更多关于Flutter自定义用户命令执行插件user_command的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter自定义用户命令执行插件user_command的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用自定义用户命令执行插件 user_command
的示例代码。请注意,这里假设你已经有一个名为 user_command
的Flutter插件,该插件允许你执行自定义的命令行指令。
1. 添加依赖
首先,你需要在你的 pubspec.yaml
文件中添加 user_command
插件的依赖。由于这是一个假设的插件,你可能需要自己实现或者找到一个类似的插件。这里假设它已经存在:
dependencies:
flutter:
sdk: flutter
user_command: ^0.1.0 # 假设的版本号
然后运行 flutter pub get
来获取依赖。
2. 插件使用示例
接下来,在你的Flutter应用中导入并使用这个插件。下面是一个简单的示例,展示如何执行一个自定义命令并处理结果。
import 'package:flutter/material.dart';
import 'package:user_command/user_command.dart'; // 假设的导入路径
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('User Command Example'),
),
body: Center(
child: UserCommandExample(),
),
),
);
}
}
class UserCommandExample extends StatefulWidget {
@override
_UserCommandExampleState createState() => _UserCommandExampleState();
}
class _UserCommandExampleState extends State<UserCommandExample> {
String _result = '';
void _executeCommand() async {
// 假设的插件方法,用于执行自定义命令
String command = 'echo Hello from user_command!';
try {
String result = await UserCommand.run(command);
setState(() {
_result = result;
});
} catch (e) {
setState(() {
_result = 'Error: ${e.message}';
});
}
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: _executeCommand,
child: Text('Execute Command'),
),
SizedBox(height: 20),
Text(
_result,
style: TextStyle(fontSize: 18),
),
],
);
}
}
3. 插件实现(假设)
由于 user_command
是一个假设的插件,这里提供一个简化的插件实现思路,仅用于说明。实际的插件实现会复杂得多,并且需要处理平台通道(Platform Channels)来与原生代码进行通信。
3.1. iOS 实现(假设)
在 ios/Classes/UserCommandPlugin.swift
中:
import Flutter
public class UserCommandPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterRegistrar) {
let channel = FlutterMethodChannel(name: "user_command", binaryMessenger: registrar.messenger())
let instance = UserCommandPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "run" {
guard let arguments = call.arguments as? String else {
result(FlutterError(code: "Invalid argument", message: "Expected a string argument", details: nil))
return
}
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", arguments]
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8) ?? ""
result(output)
} else {
result(FlutterMethodNotImplementedError(methodName: call.method))
}
}
}
3.2. Android 实现(假设)
在 android/src/main/java/com/example/user_command/UserCommandPlugin.java
中:
package com.example.user_command;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
public class UserCommandPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
private MethodChannel channel;
private ActivityPluginBinding activityBinding;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "user_command");
channel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
if (call.method.equals("run")) {
String command = call.argument("command");
if (command != null) {
new ExecuteCommandTask(result).execute(command);
} else {
result.error("INVALID_ARGUMENT", "Expected a command argument", null);
}
} else {
result.notImplemented();
}
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
@Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
this.activityBinding = binding;
}
@Override
public void onDetachedFromActivityForConfigChanges() {
this.activityBinding = null;
}
@Override
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
this.activityBinding = binding;
}
@Override
public void onDetachedFromActivity() {
this.activityBinding = null;
}
private static class ExecuteCommandTask extends AsyncTask<String, Void, String> {
private Result result;
public ExecuteCommandTask(Result result) {
this.result = result;
}
@Override
protected String doInBackground(String... params) {
String command = params[0];
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
return stringBuilder.toString();
} catch (Exception e) {
return "Error: " + e.getMessage();
}
}
@Override
protected void onPostExecute(String resultString) {
result.success(resultString);
}
}
}
总结
以上代码展示了如何在Flutter中使用一个假设的 user_command
插件来执行自定义命令。实际的插件实现会依赖于具体的平台通道代码和原生平台(iOS和Android)的实现细节。希望这个示例能帮助你理解如何在Flutter中集成和使用自定义命令执行插件。