Flutter SDK
Cross-platform SDK for Flutter applications on iOS, Android, Web, and Desktop.
Status
🔄 Coming Q4 2025 - Currently in development
Join the waitlist to be notified when available.
Planned Features
Core Features
- âś… Wallet-to-wallet calls (audio/video)
- âś… Push notifications
- âś… Messaging
- âś… Voicemail
- âś… Call history
- âś… Cross-platform support
Platform Support
- âś… iOS
- âś… Android
- âś… Web
- âś… macOS
- âś… Windows
- âś… Linux
Requirements
- Flutter 3.16+
- Dart 3.2+
Planned Installation
Add to pubspec.yaml:
dependencies:
dial_sdk: ^1.0.0Then run:
flutter pub getPlanned API
Setup
import 'package:dial_sdk/dial_sdk.dart';
final dial = DialClient(
apiKey: 'your-api-key',
walletAddress: '0x...',
);Authentication
// Sign message with wallet
final signature = await wallet.signMessage('Authenticate with Dial');
await dial.authenticate(
walletAddress: '0x...',
signature: signature,
);Making Calls
final call = await dial.calls.start(
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
type: CallType.video,
);Flutter Widget Integration
import 'package:flutter/material.dart';
import 'package:dial_sdk/dial_sdk.dart';
class CallScreen extends StatefulWidget {
@override
_CallScreenState createState() => _CallScreenState();
}
class _CallScreenState extends State<CallScreen> {
final dial = DialClient.instance;
DialCall? currentCall;
@override
void initState() {
super.initState();
// Listen for incoming calls
dial.onIncomingCall.listen((call) {
setState(() {
currentCall = call;
});
});
}
Future<void> makeCall() async {
final call = await dial.calls.start(
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
type: CallType.video,
);
setState(() {
currentCall = call;
});
}
@override
Widget build(BuildContext context) {
if (currentCall == null) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: makeCall,
child: Text('Make Call'),
),
),
);
}
return Scaffold(
body: CallWidget(call: currentCall!),
);
}
}
class CallWidget extends StatelessWidget {
final DialCall call;
const CallWidget({required this.call});
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: VideoView(
stream: call.remoteStream,
),
),
CallControls(call: call),
],
);
}
}Provider Pattern
import 'package:provider/provider.dart';
import 'package:dial_sdk/dial_sdk.dart';
class DialProvider extends ChangeNotifier {
final DialClient _dial = DialClient.instance;
DialCall? _currentCall;
DialCall? get currentCall => _currentCall;
DialProvider() {
_dial.onIncomingCall.listen((call) {
_currentCall = call;
notifyListeners();
});
}
Future<void> startCall(String to, CallType type) async {
_currentCall = await _dial.calls.start(to: to, type: type);
notifyListeners();
}
Future<void> endCall() async {
await _currentCall?.end();
_currentCall = null;
notifyListeners();
}
}
// Usage
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => DialProvider(),
child: MyApp(),
),
);
}
class CallButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<DialProvider>(
builder: (context, dialProvider, child) {
return ElevatedButton(
onPressed: () => dialProvider.startCall(
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
CallType.video,
),
child: Text('Call'),
);
},
);
}
}Riverpod Integration
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dial_sdk/dial_sdk.dart';
final dialProvider = Provider((ref) => DialClient.instance);
final currentCallProvider = StreamProvider<DialCall?>((ref) {
final dial = ref.watch(dialProvider);
return dial.onIncomingCall;
});
class CallScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentCall = ref.watch(currentCallProvider);
return currentCall.when(
data: (call) {
if (call == null) {
return Center(child: Text('No active call'));
}
return CallWidget(call: call);
},
loading: () => CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
);
}
}Messaging
// Send message
await dial.messages.send(
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
content: 'Hello!',
type: MessageType.text,
);
// Listen for messages
dial.onMessageReceived.listen((message) {
print('New message from ${message.from}: ${message.content}');
});
// Get message history
final messages = await dial.messages.getHistory(
with: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
limit: 50,
);Push Notifications
iOS
import 'package:dial_sdk/dial_sdk.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
Future<void> setupPushNotifications() async {
final messaging = FirebaseMessaging.instance;
// Request permission
await messaging.requestPermission();
// Get token
final token = await messaging.getToken();
// Register with Dial
await DialClient.instance.registerPushToken(token);
}Android
import 'package:dial_sdk/dial_sdk.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
Future<void> setupPushNotifications() async {
final messaging = FirebaseMessaging.instance;
// Get token
final token = await messaging.getToken();
// Register with Dial
await DialClient.instance.registerPushToken(token);
// Handle background messages
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await DialNotificationHandler.handle(message);
}Streams & Reactive Programming
Full support for Dart Streams:
// Call stream
dial.onIncomingCall.listen((call) {
print('Incoming call from ${call.from}');
});
// Message stream
dial.onMessageReceived.listen((message) {
print('New message: ${message.content}');
});
// Call status changes
dial.onCallStatusChanged.listen((status) {
print('Call status: $status');
});Platform-Specific Features
iOS CallKit
import 'package:dial_sdk/dial_sdk.dart';
// Automatically integrated on iOS
await dial.enableCallKit(
appName: 'MyApp',
ringtoneSound: 'ringtone.mp3',
);Android ConnectionService
import 'package:dial_sdk/dial_sdk.dart';
// Automatically integrated on Android
await dial.enableConnectionService(
serviceName: 'MyApp Calls',
);Web Support
// Works seamlessly on web
// Uses WebRTC for calls
// No special configuration neededVideo Rendering
import 'package:dial_sdk/dial_sdk.dart';
class VideoView extends StatelessWidget {
final MediaStream stream;
const VideoView({required this.stream});
@override
Widget build(BuildContext context) {
return RTCVideoView(
stream: stream,
mirror: false,
objectFit: RTCVideoViewObjectFit.cover,
);
}
}Complete Example App
import 'package:flutter/material.dart';
import 'package:dial_sdk/dial_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Dial SDK
await DialClient.initialize(
apiKey: 'your-api-key',
);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Dial Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final dial = DialClient.instance;
DialCall? currentCall;
bool isMuted = false;
bool isVideoEnabled = true;
@override
void initState() {
super.initState();
dial.onIncomingCall.listen((call) {
setState(() {
currentCall = call;
});
_showIncomingCallDialog(call);
});
dial.onCallEnded.listen((_) {
setState(() {
currentCall = null;
});
});
}
Future<void> _showIncomingCallDialog(DialCall call) async {
return showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text('Incoming Call'),
content: Text('Call from ${call.from}'),
actions: [
TextButton(
onPressed: () {
call.decline();
Navigator.pop(context);
},
child: Text('Decline'),
),
TextButton(
onPressed: () {
call.answer();
Navigator.pop(context);
},
child: Text('Answer'),
),
],
),
);
}
Future<void> makeCall() async {
final call = await dial.calls.start(
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
type: CallType.video,
);
setState(() {
currentCall = call;
});
}
@override
Widget build(BuildContext context) {
if (currentCall == null) {
return Scaffold(
appBar: AppBar(title: Text('Dial Example')),
body: Center(
child: ElevatedButton(
onPressed: makeCall,
child: Text('Make Video Call'),
),
),
);
}
return Scaffold(
body: Stack(
children: [
// Remote video (full screen)
Positioned.fill(
child: VideoView(stream: currentCall!.remoteStream),
),
// Local video (pip)
Positioned(
top: 50,
right: 20,
width: 120,
height: 160,
child: VideoView(stream: currentCall!.localStream),
),
// Controls
Positioned(
bottom: 50,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FloatingActionButton(
onPressed: () {
setState(() {
isMuted = !isMuted;
});
currentCall!.setMuted(isMuted);
},
child: Icon(
isMuted ? Icons.mic_off : Icons.mic,
),
),
FloatingActionButton(
onPressed: () {
currentCall!.end();
},
backgroundColor: Colors.red,
child: Icon(Icons.call_end),
),
FloatingActionButton(
onPressed: () {
setState(() {
isVideoEnabled = !isVideoEnabled;
});
currentCall!.setVideoEnabled(isVideoEnabled);
},
child: Icon(
isVideoEnabled ? Icons.videocam : Icons.videocam_off,
),
),
],
),
),
],
),
);
}
}Testing
import 'package:flutter_test/flutter_test.dart';
import 'package:dial_sdk/dial_sdk.dart';
import 'package:mockito/mockito.dart';
void main() {
group('Dial SDK Tests', () {
late DialClient dial;
setUp(() {
dial = DialClient(
apiKey: 'test-key',
walletAddress: '0x...',
);
});
test('should start a call', () async {
final call = await dial.calls.start(
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
type: CallType.audio,
);
expect(call, isNotNull);
expect(call.to, equals('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'));
});
});
}Performance
- Native platform channels for optimal performance
- Hardware acceleration on all platforms
- Battery-optimized
- Memory-efficient video rendering
- Background execution support
Roadmap
Q4 2025 - Alpha Release
- Core calling features
- Messaging
- Push notifications
- iOS & Android support
Q1 2026 - Beta Release
- Video conferencing
- Web support
- Desktop support (macOS, Windows, Linux)
- Screen sharing
Q2 2026 - Stable Release
- Full feature parity
- Production-ready
- Complete documentation
- Example apps
Example Projects
Complete example projects will be available:
Get Notified
Want early access?
Next Steps
- TypeScript SDK - Available now
- React Native SDK - Coming Q2 2025
- iOS SDK - Coming Q3 2025
- Android SDK - Coming Q3 2025
Last updated on