Flutter选择图片后页面不刷新,无法实时更新问题求助
问题描述
我希望App启动时随机显示一张asset图片(命名为image1至image6),通过生成1-6的随机数加载对应图片,同时允许用户在设置页将这6张默认图片替换为自定义图片。
我用Flutter实现了功能:用ImagePicker选择图片,复制到应用目录并命名为giris[ID].jpeg,路径存入sqflite数据库。但现在遇到问题:用户能正常替换默认图片,但无法在不退出页面的情况下替换已自定义的图片,只有退出并重新进入页面时图片才会更新,且没有报错信息。
现有代码
import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:persentilizlem/util/database_helper.dart'; import 'package:persentilizlem/util/girisresimmodel.dart'; import 'package:persentilizlem/util/imagepick_test.dart'; import 'package:persentilizlem/util/widgets/progressindicator.dart'; class PickImageSet extends StatefulWidget { const PickImageSet( {Key? key}) : super(key: key); @override State<PickImageSet> createState() => _PickImageSetState(); } class _PickImageSetState extends State<PickImageSet> { late DatabaseHelper _databaseHelper; File? image; final singleImagePick = ImagePicker(); ImageCache _imageCache = MyImageCache(); @override void initState() { _databaseHelper = DatabaseHelper(); super.initState(); } /////// Show image in screen @override Widget build(BuildContext context) { return Scaffold( body: SizedBox( height: double.maxFinite, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ FutureBuilder( future: _databaseHelper.girisResimListesi(), builder: (context, AsyncSnapshot<List<GirisResim>> snapshot) { if (snapshot.hasData) { return Expanded( child: GridView.builder( shrinkWrap: true, itemCount: snapshot.data!.length, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2), itemBuilder: (context, index) { return Container( padding: const EdgeInsets.all(10), margin: const EdgeInsets.all(5), decoration: containerBoxDecoration(), child: snapshot.connectionState == ConnectionState.active ? Icon( Icons.camera_alt_outlined, color: Colors.black.withOpacity(0.3), ) : InkWell( onTap: () async { ImageSource? source = await pickImageSource(context); (source != null) ? pickImage(source, snapshot.data![index].id) : null; _imageCache; setState(() {}); }, child: Stack( alignment: AlignmentDirectional.topStart, children: [ snapshot.data![index].isAsset == 0 ? Image.asset( snapshot.data![index] .resimYol, fit: BoxFit.fitHeight, ) : Image.file( File(snapshot.data![index] .resimYol), fit: BoxFit.fitHeight, ), Icon( Icons.camera_alt_outlined, color: Colors.black .withOpacity(0.3), ), ], ), ), ); })); } else { return const ProgressBar(); } }), const Text( "Giriş kısmında görünen resimleri istediğiniz 6 resim ile değiştirebilirsiniz") ], ), ), ), ); } ///// for image container decoration BoxDecoration containerBoxDecoration() { return BoxDecoration( color: Colors.white, border: Border.all(style: BorderStyle.solid, width: 0.3), borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), spreadRadius: 5, blurRadius: 7, offset: const Offset(0, 3), ) ]); } ///// pick image from source (camera or gallery) Future<ImageSource?> pickImageSource(BuildContext context) async { if (Platform.isIOS) { return showCupertinoModalPopup( context: context, builder: (context) => CupertinoActionSheet( title: const Text("İmage Kaynağı"), actions: [ CupertinoActionSheetAction( onPressed: () => Navigator.of(context).pop(ImageSource.camera), child: const Text("Kamera")), CupertinoActionSheetAction( onPressed: () => Navigator.of(context).pop(ImageSource.gallery), child: const Text("Galeri")), ], ), ); } else { return showModalBottomSheet( context: context, builder: (context) => Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.camera_alt), title: const Text("Kamera"), onTap: () => Navigator.of(context).pop(ImageSource.camera)), ListTile( leading: const Icon(Icons.image), title: const Text("Galeri"), onTap: () => Navigator.of(context).pop(ImageSource.gallery)), ], )); } } ///// for take image from source copy file app directory and write database Future<void> pickImage(ImageSource source, int? id) async { try { File image = File((await ImagePicker().pickImage(source: source,maxWidth: 250))!.path); if (image == null) return null; final directory = await getApplicationDocumentsDirectory(); final filebuster = await image.copy("${directory.path}/giris$id.jpeg"); GirisResim resimdata = GirisResim(id: id!, resimYol: filebuster.path, isAsset: 1); _databaseHelper.girisResimGuncelle(resimdata); setState(() { this.image = File(resimdata.resimYol); }); } on PlatformException catch (e) { print(e); } } /* //// I try empty cache Future<void> _deleteCacheDir() async { final cacheDir = await getTemporaryDirectory(); if (cacheDir.existsSync()) { cacheDir.deleteSync(recursive: true); } } Future<void> _deleteAppDir() async { final appDir = await getApplicationSupportDirectory(); if(appDir.existsSync()){ appDir.deleteSync(recursive: true); } } */ }
问题原因&解决方案
核心问题
- FutureBuilder缓存数据:初始只执行一次数据库查询,后续数据库更新后不会自动重新拉取数据
- Flutter图片缓存:覆盖同路径图片后,Image组件会优先使用缓存中的旧图片,不会读取新文件内容
具体修复步骤
1. 让FutureBuilder重新获取数据
用状态变量控制Future的执行,触发刷新:
// 在State类中新增变量 late Future<List<GirisResim>> _imageListFuture; @override void initState() { super.initState(); _databaseHelper = DatabaseHelper(); _imageListFuture = _databaseHelper.girisResimListesi(); } // 新增刷新方法 void _refreshImageList() { setState(() { _imageListFuture = _databaseHelper.girisResimListesi(); }); }
然后将FutureBuilder的future参数替换为_imageListFuture。
2. 清除对应图片的缓存
在更新图片后,手动清除该路径图片的缓存:
// 在pickImage方法中,数据库更新后添加 final imageKey = FileImage(File(resimdata.resimYol)).key; PaintingBinding.instance.imageCache.evict(imageKey);
3. 优化刷新逻辑
去掉无效的setState(() {}),调用_refreshImageList()触发UI更新,同时确保异步操作完成后再刷新:
// 修改pickImage方法的末尾 await _databaseHelper.girisResimGuncelle(resimdata); // 清除缓存 final imageKey = FileImage(File(resimdata.resimYol)).key; PaintingBinding.instance.imageCache.evict(imageKey); // 刷新列表 _refreshImageList(); // 修改InkWell的onTap逻辑 onTap: () async { ImageSource? source = await pickImageSource(context); if (source != null) { await pickImage(source, snapshot.data![index].id); } },
完整修改后的关键代码片段
class _PickImageSetState extends State<PickImageSet> { late DatabaseHelper _databaseHelper; late Future<List<GirisResim>> _imageListFuture; final singleImagePick = ImagePicker(); @override void initState() { _databaseHelper = DatabaseHelper(); _imageListFuture = _databaseHelper.girisResimListesi(); super.initState(); } void _refreshImageList() { setState(() { _imageListFuture = _databaseHelper.girisResimListesi(); }); } @override Widget build(BuildContext context) { return Scaffold( body: SizedBox( height: double.maxFinite, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ FutureBuilder( future: _imageListFuture, builder: (context, AsyncSnapshot<List<GirisResim>> snapshot) { // 原有UI代码不变 }), const Text("Giriş kısmında görünen resimleri istediğiniz 6 resim ile değiştirebilirsiniz") ], ), ), ), ); } Future<void> pickImage(ImageSource source, int? id) async { try { final pickedFile = await ImagePicker().pickImage(source: source, maxWidth: 250); if (pickedFile == null) return; File image = File(pickedFile.path); final directory = await getApplicationDocumentsDirectory(); final filebuster = await image.copy("${directory.path}/giris$id.jpeg"); GirisResim resimdata = GirisResim(id: id!, resimYol: filebuster.path, isAsset: 1); await _databaseHelper.girisResimGuncelle(resimdata); // 清除缓存 final imageKey = FileImage(File(resimdata.resimYol)).key; PaintingBinding.instance.imageCache.evict(imageKey); // 刷新列表 _refreshImageList(); } on PlatformException catch (e) { print(e); } } // 其他原有方法不变 }
内容的提问来源于stack exchange,提问作者GLSVR
相关产品推荐
相关产品推荐

