flutter_using_packages
Flutter - 使用包 (Packages)
Section titled “Flutter - 使用包 (Packages)”Flutter 的能力通过其丰富的包(packages)生态系统得到了显著增强。包是共享代码的捆绑,类似于其他语言中的库或模块,由 Flutter 社区和 Google 创建。它们使你能够轻松地向应用添加预构建的功能,从而节省开发时间和精力。
Pub.dev:Dart 和 Flutter 包仓库
Section titled “Pub.dev:Dart 和 Flutter 包仓库”Dart 和 Flutter 包的中心仓库是 pub.dev。你可以在其中搜索包、查看其文档、检查其流行度和健康评分,以及查找安装说明。
包通常分为以下几类:
- Dart 包: 完全用 Dart 编写,包含实用函数、类或业务逻辑。它们不依赖于 Flutter 框架,通常也可用于非 Flutter 的 Dart 项目。示例:
http(用于发起 HTTP 请求),intl(用于国际化)。 - Flutter 包(或插件包 Plugin Packages): 这些包依赖于 Flutter 框架,可能包含 Dart 代码以及平台特定的原生代码(Android/iOS/Web/Desktop)以访问平台特性。它们通常提供 Flutter 组件(Widgets)或访问设备能力。
-
- **插件(Plugins):** 专门包含使用平台通道(platform channels)实现平台特定功能的包。示例:`camera` (访问设备相机), `shared_preferences` (访问平台的简单存储), `webview_flutter` (嵌入 Web 视图)。
-
- **纯 Flutter 包(Pure Flutter Packages):** 这些包依赖于 Flutter 但只包含 Dart 代码,通常提供自定义组件、状态管理解决方案或 Flutter 特定的实用工具。示例:`provider` (状态管理), `cached_network_image` (高级图像加载)。
在你的项目中使用包
Section titled “在你的项目中使用包”向你的 Flutter 项目添加包包括以下步骤:
1. 添加依赖
Section titled “1. 添加依赖”- 打开你项目的
pubspec.yaml文件。 - 在
dependencies:部分下,添加包名称和所需的版本约束。你可以在 pub.dev 上找到最新版本和推荐的约束。 - 常见的版本约束:
-
- `^version`:允许更新到新的次要版本和补丁版本,但不允许主要版本(例如,`^2.3.0` 允许 `2.3.1`、`2.4.0`,但不允许 `3.0.0`)。这是最常见和推荐的约束。
-
- `version`:锁定到精确版本。
-
- `any`:允许任何版本(谨慎使用)。
2. 安装包
Section titled “2. 安装包”-
在终端中,从你的项目根目录运行:
-
大多数 IDE(如 VS Code, Android Studio)会自动检测
pubspec.yaml的更改,并提示你运行flutter pub get或自动运行。
3. 导入包
Section titled “3. 导入包”- 在你想要使用包的 Dart 文件中,在顶部添加
import语句,使用package:指令:
4. 使用包
Section titled “4. 使用包”- 你现在可以使用导入包提供的类、函数和组件了。
开发你自己的包
Section titled “开发你自己的包”你也可以创建自己的包,以便在项目之间共享代码或贡献给社区。
创建包或插件
Section titled “创建包或插件”-
使用 Flutter CLI:
-
这将创建一个标准的包结构:
-
lib/:包含公共 Dart 代码。
-
lib/src/:按照惯例用于私有实现代码。
-
pubspec.yaml:定义包。
-
README.md,CHANGELOG.md,LICENSE:文档和许可。
-
example/:一个可选的示例 Flutter 应用,演示如何使用该包。
-
android/,ios/等(对于插件):包含原生平台代码。
开发插件(简要概述)
Section titled “开发插件(简要概述)”开发插件包括:
- 在
lib/文件夹中定义 Dart API。 - 使用平台通道(
MethodChannel,EventChannel)在 Dart 中实现通信逻辑。 - 在 Android (Kotlin/Java) 和/或 iOS (Swift/Objective-C) 上实现对应的原生部分,它们监听平台通道,执行原生代码,并将结果发送回。
让我们根据原始教程内容完善浏览器插件示例,假设我们创建了一个名为 my_browser 的插件。
Dart API (lib/my_browser.dart)
Section titled “Dart API (lib/my_browser.dart)”import 'dart:async';import 'package:flutter/services.dart';
class MyBrowser { // 定义一个一致的通道名称 static const MethodChannel _channel = MethodChannel('com.example.my_browser/channel');
// 打开浏览器的函数 Future<void> openBrowser(String urlString) async { try { // 在原生端调用 'openBrowser' 方法 await _channel.invokeMethod('openBrowser', {'url': urlString}); } on PlatformException catch (e) { print("Failed to open browser: '${e.message}'."); // 打开浏览器失败:'${e.message}'。 // 考虑重新抛出或适当地处理错误 } }
// 示例:平台版本(通常包含在模板中) static Future<String?> get platformVersion async { final String? version = await _channel.invokeMethod('getPlatformVersion'); return version; }}Android 实现 (android/src/.../MyBrowserPlugin.kt)
Section titled “Android 实现 (android/src/.../MyBrowserPlugin.kt)”package com.example.my_browser // 调整包名
import androidx.annotation.NonNullimport io.flutter.embedding.engine.plugins.FlutterPluginimport io.flutter.plugin.common.MethodCallimport io.flutter.plugin.common.MethodChannelimport io.flutter.plugin.common.MethodChannel.MethodCallHandlerimport io.flutter.plugin.common.MethodChannel.Resultimport io.flutter.embedding.engine.plugins.activity.ActivityAwareimport io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
// 必需的导入import android.app.Activityimport android.content.Intentimport android.net.Uri
class MyBrowserPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { private lateinit var channel : MethodChannel private var activity: Activity? = null // 持有 Activity 引用
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.example.my_browser/channel") channel.setMethodCallHandler(this) }
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { if (call.method == "getPlatformVersion") { result.success("Android ${android.os.Build.VERSION.RELEASE}") } else if (call.method == "openBrowser") { val url = call.argument<String>("url") if (url != null) { openBrowser(url, result) } else { result.error("INVALID_ARGUMENT", "URL cannot be null", null) } } else { result.notImplemented() } }
private fun openBrowser(url: String, result: Result) { if (activity == null) { result.error("NO_ACTIVITY", "Cannot open browser without a foreground activity.", null) // 没有前景 Activity,无法打开浏览器。 return } try { val intent = Intent(Intent.ACTION_VIEW) intent.data = Uri.parse(url) activity?.startActivity(intent) result.success(true) // 表示成功 } catch (e: Exception) { result.error("FAILED", "Could not open browser: ${e.message}", null) // 无法打开浏览器:${e.message} } }
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) }
// ActivityAware 方法 override fun onAttachedToActivity(binding: ActivityPluginBinding) { activity = binding.activity }
override fun onDetachedFromActivityForConfigChanges() { activity = null }
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { activity = binding.activity }
override fun onDetachedFromActivity() { activity = null }}注意: Android 实现使用了新的 FlutterPlugin 和 ActivityAware 接口,与旧的 Registrar 方法相比,具有更好的生命周期管理。
iOS 实现 (ios/Classes/MyBrowserPlugin.swift)
Section titled “iOS 实现 (ios/Classes/MyBrowserPlugin.swift)”iOS 实现将类似于平台通道章节中所示的示例,但会适应插件结构。
要在本地使用你正在开发的包(在发布之前),可以在使用该包的应用的 pubspec.yaml 文件中通过路径依赖来引用它:
dependencies: flutter: sdk: flutter my_browser: # 你的本地包名称 path: ../my_browser # 包目录的相对路径包是高效 Flutter 开发的基础,它利用社区贡献并实现了代码复用。
在将包添加到项目之前,务必检查包的许可协议,并评估其维护状态和兼容性。