flutter_yuku 2.0.3 copy "flutter_yuku: ^2.0.3" to clipboard
flutter_yuku: ^2.0.3 copied to clipboard

Universal Flutter library for blockchain operations across multiple networks

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_yuku/flutter_yuku.dart';
import 'package:intl/intl.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Yuku Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  final YukuClient _client = YukuClient();
  late TabController _tabController;
  bool _isInitialized = false;
  String _status = 'Not initialized';

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 4, vsync: this);
    _initializeClient();
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  Future<void> _initializeClient() async {
    try {
      // Configure networks
      _client.setNetworkConfig(
        BlockchainNetwork.ethereum,
        const NetworkConfig(
          name: 'Ethereum Mainnet',
          rpcUrl: 'https://mainnet.infura.io/v3/YOUR_KEY',
          chainId: '1',
          network: BlockchainNetwork.ethereum,
          isTestnet: false,
        ),
      );

      _client.setNetworkConfig(
        BlockchainNetwork.solana,
        const NetworkConfig(
          name: 'Solana Mainnet',
          rpcUrl: 'https://api.mainnet-beta.solana.com',
          chainId: 'mainnet-beta',
          network: BlockchainNetwork.solana,
          isTestnet: false,
        ),
      );

      _client.setNetworkConfig(
        BlockchainNetwork.polygon,
        const NetworkConfig(
          name: 'Polygon Mainnet',
          rpcUrl: 'https://polygon-rpc.com',
          chainId: '137',
          network: BlockchainNetwork.polygon,
          isTestnet: false,
        ),
      );

      _client.setNetworkConfig(
        BlockchainNetwork.icp,
        const NetworkConfig(
          name: 'ICP Mainnet',
          rpcUrl: 'https://ic0.app',
          chainId: 'icp',
          network: BlockchainNetwork.icp,
          isTestnet: false,
        ),
      );

      await _client.initialize();
      setState(() {
        _isInitialized = true;
        _status = 'Initialized successfully';
      });
    } catch (e) {
      setState(() {
        _status = 'Initialization failed: $e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Flutter Yuku Example'),
        bottom: TabBar(
          controller: _tabController,
          tabs: const [
            Tab(icon: Icon(Icons.info), text: 'Client'),
            Tab(icon: Icon(Icons.account_balance_wallet), text: 'Wallet'),
            Tab(icon: Icon(Icons.image), text: 'NFT'),
            Tab(icon: Icon(Icons.store), text: 'Marketplace'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          ClientInfoTab(
            client: _client,
            isInitialized: _isInitialized,
            status: _status,
          ),
          WalletTab(client: _client, isInitialized: _isInitialized),
          NFTTab(client: _client, isInitialized: _isInitialized),
          MarketplaceTab(client: _client, isInitialized: _isInitialized),
        ],
      ),
    );
  }
}

// Client Info & Utils Tab
class ClientInfoTab extends StatelessWidget {
  final YukuClient client;
  final bool isInitialized;
  final String status;

  const ClientInfoTab({
    super.key,
    required this.client,
    required this.isInitialized,
    required this.status,
  });

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildSection('Status', [
            _buildInfoRow('Client Status', status),
            _buildInfoRow('Initialized', isInitialized.toString()),
          ]),
          const SizedBox(height: 20),
          if (isInitialized) ...[
            _buildSection('Client Information', [
              _buildInfoRow('Version', client.getClientInfo()['version']),
              _buildInfoRow(
                'Supported Networks',
                client.getSupportedNetworks().map((e) => e.name).join(', '),
              ),
              _buildInfoRow(
                'Provider Stats',
                client.getProviderStats().toString(),
              ),
            ]),
            const SizedBox(height: 20),
            _buildSection(
              'Supported Networks',
              client.getSupportedNetworks().map((network) {
                return _buildInfoRow(
                  YukuUtils.getNetworkDisplayName(network),
                  network.name,
                );
              }).toList(),
            ),
            const SizedBox(height: 20),
            ElevatedButton.icon(
              onPressed: () => _showUtilsDialog(context),
              icon: const Icon(Icons.build),
              label: const Text('Show Utils Examples'),
            ),
          ],
        ],
      ),
    );
  }

  void _showUtilsDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Utils Examples'),
        content: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text(
                'Address Validation:',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
              ),
              const SizedBox(height: 8),
              Text(
                'Ethereum: ${YukuUtils.isValidEthereumAddress("0xffcba0b4980eb2d2336bfdb1e5a0fc49c620908a")}',
              ),
              Text(
                'Solana: ${YukuUtils.isValidSolanaAddress("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM")}',
              ),
              Text(
                'ICP: ${YukuUtils.isValidICPPrincipal("rdmx6-jaaaa-aaaaa-aaadq-cai")}',
              ),
              const SizedBox(height: 16),
              const Text(
                'Price Formatting:',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
              ),
              const SizedBox(height: 8),
              Text('ETH: ${YukuUtils.formatPrice(1.5, "ETH")}'),
              Text('SOL: ${YukuUtils.formatPrice(100.0, "SOL")}'),
              Text('ICP: ${YukuUtils.formatPrice(10.0, "ICP")}'),
              const SizedBox(height: 16),
              const Text(
                'Address Formatting:',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
              ),
              const SizedBox(height: 8),
              Text(
                'Short: ${YukuUtils.formatAddress("0xffcba0b4980eb2d2336bfdb1e5a0fc49c620908a")}',
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  Widget _buildSection(String title, List<Widget> children) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
            const Divider(),
            ...children,
          ],
        ),
      ),
    );
  }

  Widget _buildInfoRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 150,
            child: Text(
              '$label:',
              style: const TextStyle(fontWeight: FontWeight.w500),
            ),
          ),
          Expanded(child: Text(value)),
        ],
      ),
    );
  }
}

