Why use the mistralai_dart Flutter package?
The mistralai_dart Flutter package is a lightweight, typed client for Mistral AI’s REST API. It lets you call chat completions, generate embeddings, manage fine‑tuned models, and orchestrate multi‑step agents directly from Flutter or pure Dart code without writing low‑level HTTP boilerplate.
- Typed request and response models for compile‑time safety.
- Built‑in async/await support and optional streaming for chat.
- Cross‑platform – works on Android, iOS, web, and desktop.
- Retry logic helps smooth over transient network errors.
Tip: Keep your API key out of source control. Use Flutter’s --dart-define flag or a secure secret manager.
When to choose mistralai_dart
Use this package when you need any of the following in a Flutter project:
- Real‑time conversational assistants (chatbot UI).
- Semantic search or recommendation engines powered by embeddings.
- Automated content generation (articles, code snippets, etc.).
- Fine‑tuning workflows that require programmatic model management.
Installation
Add the dependency with the Flutter CLI:
flutter pub add mistralai_dartThen run flutter pub get to fetch the package.
Basic Chat Example
The following minimal app demonstrates a single‑turn chat using MistralClient. Replace YOUR_API_KEY with a valid Mistral API key.
import 'package:flutter/material.dart';
import 'package:mistralai_dart/mistralai_dart.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: ChatScreen(),
);
}
}
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key});
@override
State createState() => _ChatScreenState();
}
class _ChatScreenState extends State {
final TextEditingController _controller = TextEditingController();
final MistralClient _client = MistralClient(apiKey: 'YOUR_API_KEY');
String _response = '';
bool _isLoading = false;
Future _send() async {
final userMessage = _controller.text.trim();
if (userMessage.isEmpty) return;
setState(() => _isLoading = true);
try {
final result = await _client.chat(messages: [
ChatMessage(role: Role.user, content: userMessage),
]);
setState(() => _response = result.choices.first.message.content);
} on MistralException catch (e) {
setState(() => _response = 'Error: ${e.message}');
} finally {
setState(() => _isLoading = false);
_controller.clear();
}
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Mistral Chat')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Text(_response, style: const TextStyle(fontSize: 16)),
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Ask something...',
border: OutlineInputBorder(),
),
onSubmitted: (_) => _send(),
),
),
const SizedBox(width: 8),
_isLoading
? const CircularProgressIndicator()
: IconButton(
icon: const Icon(Icons.send),
onPressed: _send,
),
],
),
],
),
),
);
}This example shows the typical data flow:
- Collect user input.
- Call
client.chatwith a list ofChatMessageobjects. - Update UI with the assistant’s response.
Streaming responses (optional)
If you need token‑by‑token updates, the package offers a chatStream method. Below is a concise illustration:
Future _sendStreaming() async {
final userMessage = _controller.text.trim();
if (userMessage.isEmpty) return;
setState(() => _response = '');
final stream = _client.chatStream(messages: [
ChatMessage(role: Role.user, content: userMessage),
]);
await for (final chunk in stream) {
setState(() => _response += chunk.choices.first.delta?.content ?? '');
}
}Remember to handle cancellation and errors when using streams in production.
Embedding Use‑case
Generating vector embeddings is straightforward. The following snippet creates an embedding for a short paragraph and prints the first five dimensions.
Future generateEmbedding() async {
final result = await _client.embeddings(input: 'Flutter makes beautiful UIs easy.');
final vector = result.data.first.embedding;
print('First 5 dimensions: ${vector.take(5).toList()}');
}Common Mistakes & How to Avoid Them
- Hard‑coding the API key. This exposes credentials in version control. Use environment variables or
--dart-defineinstead. - Ignoring rate limits. Mistral enforces per‑minute limits. Implement exponential back‑off or respect the
Retry-Afterheader. - Assuming the response is always non‑null. Network failures or malformed requests throw
MistralException. Always wrap calls intry/catch. - Using the same
MistralClientinstance across isolates without synchronization. The client holds an internalhttp.Client; create separate instances per isolate or share a thread‑safe wrapper.
Tip: The package includes built‑in retry logic, but you may still want to add your own exponential back‑off for critical flows.
Setup Checklist
- Obtain a Mistral API key from the Mistral console.
- Add
mistralai_darttopubspec.yamlviaflutter pub add. - Store the API key securely (e.g.,
--dart-define=MISTRAL_API_KEY=…). - Test a simple chat request before building complex UI.
- Monitor usage in the Mistral dashboard to avoid unexpected billing.
Further Reading
For detailed type definitions, streaming options, and fine‑tuning endpoints, refer to the official package documentation on Pub.dev:
FAQ
- Q: Does the package support web? A: Yes. The client uses
httpwhich works on Flutter web, mobile, and desktop. - Q: How do I handle streaming chat on a background isolate? A: Create a separate
MistralClientinside the isolate and forward streamed chunks viaSendPort. The package itself does not manage isolates. - Q: Can I use the same client for embeddings and chat? A: Absolutely. The client exposes methods for all supported endpoints.
- Q: What happens if I exceed the rate limit? A: The API returns a 429 response. The client throws a
MistralExceptioncontaining the status code; you should catch it and optionally respect theRetry-Afterheader. - Q: Is there a way to cancel an ongoing request? A: Pass a
CancelToken(if using the underlyingdioclient) or close the stream returned bychatStream. The package does not expose a direct cancel method.
Frequently Asked Questions
Does the mistralai_dart package work on Flutter web?
Yes. The client uses the standard http package, which is compatible with Flutter web, mobile, and desktop platforms.
How can I keep my Mistral API key secure in a Flutter project?
Avoid hard‑coding the key. Use Flutter's --dart-define flag, a .env file with flutter_dotenv, or a secure secret manager. Never commit the key to version control.
What should I do if I receive a 429 Too Many Requests error?
The client throws a MistralException containing the status code. Inspect the response's Retry-After header and implement exponential back‑off before retrying.
Can I stream chat responses token by token?
Yes. Use the client.chatStream method, which returns a Stream of partial responses. Remember to handle cancellation and errors when consuming the stream.
Is there built‑in support for fine‑tuning models?
The package includes endpoints for managing fine‑tuned models. Refer to the official documentation on pub.dev for request/response models and usage examples.