Flutter中Provider实现收藏列表的两类问题:图标变色与显示异常
Flutter Provider收藏列表功能问题修复
问题描述
我用Flutter的Provider实现收藏列表功能,需求为:ListView展示从JSON获取的数据,每个ListTile包含标题(数据内容)、前置ID、后置收藏图标,点击图标时该图标变为红色。目前存在两个问题:
- 点击单个收藏图标时,所有图标短暂变红后恢复,无法单独维持红色状态;
- 点击列表图标跳转至收藏列表页面时,页面未正确显示收藏项,而是显示「Instance of 'Food'」。
相关代码
首页代码
import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:note/Designs/Favorite_List.dart'; import 'package:note/Models/Favorite_List_Provider.dart'; import 'package:note/Models/Food_Models.dart'; import 'package:provider/provider.dart'; import '../Services/Fetch_Data.dart'; import 'Details_DesignOfDesignOne.dart'; class DesignOne extends StatefulWidget { const DesignOne({super.key, required this.Image, required widget.Desc}); final String Image; final String Desc; @override State<DesignOne> createState() => _DesignOneState(); } class _DesignOneState extends State<DesignOne> { late Future<Food> futureFood; @override void initState() { super.initState(); futureFood = fetchData(widget.Image, widget.Desc); } @override Widget build(BuildContext context) { bool ischecked = false; final provider = favorite.of(context); return Scaffold( backgroundColor: Colors.grey.shade200, appBar: AppBar( title: Text('Design one'), actions: [ Padding( padding: const EdgeInsets.all(8.0), child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => FavoriteScreen(Desc: '', Image: ''), ), ); }, child: Icon( Icons.list, size: 30, ), ), ) ], ), body: Consumer<favorite>( builder: (BuildContext context, favorite value, child) { return Center( child: FutureBuilder<Food>( future: fetchData(widget.Image, widget.Desc), builder: (context, snapshot) { if (snapshot.hasData) { return ListView.builder( shrinkWrap: true, itemCount: snapshot.data!.categories.length, itemBuilder: (contxt, index) { final fav = snapshot.data!; return InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => DetailsDo( Desc: snapshot.data!.categories[index] .strCategoryDescription, Image: snapshot .data!.categories[index].strCategoryThumb, ), ), ); }, child: Card( elevation: 2, shape: RoundedRectangleBorder( side: BorderSide(color: Colors.white70, width: 1), borderRadius: BorderRadius.circular(50), ), child: ListTile( title: Text( snapshot.data!.categories[index].strCategory .toString(), style: GoogleFonts.montserrat( fontSize: 20, fontWeight: FontWeight.w600, fontStyle: FontStyle.italic, ), ), leading: CircleAvatar( backgroundColor: Color.fromARGB(213, 255, 251, 251), child: Text( snapshot.data!.categories[index].idCategory .toString(), style: GoogleFonts.montserrat( fontSize: 20, fontWeight: FontWeight.w600, fontStyle: FontStyle.italic, color: Color.fromARGB(255, 148, 148, 135)), ), ), trailing: IconButton( icon: (provider.isExist(fav)) ? const Icon(Icons.favorite, color: Colors.red) : const Icon(Icons.favorite_border), onPressed: () { provider.toggleFavorite(fav); }, ), ), ), ); }); } else if (snapshot.hasError) { return Text('${snapshot.error}'); } return const CircularProgressIndicator(); }, ), ); }), ); } }
收藏列表页面代码
import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:note/Models/Favorite_List_Provider.dart'; import 'package:provider/provider.dart'; import '../Models/Food_Models.dart'; import '../Services/Fetch_Data.dart'; class FavoriteScreen extends StatefulWidget { const FavoriteScreen({super.key, required widget.Image, required widget.Desc}); final String Image; final String Desc; @override State<FavoriteScreen> createState() => _FavoriteScreenState(); } class _FavoriteScreenState extends State<FavoriteScreen> { @override Widget build(BuildContext context) { final provider = favorite.of(context); final favr = provider.favoriteList; return Scaffold( appBar: AppBar( title: Text("Favorites"), centerTitle: true, ), body: Consumer<favorite>( builder: (context, favorite value, child) { return FutureBuilder<Food>( future: fetchData(widget.Image, widget.Desc), builder: (context, snapshot) { if (snapshot.hasData) { return ListView.builder( shrinkWrap: true, itemCount: favr.length, itemBuilder: (context, index) { final fav = favr; return ListView(shrinkWrap: true, children: [ ListTile( title: Text(fav[index].toString()), ), ]); }); } return const CircularProgressIndicator(); }); }, )); } }
Provider代码
import 'Food_Models.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class favorite extends ChangeNotifier { List<Food> _favoriteList = []; List<Food> get favoriteList => _favoriteList; void toggleFavorite(Food fav) { final isExist = _favoriteList.contains(fav); if (isExist) { _favoriteList.remove(fav); } else { _favoriteList.add(fav); } notifyListeners(); } bool isExist(Food fav) { final isExist = _favoriteList.contains(fav); return isExist; } void clearFavorite() { _favoriteList = []; notifyListeners(); } static favorite of( BuildContext context, { bool listen = true, }) { return Provider.of<favorite>( context, listen: listen, ); } }
问题修复方案
问题1:收藏图标状态异常修复
问题根源:
- 你将整个
Food对象(而非单个分类项)传入收藏方法,导致所有图标同时触发状态变化; build方法中重复调用fetchData,每次状态更新都会重新加载数据,重置图标状态。
修复步骤:
- 使用初始化时的
futureFood替代build中重复调用的fetchData; - 针对单个分类项(
snapshot.data!.categories[index])进行收藏判断和操作; - 确保分类模型类重写
==和hashCode,让contains能正确识别对象。
修改后的首页核心代码:
// 使用初始化的futureFood,避免重复请求 child: FutureBuilder<Food>( future: futureFood, builder: (context, snapshot) { if (snapshot.hasData) { return ListView.builder( shrinkWrap: true, itemCount: snapshot.data!.categories.length, itemBuilder: (contxt, index) { // 获取单个分类项 final category = snapshot.data!.categories[index]; return InkWell( ... child: Card( ... child: ListTile( ... trailing: IconButton( // 针对单个分类项判断收藏状态 icon: (provider.isExist(category)) ? const Icon(Icons.favorite, color: Colors.red) : const Icon(Icons.favorite_border), onPressed: () { // 传入单个分类项进行收藏切换 provider.toggleFavorite(category); }, ), ), ), ); }); } ... }, ),
分类模型重写示例:
class Category { final String idCategory; final String strCategory; // 其他属性... @override bool operator ==(Object other) => identical(this, other) || other is Category && runtimeType == other.runtimeType && idCategory == other.idCategory; @override int get hashCode => idCategory.hashCode; }
问题2:收藏列表显示异常修复
问题根源:
- 直接调用对象的
toString(),未访问具体属性; - 多余的
FutureBuilder嵌套,且嵌套ListView导致布局异常。
修复步骤:
- 移除
FutureBuilder,直接使用Provider中的收藏数据; - 访问收藏项的具体属性(如
strCategory)进行展示; - 取消
ListView嵌套,直接返回ListTile。
修改后的收藏列表页面代码:
import 'package:flutter/material.dart'; import 'package:note/Models/Favorite_List_Provider.dart'; import 'package:provider/provider.dart'; import '../Models/Food_Models.dart'; class FavoriteScreen extends StatefulWidget { const FavoriteScreen({super.key, required this.Image, required widget.Desc}); final String Image; final String Desc; @override State<FavoriteScreen> createState() => _FavoriteScreenState(); } class _FavoriteScreenState extends State<FavoriteScreen> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Favorites"), centerTitle: true, ), body: Consumer<favorite>( builder: (context, favorite value, child) { // 空状态提示 if (value.favoriteList.isEmpty) { return const Center(child: Text("暂无收藏")); } return ListView.builder( shrinkWrap: true, itemCount: value.favoriteList.length, itemBuilder: (context, index) { final category = value.favoriteList[index]; return ListTile( title: Text(category.strCategory), leading: CircleAvatar( child: Text(category.idCategory), ), ); }, ); }, ), ); } }
内容的提问来源于stack exchange,提问作者Badriya Haddad
相关产品推荐
相关产品推荐

