Selama beberapa tahun terakhir, saya sudah beberapa kali menulis utilitas Flutter yang akhirnya digunakan di lebih dari satu proyek. Awalnya, saya hanya copy-paste kode antar proyek β solusi yang bekerja tapi tidak skalabel dan menciptakan banyak versi yang berbeda-beda.
Membuat package dan mempublikasikannya ke pub.dev mengubah cara kerja itu sepenuhnya. Perubahan diperbaiki di satu tempat, semua proyek yang menggunakannya tinggal update versi. Dan kalau package tersebut berguna untuk orang lain, ada kepuasan tersendiri melihat download count bertambah dari developer yang tidak Anda kenal.
Artikel ini adalah panduan lengkap dari nol sampai package Anda muncul di pub.dev.
Perbedaan Package, Plugin, dan Module
Sebelum mulai, klarifikasi terminologi yang sering membingungkan:
Package adalah kode Dart murni β tidak ada kode platform native (Android/iOS). Utility library, widget kustom, state management helper β semua ini biasanya package.
Plugin adalah package yang mengandung kode platform native selain Dart. Kalau Anda mengakses kamera, Bluetooth, atau API sistem yang tidak tersedia di Dart murni, Anda membutuhkan plugin.
Untuk kebanyakan use case β widget kustom, utility function, extension β package Dart murni sudah cukup dan jauh lebih mudah dimaintain.
Membuat Struktur Package
flutter create --template=package nama_package_saya
Ini menghasilkan struktur:
nama_package_saya/
βββ lib/
β βββ nama_package_saya.dart # File utama β export semua public API
β βββ src/ # Implementasi (tidak di-export langsung)
β βββ widget_utama.dart
β βββ utility.dart
βββ test/
β βββ nama_package_saya_test.dart
βββ example/ # Contoh penggunaan (SANGAT penting)
β βββ lib/
β β βββ main.dart
β βββ pubspec.yaml
βββ CHANGELOG.md
βββ LICENSE
βββ README.md
βββ pubspec.yaml
Satu pola penting: semua kode implementasi ada di folder src/, dan hanya yang perlu di-expose ke pengguna yang di-export dari file utama:
// lib/nama_package_saya.dart
library nama_package_saya;
export 'src/widget_utama.dart';
export 'src/models/data_model.dart';
// src/internal_helper.dart TIDAK di-export β hanya untuk internal
Ini memberikan kontrol atas public API package. Pengguna tidak bisa (dan tidak perlu) mengakses implementasi internal yang bisa berubah kapan saja.
pubspec.yaml: Detail yang Menentukan Skor
pub.dev menghitung "pub points" berdasarkan berbagai kriteria, salah satunya adalah kelengkapan pubspec.yaml:
name: nama_package_saya
description: >
Deskripsi yang jelas dan informatif tentang apa yang package ini lakukan,
masalah apa yang dipecahkan, dan untuk siapa package ini ditujukan.
Minimal 60 karakter untuk mendapatkan poin penuh.
version: 1.0.0
homepage: https://github.com/username/nama_package_saya
repository: https://github.com/username/nama_package_saya
issue_tracker: https://github.com/username/nama_package_saya/issues
environment:
sdk: '>=3.0.0 <4.0.0'
flutter: '>=3.10.0'
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
# Untuk plugin (jika ada kode native):
# flutter:
# plugin:
# platforms:
# android:
# package: com.contoh.nama_package
# pluginClass: NamaPackagePlugin
# ios:
# pluginClass: NamaPackagePlugin
API Design: Yang Paling Menentukan Kualitas Package
Package yang sukses di pub.dev bukan hanya yang fungsional β tapi yang API-nya intuitif dan mudah digunakan. Beberapa prinsip yang saya pegang:
Ikuti konvensi Flutter dan Dart. Pengguna package Anda sudah terbiasa dengan API Flutter. Nama method, parameter, dan pola yang konsisten dengan Flutter standard library akan menurunkan kurva belajar drastis.
Gunakan named parameters untuk parameter opsional. Positional parameters untuk konstruktor widget rawan membingungkan ketika urutannya tidak intuitif:
// Kurang baik
MyWidget(Colors.blue, 16.0, 'Label', true)
// Lebih baik
MyWidget(
warna: Colors.blue,
ukuranFont: 16.0,
label: 'Label',
aktif: true,
)
Sediakan nilai default yang masuk akal. Pengguna harus bisa menggunakan widget Anda dengan konfigurasi minimal dan mendapatkan hasil yang sudah bagus. Customization adalah fitur, bukan requirement.
Ikuti prinsip least surprise. Jika nama method Anda adalah getData(), pengguna mengharapkan ia mengembalikan data β bukan mengubah state atau memiliki side effect tersembunyi.
Dokumentasi: Penentu Apakah Package Anda Dipakai
Kode yang tidak terdokumentasi tidak akan dipakai orang lain β sederhana. pub.dev menampilkan API documentation yang di-generate dari doc comments di kode Anda:
/// Widget yang menampilkan teks dengan animasi ketik.
///
/// Cocok digunakan untuk efek "typing" pada chatbot, tutorial,
/// atau konten yang ingin diperkenalkan secara bertahap.
///
/// Contoh penggunaan:
///
/// ```dart
/// TypingText(
/// teks: 'Halo! Bagaimana saya bisa membantu Anda?',
/// kecepatan: const Duration(milliseconds: 50),
/// onSelesai: () => setState(() => _tampilkanTombol = true),
/// )
/// ```
class TypingText extends StatefulWidget {
/// Teks yang akan ditampilkan dengan efek ketik.
final String teks;
/// Interval waktu antar karakter.
///
/// Nilai default adalah 50ms. Nilai yang lebih besar menghasilkan
/// efek yang lebih lambat.
final Duration kecepatan;
/// Callback yang dipanggil ketika semua teks selesai ditampilkan.
final VoidCallback? onSelesai;
const TypingText({
super.key,
required this.teks,
this.kecepatan = const Duration(milliseconds: 50),
this.onSelesai,
});
// ...
}
Testing: Non-Negotiable untuk Package Publik
Package tanpa test adalah package yang tidak bisa Anda percaya untuk diupdate dengan aman. Dan pengguna yang berpengalaman akan melihat coverage sebelum memutuskan menggunakan package Anda.
// test/typing_text_test.dart
void main() {
group('TypingText', () {
testWidgets('menampilkan karakter secara bertahap', (tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
body: TypingText(teks: 'Halo'),
),
),
);
// Awalnya tidak ada teks
expect(find.text('Halo'), findsNothing);
// Setelah 50ms, karakter pertama muncul
await tester.pump(const Duration(milliseconds: 50));
expect(find.text('H'), findsOneWidget);
// Setelah 200ms, semua karakter muncul
await tester.pump(const Duration(milliseconds: 200));
expect(find.text('Halo'), findsOneWidget);
});
testWidgets('memanggil onSelesai setelah semua teks tampil', (tester) async {
bool selesai = false;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: TypingText(
teks: 'Hi',
onSelesai: () => selesai = true,
),
),
),
);
await tester.pump(const Duration(milliseconds: 200));
expect(selesai, isTrue);
});
});
}
Proses Publikasi
# Verifikasi package siap dipublikasikan
flutter pub publish --dry-run
# Jika tidak ada error, publikasikan
flutter pub publish
Perintah --dry-run melakukan semua pengecekan tanpa benar-benar upload. Perbaiki semua warning yang muncul β setiap warning mengurangi pub points dan merusak kesan profesional.
Setelah publikasi, pub.dev membutuhkan beberapa menit untuk menganalisis package dan menghitung pub points. Targetkan 130/140 poin β ini menandakan package yang well-maintained dan terdokumentasi dengan baik.
CHANGELOG.md: Komunikasi yang Sering Dilupakan
Setiap update versi harus disertai perubahan di CHANGELOG.md. Format yang saya gunakan (mengikuti keepachangelog.com):
## [1.1.0] - 2026-02-17
### Added
- Tambahkan parameter `kurva` untuk mengkustomisasi kurva animasi
- Dukungan untuk teks multi-baris
### Fixed
- Perbaiki memory leak ketika widget di-dispose saat animasi berjalan
### Changed
- Default kecepatan diubah dari 50ms ke 40ms per karakter
## [1.0.0] - 2026-02-01
- Rilis awal
Kesimpulan
Membuat package Flutter yang baik adalah latihan dalam empati terhadap developer lain β memaksa Anda berpikir dari perspektif pengguna API, bukan penulis implementasi. API yang intuitif, dokumentasi yang lengkap, test yang komprehensif, dan versioning yang teratur.
Kualitas yang sama yang membuat package Anda dihargai pengguna pub.dev adalah kualitas yang membuat kode internal di proyek Anda lebih mudah dimaintain. Mendisiplinkan diri untuk menulis package yang "layak dipublikasikan" β bahkan untuk kode yang tidak akan pernah dipublikasikan β adalah cara yang efektif untuk meningkatkan kualitas koding secara keseluruhan.
Belum ada komentar. Jadilah yang pertama menulis.
Tulis Komentar