paperfly_courier 0.1.0
paperfly_courier: ^0.1.0 copied to clipboard
Official Dart/Flutter client for the Paperfly (Bangladesh) merchant courier API — create parcels, exchange orders, track shipments, cancel orders, and parse webhook events.
import 'package:flutter/material.dart';
import 'package:paperfly_courier/paperfly_courier.dart';
void main() => runApp(const PaperflyExampleApp());
class PaperflyExampleApp extends StatelessWidget {
const PaperflyExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Paperfly Courier Example',
theme: ThemeData(colorSchemeSeed: Colors.blue, useMaterial3: true),
home: const CreateParcelPage(),
);
}
}
/// Demonstrates creating a parcel and then tracking or cancelling it.
///
/// Replace the placeholder credentials below with your own merchant
/// username, password, and `paperflykey` header value (Developer &
/// Plugins → API Docs in the merchant dashboard). Never ship real
/// credentials inside a client app you distribute publicly — call
/// Paperfly through your own backend instead, and use this page only as
/// a reference for the request/response shapes.
class CreateParcelPage extends StatefulWidget {
const CreateParcelPage({super.key});
@override
State<CreateParcelPage> createState() => _CreateParcelPageState();
}
class _CreateParcelPageState extends State<CreateParcelPage> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController(text: 'Jane Doe');
final _phoneController = TextEditingController(text: '01700000000');
final _addressController =
TextEditingController(text: 'House 12, Road 5, Banani, Dhaka');
final _productController = TextEditingController(text: 'Blue T-Shirt (L)');
final _priceController = TextEditingController(text: '590');
final _weightController = TextEditingController(text: '0.5');
late final PaperflyClient _client = PaperflyClient(
merchantUsername: 'YOUR_MERCHANT_USERNAME',
merchantPassword: 'YOUR_MERCHANT_PASSWORD',
paperflyKey: 'YOUR_PAPERFLY_KEY',
);
bool _loading = false;
String? _resultText;
String? _lastReference;
@override
void dispose() {
_client.close();
_nameController.dispose();
_phoneController.dispose();
_addressController.dispose();
_productController.dispose();
_priceController.dispose();
_weightController.dispose();
super.dispose();
}
Future<void> _createParcel() async {
if (!_formKey.currentState!.validate()) return;
setState(() {
_loading = true;
_resultText = null;
});
final reference = 'ORDER-${DateTime.now().millisecondsSinceEpoch}';
try {
final order = await _client.createOrder(
PaperflyOrderRequest(
merchantOrderReference: reference,
storeName: 'My Shop',
productBrief: _productController.text,
packagePrice: num.parse(_priceController.text),
maxWeight: num.parse(_weightController.text),
customerName: _nameController.text,
customerAddress: _addressController.text,
customerPhone: _phoneController.text,
),
);
setState(() {
_lastReference = reference;
_resultText = 'Created ${order.trackingNumber} '
'(barcode ${order.trackingBarcode})';
});
} on PaperflyException catch (e) {
setState(() => _resultText = 'Error: $e');
} finally {
setState(() => _loading = false);
}
}
Future<void> _trackParcel() async {
final reference = _lastReference;
if (reference == null) return;
setState(() {
_loading = true;
_resultText = null;
});
try {
final tracking = await _client.trackOrder(reference);
final timeline = tracking.latest?.timeline ?? const [];
setState(() {
_resultText = timeline.isEmpty
? 'No tracking milestones yet.'
: timeline.map((m) => '${m.stage}: ${m.time}').join('\n');
});
} on PaperflyException catch (e) {
setState(() => _resultText = 'Error: $e');
} finally {
setState(() => _loading = false);
}
}
Future<void> _cancelParcel() async {
final reference = _lastReference;
if (reference == null) return;
setState(() {
_loading = true;
_resultText = null;
});
try {
final cancelled = await _client.cancelOrder(reference);
setState(() => _resultText = cancelled.message);
} on PaperflyException catch (e) {
setState(() => _resultText = 'Error: $e');
} finally {
setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Create Paperfly Parcel')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'Customer name'),
validator: (v) => (v == null || v.isEmpty) ? 'Required' : null,
),
const SizedBox(height: 8),
TextFormField(
controller: _phoneController,
decoration: const InputDecoration(labelText: 'Customer phone'),
validator: (v) => (v == null || v.isEmpty) ? 'Required' : null,
),
const SizedBox(height: 8),
TextFormField(
controller: _addressController,
decoration:
const InputDecoration(labelText: 'Customer address'),
validator: (v) => (v == null || v.isEmpty) ? 'Required' : null,
),
const SizedBox(height: 8),
TextFormField(
controller: _productController,
decoration: const InputDecoration(labelText: 'Product brief'),
validator: (v) => (v == null || v.isEmpty) ? 'Required' : null,
),
const SizedBox(height: 8),
TextFormField(
controller: _priceController,
decoration:
const InputDecoration(labelText: 'Package price (BDT)'),
keyboardType: TextInputType.number,
validator: (v) =>
num.tryParse(v ?? '') == null ? 'Enter a number' : null,
),
const SizedBox(height: 8),
TextFormField(
controller: _weightController,
decoration: const InputDecoration(labelText: 'Weight (kg)'),
keyboardType: TextInputType.number,
validator: (v) =>
num.tryParse(v ?? '') == null ? 'Enter a number' : null,
),
const SizedBox(height: 16),
FilledButton(
onPressed: _loading ? null : _createParcel,
child: const Text('Create parcel'),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed:
(_loading || _lastReference == null) ? null : _trackParcel,
child: const Text('Track'),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton(
onPressed: (_loading || _lastReference == null)
? null
: _cancelParcel,
child: const Text('Cancel'),
),
),
],
),
const SizedBox(height: 16),
if (_loading) const Center(child: CircularProgressIndicator()),
if (_resultText != null) Text(_resultText!),
],
),
),
),
);
}
}