cashed_ar 1.0.1 copy "cashed_ar: ^1.0.1" to clipboard
cashed_ar: ^1.0.1 copied to clipboard

A Flutter plugin for displaying AR models with intelligent caching on both Android and iOS platforms.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:cashed_ar/cashed_ar.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Cashed AR Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const ExampleHomePage(),
    );
  }
}

class ExampleHomePage extends StatefulWidget {
  const ExampleHomePage({Key? key}) : super(key: key);

  @override
  State<ExampleHomePage> createState() => _ExampleHomePageState();
}

class _ExampleHomePageState extends State<ExampleHomePage> {
  final List<ARProduct> _products = [
    ARProduct(
      id: 'chair_1',
      name: 'Modern Office Chair',
      description: 'Ergonomic office chair with lumbar support',
      androidModelUrl:
          'https://modelviewer.dev/shared-assets/models/Astronaut.glb',
      iosModelUrl:
          'https://developer.apple.com/augmented-reality/quick-look/models/teapot/teapot.usdz',
      thumbnailUrl:
          'https://via.placeholder.com/200x200/4CAF50/FFFFFF?text=Chair',
    ),
    ARProduct(
      id: 'table_1',
      name: 'Wooden Dining Table',
      description: 'Solid oak dining table for 6 people',
      androidModelUrl:
          'https://modelviewer.dev/shared-assets/models/MaterialsVariantsShoe.glb',
      iosModelUrl:
          'https://developer.apple.com/augmented-reality/quick-look/models/drummertoy/toy_drummer.usdz',
      thumbnailUrl:
          'https://via.placeholder.com/200x200/FF9800/FFFFFF?text=Table',
    ),
    ARProduct(
      id: 'lamp_1',
      name: 'Designer Desk Lamp',
      description: 'Modern LED desk lamp with adjustable brightness',
      androidModelUrl:
          'https://modelviewer.dev/shared-assets/models/reflective-spheres.glb',
      iosModelUrl:
          'https://developer.apple.com/augmented-reality/quick-look/models/retrotv/tv_retro.usdz',
      thumbnailUrl:
          'https://via.placeholder.com/200x200/2196F3/FFFFFF?text=Lamp',
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Cashed AR Example'),
        backgroundColor: Colors.blue,
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildHeader(),
            const SizedBox(height: 24),
            _buildQuickActionsSection(),
            const SizedBox(height: 24),
            _buildProductsSection(),
          ],
        ),
      ),
    );
  }

  Widget _buildHeader() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              'Welcome to Cashed AR',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            const Text(
              'Experience AR models with intelligent caching across iOS and Android platforms.',
              style: TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 16),
            Row(
              children: [
                Icon(Icons.phone_android, color: Colors.green),
                const SizedBox(width: 8),
                const Text('Android: GLB + model_viewer_plus'),
              ],
            ),
            const SizedBox(height: 8),
            Row(
              children: [
                Icon(Icons.phone_iphone, color: Colors.blue),
                const SizedBox(width: 8),
                const Text('iOS: USDZ + QuickLook AR'),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildQuickActionsSection() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              'Quick Actions',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _showFullScreenARExample,
                    icon: const Icon(Icons.view_in_ar),
                    label: const Text('Full Screen AR'),
                  ),
                ),
                const SizedBox(width: 16),
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _cleanupCache,
                    icon: const Icon(Icons.cleaning_services),
                    label: const Text('Clean Cache'),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 12),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton.icon(
                onPressed: _showCacheStatus,
                icon: const Icon(Icons.info),
                label: const Text('Cache Status'),
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.grey[600],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildProductsSection() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'AR Product Gallery',
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        GridView.builder(
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            childAspectRatio: 0.8,
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
          ),
          itemCount: _products.length,
          itemBuilder: (context, index) {
            final product = _products[index];
            return _buildProductCard(product);
          },
        ),
      ],
    );
  }

  Widget _buildProductCard(ARProduct product) {
    // Define color scheme based on product
    Color primaryColor = Colors.blue;
    Color accentColor = Colors.blueAccent;

    if (product.id == 'table_1') {
      primaryColor = Colors.orange;
      accentColor = Colors.deepOrange;
    } else if (product.id == 'lamp_1') {
      primaryColor = Colors.purple;
      accentColor = Colors.deepPurple;
    }

    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(20),
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            Colors.white,
            primaryColor.withOpacity(0.05),
          ],
        ),
        boxShadow: [
          BoxShadow(
            color: primaryColor.withOpacity(0.15),
            spreadRadius: 0,
            blurRadius: 20,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Product Image Section
            Expanded(
              flex: 3,
              child: Container(
                width: double.infinity,
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [
                      primaryColor.withOpacity(0.1),
                      primaryColor.withOpacity(0.3),
                    ],
                  ),
                ),
                child: Stack(
                  children: [
                    // Fallback gradient background
                    Container(
                      width: double.infinity,
                      height: double.infinity,
                      decoration: BoxDecoration(
                        gradient: RadialGradient(
                          center: Alignment.center,
                          radius: 1.0,
                          colors: [
                            Colors.white.withOpacity(0.8),
                            primaryColor.withOpacity(0.2),
                          ],
                        ),
                      ),
                    ),
                    // Product icon in center
                    Center(
                      child: Container(
                        width: 80,
                        height: 80,
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(40),
                          boxShadow: [
                            BoxShadow(
                              color: primaryColor.withOpacity(0.2),
                              spreadRadius: 2,
                              blurRadius: 10,
                              offset: const Offset(0, 4),
                            ),
                          ],
                        ),
                        child: Icon(
                          product.id == 'chair_1'
                              ? Icons.chair_alt
                              : product.id == 'table_1'
                                  ? Icons.table_restaurant
                                  : Icons.light,
                          size: 40,
                          color: primaryColor,
                        ),
                      ),
                    ),
                    // AR Badge
                    Positioned(
                      top: 12,
                      right: 12,
                      child: Container(
                        padding: const EdgeInsets.symmetric(
                            horizontal: 8, vertical: 4),
                        decoration: BoxDecoration(
                          color: accentColor,
                          borderRadius: BorderRadius.circular(12),
                          boxShadow: [
                            BoxShadow(
                              color: accentColor.withOpacity(0.3),
                              spreadRadius: 1,
                              blurRadius: 4,
                              offset: const Offset(0, 2),
                            ),
                          ],
                        ),
                        child: const Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Icon(
                              Icons.view_in_ar,
                              color: Colors.white,
                              size: 12,
                            ),
                            SizedBox(width: 4),
                            Text(
                              'AR',
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 10,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
            // Product Info Section
            Expanded(
              flex: 3,
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      product.name,
                      style: TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                        color: Colors.grey[800],
                      ),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                    const SizedBox(height: 4),
                    Expanded(
                      child: Text(
                        product.description,
                        style: TextStyle(
                          fontSize: 12,
                          color: Colors.grey[600],
                          height: 1.3,
                        ),
                        maxLines: 2,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ),
                    const SizedBox(height: 12),
                    // Enhanced AR Button
                    Container(
                      width: double.infinity,
                      height: 36,
                      decoration: BoxDecoration(
                        gradient: LinearGradient(
                          colors: [primaryColor, accentColor],
                        ),
                        borderRadius: BorderRadius.circular(18),
                        boxShadow: [
                          BoxShadow(
                            color: primaryColor.withOpacity(0.3),
                            spreadRadius: 0,
                            blurRadius: 8,
                            offset: const Offset(0, 4),
                          ),
                        ],
                      ),
                      child: Material(
                        color: Colors.transparent,
                        child: InkWell(
                          borderRadius: BorderRadius.circular(18),
                          onTap: () => _showProductAR(product),
                          child: const Center(
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                Icon(
                                  Icons.view_in_ar,
                                  color: Colors.white,
                                  size: 18,
                                ),
                                SizedBox(width: 8),
                                Text(
                                  'View in AR',
                                  style: TextStyle(
                                    color: Colors.white,
                                    fontSize: 14,
                                    fontWeight: FontWeight.w600,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  void _showProductAR(ARProduct product) {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => ProductARScreen(product: product),
      ),
    );
  }

  void _showFullScreenARExample() async {
    final product = _products.first;
    await CashedARViewer.showARModal(
      context,
      androidModelUrl: product.androidModelUrl,
      iosModelUrl: product.iosModelUrl,
      productId: product.id,
      productName: product.name,
      onModelLoading: () {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(const SnackBar(content: Text('Loading AR model...')));
      },
      onModelLoaded: () {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('AR model loaded successfully!')),
        );
      },
      onError: (error) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text('AR Error: $error')));
      },
    );
  }

  void _cleanupCache() async {
    try {
      // Clear all cached AR files
      await ARCacheManager.clearAllCache();

      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Cache cleared successfully!')),
        );
      }
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text('Cache cleanup failed: $e')));
      }
    }
  }

  void _showCacheStatus() async {
    final cachedFiles = <String>[];

    for (final product in _products) {
      // Check both platforms
      final androidCachedPath = await ARCacheManager.getCachedFilePath(
        product.androidModelUrl,
        product.id,
        ARPlatform.android,
      );

      final iosCachedPath = await ARCacheManager.getCachedFilePath(
        product.iosModelUrl,
        product.id,
        ARPlatform.ios,
      );

      // Add to list if cached on either platform
      if (androidCachedPath != null || iosCachedPath != null) {
        String platformInfo = '';
        if (androidCachedPath != null && iosCachedPath != null) {
          platformInfo = ' (Android + iOS)';
        } else if (androidCachedPath != null) {
          platformInfo = ' (Android)';
        } else {
          platformInfo = ' (iOS)';
        }
        cachedFiles.add('${product.name}$platformInfo');
      }

      // Debug logging
      debugPrint('Cache check for ${product.name}:');
      debugPrint('  Android: $androidCachedPath');
      debugPrint('  iOS: $iosCachedPath');
    }

    if (mounted) {
      showDialog(
        context: context,
        builder: (context) => AlertDialog(
          title: const Text('Cache Status'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                'Cached Products: ${cachedFiles.length}/${_products.length}',
              ),
              const SizedBox(height: 16),
              if (cachedFiles.isNotEmpty) ...[
                const Text('Cached:'),
                ...cachedFiles.map((name) => Text('• $name')),
              ] else
                const Text('No cached files found'),
            ],
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(context).pop(),
              child: const Text('OK'),
            ),
          ],
        ),
      );
    }
  }
}

