import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path/path.dart' as path; import 'package:supabase_flutter/supabase_flutter.dart'; class ImageUploadTest extends StatefulWidget { const ImageUploadTest({super.key}); @override State createState() => _ImageUploadTestState(); } class _ImageUploadTestState extends State { final _supabase = Supabase.instance.client; String? _selectedImagePath; String? _uploadedImageUrl; bool _isLoading = false; String _status = ''; List _buckets = []; String _selectedBucket = ''; @override void initState() { super.initState(); _loadBuckets(); } Future _loadBuckets() async { setState(() { _isLoading = true; _status = 'Loading buckets...'; }); try { final buckets = await _supabase.storage.listBuckets(); final bucketNames = buckets.map((b) => b.name).toList(); setState(() { _buckets = bucketNames; if (bucketNames.isNotEmpty) { _selectedBucket = bucketNames.first; } _status = 'Found ${bucketNames.length} buckets: ${bucketNames.join(', ')}'; }); } catch (e) { setState(() { _status = 'Error loading buckets: $e'; }); } finally { setState(() { _isLoading = false; }); } } Future _pickImage() async { try { setState(() { _status = 'Selecting image...'; }); final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); if (image != null) { setState(() { _selectedImagePath = image.path; _status = 'Image selected: ${path.basename(image.path)}'; }); } else { setState(() { _status = 'Image selection canceled'; }); } } catch (e) { setState(() { _status = 'Error picking image: $e'; }); } } Future _uploadImage() async { if (_selectedImagePath == null || _selectedBucket.isEmpty) { setState(() { _status = 'No image selected or bucket chosen'; }); return; } setState(() { _isLoading = true; _status = 'Uploading image to $_selectedBucket...'; }); try { final file = File(_selectedImagePath!); if (!await file.exists()) { setState(() { _status = 'File does not exist: $_selectedImagePath'; _isLoading = false; }); return; } final fileExtension = path.extension(file.path).toLowerCase(); final fileName = 'test_${DateTime.now().millisecondsSinceEpoch}$fileExtension'; // Read file as bytes final bytes = await file.readAsBytes(); // Upload to selected bucket await _supabase.storage .from(_selectedBucket) .uploadBinary( fileName, bytes, fileOptions: const FileOptions(contentType: 'image/jpeg'), ); // Get public URL final imageUrl = _supabase.storage .from(_selectedBucket) .getPublicUrl(fileName); setState(() { _uploadedImageUrl = imageUrl; _status = 'Upload successful to $_selectedBucket bucket'; }); } catch (e) { setState(() { _status = 'Upload error: $e'; }); } finally { setState(() { _isLoading = false; }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Image Upload Test'), actions: [ IconButton(icon: const Icon(Icons.refresh), onPressed: _loadBuckets), ], ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Status Container( width: double.infinity, padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.grey.shade200, borderRadius: BorderRadius.circular(8), ), child: Text(_status), ), const SizedBox(height: 20), // Bucket selection Text( 'Select Bucket:', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 8), if (_buckets.isEmpty) const Text('No buckets available') else DropdownButton( value: _selectedBucket, isExpanded: true, items: _buckets.map((bucket) { return DropdownMenuItem( value: bucket, child: Text(bucket), ); }).toList(), onChanged: (value) { if (value != null) { setState(() { _selectedBucket = value; }); } }, ), const SizedBox(height: 20), // Image selection Text('Image:', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), Row( children: [ Expanded( child: Text( _selectedImagePath != null ? 'Selected: ${path.basename(_selectedImagePath!)}' : 'No image selected', ), ), ElevatedButton.icon( onPressed: _pickImage, icon: const Icon(Icons.image), label: const Text('Select Image'), ), ], ), if (_selectedImagePath != null) ...[ const SizedBox(height: 16), const Text('Selected Image Preview:'), const SizedBox(height: 8), Container( height: 150, width: double.infinity, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(8), ), child: kIsWeb ? Image.network(_selectedImagePath!) : Image.file(File(_selectedImagePath!)), ), ], const SizedBox(height: 20), // Upload button SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _isLoading || _selectedImagePath == null ? null : _uploadImage, icon: _isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.cloud_upload), label: Text(_isLoading ? 'Uploading...' : 'Upload Image'), ), ), const SizedBox(height: 20), // Result if (_uploadedImageUrl != null) ...[ Text( 'Uploaded Image:', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 8), SelectableText('URL: $_uploadedImageUrl'), const SizedBox(height: 8), Container( width: double.infinity, height: 200, decoration: BoxDecoration( color: Colors.grey.shade200, borderRadius: BorderRadius.circular(8), ), child: Image.network( _uploadedImageUrl!, fit: BoxFit.contain, errorBuilder: (context, error, stackTrace) { return Center(child: Text('Error loading image: $error')); }, ), ), ], ], ), ), ); } // Helper method to check if running on the web static bool get kIsWeb { try { return identical(0, 0.0); } catch (e) { return false; } } }