A Dart/Flutter library for building Nostr clients. Built with simplicity in mind, dart_nostr handles the complexity of the Nostr protocol so you can focus on creating great user experiences.
Nostr (Notes and Other Stuff Transmitted by Relays) is a simple, open protocol for global, decentralized, and censorship-resistant social media. This library gives you everything you need to build Nostr clients in Dart or Flutter.
- Complete key management (generation, derivation, encoding/decoding)
- Event creation, signing, and verification
- WebSocket relay connections with automatic reconnection
- Event subscriptions with stream and future-based APIs
- Support for 40+ NIPs out of the box
- NIP-05 verification
- NIP-19 entity encoding (nevent, nprofile, etc.)
- Proof of work support (NIP-13)
- And much more
Currently implements: 01, 02, 03, 04, 05, 06, 08, 09, 10, 11, 13, 14, 15, 18, 19, 21, 23, 24, 25, 27, 28, 30, 31, 32, 36, 38, 39, 40, 45, 47, 48, 50, 51, 52, 53, 56, 57, 58, 72, 75, 78, 84, 89, 94, 98, 99.
Note: NIP-42 and NIP-44 are planned for future releases. Platform-specific NIPs like NIP-07 (web browser extensions) aren't directly applicable to this library.
Add dart_nostr to your pubspec.yaml:
dependencies:
dart_nostr: ^9.2.4Or use the command line:
flutter pub add dart_nostr # Flutter
dart pub add dart_nostr # DartHere's a simple example to get you started:
import 'package:dart_nostr/dart_nostr.dart';
void main() async {
// Initialize
final nostr = Nostr.instance;
// Generate keys
final keyPair = nostr.keysService.generateKeyPair();
print('Public key: ${keyPair.public}');
// Connect to relays
await nostr.relaysService.init(
relaysUrl: ['wss://relay.damus.io', 'wss://nos.lol'],
);
// Create and publish an event
final event = NostrEvent.fromPartialData(
kind: 1,
content: 'Hello Nostr!',
keyPairs: keyPair,
);
nostr.relaysService.sendEventToRelays(event);
// Subscribe to events
final stream = nostr.relaysService.startEventsSubscription(
request: NostrRequest(filters: [
NostrFilter(kinds: [1], limit: 10),
]),
);
stream.stream.listen((event) {
print('Received: ${event.content}');
});
}You can use dart_nostr in two ways depending on your needs:
// Singleton - shared state across your app
final nostr = Nostr.instance;// Multiple instances - isolated state
final nostr1 = Nostr();
final nostr2 = Nostr(); // Completely independentMost apps work well with the singleton. Use multiple instances if you need to connect to different relay sets simultaneously or want to isolate different parts of your app.
final keyPair = nostr.keysService.generateKeyPair();
print(keyPair.public); // Your public key
print(keyPair.private); // Keep this secret!// If you already have a private key
final keyPair = nostr.keysService
.generateKeyPairFromExistingPrivateKey(myPrivateKey);final signature = nostr.keysService.sign(
privateKey: keyPair.private,
message: "GM",
);
final isValid = nostr.keysService.verify(
publicKey: keyPair.public,
message: "GM",
signature: signature,
);// Encode to human-friendly formats
final nsec = nostr.keysService.encodePrivateKeyToNsec(privateKey);
final npub = nostr.keysService.encodePublicKeyToNpub(publicKey);
// Decode back
final privateKey = nostr.keysService.decodeNsecKeyToPrivateKey(nsec);
final publicKey = nostr.keysService.decodeNpubKeyToPublicKey(npub);The easiest way:
final event = NostrEvent.fromPartialData(
kind: 1, // Text note
content: 'Hello Nostr!',
keyPairs: keyPair,
tags: [
['p', 'pubkey_to_mention'],
['e', 'event_id_to_reference'],
],
);
// Event is already signed and has an ID
print(event.id);
print(event.sig);await nostr.relaysService.init(
relaysUrl: [
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
],
);Real-time streaming:
final stream = nostr.relaysService.startEventsSubscription(
request: NostrRequest(filters: [
NostrFilter(
kinds: [1],
authors: [keyPair.public],
limit: 50,
),
]),
);
stream.stream.listen((event) {
print(event.content);
});
// Don't forget to close when done
stream.close();One-time fetch (waits for EOSE):
final events = await nostr.relaysService.startEventsSubscriptionAsync(
request: NostrRequest(filters: [
NostrFilter(kinds: [1], limit: 20),
]),
);
print('Got ${events.length} events');nostr.relaysService.sendEventToRelays(event);
// Or wait for confirmation
final ok = await nostr.relaysService.sendEventToRelaysAsync(event);
print(ok.message);final verified = await nostr.utilsService.verifyNip05(
internetIdentifier: "user@domain.com",
pubKey: publicKey,
);// Create shareable event links
final nevent = nostr.utilsService.encodeNevent(
eventId: event.id,
pubkey: keyPair.public,
userRelays: ['wss://relay.damus.io'],
);
// Create profile links
final nprofile = nostr.utilsService.encodeNProfile(
pubkey: keyPair.public,
userRelays: ['wss://relay.damus.io'],
);final countEvent = NostrCountEvent.fromPartialData(
eventsFilter: NostrFilter(kinds: [1], authors: [keyPair.public]),
);
final count = await nostr.relaysService.sendCountEventToRelaysAsync(countEvent);
print('User has ${count.count} text notes');final info = await nostr.relaysService.relayInformationsDocumentNip11(
relayUrl: "wss://relay.damus.io",
);
print(info?.name);
print(info?.supportedNips);The example directory contains runnable examples showing:
- Basic key management
- Creating and publishing events
- Subscribing to event streams
- Working with different event kinds
- NIP-05 verification flows
- And more real-world scenarios
Full API documentation is available at pub.dev.
Found a bug? Have a feature idea? Contributions are welcome!
- Check existing issues or create a new one
- Fork the repository
- Create your feature branch
- Make your changes
- Submit a pull request
Please ensure your code follows the existing style and includes tests where appropriate.
MIT License - see LICENSE for details.
- Open an issue
- Start a discussion