Flutter如何用Change Notifier Provider实现点击收藏图标添加影片到收藏页
ChangeNotifierProvider 修复方案
1. 实现MovieProvider状态类
新增movie_provider.dart文件,代码如下:
import 'package:flutter/foundation.dart'; import 'movie.dart'; // 导入你定义的Movie类的文件 class MovieProvider extends ChangeNotifier { // 存储收藏的电影列表 final List<Movie> _favorites = []; // 对外暴露只读的收藏列表 List<Movie> get favorites => List.unmodifiable(_favorites); // 切换电影收藏状态 void toggleFavorite(Movie movie) { if (_favorites.any((element) => element.title == movie.title)) { // 已收藏则移除 _favorites.removeWhere((element) => element.title == movie.title); } else { // 未收藏则添加 _favorites.add(movie); } // 通知所有监听的页面刷新 notifyListeners(); } // 判断某部电影是否已收藏 bool isFavorite(Movie movie) { return _favorites.any((element) => element.title == movie.title); } }
注:这里用title作为唯一标识判断电影是否重复,你也可以给Movie类新增id字段用于判断,避免重名电影误判
2. 全局注册Provider
修改main.dart入口代码,将Provider注册到应用顶层,确保所有页面都能访问到状态:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'movie_provider.dart'; void main() { runApp( ChangeNotifierProvider( create: (context) => MovieProvider(), child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( // 你的原有应用配置 title: '电影应用', home: const YourHomePage(), // 替换为你的首页 ); } }
3. 修改详情页逻辑
修改MovieScreen中收藏图标的相关代码,绑定点击事件和状态:
// 先构造当前页面对应的Movie对象 Movie currentMovie = Movie( imgUrl: widget.photo, title: widget.title, categories: widget.categories, year: widget.year, country: widget.country, length: widget.length, description: widget.description, screenshots: widget.screenshots, ); // 修改收藏IconButton代码 IconButton( padding: EdgeInsets.only(right: 20), onPressed: () { // 调用provider方法切换收藏状态,listen设为false避免不必要的重绘 Provider.of<MovieProvider>(context, listen: false).toggleFavorite(currentMovie); }, // 根据收藏状态切换图标 icon: Icon( Provider.of<MovieProvider>(context).isFavorite(currentMovie) ? Icons.favorite : Icons.favorite_outline ), iconSize: 30, color: Colors.red, ),
4. 修改收藏页逻辑
将收藏页写死的卡片替换为遍历收藏列表动态生成:
class _MyListState extends State<MyList> { @override Widget build(BuildContext context) { // 获取收藏列表 final favoriteMovies = Provider.of<MovieProvider>(context).favorites; return Scaffold( body: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Navbar1(), Container( width: MediaQuery.of(context).size.width - 60, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: ListView( children: <Widget>[ const SizedBox(height: 50), HeadMenuMylist(), const SizedBox(height: 20), GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), mainAxisSpacing: 10, crossAxisSpacing: 10, crossAxisCount: 2, childAspectRatio: 1 / 2, // 遍历收藏列表生成卡片 children: favoriteMovies.map((movie) { return Stack( children: [ Positioned.fill( child: Container( height: 200, foregroundDecoration: BoxDecoration( borderRadius: BorderRadius.circular(10), gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Colors.black], ), ), child: ClipRRect( borderRadius: BorderRadius.circular(10), child: Image.network( movie.imgUrl, loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { if (loadingProgress == null) return child; return Center( child: SizedBox( height: 50, width: 50, child: CircularProgressIndicator( strokeWidth: 4, color: Colors.red, ), ), ); }, fit: BoxFit.cover, ), ), ), ), Padding( padding: const EdgeInsets.only(bottom: 15), child: Align( alignment: Alignment.bottomCenter, child: Text( movie.title, style: GoogleFonts.openSans( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w700), ), ), ), Padding( padding: const EdgeInsets.only(top: 10, right: 10), child: Align( alignment: Alignment.topRight, child: GestureDetector( onTap: () { // 点击删除按钮移除收藏 Provider.of<MovieProvider>(context, listen: false).toggleFavorite(movie); }, child: const Icon( Icons.delete, color: Colors.white, ), )), ), ], ); }).toList(), ), ], ), ), ), ], ), ); } }
替代方案
- GetX状态管理:代码更简洁,无需顶层注册,定义继承自GetxController的类存储收藏列表,通过
Get.put()实例化,Get.find()全局调用即可,适合小型项目快速开发。 - 本地持久化扩展:如果需要退出应用后收藏数据不丢失,可以搭配Hive或shared_preferences存储序列化后的Movie数据,应用启动时读取到Provider中即可。
内容的提问来源于stack exchange,提问作者ArQczeg14
相关产品推荐
相关产品推荐

