Skip to main content

Quick Start

What we can learn from this tutorial

  • how to integrate the Web3MQ Flutter SDK into your own projects
  • how to initialize the Web3MQ client and configure it
  • how to create & join a group
  • how to send & receive messages specifically in a group

REQUIREMENTS

  • Dart SDK: “>=2.19.4 <4.0.0”
  • Flutter SDK: “>=3.7.0”
  • A Web3MQ API Key

Before all, you should have Flutter development environment, see more details on: https://docs.flutter.dev/get-started/install

note

While we are committed to building an open and collectively owned public good, our early stage testnet requires an API key in order to connect. This is to control capacity to make sure that each early partner and developer is able to build a great experience on top of Web3MQ. Apply here.

Add dependency

Add this to your package’s pubspec.yaml file, use the latestversion

dependencies: 
web3mq: 0.2.0-dev.3

You should then run flutter packages get

Example Project

There is a detailed Flutter example project in the example folder. You can directly run and play on it.

Setup API Client

First you need to instantiate a chat client. The chat client will manage API call, event handling and manage the web socket connection to Web3mq servers. You should only create the client once and re-use it across your application. The simplest way to initialize the client is to pass your API key to the constructor. The API key should be applied from our team.

final client = Web3MQClient("api-key");

Endpoint

You can customize the baseURL, which is set to TestnetEndpoint.sg1 by default, or use UtilsApi().findTheLowestLatencyEndpoint() to get the endpoint with the lowest latency and assign it to baseURL.

final endpoint = await UtilsApi().findTheLowestLatencyDevEndpoint();
final client = Web3MQClient("api-key", baseURL: endpoint);

During this initial testing phase, we’ve hosted complete networks of Web3MQ nodes in different regions around the globe. Connect to these endpoints below, to access the Web3MQ Testnet.

Wallet Connector

Some methods that SDK provides require wallet signature, you should setup the WalletConnector before calling those methods.

client.walletConnector = yourWalletConnector;

Each project may have a different way to connect to a wallet. We provide a unified interface as the entry point for connecting and signing wallets. When the SDK needs wallet information or signatures, it will call the corresponding methods in WalletConnector. Because wallet communication supports the unified CAIP standard, whether through WalletConnectV2 or other wallet communication SDKs, it can be easily adapted to the WalletConnectorinterface.

abstract class WalletConnector {
/// Gets the signature of `personal_sign`.
Future<String> personalSign(String message, String address,
{String? password});

/// Connects wallet, and gets the user wallet info.
Future<Wallet> connectWallet();
}

abstract class Wallet {
/// account_id support CAIP-10
final List<String> accounts;

List<DID> get dids;

Wallet(this.accounts);
}

here’s an example code for implementing a WalletConnectV2 connector:

WalletConnectV2 Connector

And InnerWalletConnector, this may be helpful when debug:

InnerWalletConnector

Offline storage

To add data persistence you can extend the class PersistenceClient and pass an instance to the ChatClient. Web3MQPersistenceClient is a default implementation.

client.persistenceClient = Web3MQPersistenceClient();

Create credentials

For any first-time user of Web3MQ’s network, you’ll need to create credentials on Web3MQ’s network.

This method needs wallet signature, make sure you have setup WalletConnector already. credentials contains your PrivateKey and UserId.

note

DIDs have two parts: a type and a value. The type can be "eth" or "starknet", and the value is a wallet address.

// Connect wallet. This may throw an error, especially if you are trying to connect to a wallet using walletconnectV2, but your device does not have a wallet that supports walletconnectV2.
final wallet = await walletConnector.connectWallet();

final did = wallet.dids.first;

// Keep your credentials in a safe place!
final credentials = await client.createCredentials(did, password);

Connect

After you have created credentials, let’s continue to connect to Web3MQ’s network.

Session key

The SessionKey is a temporary key that is used to connect to Web3MQ’s network.

To generate a session key with DID and PrivateKey:

final sessionKey = await client.generateSessionKey(credentials.did, credentials.privateKey, expiredDuration);

