Answer
Overview
Packages and Plugins are reusable code libraries in Flutter. The key difference is that packages contain pure Dart code, while plugins include platform-specific native code (Android/iOS).
Comparison Table
| Feature | Package | Plugin |
|---|---|---|
| Code | Pure Dart only | Dart + Native (Java/Kotlin/Swift/Objective-C) |
| Platform Access | ❌ No | ✅ Yes |
| Examples | provider, http, intl | camera, battery, location |
| Complexity | Simple | Complex |
| Platform Channels | Not used | Uses MethodChannel/EventChannel |
| Dependencies | Dart only | Dart + Android/iOS SDKs |
| Use Case | Business logic, UI | Device features |
Packages
What is a Package?
A package contains pure Dart code that runs on any platform.
Examples
- provider - State management
- http - HTTP networking
- intl - Internationalization
- uuid - UUID generation
- equatable - Value equality
Installation
yamldependencies: provider: ^6.0.5 http: ^1.1.0 intl: ^0.18.0
Usage Example
dartimport 'package:http/http.dart' as http; import 'package:provider/provider.dart'; // Pure Dart code - works everywhere Future<void> fetchData() async { final response = await http.get(Uri.parse('https://api.example.com')); print(response.body); } // State management with provider class Counter with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } }
Plugins
What is a Plugin?
A plugin includes Dart code + platform-specific native code to access device features.
Examples
- camera - Camera access
- battery - Battery info
- location - GPS location
- shared_preferences - Local storage
- url_launcher - Open URLs
- image_picker - Pick images
Installation
yamldependencies: camera: ^0.10.5 battery_plus: ^4.0.0 geolocator: ^10.0.0 shared_preferences: ^2.2.0
Usage Example
dartimport 'package:battery_plus/battery_plus.dart'; import 'package:geolocator/geolocator.dart'; import 'package:shared_preferences/shared_preferences.dart'; // Access battery (native Android/iOS code) Future<int> getBatteryLevel() async { final battery = Battery(); return await battery.batteryLevel; } // Access GPS (native code) Future<Position> getCurrentLocation() async { return await Geolocator.getCurrentPosition(); } // Local storage (native SharedPreferences/UserDefaults) Future<void> saveData(String key, String value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString(key, value); }
How Plugins Work
Architecture
text┌────────────────────────────────────────┐ │ Flutter App (Dart) │ │ ↓ │ │ Plugin Dart Code │ │ ↓ │ │ Platform Channel (MethodChannel) │ └────────────────────────────────────────┘ ↓ ↓ ┌──────────────────┐ ┌──────────────────┐ │ Android │ │ iOS │ │ (Kotlin/Java) │ │ (Swift/Obj-C) │ │ ↓ │ │ ↓ │ │ Native APIs │ │ Native APIs │ └──────────────────┘ └──────────────────┘
Platform Channel Example
Dart side:
dartimport 'package:flutter/services.dart'; class BatteryPlugin { static const platform = MethodChannel('com.example/battery'); Future<int> getBatteryLevel() async { try { final int result = await platform.invokeMethod('getBatteryLevel'); return result; } catch (e) { return -1; } } }
Android side (Kotlin):
kotlinclass MainActivity: FlutterActivity() { private val CHANNEL = "com.example/battery" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) .setMethodCallHandler { call, result -> if (call.method == "getBatteryLevel") { val batteryLevel = getBatteryLevel() result.success(batteryLevel) } } } private fun getBatteryLevel(): Int { val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) } }
iOS side (Swift):
swift@UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let controller = window?.rootViewController as! FlutterViewController let batteryChannel = FlutterMethodChannel(name: "com.example/battery", binaryMessenger: controller.binaryMessenger) batteryChannel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if call.method == "getBatteryLevel" { self.getBatteryLevel(result: result) } }) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func getBatteryLevel(result: FlutterResult) { UIDevice.current.isBatteryMonitoringEnabled = true let batteryLevel = Int(UIDevice.current.batteryLevel * 100) result(batteryLevel) } }
Finding Packages/Plugins
Pub.dev (Official Repository)
Visit pub.dev to browse packages.
Quality Indicators:
- ✅ Pub Points (max 160)
- ✅ Popularity score
- ✅ Likes count
- ✅ Verified publishers
- ✅ Null safety support
- ✅ Recent updates
Popular Packages
State Management:
- provider
- riverpod
- flutter_bloc
- get
Networking:
- http
- dio
Local Storage:
- shared_preferences
- hive
- sqflite
UI/UX:
- flutter_svg
- cached_network_image
- shimmer
- lottie
Utils:
- intl
- uuid
- path_provider
- url_launcher
Creating Your Own Package
Create Package
bashflutter create --template=package my_package
Package Structure
textmy_package/ ├── lib/ │ ├── my_package.dart # Main export file │ └── src/ │ └── my_class.dart # Implementation ├── test/ │ └── my_package_test.dart ├── pubspec.yaml ├── README.md ├── CHANGELOG.md └── LICENSE
Example Package
lib/my_package.dart:
dartlibrary my_package; export 'src/string_utils.dart';
lib/src/string_utils.dart:
dartclass StringUtils { static String capitalize(String text) { if (text.isEmpty) return text; return '${text[0].toUpperCase()}${text.substring(1)}'; } static String reverse(String text) { return text.split('').reversed.join(''); } }
pubspec.yaml:
yamlname: my_package description: Useful string utilities version: 1.0.0 homepage: https://github.com/user/my_package environment: sdk: '>=3.0.0 <4.0.0' dependencies: flutter: sdk: flutter dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0
Publish to Pub.dev
bash# Dry run flutter pub publish --dry-run # Publish flutter pub publish
Creating Your Own Plugin
Create Plugin
bashflutter create --template=plugin my_plugin
Plugin Structure
textmy_plugin/ ├── lib/ │ └── my_plugin.dart # Dart API ├── android/ │ └── src/main/kotlin/ # Android code ├── ios/ │ └── Classes/ # iOS code ├── example/ │ └── lib/main.dart # Demo app └── pubspec.yaml
Example Plugin
lib/my_plugin.dart:
dartimport 'package:flutter/services.dart'; class MyPlugin { static const MethodChannel _channel = MethodChannel('my_plugin'); static Future<String> getPlatformVersion() async { final String version = await _channel.invokeMethod('getPlatformVersion'); return version; } }
android/src/main/kotlin/MyPlugin.kt:
kotlinclass MyPlugin: FlutterPlugin, MethodCallHandler { override fun onMethodCall(call: MethodCall, result: Result) { if (call.method == "getPlatformVersion") { result.success("Android ${android.os.Build.VERSION.RELEASE}") } else { result.notImplemented() } } }
ios/Classes/MyPlugin.swift:
swiftpublic class MyPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "my_plugin", binaryMessenger: registrar.messenger()) let instance = MyPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { if call.method == "getPlatformVersion" { result("iOS " + UIDevice.current.systemVersion) } else { result(FlutterMethodNotImplemented) } } }
Best Practices
Important: Always check package quality before using
✅ Choose Quality Packages
yaml# Good - High pub points, recent update dependencies: provider: ^6.0.5 # 160/160 points, 1M+ downloads # Risky - Low quality indicators dependencies: unknown_package: ^0.0.1 # 50/160 points, old version
Version Constraints
yaml# ✅ Good - Allow compatible updates dependencies: http: ^1.1.0 # ❌ Bad - Locked to exact version dependencies: http: 1.1.0
Minimal Dependencies
yaml# ✅ Good - Only what you need dependencies: http: ^1.1.0 # ❌ Bad - Unused dependencies dependencies: http: ^1.1.0 dio: ^5.0.0 # Don't need both!