komodo_defi_sdk 0.4.0+3
komodo_defi_sdk: ^0.4.0+3 copied to clipboard
A high-level opinionated library that provides a simple way to build cross-platform Komodo Defi Framework applications (primarily focused on wallets). This package seves as the entry point for the pac [...]
example/lib/main.dart
// lib/main.dart
import 'dart:async';
import 'dart:developer' as developer;
import 'package:dragon_logs/dragon_logs.dart' as dragon;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kdf_sdk_example/blocs/auth/auth_bloc.dart';
import 'package:kdf_sdk_example/screens/asset_page.dart';
import 'package:kdf_sdk_example/widgets/instance_manager/instance_view.dart';
import 'package:kdf_sdk_example/widgets/instance_manager/kdf_instance_drawer.dart';
import 'package:kdf_sdk_example/widgets/instance_manager/kdf_instance_state.dart';
import 'package:komodo_cex_market_data/komodo_cex_market_data.dart'
show sparklineRepository;
import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart';
import 'package:logging/logging.dart';
final GlobalKey<ScaffoldMessengerState> _scaffoldKey =
GlobalKey<ScaffoldMessengerState>();
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Setup logging package listener to output to dart:developer log
Logger.root.level = kDebugMode ? Level.ALL : Level.INFO;
Logger.root.onRecord.listen((record) {
developer.log(
record.message,
time: record.time,
level: record.level.value,
name: record.loggerName,
error: record.error,
stackTrace: record.stackTrace,
);
});
await dragon.DragonLogs.init();
// Create instance manager
final instanceManager = KdfInstanceManager();
// Create default SDK instance with config
final defaultSdk = KomodoDefiSdk(config: _config);
await defaultSdk.initialize();
dragon.log('Default SDK instance initialized');
unawaited(
sparklineRepository.init().catchError((
Object? error,
StackTrace? stackTrace,
) {
dragon.log('Error during sparklineRepository initialization: $error');
debugPrintStack(stackTrace: stackTrace);
}),
);
// Register default instance
await instanceManager.registerInstance('Local Instance', _config, defaultSdk);
dragon.log('Registered default instance');
runApp(
MultiRepositoryProvider(
providers: [RepositoryProvider<KomodoDefiSdk>.value(value: defaultSdk)],
child: KdfInstanceManagerProvider(
notifier: instanceManager,
child: MaterialApp(
scaffoldMessengerKey: _scaffoldKey,
navigatorKey: _navigatorKey,
theme: ThemeData(colorSchemeSeed: Colors.blue, useMaterial3: true),
darkTheme: ThemeData(
colorSchemeSeed: Colors.blue,
useMaterial3: true,
brightness: Brightness.dark,
),
home: const KomodoApp(),
),
),
),
);
}
// Default SDK configuration
const KomodoDefiSdkConfig _config = KomodoDefiSdkConfig();
class KomodoApp extends StatefulWidget {
const KomodoApp({super.key});
@override
State<KomodoApp> createState() => _KomodoAppState();
}
class _KomodoAppState extends State<KomodoApp> {
// Instance-specific state management
final Map<String, InstanceState> _instanceStates = {};
final Map<String, KdfUser?> _currentUsers = {};
final Map<String, String> _statusMessages = {};
int _selectedInstanceIndex = 0;
// Form controllers and state
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _searchController = TextEditingController();
List<Asset> _filteredAssets = [];
Map<AssetId, Asset>? _allAssets;
bool _initialized = false;
@override
void initState() {
super.initState();
_searchController.addListener(_filterAssets);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_initialized) {
_initializeInstances();
_initialized = true;
}
}
Future<void> _initializeInstances() async {
final manager = KdfInstanceManagerProvider.of(context);
// Initialize state for each instance
for (final instance in manager.instances.values) {
await _initializeInstance(instance);
}
}
Future<void> _initializeInstance(KdfInstanceState instance) async {
_getOrCreateInstanceState(instance.name);
// Initialize assets
_allAssets = instance.sdk.assets.available;
_filterAssets();
// Initialize auth state
final user = await instance.sdk.auth.currentUser;
_updateInstanceUser(instance.name, user);
// Setup auth state listener
instance.sdk.auth.authStateChanges.listen((user) {
_updateInstanceUser(instance.name, user);
});
// Load known users
await _fetchKnownUsers(instance);
dragon.log('Initialized instance ${instance.name}');
}
void _updateInstanceUser(String instanceName, KdfUser? user) {
setState(() {
_currentUsers[instanceName] = user;
_statusMessages[instanceName] =
user != null
? 'Current wallet: ${user.walletId.name}'
: 'Not signed in';
});
dragon.DragonLogs.setSessionMetadata({
'instance': instanceName,
if (user != null) 'user': user.walletId.compoundId,
});
dragon.log(
user != null
? 'User ${user.walletId.compoundId} authenticated in $instanceName'
: 'User signed out of $instanceName',
);
}
Future<void> _fetchKnownUsers(KdfInstanceState instance) async {
try {
await instance.sdk.ensureInitialized();
final users = await instance.sdk.auth.getUsers();
final state = _getOrCreateInstanceState(instance.name);
state.knownUsers = users;
setState(() {});
} catch (e, s) {
dragon.log('Error fetching known users: $e', 'ERROR');
debugPrintStack(stackTrace: s);
}
}
void _filterAssets() {
final query = _searchController.text.toLowerCase();
final assets = _allAssets;
if (assets == null) return;
setState(() {
_filteredAssets =
assets.values.where((v) {
final asset = v.id.name;
final id = v.id.id;
return asset.toLowerCase().contains(query) ||
id.toLowerCase().contains(query);
}).toList();
});
}
InstanceState _getOrCreateInstanceState(String instanceName) {
return _instanceStates.putIfAbsent(instanceName, InstanceState.new);
}
@override
Widget build(BuildContext context) {
final manager = KdfInstanceManagerProvider.of(context);
final instances = manager.instances.values.toList();
return Scaffold(
drawer: const KdfInstanceDrawer(),
appBar: AppBar(
title: Text(
instances.isEmpty
? 'KDF Demo'
: 'KDF Demo - ${instances[_selectedInstanceIndex].name}',
),
actions: [
if (instances.isNotEmpty) ...[
Badge(
backgroundColor:
instances[_selectedInstanceIndex].isConnected
? Colors.green
: Colors.red,
child: const Icon(Icons.cloud),
),
IconButton(
icon: const Icon(Icons.download),
tooltip: 'Export Logs',
onPressed: () async {
await dragon.DragonLogs.exportLogsToDownload();
_scaffoldKey.currentState?.showSnackBar(
const SnackBar(content: Text('Logs exported')),
);
},
),
const SizedBox(width: 16),
],
],
),
body:
instances.isEmpty
? const Center(child: Text('No KDF instances configured'))
: IndexedStack(
index: _selectedInstanceIndex,
children: [
for (final instance in instances)
Padding(
padding: const EdgeInsets.all(16),
child: BlocProvider(
create: (context) => AuthBloc(sdk: instance.sdk),
child: BlocListener<AuthBloc, AuthState>(
listener: (context, state) {
final user =
state.isAuthenticated ? state.user : null;
_updateInstanceUser(instance.name, user);
},
child: Form(
key: _formKey,
autovalidateMode:
AutovalidateMode.onUserInteraction,
child: InstanceView(
instance: instance,
state: 'active',
statusMessage:
_statusMessages[instance.name] ??
'Not initialized',
searchController: _searchController,
filteredAssets: _filteredAssets,
onNavigateToAsset:
(asset) =>
_onNavigateToAsset(instance, asset),
),
),
),
),
),
],
),
bottomNavigationBar: _buildInstanceNavigator(instances),
);
}
Widget _buildInstanceNavigator(List<KdfInstanceState> instances) {
if (instances.length <= 1) return const SizedBox.shrink();
return NavigationBar(
selectedIndex: _selectedInstanceIndex,
onDestinationSelected: (index) {
setState(() => _selectedInstanceIndex = index);
},
destinations: [
for (final instance in instances)
NavigationDestination(
icon: Badge(
backgroundColor: instance.isConnected ? Colors.green : Colors.red,
child: const Icon(Icons.cloud),
),
label: instance.name,
),
],
);
}
void _onNavigateToAsset(KdfInstanceState instance, Asset asset) {
_navigatorKey.currentState?.push(
MaterialPageRoute<void>(builder: (context) => AssetPage(asset)),
);
}
@override
void dispose() {
_searchController.dispose();
for (final state in _instanceStates.values) {
state.dispose();
}
_instanceStates.clear();
super.dispose();
}
}
abstract class InstanceData {
// Base interface for instance-specific data
}
class InstanceState extends InstanceData {
final walletNameController = TextEditingController();
final passwordController = TextEditingController();
List<KdfUser> knownUsers = [];
bool isHdMode = true;
bool obscurePassword = true;
void dispose() {
walletNameController.dispose();
passwordController.dispose();
}
}