you could persist SessionKey for the connection next time.

Connect with a SessionKey

Now you can connect to Web3MQ with the SessionKey.

await client.connectUser(sessionKey)

listen the connectionStatusStream to track connection status.

client.connectionStatusStream.listen((event) {
// handle the event
});

Chat

Let’s continue by sending your first message to another user and joining a chat group and sending messages.

Chat with another user

It's very easy to send a message to another user, you just need to know their UserId.

// To get the Web3MQ UserId corresponding to a wallet address,
// you can call `client.userInfo(didType, didValue)`.
final userInfo = await client.userInfo('eth', address);
if (userInfo == null) {
throw Exception('User not found');
}

final message = await client.sendText('hello, world!', userInfo.userId);

// If you'd like to track the messag status updting
client.messageStatusUpdingStream.listen((res) {
// handle the status updting response
if (res.messageId == message.messageId) {

}
});

Create a chat group

A group contains messages and a list of members who are permanently associated with the group. More docs about group see Group.

One way to join a group is to create your own group.

final group = await client.createGroup('Group Name', 'avatar url',
permissions: GroupPermission.public);

Group chat permission currently only has group:join rule, which indicates the permission to join a group. group:join rule has GroupPermission.public and GroupPermission.invite values, and its value type is ‘enum’

  • GroupPermission.public: Everyone can join without restrictions.
  • GroupPermission.invite: Only the group owner can invite friends to join.

Join a chat group

If you prefer to join a existing group, you can use the joinGroup method with the group id:

await client.joinGroup(groupId);

Send a message to a group

Now we have a group, let’s send a message to the group:

final message = await client.sendText('hello, world!', group.groupId);

Receive messages

To receive new messages from the group or single chat, listen to the newMessageStream event:

client.newMessageStream.listen((message) {
// filter the messages you need to handle.

if (message.topic == group.groupId) {
// handle the message in the group
}
if (message.topic == userId) {
// handle the message with the user
}
})

Query the message list

If you want to view the historical messages:

final pagination = TimestampPagination(limit: 20, timestampBefore: DateTime.now().millisecondsSinceEpoch);
// group messages
final messages = await client.queryMessagesByGroupId(group.groupId, pagination)
// single chat messages
final messages = await client.queryMessagesByUserId(userId, pagination)

Code example

import 'package:flutter/material.dart';
import 'package:flutter_application_1/wallet_connectv2_connector.dart';
import 'package:web3mq/web3mq.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());

final walletConnector = WalletConnectV2Connector();
final url = await UtilsApi().findTheLowestLatencyEndpoint();
final client = Web3MQClient('apiKey', baseURL: url, wc: walletConnector);

// This may throws an error when your device does not have a wallet that supports walletconnectV2.
final wallet = await walletConnector.connectWallet();

final did = wallet.dids.firstOrNull;
if (did == null) {
throw Exception('No did found');
}
final user = await client.userInfo(did.type, did.value);
if (user == null) {
// User not found, we need to create credentials firstly.
final credentials = await client.createCredentials(did, 'your-password');
final sessionKey = await client.generateSessionKey(
did, credentials.userId, const Duration(days: 7));
await client.connectUser(sessionKey);
} else {
// if there's no private key cache, we need to ask for the password
// to retrieve the private key. Otherwise, we can just use the private key
// to generate the session key.
// `final sessionKey = await client.generateSessionKey(
// did, user.userId, const Duration(days: 7));`
final sessionKey = await client.generateSessionKeyWithPassword(
did, 'your-password', const Duration(days: 7));
await client.connectUser(sessionKey);
}

final group = await client.createGroup('test-group-name', null);
final message = await client.sendText('hello, chat', group.groupId);
client.messageStatusUpdingStream.listen((event) {
if (message.messageId == event.messageId &&
event.messageStatus == 'received') {
print('message sent successfully');
}
});
client.newMessageStream.listen((message) {
if (message.topic == group.groupId) {
print('message received: ${message.text}');
}
});
}

What’s next

For detailed API documentation, please refer to other sections. Chat Client Docs