A Flutter plugin is a package that wraps native platform code — Kotlin/Java for Android and Swift/Objective-C for iOS — and exposes it through a consistent Dart API. Plugins are what allow Flutter apps to feel fully native while sharing a single codebase.
What Are Flutter Plugins?
Plugins bridge Flutter code and the underlying native platform code, giving you access to device features that cannot be implemented in pure Dart.
Dart Code (Flutter App)
|
| Platform Channel
v
+--------------------+ +--------------------+
| Android Plugin | | iOS Plugin |
| (Kotlin / Java) | | (Swift / ObjC) |
+--------------------+ +--------------------+
| |
Android APIs iOS APIs
(Camera, GPS, etc.) (Camera, GPS, etc.)
Why use plugins:
- Reuse community-maintained code for standard device features
- Provide a consistent API across iOS and Android
- Avoid writing separate native code for every platform yourself
- Access a large ecosystem of packages on pub.dev
Common Flutter Plugins
| Plugin | Purpose |
|---|---|
http | HTTP requests and REST API calls |
dio | Advanced HTTP client with interceptors |
shared_preferences | Persistent key-value storage |
firebase_core | Firebase SDK initialization |
sqflite | SQLite database access |
google_maps_flutter | Google Maps integration |
camera | Device camera access |
geolocator | GPS and location services |
image_picker | Gallery and camera image selection |
url_launcher | Open URLs in the browser |
How to Use Plugins
Using a plugin is a three-step process.
Step 1 — Find the Plugin
Browse pub.dev, the official Dart package repository. Review the package's:
- Popularity score and likes
- Null safety support
- Platform compatibility (iOS, Android, Web)
- Last published date and maintenance status
Step 2 — Add the Plugin
Add the package to your project using the flutter pub add command:
flutter pub add url_launcher
flutter pub add http
flutter pub add camera
This automatically updates pubspec.yaml and runs flutter pub get.
Alternatively, add it manually to pubspec.yaml:
dependencies:
flutter:
sdk: flutter
url_launcher: ^6.2.0
http: ^1.1.0
shared_preferences: ^2.2.0
Then run:
flutter pub get
Step 3 — Use the Plugin
Import the package in your Dart file and use its API:
import 'package:url_launcher/url_launcher.dart';
Future<void> _openWebsite() async {
final Uri url = Uri.parse('https://flutter.dev');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
}
HTTP Plugin Example
A complete example fetching data from a REST API using the http package.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<List<dynamic>> fetchPosts() async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/posts'),
);
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Failed to load posts: ${response.statusCode}');
}
}
class PostList extends StatefulWidget {
const PostList({super.key});
@override
State<PostList> createState() => _PostListState();
}
class _PostListState extends State<PostList> {
late Future<List<dynamic>> futurePosts;
@override
void initState() {
super.initState();
futurePosts = fetchPosts();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const AppBar(title: Text('API Data')),
body: FutureBuilder<List<dynamic>>(
future: futurePosts,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final post = snapshot.data![index];
return ListTile(
title: Text(post['title']),
subtitle: Text(post['body']),
);
},
);
}
return const Center(child: Text('No data'));
},
),
);
}
}
Shared Preferences Plugin
The shared_preferences plugin stores simple key-value data persistently on the device.
flutter pub add shared_preferences
import 'package:shared_preferences/shared_preferences.dart';
// Save a value
Future<void> saveUsername(String username) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', username);
}
// Load a value
Future<String> loadUsername() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('username') ?? 'Guest';
}
// Delete a value
Future<void> clearUsername() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('username');
}
Native Features via Plugins
Several device capabilities require explicit plugin installation and permission configuration.
Camera
flutter pub add camera
After adding, configure permissions:
- Android — add to
android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA"/>
- iOS — add to
ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>This app requires camera access to take photos.</string>
Creating Your Own Plugin
When no existing plugin meets your needs, create a custom one.
flutter create --template=plugin my_custom_plugin
This generates a plugin project structure:
my_custom_plugin/
├── lib/
│ └── my_custom_plugin.dart <-- Dart API
├── android/ <-- Kotlin/Java implementation
├── ios/ <-- Swift/Objective-C implementation
├── example/ <-- Example app
└── pubspec.yaml
The Dart API communicates with native code through Platform Channels:
// In the Dart plugin code
static const MethodChannel _channel = MethodChannel('my_custom_plugin');
static Future<String> getPlatformVersion() async {
final version = await _channel.invokeMethod<String>('getPlatformVersion');
return version ?? 'Unknown';
}
On the native side (Android Kotlin example):
class MyCustomPlugin : FlutterPlugin, MethodCallHandler {
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
}
Managing Plugin Compatibility
Version Constraints
Specify compatible version ranges in pubspec.yaml to avoid breaking changes:
dependencies:
http: ">=1.0.0 <2.0.0"
shared_preferences: ^2.2.0 # Compatible with >=2.2.0 <3.0.0
Dependency Conflicts
Flutter's dependency resolver automatically finds a compatible version. If it fails:
- Run
flutter pub depsto view the dependency tree - Identify the conflicting packages
- Override specific versions using
dependency_overridesinpubspec.yaml
Multi-Platform Testing
Always test plugins on real devices or emulators for both iOS and Android. Platform-specific behavior can differ significantly even when the Dart API appears identical.
# Run on specific device
flutter run -d <device_id>
# List all available devices
flutter devices