Components that are using the Notifier are not getting updated, when the child components are changing the state
state_provider.dart
class StateProvider extends ChangeNotifier {
// user data
String _name = '';
String _phoneNumber = '';
String _email = '';
String? _gender;
String _locationSerachText = '';
Map<String, dynamic> _viewPort = {};
final List<String> _tags = [];
List<LocationCard> _locationCards = [];
// Getters for user data
String get name => _name;
String get phoneNumber => _phoneNumber;
String get email => _email;
String? get gender => _gender;
Map<String, dynamic> get viewPort => _viewPort;
String get locationSerachText => _locationSerachText;
List<String> get tags => _tags;
List<LocationCard> get locationCards => _locationCards;
// Setters for user data
set name(String value) {
_name = value;
notifyListeners();
}
set viewPort(Map<String, dynamic> value) {
_viewPort = value;
notifyListeners();
}
set locationSerachText(String value) {
_locationSerachText = value;
notifyListeners();
}
set locationCards(List<LocationCard> value) {
_locationCards = value;
notifyListeners();
}
set phoneNumber(String value) {
_phoneNumber = value;
notifyListeners();
}
set email(String value) {
_email = value;
notifyListeners();
}
set gender(String? value) {
_gender = value;
notifyListeners();
}
// Method to add tag
void addTag(String tag) {
_tags.add(tag);
notifyListeners();
}
// Method to remove tag
void removeTag(String tag) {
_tags.remove(tag);
notifyListeners();
}
}
powerup_profile_screen.dart
class PowerUpProfileScreen extends StatefulWidget {
@override
_PowerUpProfileScreenState createState() => _PowerUpProfileScreenState();
}
class _PowerUpProfileScreenState extends State<PowerUpProfileScreen> {
@override
Widget build(
BuildContext context,
) {
final stateProvider = Provider.of<StateProvider>(context, listen: true);
bool isAtLeastOneTagSelected() {
return stateProvider.tags.isNotEmpty;
}
Color getFinishButtonColor() {
return isAtLeastOneTagSelected() ? Colors.blue : Colors.grey;
}
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: SvgPicture.asset('assets/icons/back-arrow.svg'),
onPressed: () {
Navigator.of(context).pop();
},
),
actions: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 100.0),
child: buildDotsDecorator(
2,
List.generate(3, (index) => 'Page $index'),
),
)
],
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 5.0),
child: Container(
color: GuideroTheme.darkTheme.primaryColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CustomHeaderText(text: 'Power up your profile'),
const SizedBox(
height: 12,
),
const ParagrapghText(
text: 'Choose tags that match your expertise and interests',
fontSize: 16,
textAlign: TextAlign.left,
),
const SizedBox(
height: 16.0,
),
const ParagrapghText(
text: 'You can select more than one tag',
fontSize: 16,
textAlign: TextAlign.left,
),
const SizedBox(
height: 32.0,
),
buildTagCards([
'Cafes & Pubs',
'Resorts',
'Hotels',
'Social Events',
'Tourist Spots'
]),
const Spacer(),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: CustomTextButton(
text: 'Finish',
onPressed: isAtLeastOneTagSelected()
? () {
print("finish clicked ");
}
: null,
backgroundColor: getFinishButtonColor(),
),
),
],
),
),
),
);
}
Widget buildTagCards(List<String> tagTexts) {
return Wrap(
spacing: 30.0,
runSpacing: 16.0,
alignment: WrapAlignment.start,
children: tagTexts.map((tagText) {
return TagCard(
tagText: tagText,
);
}).toList(),
);
}
}
Inside the TagCard widget, tags are added and removed 👇🏽
class TagCard extends StatefulWidget {
final String tagText;
TagCard({Key? key, required this.tagText}) : super(key: key);
@override
State<TagCard> createState() => _TagCardState();
}
class _TagCardState extends State<TagCard> {
bool isSelected = false;
@override
Widget build(BuildContext context) {
final stateProvider = Provider.of<StateProvider>(context);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(99),
border: Border.all(
color: isSelected ? Colors.white : Colors.white.withOpacity(0.5),
width: 1),
color: Colors.transparent,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () {
isSelected = stateProvider.tags.any(
(tag) => tag.toLowerCase() == widget.tagText.toLowerCase());
print('Tag tapped: ${widget.tagText}');
print('Tag selected: $isSelected');
if (isSelected) {
stateProvider.tags.remove(widget.tagText);
} else {
stateProvider.tags.add(widget.tagText);
}
setState(() {
isSelected = !isSelected;
});
print("other tags ${stateProvider.viewPort}");
print('Selcted tags : ${stateProvider.tags}');
},
child: Text(
widget.tagText,
style: const TextStyle(
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.w500),
),
),
],
),
);
}
}
Button colour is changed accordingly 👇🏽
final stateProvider = Provider.of<StateProvider>(context, listen: true);
bool isAtLeastOneTagSelected() {
return stateProvider.tags.isNotEmpty;
}
Color getFinishButtonColor() {
return isAtLeastOneTagSelected() ? Colors.blue : Colors.grey;
}
When the tags are selected/removed the state is not reflected in the parent component.
How to resolve this 💁🏽♂️
Tried getting the value directly
backgroundColor: stateProvider.tags.isNotEmpty ? Colors.blue : Colors.grey;
2
Answers
You are mixin to state-management techniques, the internal state of stateful widget
TagCard
and the state of your change notifier, and their are not aligned. Your tag cardisSelected
is not defined by the change notifier but with the internal state of the StatefulWidget.I would simplify your
TagCard
widget and do as with many other widgets with a similar behavior in Flutter (CheckBox
,Switch
,Radio
, …), make it such that it does not maintain any state. Instead, when the state ofTagCard
needs to change, the widget calls the [onSelect] or [onDeselect] callbacks and widgets that use a tag card will listen for the callbacks, update the state accordingly and rebuild the tag card with new values to update the visual appearance of the tag cards.and if you don’t want to use consumer you can:
to see more link