Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Dart SDK

The official Dart SDK provides full type safety, async support, and convenient methods for all API operations. Ideal for Flutter mobile and web applications.

Add to your pubspec.yaml:

dependencies:
mando_sdk: ^1.0.0

Then run:

Terminal window
flutter pub get
# or
dart pub get
  • Dart 3.0+
  • Flutter 3.10+ (for Flutter apps)
import 'dart:io';
import 'package:mando_sdk/mando_sdk.dart';
void main() async {
final client = MandoClient(
apiKey: Platform.environment['MANDO_API_KEY']!,
baseUrl: 'https://www.mando.fi/api', // optional, this is the default
);
// List all products
final products = await client.plu.list();
print(products.data);
}
final client = MandoClient(
// Required
apiKey: 'your-api-key',
// Optional
baseUrl: 'https://www.mando.fi/api',
timeout: Duration(seconds: 30),
retries: 3,
retryDelay: Duration(seconds: 1),
);
// List all products
final products = await client.plu.list();
// With pagination
final page = await client.plu.list(
page: 1,
pageSize: 50,
);
// With filters
final active = await client.plu.list(
active: true,
groupId: 'beverages',
);
final product = await client.plu.get('550e8400-e29b-41d4-a716-446655440000');
print(product.data.name);
final newProduct = await client.plu.create({
'name': 'Espresso',
'price': 3.50,
'groupId': 'beverages',
'taxId': 'standard-vat',
});
print(newProduct.data.id);
final updated = await client.plu.update('product-id', {
'price': 4.00,
'active': true,
});
import 'package:mando_sdk/mando_sdk.dart';
try {
final product = await client.plu.get('invalid-id');
} on MandoError catch (e) {
print('Error ${e.status}: ${e.message}');
print('Code: ${e.code}');
// Handle specific errors
switch (e.status) {
case 404:
print('Product not found');
break;
case 429:
// Rate limited - retry after delay
final retryAfter = e.retryAfter ?? 60;
print('Rate limited. Retry after $retryAfter seconds');
await Future.delayed(Duration(seconds: retryAfter));
break;
}
} catch (e) {
print('Unexpected error: $e');
}

Iterate through all pages automatically:

// Stream for all products
await for (final product in client.plu.listAll()) {
print(product.name);
}
// Or collect all at once
final allProducts = await client.plu.listAll().toList();

All responses are fully typed:

import 'package:mando_sdk/mando_sdk.dart';
Future<List<String>> getProductNames(MandoClient client) async {
final response = await client.plu.list();
return response.data.map((plu) => plu.name).toList();
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:mando_sdk/mando_sdk.dart';
void main() {
runApp(
Provider<MandoClient>(
create: (_) => MandoClient(
apiKey: const String.fromEnvironment('MANDO_API_KEY'),
),
child: const MyApp(),
),
);
}
class ProductListScreen extends StatelessWidget {
const ProductListScreen({super.key});
@override
Widget build(BuildContext context) {
final client = context.read<MandoClient>();
return FutureBuilder(
future: client.plu.list(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final products = snapshot.data!.data;
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
subtitle: Text('\$${product.price.toStringAsFixed(2)}'),
);
},
);
},
);
}
}
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mando_sdk/mando_sdk.dart';
final mandoClientProvider = Provider<MandoClient>((ref) {
return MandoClient(
apiKey: const String.fromEnvironment('MANDO_API_KEY'),
);
});
final productsProvider = FutureProvider<List<Plu>>((ref) async {
final client = ref.watch(mandoClientProvider);
final response = await client.plu.list();
return response.data;
});
class ProductListScreen extends ConsumerWidget {
const ProductListScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final productsAsync = ref.watch(productsProvider);
return productsAsync.when(
data: (products) => ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) => ListTile(
title: Text(products[index].name),
),
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
);
}
}
final client = MandoClient(
apiKey: 'your-key',
headers: {
'X-Custom-Header': 'value',
},
);
final client = MandoClient(
apiKey: 'your-key',
onRequest: (request) {
print('Making request to ${request.url}');
return request;
},
onResponse: (response) {
print('Received ${response.statusCode}');
return response;
},
);
import 'package:dio/dio.dart';
final cancelToken = CancelToken();
// Cancel after 5 seconds
Future.delayed(Duration(seconds: 5), () {
cancelToken.cancel('Timeout');
});
try {
final products = await client.plu.list(cancelToken: cancelToken);
} on DioException catch (e) {
if (CancelToken.isCancel(e)) {
print('Request was cancelled');
}
}
import 'package:flutter/material.dart';
import 'package:mando_sdk/mando_sdk.dart';
class ProductSearchScreen extends StatefulWidget {
final MandoClient client;
const ProductSearchScreen({super.key, required this.client});
@override
State<ProductSearchScreen> createState() => _ProductSearchScreenState();
}
class _ProductSearchScreenState extends State<ProductSearchScreen> {
final _searchController = TextEditingController();
List<Plu> _products = [];
bool _loading = false;
Future<void> _search(String query) async {
setState(() => _loading = true);
try {
final response = await widget.client.plu.list(
search: query,
active: true,
);
setState(() => _products = response.data);
} on MandoError catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: ${e.message}')),
);
} finally {
setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Product Search')),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: TextField(
controller: _searchController,
decoration: const InputDecoration(
hintText: 'Search products...',
prefixIcon: Icon(Icons.search),
),
onSubmitted: _search,
),
),
if (_loading)
const Center(child: CircularProgressIndicator())
else
Expanded(
child: ListView.builder(
itemCount: _products.length,
itemBuilder: (context, index) {
final product = _products[index];
return ListTile(
title: Text(product.name),
subtitle: Text('\$${product.price.toStringAsFixed(2)}'),
trailing: product.active
? const Icon(Icons.check_circle, color: Colors.green)
: const Icon(Icons.cancel, color: Colors.red),
);
},
),
),
],
),
);
}
}