// Wallet Operations Tab
class WalletTab extends StatefulWidget {
  final YukuClient client;
  final bool isInitialized;

  const WalletTab({
    super.key,
    required this.client,
    required this.isInitialized,
  });

  @override
  State<WalletTab> createState() => _WalletTabState();
}

class _WalletTabState extends State<WalletTab> {
  final _addressController = TextEditingController();
  final _amountController = TextEditingController();
  final _toAddressController = TextEditingController();
  String _result = '';
  BlockchainNetwork _selectedNetwork = BlockchainNetwork.ethereum;

  @override
  void dispose() {
    _addressController.dispose();
    _amountController.dispose();
    _toAddressController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (!widget.isInitialized) {
      return const Center(child: Text('Client not initialized'));
    }

    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'Wallet Operations',
                    style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                  ),
                  const Divider(),
                  DropdownButton<BlockchainNetwork>(
                    value: _selectedNetwork,
                    isExpanded: true,
                    items: widget.client.getSupportedNetworks().map((network) {
                      return DropdownMenuItem(
                        value: network,
                        child: Text(YukuUtils.getNetworkDisplayName(network)),
                      );
                    }).toList(),
                    onChanged: (network) {
                      if (network != null) {
                        setState(() => _selectedNetwork = network);
                      }
                    },
                  ),
                  const SizedBox(height: 16),
                  TextField(
                    controller: _addressController,
                    decoration: const InputDecoration(
                      labelText: 'Wallet Address',
                      border: OutlineInputBorder(),
                    ),
                  ),
                  const SizedBox(height: 16),
                  ElevatedButton.icon(
                    onPressed: _validateAddress,
                    icon: const Icon(Icons.check_circle),
                    label: const Text('Validate Address'),
                  ),
                  const SizedBox(height: 8),
                  ElevatedButton.icon(
                    onPressed: _checkBalance,
                    icon: const Icon(Icons.account_balance),
                    label: const Text('Check Balance (Demo)'),
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 16),
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'Send Transaction (Demo)',
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                  ),
                  const Divider(),
                  TextField(
                    controller: _toAddressController,
                    decoration: const InputDecoration(
                      labelText: 'To Address',
                      border: OutlineInputBorder(),
                    ),
                  ),
                  const SizedBox(height: 16),
                  TextField(
                    controller: _amountController,
                    decoration: const InputDecoration(
                      labelText: 'Amount',
                      border: OutlineInputBorder(),
                    ),
                    keyboardType: TextInputType.number,
                  ),
                  const SizedBox(height: 16),
                  ElevatedButton.icon(
                    onPressed: _sendTransaction,
                    icon: const Icon(Icons.send),
                    label: const Text('Send Transaction'),
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 16),
          if (_result.isNotEmpty)
            Card(
              color: Colors.blue.shade50,
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Result:',
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 8),
                    Text(_result),
                  ],
                ),
              ),
            ),
        ],
      ),
    );
  }

  void _validateAddress() {
    final address = _addressController.text;
    bool isValid = false;

    switch (_selectedNetwork) {
      case BlockchainNetwork.ethereum:
      case BlockchainNetwork.polygon:
      case BlockchainNetwork.bsc:
        isValid = YukuUtils.isValidEthereumAddress(address);
        break;
      case BlockchainNetwork.solana:
        isValid = YukuUtils.isValidSolanaAddress(address);
        break;
      case BlockchainNetwork.icp:
        isValid = YukuUtils.isValidICPPrincipal(address);
        break;
      default:
        isValid = address.isNotEmpty;
    }

    setState(() {
      _result = 'Address validation: ${isValid ? "✓ Valid" : "✗ Invalid"}';
    });
  }

  void _checkBalance() {
    final address = _addressController.text;
    if (address.isEmpty) {
      setState(() => _result = 'Please enter an address');
      return;
    }

    // Demo balance
    final demoBalance = (100 + address.length % 50).toDouble();
    final currency = _getCurrencySymbol();

    setState(() {
      _result = 'Balance: ${YukuUtils.formatPrice(demoBalance, currency)}';
    });
  }

  void _sendTransaction() {
    final to = _toAddressController.text;
    final amount = double.tryParse(_amountController.text) ?? 0;

    if (to.isEmpty || amount <= 0) {
      setState(() => _result = 'Please enter valid recipient and amount');
      return;
    }

    final currency = _getCurrencySymbol();
    final txHash = 'tx_${DateTime.now().millisecondsSinceEpoch}';

    setState(() {
      _result =
          'Transaction sent!\n'
          'To: ${YukuUtils.formatAddress(to)}\n'
          'Amount: ${YukuUtils.formatPrice(amount, currency)}\n'
          'Hash: $txHash';
    });
  }

  String _getCurrencySymbol() {
    switch (_selectedNetwork) {
      case BlockchainNetwork.ethereum:
        return 'ETH';
      case BlockchainNetwork.solana:
        return 'SOL';
      case BlockchainNetwork.polygon:
        return 'MATIC';
      case BlockchainNetwork.bsc:
        return 'BNB';
      case BlockchainNetwork.icp:
        return 'ICP';
      default:
        return 'TOKEN';
    }
  }
}

