Flutter+Supabase实现房源收藏/取消收藏功能技术求助
实现Supabase收藏/取消收藏房源功能的解决方案
1. 确保Supabase初始化完成
先确认你的Flutter项目已完成Supabase初始化,示例代码如下:
import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Supabase.initialize( url: '你的Supabase项目URL', anonKey: '你的Supabase匿名密钥', ); runApp(const MyApp()); } // 全局Supabase客户端实例 final supabase = Supabase.instance.client;
2. 封装Supabase操作工具类
创建SupabaseSavedService类,封装对saved表的查询、插入、删除操作:
import 'package:supabase_flutter/supabase_flutter.dart'; class SupabaseSavedService { final SupabaseClient _client = Supabase.instance.client; // 检查当前用户是否已收藏指定房源 Future<bool> checkIfSaved(String userId, int propertyId) async { final response = await _client .from('saved') .select() .eq('userid', userId) .eq('propertyid', propertyId) .maybeSingle(); // 无匹配记录时返回null,不抛出异常 return response != null; } // 收藏房源:向saved表插入记录 Future<void> saveProperty(String userId, int propertyId) async { try { await _client.from('saved').insert({ 'userid': userId, 'propertyid': propertyId, // created_at由Supabase自动生成,无需手动传入 }); } on PostgrestException catch (e) { // 处理重复收藏的唯一约束冲突 if (e.code == '23505') return; rethrow; } } // 取消收藏:删除saved表中的对应记录 Future<void> unsaveProperty(String userId, int propertyId) async { await _client .from('saved') .delete() .eq('userid', userId) .eq('propertyid', propertyId); } }
重要:在Supabase控制台给
saved表添加userid+propertyid联合唯一约束,避免同一用户重复收藏同一房源。
3. 在房源列表项中集成收藏按钮
结合你已实现的CustomLikeButton,在房源列表项中完成完整功能逻辑:
import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'like_button.dart'; import 'supabase_saved_service.dart'; class PropertyItem extends StatefulWidget { final Map<String, dynamic> property; const PropertyItem({super.key, required this.property}); @override State<PropertyItem> createState() => _PropertyItemState(); } class _PropertyItemState extends State<PropertyItem> { final _savedService = SupabaseSavedService(); bool _isLiked = false; bool _isLoading = false; late final String _currentUserId; @override void initState() { super.initState(); // 获取当前登录用户ID(需确保用户已登录) _currentUserId = supabase.auth.currentUser!.id; // 初始化时检查房源收藏状态 _checkSavedStatus(); } Future<void> _checkSavedStatus() async { final propertyId = widget.property['id'] as int; _isLiked = await _savedService.checkIfSaved(_currentUserId, propertyId); if (mounted) setState(() {}); } Future<void> _toggleLike() async { if (_isLoading) return; setState(() => _isLoading = true); final propertyId = widget.property['id'] as int; try { if (_isLiked) { // 取消收藏 await _savedService.unsaveProperty(_currentUserId, propertyId); setState(() => _isLiked = false); } else { // 添加收藏 await _savedService.saveProperty(_currentUserId, propertyId); setState(() => _isLiked = true); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('操作失败: ${e.toString()}')), ); } } finally { if (mounted) setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), margin: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade200), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.property['address'] ?? ''), const SizedBox(height: 8), Text('价格: ${widget.property['price']}'), const SizedBox(height: 8), Text(widget.property['description'] ?? ''), ], ), ), // 使用你自定义的收藏按钮 CustomLikeButton( isLiked: _isLiked, onTap: _toggleLike, ), // 加载状态指示器 if (_isLoading) const Padding( padding: EdgeInsets.only(left: 8), child: SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ), ), ], ), ); } }
4. 关键配置说明
- 用户登录校验:在页面初始化时检查用户登录状态,未登录则引导登录,避免
currentUser为null的异常。 - RLS行级安全策略:在Supabase控制台给
saved表配置以下策略:SELECT:允许用户查看自己的收藏记录(auth.id() = userid)INSERT:允许用户插入自己的收藏记录(auth.id() = userid)DELETE:允许用户删除自己的收藏记录(auth.id() = userid)
内容的提问来源于stack exchange,提问作者Chanda Musonda
相关产品推荐
相关产品推荐

