Flutter+Provider出现Type 'Null' is not a subtype of type 'int'错误求助
Flutter收藏页报错:type 'Null' is not a subtype of type 'int' 解决方法
问题背景
开发Flutter应用时,实现用户点击保存文章ID并在收藏页展示的功能,但每次进入收藏页都会报错:type 'Null' is not a subtype of type 'int'。
用户点击保存文章的页面
需展示收藏文章的页面
相关代码
配置ChangeNotifier Provider的Main代码
class _MyAppState extends State<MyApp> { var data; var id; List<ArticleModel> allArticles = []; @override Widget build(BuildContext context) { return ChangeNotifierProvider<FavoriteProvider>( create: (context) => FavoriteProvider(allArticles), builder: (contex, _) { return MaterialApp( debugShowCheckedModeBanner: false, /// Google Analytics navigatorObservers: [locator<AnalyticsService>().getAnalyticsObserve(),], theme: assoFacileNewsTheme, /// Routes initialRoute: "/splashScreen", routes: { /// Primary pages "/splashScreen": (_) => SplashScreen(), "/onboardingScreens": (_) => OnboardingScreens(), /// bottom navigation bar pages "/homepage": (_) => HomePage(), "/formazione": (_) => FormazionePage(), "/favorite_articles": (_) => FavoritePage(id: id,), }, ); }, ); } }
ChangeNotifier页面代码
class FavoriteProvider extends ChangeNotifier { final List<ArticleModel> _allArticles = []; final List<int> _favoriteArticleIds = []; List<int> get favoriteArticleIds => _favoriteArticleIds; int get favoriteCount => _favoriteArticleIds.length; FavoriteProvider(List<ArticleModel> allArticles) {allArticles.addAll(allArticles);} /// Function to check if an article is favorited bool isFavorite(int favoriteArticleIds) {return _favoriteArticleIds.contains(favoriteArticleIds);} // was articleId /// Function to add or remove an article from favorites void toggleFavorite(int favoriteArticleIds, context) async { SharedPreferences prefs = await SharedPreferences.getInstance(); if (_favoriteArticleIds.contains(favoriteArticleIds)) { _favoriteArticleIds.remove(favoriteArticleIds); print("Removed $favoriteArticleIds from _favoriteArticleIds"); ScaffoldMessenger.of(context).showSnackBar(const SnackBar(duration: Duration(seconds: 3),backgroundColor: Colors.white, content: Text("Articolo rimosso", style: TextStyle(color: Colors.black),),),); } else { _favoriteArticleIds.add(favoriteArticleIds); print("Saved $favoriteArticleIds to _favoriteArticleIds"); ScaffoldMessenger.of(context).showSnackBar(const SnackBar(duration: Duration(seconds: 3),backgroundColor: Colors.white, content: Text("Articolo salvato", style: TextStyle(color: Colors.black),),),); } notifyListeners(); } /// Fetch favourite articles Future<List<ArticleModel>> fetchFavoriteArticles() async { final favoriteArticles = _allArticles.where((article) => _favoriteArticleIds.contains(article.id)).toList(); print("Favorite Articles in fetchFavoriteArticles: ${favoriteArticles.length}"); return favoriteArticles; } }
文章页面代码
class ArticlePage extends StatefulWidget { final int id; final String? urlImage; final String? title; final String? description; final dynamic data; const ArticlePage({Key? key, required widget.data, required widget.id, widget.urlImage, widget.title, widget.description}) : super(key: key); @override _ArticlePageState createState() => _ArticlePageState(data, id, urlImage, title, description); } class _ArticlePageState extends State<ArticlePage> { int id;final urlImage;final title;final description;final data; late FavoriteProvider favoriteArticles; late bool _favoriteArticlesReady; late bool isFavorite = false; _ArticlePageState(this.data, this.id, this.urlImage, this.title, this.description,); /// Function to save and remove articles void btnFunction() { favoriteArticles.toggleFavorite(widget.id, context); setState(() { isFavorite = !isFavorite; }); } @override void initState() { super.initState(); favoriteArticles = Provider.of<FavoriteProvider>(context, listen: false); isFavorite = favoriteArticles.isFavorite(widget.id); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: Consumer<FavoriteProvider>(builder: (context, favoriteArticles, _) { return CustomScrollView( slivers: <Widget>[ SliverAppBar( leading: const PreviousBtn(), flexibleSpace: FlexibleSpaceBar( centerTitle: true, background: CachedNetworkImage( imageUrl: data["_embedded"]["wp:featuredmedia"][0]["link"], fit: BoxFit.cover, placeholder: (context, url) => Image.asset("assets/gif/shimmer.gif", fit: BoxFit.cover,), errorWidget: (context, url, error) => Image.asset("assets/images/unloadedImage.png", width: 250, height: 250, ), ), ), actions: [ Visibility(child: Text(data['id'].toString()), visible: false,), /// Favorite icon Padding( padding: const EdgeInsets.only(right: 10.0), child: Container( margin: const EdgeInsets.only(top: 12.0, bottom: 10.0,), width: 35, decoration: BoxDecoration(borderRadius: BorderRadius.circular(50), color: Colors.white,), child: IconButton( icon: isFavorite ? const Icon(Icons.bookmark_outlined, color: Color(0xFF0D47A1), size: 18,) : const Icon(Icons.bookmark_outline_outlined, color: Color(0xFF0D47A1), size: 18,), onPressed: btnFunction, ), ), ), /// Share article icon Padding( padding: const EdgeInsets.only(right: 15.0), child: IconButton( icon: Image.asset('assets/images/share_icon.png',), onPressed: () { Share.share(data["link"], subject: "Leggi l'articolo di AssoFacile News $title", ); }, ), ), ], floating: true, expandedHeight: 320, ), SliverList( delegate: SliverChildListDelegate([ Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ const SizedBox(height: 20,), /// Article title Text( data["title"]["rendered"].toString().replaceAll("<p>", "").replaceAll("’", "'").replaceAll("</p>", ""), style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 20, fontFamily: "Raleway",), ), const SizedBox(height: 0), /// Text content HtmlWidget( data['content']['rendered'].toString() .replaceAll("<p>", "") .replaceAll("’", "'") .replaceAll("<img>", "") .replaceAll("<div>", "<div>").replaceAll("</div>", "</div>") .replaceAll("</p>", ""), ), const SizedBox(height: 20), ], ), ), ], ), ), ], ); } ), ); } }
收藏页面代码
class FavoritePage extends StatefulWidget { final int id; final data; const FavoritePage({Key? key, widget.data, required widget.id}) : super(key: key); @override _FavoritePageState createState() => _FavoritePageState(); } class _FavoritePageState extends State<FavoritePage> { String title = "Articoli Preferiti"; @override Widget build(BuildContext context) { print("Favourite page article id: ${widget.id}"); final importFavourites = Provider.of<FavoriteProvider>(context, listen: false).fetchFavoriteArticles; return Scaffold( drawer: NavbarMenu(), appBar: CustomAppBar(leadingBtn: const DehazeIconBtn(), title: title,), body: FutureBuilder( future: importFavourites(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasData) { final favoriteArticlesData = snapshot.data as List<ArticleModel>; print("Favorite Articles Length: ${favoriteArticlesData.length}"); if (favoriteArticlesData.isEmpty) { return const EmptyArticle(); } return ListView.builder( itemCount: favoriteArticlesData.length, itemBuilder: (context, index) { final article = favoriteArticlesData[index]; return Card( margin: const EdgeInsets.all(8), elevation: 5, shadowColor: Colors.black26, color: Colors.white, child: InkWell( child: ClipRRect( borderRadius: BorderRadius.circular(10), child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox( height: 190, width: double.infinity, child: Image( image: AdvancedNetworkImage( article.urlImageSource ?? "", useDiskCache: true, cacheRule: const CacheRule(maxAge: Duration(days: 1)), ), fit: BoxFit.cover, ), ), Column( children: [ Padding( padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), child: Row( children: [ Expanded( child: Text( article.title?.replaceAll("’", "'").replaceAll("<p>", "").replaceAll("</p>", "") ?? "", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, fontFamily: "Raleway",), overflow: TextOverflow.ellipsis, maxLines: 2, ), ), ], ), ) ], ), ], ), ), onTap: () { final id = article.id; Navigator.push( context, MaterialPageRoute( builder: (context) => ArticlePage(data: article, id: id), ), ); }, ), ); }, ); } else { return const EmptyArticle(); } } else { return const CircularProgressIndicator(); } }, ), ); } }
问题根源与修复方案
1. 路由传参的空值问题
_MyAppState中id变量未初始化,默认是null,但FavoritePage要求传入非空的int类型id,进入收藏页时直接触发类型转换错误。
修复:
收藏页不需要接收单个文章ID,它从FavoriteProvider获取所有收藏ID列表,因此删除不必要的参数:
- 修改
FavoritePage定义:
class FavoritePage extends StatefulWidget { const FavoritePage({Key? key}) : super(key: key); @override _FavoritePageState createState() => _FavoritePageState(); }
- 修改路由配置:
"/favorite_articles": (_) => FavoritePage(),
2. FavoriteProvider构造函数逻辑错误
构造函数中allArticles.addAll(allArticles);是将参数列表添加到自身,导致_allArticles始终为空,后续筛选收藏文章失效。
修复:
将参数列表添加到类内部的_allArticles:
FavoriteProvider(List<ArticleModel> allArticles) { _allArticles.addAll(allArticles); }
3. 收藏状态同步问题
ArticlePage手动维护isFavorite状态,易与FavoriteProvider状态不同步。
修复:
通过Consumer监听FavoriteProvider变化,自动更新图标状态:
// 替换ArticlePage中的IconButton部分 IconButton( icon: favoriteArticles.isFavorite(widget.id) ? const Icon(Icons.bookmark_outlined, color: Color(0xFF0D47A1), size: 18,) : const Icon(Icons.bookmark_outline_outlined, color: Color(0xFF0D47A1), size: 18,), onPressed: () => favoriteArticles.toggleFavorite(widget.id, context), ),
同时删除_ArticlePageState中的isFavorite变量和btnFunction。
4. SharedPreferences未持久化数据
当前代码调用了SharedPreferences但未实际保存/读取收藏ID,重启应用后数据丢失。
修复:
在FavoriteProvider中添加本地存储逻辑:
class FavoriteProvider extends ChangeNotifier { final List<ArticleModel> _allArticles = []; final List<int> _favoriteArticleIds = []; List<int> get favoriteArticleIds => _favoriteArticleIds; int get favoriteCount => _favoriteArticleIds.length; FavoriteProvider(List<ArticleModel> allArticles) { _allArticles.addAll(allArticles); _loadFavorites(); } Future<void> _loadFavorites() async { SharedPreferences prefs = await SharedPreferences.getInstance(); List<String>? savedIds = prefs.getStringList('favorite_article_ids'); if (savedIds != null) { _favoriteArticleIds.addAll(savedIds.map((id) => int.parse(id))); notifyListeners(); } } Future<void> _saveFavorites() async { SharedPreferences prefs = await SharedPreferences.getInstance(); List<String> ids = _favoriteArticleIds.map((id) => id.toString()).toList(); await prefs.setStringList('favorite_article_ids', ids); } bool isFavorite(int articleId) { return _favoriteArticleIds.contains(articleId); } void toggleFavorite(int articleId, context) async { if (_favoriteArticleIds.contains(articleId)) { _favoriteArticleIds.remove(articleId); print("Removed $articleId from _favoriteArticleIds"); ScaffoldMessenger.of(context).showSnackBar(const SnackBar(duration: Duration(seconds: 3),backgroundColor: Colors.white, content: Text("Articolo rimosso", style: TextStyle(color: Colors.black),),),); } else { _favoriteArticleIds.add(articleId); print("Saved $articleId to _favoriteArticleIds"); ScaffoldMessenger.of(context).showSnackBar(const SnackBar(duration: Duration(seconds: 3),backgroundColor: Colors.white, content: Text("Articolo salvato", style: TextStyle(color: Colors.black),),),); } await _saveFavorites(); notifyListeners(); } Future<List<ArticleModel>> fetchFavoriteArticles() async { final favoriteArticles = _allArticles.where((article) => _favoriteArticleIds.contains(article.id)).toList(); print("Favorite Articles in fetchFavoriteArticles: ${favoriteArticles.length}"); return favoriteArticles; } }
内容的提问来源于stack exchange,提问作者Pimpi Rimpà
相关产品推荐
相关产品推荐

