💄 Optimize poll
This commit is contained in:
@@ -62,9 +62,19 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
bool? _yesNoSelected;
|
bool? _yesNoSelected;
|
||||||
int? _ratingSelected; // 1..5
|
int? _ratingSelected; // 1..5
|
||||||
|
|
||||||
|
/// Flag to track if user has edited the current question to prevent provider rebuilds from resetting state
|
||||||
|
bool _userHasEdited = false;
|
||||||
|
|
||||||
|
/// Listener for text controller to mark as edited when user types
|
||||||
|
late final VoidCallback _controllerListener;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_controllerListener = () {
|
||||||
|
_userHasEdited = true;
|
||||||
|
};
|
||||||
|
_textController.addListener(_controllerListener);
|
||||||
_answers = Map<String, dynamic>.from(widget.initialAnswers ?? {});
|
_answers = Map<String, dynamic>.from(widget.initialAnswers ?? {});
|
||||||
// Set initial collapse state based on the parameter
|
// Set initial collapse state based on the parameter
|
||||||
_isCollapsed = !widget.isInitiallyExpanded;
|
_isCollapsed = !widget.isInitiallyExpanded;
|
||||||
@@ -75,6 +85,11 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
_isModifying = false;
|
_isModifying = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Load initial answers into local state
|
||||||
|
if (_questions != null) {
|
||||||
|
_loadCurrentIntoLocalState();
|
||||||
|
_userHasEdited = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initializeFromPollData(SnPollWithStats poll) {
|
void _initializeFromPollData(SnPollWithStats poll) {
|
||||||
@@ -101,6 +116,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_textController.removeListener(_controllerListener);
|
||||||
_textController.dispose();
|
_textController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@@ -111,11 +127,11 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
final q = _current;
|
final q = _current;
|
||||||
final saved = _answers[q.id];
|
final saved = _answers[q.id];
|
||||||
|
|
||||||
|
if (!_userHasEdited) {
|
||||||
_singleChoiceSelected = null;
|
_singleChoiceSelected = null;
|
||||||
_multiChoiceSelected.clear();
|
_multiChoiceSelected.clear();
|
||||||
_yesNoSelected = null;
|
_yesNoSelected = null;
|
||||||
_ratingSelected = null;
|
_ratingSelected = null;
|
||||||
_textController.text = '';
|
|
||||||
|
|
||||||
switch (q.type) {
|
switch (q.type) {
|
||||||
case SnPollQuestionType.singleChoice:
|
case SnPollQuestionType.singleChoice:
|
||||||
@@ -133,10 +149,15 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
if (saved is int) _ratingSelected = saved;
|
if (saved is int) _ratingSelected = saved;
|
||||||
break;
|
break;
|
||||||
case SnPollQuestionType.freeText:
|
case SnPollQuestionType.freeText:
|
||||||
if (saved is String) _textController.text = saved;
|
if (saved is String) {
|
||||||
|
_textController.removeListener(_controllerListener);
|
||||||
|
_textController.text = saved;
|
||||||
|
_textController.addListener(_controllerListener);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool _isCurrentAnswered() {
|
bool _isCurrentAnswered() {
|
||||||
final q = _current;
|
final q = _current;
|
||||||
@@ -214,6 +235,9 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
data: {'answer': _answers},
|
data: {'answer': _answers},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Refresh poll data to show submitted answer
|
||||||
|
ref.invalidate(pollWithStatsProvider(widget.pollId));
|
||||||
|
|
||||||
// Only call onSubmit after server accepts
|
// Only call onSubmit after server accepts
|
||||||
widget.onSubmit(Map<String, dynamic>.unmodifiable(_answers));
|
widget.onSubmit(Map<String, dynamic>.unmodifiable(_answers));
|
||||||
|
|
||||||
@@ -236,6 +260,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
if (_index < _questions!.length - 1) {
|
if (_index < _questions!.length - 1) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_index++;
|
_index++;
|
||||||
|
_userHasEdited = false;
|
||||||
_loadCurrentIntoLocalState();
|
_loadCurrentIntoLocalState();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -250,6 +275,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
if (_index > 0) {
|
if (_index > 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_index--;
|
_index--;
|
||||||
|
_userHasEdited = false;
|
||||||
_loadCurrentIntoLocalState();
|
_loadCurrentIntoLocalState();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -342,7 +368,11 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
RadioListTile<String>(
|
RadioListTile<String>(
|
||||||
value: opt.id,
|
value: opt.id,
|
||||||
groupValue: _singleChoiceSelected,
|
groupValue: _singleChoiceSelected,
|
||||||
onChanged: (val) => setState(() => _singleChoiceSelected = val),
|
onChanged:
|
||||||
|
(val) => setState(() {
|
||||||
|
_singleChoiceSelected = val;
|
||||||
|
_userHasEdited = true;
|
||||||
|
}),
|
||||||
title: Text(opt.label),
|
title: Text(opt.label),
|
||||||
subtitle: opt.description != null ? Text(opt.description!) : null,
|
subtitle: opt.description != null ? Text(opt.description!) : null,
|
||||||
),
|
),
|
||||||
@@ -364,6 +394,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
} else {
|
} else {
|
||||||
_multiChoiceSelected.remove(opt.id);
|
_multiChoiceSelected.remove(opt.id);
|
||||||
}
|
}
|
||||||
|
_userHasEdited = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
title: Text(opt.label),
|
title: Text(opt.label),
|
||||||
@@ -386,6 +417,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
onSelectionChanged: (sel) {
|
onSelectionChanged: (sel) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_yesNoSelected = sel.isEmpty ? null : sel.first;
|
_yesNoSelected = sel.isEmpty ? null : sel.first;
|
||||||
|
_userHasEdited = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
multiSelectionEnabled: false,
|
multiSelectionEnabled: false,
|
||||||
@@ -411,6 +443,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_ratingSelected = value;
|
_ratingSelected = value;
|
||||||
|
_userHasEdited = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -441,6 +474,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_isModifying = true;
|
_isModifying = true;
|
||||||
_index = 0; // Reset to first question for modification
|
_index = 0; // Reset to first question for modification
|
||||||
|
_userHasEdited = false;
|
||||||
_loadCurrentIntoLocalState();
|
_loadCurrentIntoLocalState();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -487,32 +521,6 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (poll.title != null || poll.description != null)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (poll.title?.isNotEmpty ?? false)
|
|
||||||
Text(
|
|
||||||
poll.title!,
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
if (poll.description?.isNotEmpty ?? false)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 4),
|
|
||||||
child: Text(
|
|
||||||
poll.description!,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).textTheme.bodyMedium?.color?.withOpacity(0.7),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
for (final q in _questions!)
|
for (final q in _questions!)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16.0),
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
|
|||||||
Reference in New Issue
Block a user