// NFT Operations Tab
class NFTTab extends StatefulWidget {
  final YukuClient client;
  final bool isInitialized;

  const NFTTab({super.key, required this.client, required this.isInitialized});

  @override
  State<NFTTab> createState() => _NFTTabState();
}

class _NFTTabState extends State<NFTTab> {
  final List<NFT> _demoNFTs = [];

  @override
  void initState() {
    super.initState();
    _generateDemoNFTs();
  }

  void _generateDemoNFTs() {
    _demoNFTs.addAll([
      NFT(
        id: 'nft_1',
        tokenId: '1',
        contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D',
        network: BlockchainNetwork.ethereum,
        metadata: const NFTMetadata(
          name: 'Bored Ape #1234',
          description: 'A unique Bored Ape NFT',
          image: 'https://example.com/ape.png',
          attributes: {'Rarity': 'Rare', 'Background': 'Blue'},
          properties: {},
          externalUrl: 'https://example.com',
        ),
        owner: '0xffcba0b4980eb2d2336bfdb1e5a0fc49c620908a',
        creator: '0xffcba0b4980eb2d2336bfdb1e5a0fc49c620908a',
        createdAt: DateTime.now().subtract(const Duration(days: 30)),
        updatedAt: DateTime.now(),
        status: 'active',
        currentValue: 2.5,
        valueCurrency: 'ETH',
        transactionHistory: const ['tx1', 'tx2'],
      ),
      NFT(
        id: 'nft_2',
        tokenId: '42',
        contractAddress: 'DRiP2Pn2K6fuMLKQmt5rZWspbA485azs9kV75r3kTTZh',
        network: BlockchainNetwork.solana,
        metadata: const NFTMetadata(
          name: 'Solana Monkey #42',
          description: 'A cool Solana Monkey',
          image: 'https://example.com/monkey.png',
          attributes: {'Rarity': 'Epic', 'Type': 'Legendary'},
          properties: {},
          externalUrl: 'https://example.com',
        ),
        owner: '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',
        creator: '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',
        createdAt: DateTime.now().subtract(const Duration(days: 15)),
        updatedAt: DateTime.now(),
        status: 'active',
        currentValue: 150.0,
        valueCurrency: 'SOL',
        transactionHistory: const ['tx3'],
      ),
      NFT(
        id: 'nft_3',
        tokenId: '7',
        contractAddress: 'rdmx6-jaaaa-aaaaa-aaadq-cai',
        network: BlockchainNetwork.icp,
        metadata: const NFTMetadata(
          name: 'ICP Punk #7',
          description: 'A punk on Internet Computer',
          image: 'https://example.com/punk.png',
          attributes: {'Rarity': 'Legendary', 'Eyes': 'Laser'},
          properties: {},
          externalUrl: 'https://example.com',
        ),
        owner: 'rdmx6-jaaaa-aaaaa-aaadq-cai',
        creator: 'rdmx6-jaaaa-aaaaa-aaadq-cai',
        createdAt: DateTime.now().subtract(const Duration(days: 7)),
        updatedAt: DateTime.now(),
        status: 'active',
        currentValue: 50.0,
        valueCurrency: 'ICP',
        transactionHistory: const ['tx4', 'tx5', 'tx6'],
      ),
    ]);
  }

