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
46
downloads

Documentation

API reference

Publisher

verified publisherweb3.nativemind.net

Weekly Downloads

Universal Flutter library for blockchain operations across multiple networks

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

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

More

Packages that depend on flutter_yuku