Skip to content

flutter_using_packages

Flutter’s power is significantly enhanced by its rich ecosystem of packages. Packages are bundles of shared code, similar to libraries or modules in other languages, created by the Flutter community and Google. They allow you to easily add pre-built functionality to your app, saving development time and effort.

Pub.dev: The Dart and Flutter Package Repository

Section titled “Pub.dev: The Dart and Flutter Package Repository”

The central repository for Dart and Flutter packages is pub.dev. You can search for packages, view their documentation, check their popularity and health scores, and find installation instructions.

Packages generally fall into these categories:

  • Dart Packages: Written purely in Dart, containing utility functions, classes, or business logic. They don’t depend on the Flutter framework and can often be used in non-Flutter Dart projects as well. Example: http (for making HTTP requests), intl (for internationalization).
  • Flutter Packages (or Plugin Packages): These packages depend on the Flutter framework and may include Dart code combined with platform-specific native code (Android/iOS/Web/Desktop) to access platform features. They often provide Flutter widgets or access to device capabilities.
  • - **Plugins:** Packages that specifically include platform-specific implementation using platform channels. Example: `camera` (accessing device camera), `shared_preferences` (accessing platform's simple storage), `webview_flutter` (embedding web views).
  • - **Pure Flutter Packages:** Packages that depend on Flutter but contain only Dart code, often providing custom widgets, state management solutions, or Flutter-specific utilities. Example: `provider` (state management), `cached_network_image` (advanced image loading).

Adding a package to your Flutter project involves these steps:

  • Open your project’s pubspec.yaml file.
  • Under the dependencies: section, add the package name and the desired version constraint. You can find the latest version and recommended constraint on pub.dev.
  • Common version constraints:
  • - `^version`: Allows updates to new minor and patch versions but not major versions (e.g., `^2.3.0` allows `2.3.1`, `2.4.0`, but not `3.0.0`). This is the most common and recommended constraint.
  • - `version`: Locks to an exact version.
  • - `any`: Allows any version (use with caution).
  • From your project’s root directory in the terminal, run:

  • Most IDEs (VS Code, Android Studio) will automatically detect changes in pubspec.yaml and prompt you to run flutter pub get or run it for you.

  • In the Dart file where you want to use the package, add an import statement at the top, using the package: directive:
  • You can now use the classes, functions, and widgets provided by the imported package.

You can also create your own packages to share code between projects or contribute to the community.

  • Use the Flutter CLI:

  • This creates a standard package structure:

    • lib/: Contains the public Dart code.
    • lib/src/: Conventionally used for private implementation code.
    • pubspec.yaml: Defines the package.
    • README.md, CHANGELOG.md, LICENSE: Documentation and licensing.
    • example/: An optional example Flutter application demonstrating how to use the package.
    • android/, ios/, etc. (for plugins): Contain the native platform code.

Developing a plugin involves:

  1. Defining the Dart API in the lib/ folder.
  2. Implementing the communication logic using Platform Channels (MethodChannel, EventChannel) in Dart.
  3. Implementing the native counterparts on Android (Kotlin/Java) and/or iOS (Swift/Objective-C) that listen on the platform channel, execute the native code, and send results back.

Let’s refine the browser plugin example from the original tutorial content, assuming we created a plugin named my_browser.

import 'dart:async';
import 'package:flutter/services.dart';
class MyBrowser {
// Define the channel name consistently
static const MethodChannel _channel = MethodChannel('com.example.my_browser/channel');
// Method to open the browser
Future<void> openBrowser(String urlString) async {
try {
// Invoke the 'openBrowser' method on the native side
await _channel.invokeMethod('openBrowser', {'url': urlString});
} on PlatformException catch (e) {
print("Failed to open browser: '${e.message}'.");
// Consider re-throwing or handling the error appropriately
}
}
// Example: Platform version (often included in template)
static Future<String?> get platformVersion async {
final String? version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}

Android Implementation (android/src/.../MyBrowserPlugin.kt)

Section titled “Android Implementation (android/src/.../MyBrowserPlugin.kt)”
package com.example.my_browser // Adjust package name
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
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
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
// Required imports
import android.app.Activity
import android.content.Intent
import android.net.Uri
class MyBrowserPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
private lateinit var channel : MethodChannel
private var activity: Activity? = null // Hold activity reference
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)
return
}
try {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
activity?.startActivity(intent)
result.success(true) // Indicate success
} catch (e: Exception) {
result.error("FAILED", "Could not open browser: ${e.message}", null)
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
// ActivityAware methods
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
}
}

Note: The Android implementation uses the newer FlutterPlugin and ActivityAware interfaces for better lifecycle management compared to the older Registrar approach.

iOS Implementation (ios/Classes/MyBrowserPlugin.swift)

Section titled “iOS Implementation (ios/Classes/MyBrowserPlugin.swift)”

The iOS implementation would be similar to the one shown in the Platform Channels chapter, adapted to the plugin structure.

To use a package you are developing locally (before publishing), you can reference it using a path dependency in the pubspec.yaml of the app using it:

dependencies:
flutter:
sdk: flutter
my_browser: # Your local package name
path: ../my_browser # Relative path to the package directory

Packages are fundamental to efficient Flutter development, leveraging community contributions and enabling code reuse.

Always check package licenses and evaluate their maintenance status and compatibility before adding them to your project.