class ProductARScreen extends StatelessWidget {
  final ARProduct product;

  const ProductARScreen({Key? key, required this.product}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(product.name),
        backgroundColor: Colors.blue,
        foregroundColor: Colors.white,
      ),
      body: Column(
        children: [
          Container(
            padding: const EdgeInsets.all(16),
            child: Text(
              product.description,
              style: const TextStyle(fontSize: 16),
              textAlign: TextAlign.center,
            ),
          ),
          Expanded(
            child: CashedARViewer(
              androidModelUrl: product.androidModelUrl,
              iosModelUrl: product.iosModelUrl,
              productId: product.id,
              productName: product.name,
              cacheOptions: const ARCacheOptions(
                showLoadingDialog: true,
                showProgress: true,
              ),
              onModelLoading: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Loading ${product.name}...')),
                );
              },
              onModelLoaded: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('${product.name} loaded!')),
                );
              },
              onError: (error) {
                ScaffoldMessenger.of(
                  context,
                ).showSnackBar(SnackBar(content: Text('Error: $error')));
              },
            ),
          ),
        ],
      ),
    );
  }
}

class ARProduct {
  final String id;
  final String name;
  final String description;
  final String androidModelUrl;
  final String iosModelUrl;
  final String thumbnailUrl;

  ARProduct({
    required this.id,
    required this.name,
    required this.description,
    required this.androidModelUrl,
    required this.iosModelUrl,
    required this.thumbnailUrl,
  });
}
6
likes
150
points
164
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for displaying AR models with intelligent caching on both Android and iOS platforms.

Repository (GitHub)
View/report issues

Topics

#ar #augmented-reality #caching #model-viewer #models-3d

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

dio, flutter, model_viewer_plus, path_provider, plugin_platform_interface

More

Packages that depend on cashed_ar

Packages that implement cashed_ar