  @override
  Widget build(BuildContext context) {
    if (!widget.isInitialized) {
      return const Center(child: Text('Client not initialized'));
    }

    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.all(16),
          child: Text(
            'Demo NFT Collection (${_demoNFTs.length} items)',
            style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
          ),
        ),
        Expanded(
          child: ListView.builder(
            padding: const EdgeInsets.all(16),
            itemCount: _demoNFTs.length,
            itemBuilder: (context, index) {
              final nft = _demoNFTs[index];
              return Card(
                margin: const EdgeInsets.only(bottom: 16),
                child: ListTile(
                  leading: CircleAvatar(
                    backgroundColor: _getNetworkColor(nft.network),
                    child: Text(
                      nft.tokenId,
                      style: const TextStyle(color: Colors.white),
                    ),
                  ),
                  title: Text(
                    nft.metadata.name,
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                  subtitle: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(nft.metadata.description),
                      const SizedBox(height: 4),
                      Text(
                        'Network: ${nft.networkDisplayName}',
                        style: TextStyle(color: _getNetworkColor(nft.network)),
                      ),
                      Text('Owner: ${nft.formattedOwner}'),
                      Text('Value: ${nft.formattedValue}'),
                      Text('Rarity: ${nft.rarity.name}'),
                    ],
                  ),
                  isThreeLine: true,
                  onTap: () => _showNFTDetails(nft),
                ),
              );
            },
          ),
        ),
      ],
    );
  }

  Color _getNetworkColor(BlockchainNetwork network) {
    switch (network) {
      case BlockchainNetwork.ethereum:
        return Colors.blue;
      case BlockchainNetwork.solana:
        return Colors.purple;
      case BlockchainNetwork.polygon:
        return Colors.deepPurple;
      case BlockchainNetwork.bsc:
        return Colors.orange;
      case BlockchainNetwork.icp:
        return Colors.teal;
      default:
        return Colors.grey;
    }
  }

  void _showNFTDetails(NFT nft) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(nft.metadata.name),
        content: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              _buildDetailRow('Token ID', nft.tokenId),
              _buildDetailRow('Contract', nft.formattedContractAddress),
              _buildDetailRow('Network', nft.networkDisplayName),
              _buildDetailRow('Owner', nft.formattedOwner),
              _buildDetailRow('Creator', nft.formattedCreator),
              _buildDetailRow('Value', nft.formattedValue),
              _buildDetailRow('Rarity', nft.rarity.name),
              _buildDetailRow('Status', nft.status),
              _buildDetailRow(
                'Created',
                DateFormat('yyyy-MM-dd HH:mm').format(nft.createdAt),
              ),
              _buildDetailRow('Transferable', nft.isTransferable.toString()),
              const SizedBox(height: 8),
              const Text(
                'Attributes:',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
              ...nft.metadata.attributes.entries.map(
                (e) => Text('  ${e.key}: ${e.value}'),
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  Widget _buildDetailRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 100,
            child: Text(
              '$label:',
              style: const TextStyle(fontWeight: FontWeight.w500),
            ),
          ),
          Expanded(child: Text(value)),
        ],
      ),
    );
  }
}

// Marketplace Operations Tab
class MarketplaceTab extends StatefulWidget {
  final YukuClient client;
  final bool isInitialized;

  const MarketplaceTab({
    super.key,
    required this.client,
    required this.isInitialized,
  });

