'Flutter provider profile picture not updating
I am building a method that the user can select a prefered profile picture to show arround the app, using provider package. I used shared_preferences to save the profile picture preferences on locally as a int value. And it worked, means I can save the profile picture to local system. But the problem is, the provider package completely became useless in this case, because I have to convert the widget to statefull and call the setState method when ever I insert a profilePicture widget inside the widget tree. And even the profilePicture widget in the HomeScreen not updating this way. I want to know how can I use the provider package for this issue instead of using statefulWidgets.
watch the Gif or video
This is the Provider class I created:
class ProfilePicProvider with ChangeNotifier {
ProfilePicPref profilePicPreferences = ProfilePicPref();
int _svgNumber = 1;
int get svgNumber => _svgNumber;
set svgNumber(int value) {
_svgNumber = value;
profilePicPreferences.setProfilePic(value);
notifyListeners();
}
void changePic(int val) {
_svgNumber = val;
profilePicPreferences.setProfilePic(val);
notifyListeners();
}
}
This is the sharedPreferences class
class ProfilePicPref {
static const PRO_PIC_STS = 'PROFILESTATUS';
setProfilePic(int svgNo) async {
SharedPreferences profilePref = await SharedPreferences.getInstance();
profilePref.setInt(PRO_PIC_STS, svgNo);
}
Future<int> getProfilePicture() async {
SharedPreferences profilePref = await SharedPreferences.getInstance();
return profilePref.getInt(PRO_PIC_STS) ?? 1;
}
}
This is the image selection screen and save that data to sharedPreferences class
class SelectProfilePicture extends StatefulWidget {
const SelectProfilePicture({Key? key}) : super(key: key);
@override
State<SelectProfilePicture> createState() => _SelectProfilePictureState();
}
class _SelectProfilePictureState extends State<SelectProfilePicture> {
int svgNumber = 1;
ProfilePicProvider proProvider = ProfilePicProvider();
@override
void initState() {
getCurrentProfilePicture();
super.initState();
}
void getCurrentProfilePicture() async {
proProvider.svgNumber =
await proProvider.profilePicPreferences.getProfilePicture();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
CurrentAccountPicture(
path: 'assets/svg/${proProvider.svgNumber}.svg'),
Expanded(
child: GridView.builder(
itemCount: 15,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
svgNumber = index + 1;
});
proProvider.changePic(index + 1);
proProvider.svgNumber = index + 1;
},
child: SvgPicture.asset('assets/svg/${index + 1}.svg'),
);
},
),
),
],
),
);
}
}
This is the HomeScreen which is not updating the profile image whether it is statefull or stateless
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final proPicProvider = Provider.of<ProfilePicProvider>(context);
return Scaffold(
body:
Column(
children: [
Row(
children: [
CurrentAccountPicture(
path: 'assets/svg/${proPicProvider.svgNumber}.svg'),
],
),
],
),
);
}
}
example:
I have to convert the widget to statefull and call setState method to get the current profile picture from sharedPreferences. You may find this screen from the GIF I provided.
class Progress extends StatefulWidget {
const Progress({Key? key}) : super(key: key);
@override
State<Progress> createState() => _ProgressState();
}
class _ProgressState extends State<Progress> {
ProfilePicProvider proProvider = ProfilePicProvider();
@override
void initState() {
getCurrentProfilePicture();
super.initState();
}
void getCurrentProfilePicture() async {
proProvider.svgNumber =
await proProvider.profilePicPreferences.getProfilePicture();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SizedBox(
height: 130.0,
width: 130.0,
child: SvgPicture.asset(
'assets/svg/${proProvider.svgNumber}.svg'),
),
),
);
}
}
Solution 1:[1]
The problem is in _SelectProfilePictureState when you create new instance of your ChangeNotifier:
ProfilePicProvider proProvider = ProfilePicProvider();. It means you are not using the provider available across the context but creating new one every time. So when the value of your provider changed it has effect only inside _SelectProfilePictureState. Instead of creating new instance you must call it always using the context:
class SelectProfilePicture extends StatefulWidget {
const SelectProfilePicture({Key? key}) : super(key: key);
@override
State<SelectProfilePicture> createState() => _SelectProfilePictureState();
}
class _SelectProfilePictureState extends State<SelectProfilePicture> {
int svgNumber = 1;
// [removed] ProfilePicProvider proProvider = ProfilePicProvider();
//removed
/*void getCurrentProfilePicture() async {
proProvider.svgNumber =
await proProvider.profilePicPreferences.getProfilePicture();
setState(() {});
}*/
@override
Widget build(BuildContext context) {
//use provider from the context
final proProvider = Provider.of<ProfilePicProvider>(context,listen:true);
return Scaffold(
body: Column(
children: [
CurrentAccountPicture(
path: 'assets/svg/${proProvider.svgNumber}.svg'),
Expanded(
child: GridView.builder(
itemCount: 15,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
svgNumber = index + 1;
});
proProvider.changePic(index + 1);
proProvider.svgNumber = index + 1;
},
child: SvgPicture.asset('assets/svg/${index + 1}.svg'),
);
},
),
),
],
),
);
}
}
If you enter the application you may want send initially selected image to your provider:
Add parameter to the constructor of ProfilePicProvider:
ProfilePicProvider(SharedPreferences prefs): _svgNumber = prefs.getInt(ProfilePicPref.PRO_PIC_STS) ?? 1;
In main.dart:
Future<void> main()async{
WidgetsFlutterBinding.ensureInitialized();
var prefs = await SharedPreferences.getInstance();
runApp(
MultiProvider(
providers:[
ChangeNotifierProvider( create:(_) => ProfilePicProvider(prefs))
],
child: yourtopWidget
)
);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