  @override
  State<MarketplaceTab> createState() => _MarketplaceTabState();
}

class _MarketplaceTabState extends State<MarketplaceTab> {
  final List<NFTListing> _demoListings = [];
  final _priceController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _generateDemoListings();
  }

  @override
  void dispose() {
    _priceController.dispose();
    super.dispose();
  }

  void _generateDemoListings() {
    _demoListings.addAll([
      NFTListing(
        id: 'listing_1',
        nftId: 'nft_1',
        contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D',
        network: BlockchainNetwork.ethereum,
        price: 2.5,
        currency: 'ETH',
        sellerAddress: '0xffcba0b4980eb2d2336bfdb1e5a0fc49c620908a',
        createdAt: DateTime.now().subtract(const Duration(hours: 6)),
        expiresAt: DateTime.now().add(const Duration(days: 7)),
        status: ListingStatus.active,
        marketplaceProvider: 'opensea',
      ),
      NFTListing(
        id: 'listing_2',
        nftId: 'nft_2',
        contractAddress: 'DRiP2Pn2K6fuMLKQmt5rZWspbA485azs9kV75r3kTTZh',
        network: BlockchainNetwork.solana,
        price: 150.0,
        currency: 'SOL',
        sellerAddress: '9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM',
        createdAt: DateTime.now().subtract(const Duration(hours: 12)),
        expiresAt: DateTime.now().add(const Duration(days: 3)),
        status: ListingStatus.active,
        marketplaceProvider: 'magiceden',
      ),
      NFTListing(
        id: 'listing_3',
        nftId: 'nft_3',
        contractAddress: 'rdmx6-jaaaa-aaaaa-aaadq-cai',
        network: BlockchainNetwork.icp,
        price: 50.0,
        currency: 'ICP',
        sellerAddress: 'rdmx6-jaaaa-aaaaa-aaadq-cai',
        createdAt: DateTime.now().subtract(const Duration(days: 1)),
        status: ListingStatus.sold,
        buyerAddress: 'buyer-principal-id',
        soldAt: DateTime.now().subtract(const Duration(hours: 2)),
        marketplaceProvider: 'yuku',
      ),
    ]);
  }

  @override
  Widget build(BuildContext context) {
    if (!widget.isInitialized) {
      return const Center(child: Text('Client not initialized'));
    }

    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.all(16),
          child: Row(
            children: [
              const Expanded(
                child: Text(
                  'Marketplace Listings',
                  style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                ),
              ),
              IconButton(
                icon: const Icon(Icons.add_shopping_cart),
                onPressed: _showCreateListingDialog,
                tooltip: 'Create Listing',
              ),
            ],
          ),
        ),
        Expanded(
          child: ListView.builder(
            padding: const EdgeInsets.all(16),
            itemCount: _demoListings.length,
            itemBuilder: (context, index) {
              final listing = _demoListings[index];
              return Card(
                margin: const EdgeInsets.only(bottom: 16),
                child: ListTile(
                  leading: CircleAvatar(
                    backgroundColor: _getStatusColor(listing.status),
                    child: Icon(
                      _getStatusIcon(listing.status),
                      color: Colors.white,
                    ),
                  ),
                  title: Text(
                    listing.formattedPrice,
                    style: const TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 18,
                    ),
                  ),
                  subtitle: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('NFT ID: ${listing.nftId}'),
                      Text('Network: ${_getNetworkName(listing.network)}'),
                      Text(
                        'Seller: ${YukuUtils.formatAddress(listing.sellerAddress)}',
                      ),
                      Text('Status: ${listing.status.name}'),
                      if (listing.expiresAt != null)
                        Text(
                          'Expires: ${DateFormat('yyyy-MM-dd HH:mm').format(listing.expiresAt!)}',
                        ),
                      if (listing.soldAt != null)
                        Text(
                          'Sold: ${DateFormat('yyyy-MM-dd HH:mm').format(listing.soldAt!)}',
                        ),
                    ],
                  ),
                  isThreeLine: true,
                  trailing: listing.isActive
                      ? IconButton(
                          icon: const Icon(Icons.shopping_cart),
                          onPressed: () => _buyNFT(listing),
                        )
                      : null,
                  onTap: () => _showListingDetails(listing),
                ),
              );
            },
          ),
        ),
      ],
    );
  }

  Color _getStatusColor(ListingStatus status) {
    switch (status) {
      case ListingStatus.active:
        return Colors.green;
      case ListingStatus.sold:
        return Colors.blue;
      case ListingStatus.cancelled:
        return Colors.red;
      case ListingStatus.expired:
        return Colors.grey;
    }
  }

  IconData _getStatusIcon(ListingStatus status) {
    switch (status) {
      case ListingStatus.active:
        return Icons.store;
      case ListingStatus.sold:
        return Icons.check_circle;
      case ListingStatus.cancelled:
        return Icons.cancel;
      case ListingStatus.expired:
        return Icons.timer_off;
    }
  }

  String _getNetworkName(BlockchainNetwork network) {
    return YukuUtils.getNetworkDisplayName(network);
  }

  void _showCreateListingDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Create Listing (Demo)'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: _priceController,
              decoration: const InputDecoration(
                labelText: 'Price',
                border: OutlineInputBorder(),
              ),
              keyboardType: TextInputType.number,
            ),
            const SizedBox(height: 16),
            const Text('Select NFT and network in production'),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: const Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () {
              Navigator.of(context).pop();
              _createListing();
            },
            child: const Text('Create'),
          ),
        ],
      ),
    );
  }

  void _createListing() {
    final price = double.tryParse(_priceController.text) ?? 1.0;
    final newListing = NFTListing(
      id: 'listing_${DateTime.now().millisecondsSinceEpoch}',
      nftId: 'nft_new',
      contractAddress: '0x' + '0' * 40,
      network: BlockchainNetwork.ethereum,
      price: price,
      currency: 'ETH',
      sellerAddress: '0xffcba0b4980eb2d2336bfdb1e5a0fc49c620908a',
      createdAt: DateTime.now(),
      expiresAt: DateTime.now().add(const Duration(days: 7)),
      status: ListingStatus.active,
      marketplaceProvider: 'demo',
    );

    setState(() {
      _demoListings.insert(0, newListing);
    });

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Listing created successfully!')),
    );
  }

  void _buyNFT(NFTListing listing) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Buy NFT'),
        content: Text(
          'Purchase this NFT for ${listing.formattedPrice}?\n\n'
          'This is a demo action.',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: const Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () {
              Navigator.of(context).pop();
              setState(() {
                final index = _demoListings.indexOf(listing);
                _demoListings[index] = listing.copyWith(
                  status: ListingStatus.sold,
                  buyerAddress: '0xBUYER',
                  soldAt: DateTime.now(),
                );
              });
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('NFT purchased successfully!')),
              );
            },
            child: const Text('Buy Now'),
          ),
        ],
      ),
    );
  }

  void _showListingDetails(NFTListing listing) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Listing Details'),
        content: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              _buildDetailRow('ID', listing.id),
              _buildDetailRow('NFT ID', listing.nftId),
              _buildDetailRow('Price', listing.formattedPrice),
              _buildDetailRow('Network', _getNetworkName(listing.network)),
              _buildDetailRow(
                'Seller',
                YukuUtils.formatAddress(listing.sellerAddress),
              ),
              _buildDetailRow('Status', listing.status.name),
              _buildDetailRow('Marketplace', listing.marketplaceProvider),
              _buildDetailRow(
                'Created',
                DateFormat('yyyy-MM-dd HH:mm').format(listing.createdAt),
              ),
              if (listing.expiresAt != null)
                _buildDetailRow(
                  'Expires',
                  DateFormat('yyyy-MM-dd HH:mm').format(listing.expiresAt!),
                ),
              if (listing.buyerAddress != null)
                _buildDetailRow(
                  'Buyer',
                  YukuUtils.formatAddress(listing.buyerAddress!),
                ),
              if (listing.soldAt != null)
                _buildDetailRow(
                  'Sold At',
                  DateFormat('yyyy-MM-dd HH:mm').format(listing.soldAt!),
                ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  Widget _buildDetailRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 100,
            child: Text(
              '$label:',
              style: const TextStyle(fontWeight: FontWeight.w500),
            ),
          ),
          Expanded(child: Text(value)),
        ],
      ),
    );
  }
}
1
likes
140
points
47
downloads

Documentation

API reference

Publisher

verified publisherweb3.nativemind.net

Weekly Downloads

Universal Flutter library for blockchain operations across multiple networks

Homepage

License

unknown (license)

Dependencies

convert, crypto, equatable, flutter, http, json_annotation, shared_preferences

More

Packages that depend on flutter_yuku