Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion core/lib/src/domain/entities/question_answer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ class QuestionAnswer<T> extends Equatable {
final T answer;

@override
// TODO(dev): implement props.
List<Object?> get props => [answer];

const QuestionAnswer(this.answer);
Expand Down
28 changes: 15 additions & 13 deletions core/lib/src/presentation/choice_question/choice_question_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class ChoiceQuestionPage extends StatefulWidget {
/// This field contains the content for a page, including options.
final ChoiceQuestionData data;

// Contains the options indices that the user selected.
final QuestionAnswer<List<int>>? answer;

/// Callback that is called when [ChoiceQuestionData.isSkip] is true or at
/// least one option has been selected.
final OnSendCallback onSend;
Expand All @@ -30,6 +33,7 @@ class ChoiceQuestionPage extends StatefulWidget {
const ChoiceQuestionPage({
required this.data,
required this.onSend,
this.answer,
this.onSecondaryButtonTap,
super.key,
});
Expand All @@ -44,29 +48,30 @@ class _ChoiceQuestionPageState extends State<ChoiceQuestionPage>
bool _canBeSend = false;

/// Stores the indices of the selected options.
List<int> _selectedItems = List.empty();
List<int> _answer = List.empty();

@override
void initState() {
final selectedOptions = widget.data.selectedOptions;
final selectedOptions =
widget.answer?.answer ?? widget.data.selectedOptions;
super.initState();
if (selectedOptions != null) {
_selectedItems = selectedOptions;
_answer = selectedOptions;
_canBeSend = true;
}
}

void _onInputChanged(List<int>? selectedItems) {
setState(() {
_selectedItems = selectedItems ?? List.empty();
_answer = selectedItems ?? List.empty();
});

if (!widget.data.isSkip) {
final canBeSend = widget.data.ruleType.canBeSend(
widget.data.ruleValue,
_selectedItems.length,
_answer.length,
) &&
_selectedItems.isNotEmpty;
_answer.isNotEmpty;
setState(() => _canBeSend = canBeSend);
}
}
Expand Down Expand Up @@ -120,12 +125,11 @@ class _ChoiceQuestionPageState extends State<ChoiceQuestionPage>
onChanged: _onInputChanged,
activeColor: theme.activeColor,
inactiveColor: theme.inactiveColor,
selectedOptions: List.of(_selectedItems),
selectedOptions: List.of(_answer),
)
: _QuestionRadioButtons(
selectedOption: _selectedItems.isEmpty
? null
: _selectedItems.first,
selectedOption:
_answer.isEmpty ? null : _answer.first,
options: options,
onChanged: (selectedItem) => _onInputChanged(
selectedItem == null ? null : [selectedItem],
Expand Down Expand Up @@ -159,9 +163,7 @@ class _ChoiceQuestionPageState extends State<ChoiceQuestionPage>
onPressed: () {
widget.onSend.call(
index: widget.data.index,
answer: QuestionAnswer<List<int>>(
_selectedItems,
),
answer: QuestionAnswer<List<int>>(_answer),
);
},
isEnabled: widget.data.isSkip || _canBeSend,
Expand Down
75 changes: 61 additions & 14 deletions core/lib/src/presentation/input_question/input_question_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class InputQuestionPage extends StatefulWidget {
/// Contains the content for a page.
final InputQuestionData data;

// Contains string that the user enter.
final QuestionAnswer? answer;

/// Callback that is called after pressing bottom button if input data is
/// valid or when the question can be skipped.
final OnSendCallback onSend;
Expand All @@ -34,6 +37,7 @@ class InputQuestionPage extends StatefulWidget {
const InputQuestionPage({
required this.data,
required this.onSend,
this.answer,
this.onSecondaryButtonTap,
super.key,
});
Expand All @@ -53,6 +57,19 @@ class _InputQuestionPageState extends State<InputQuestionPage> {

bool get isDateType => widget.data.validator.type == InputType.date;

@override
void initState() {
super.initState();

final questionAnswer = widget.answer;

if (questionAnswer is QuestionAnswer<String>) {
_input = questionAnswer.answer;
} else if (questionAnswer is QuestionAnswer<DateTime>) {
_dateTime = questionAnswer.answer;
}
}

@override
Widget build(BuildContext context) {
final theme =
Expand Down Expand Up @@ -116,6 +133,7 @@ class _InputQuestionPageState extends State<InputQuestionPage> {
? _InputDate(
border: border,
dateTime: _dateTime,
hasInitialValue: widget.answer != null,
hintText: hintText ?? '',
onChanged: (value) {
if (value != null) {
Expand All @@ -135,6 +153,7 @@ class _InputQuestionPageState extends State<InputQuestionPage> {
onChanged: (input) =>
setState(() => _input = input),
theme: theme,
initialValue: _input,
validator: (text) => _canBeSkippedNumber
? null
: widget.data.validator.validate(text),
Expand Down Expand Up @@ -206,6 +225,9 @@ class _InputDate extends StatelessWidget {
/// The selected date value.
final DateTime dateTime;

/// Whether this Input Data has initial date.
final bool hasInitialValue;

/// The hint text displayed in the input field.
final String hintText;

Expand All @@ -221,6 +243,7 @@ class _InputDate extends StatelessWidget {
const _InputDate({
required this.border,
required this.dateTime,
required this.hasInitialValue,
required this.hintText,
required this.onChanged,
required this.theme,
Expand All @@ -246,6 +269,7 @@ class _InputDate extends StatelessWidget {
border: border,
),
format: DateFormat('dd.MM.yyyy'),
initialValue: hasInitialValue ? dateTime : null,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: validator,
onChanged: onChanged,
Expand All @@ -266,7 +290,7 @@ class _InputDate extends StatelessWidget {
}

/// Widget that represents an input field for entering a numeric value.
class _InputNumber extends StatelessWidget {
class _InputNumber extends StatefulWidget {
/// The border for the input field.
final OutlineInputBorder border;

Expand All @@ -282,41 +306,64 @@ class _InputNumber extends StatelessWidget {
/// The validator function that validates the input value.
final String? Function(String?) validator;

final String? initialValue;

const _InputNumber({
required this.border,
required this.hintText,
required this.onChanged,
required this.theme,
required this.validator,
required this.initialValue,
});

@override
State<_InputNumber> createState() => _InputNumberState();
}

class _InputNumberState extends State<_InputNumber> {
late TextEditingController _controller;

@override
void initState() {
super.initState();
_controller = TextEditingController(text: widget.initialValue);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(_radius)),
color: theme.inputFill,
color: widget.theme.inputFill,
),
child: Form(
child: TextFormField(
maxLines: theme.lines,
controller: _controller,
maxLines: widget.theme.lines,
style: TextStyle(
color: theme.textColor,
fontSize: theme.textSize,
color: widget.theme.textColor,
fontSize: widget.theme.textSize,
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: validator,
onChanged: onChanged,
validator: widget.validator,
onChanged: widget.onChanged,
decoration: InputDecoration(
fillColor: theme.inputFill,
hintText: hintText,
fillColor: widget.theme.inputFill,
hintText: widget.hintText,
hintStyle: TextStyle(
color: theme.hintColor,
fontSize: theme.hintSize,
color: widget.theme.hintColor,
fontSize: widget.theme.hintSize,
),
enabledBorder: border,
focusedBorder: border,
border: border,
enabledBorder: widget.border,
focusedBorder: widget.border,
border: widget.border,
),
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class SliderQuestionPage extends StatefulWidget {
/// slider, the initial value of the slider, and other properties.
final SliderQuestionData data;

/// Contains the number that the user selected.
final QuestionAnswer<double>? answer;

/// Callback that is called after pressing bottom button.
final OnSendCallback onSend;

Expand All @@ -23,6 +26,7 @@ class SliderQuestionPage extends StatefulWidget {
const SliderQuestionPage({
required this.data,
required this.onSend,
this.answer,
this.onSecondaryButtonTap,
super.key,
});
Expand All @@ -37,7 +41,7 @@ class _SliderQuestionPageState extends State<SliderQuestionPage> {
@override
void initState() {
super.initState();
_answer = widget.data.initialValue.toDouble();
_answer = widget.answer?.answer ?? widget.data.initialValue.toDouble();
}

@override
Expand Down Expand Up @@ -82,7 +86,7 @@ class _SliderQuestionPageState extends State<SliderQuestionPage> {
child: _QuestionSlider(
minValue: widget.data.minValue,
maxValue: widget.data.maxValue,
initialValue: widget.data.initialValue,
initialValue: _answer,
onChanged: (value) => setState(() => _answer = value),
theme: theme,
divisions: widget.data.divisions,
Expand Down Expand Up @@ -140,7 +144,7 @@ class _QuestionSlider extends StatefulWidget {
final int maxValue;

/// Initial value of the slider.
final int initialValue;
final double initialValue;

/// Number of divisions for the slider.
final int divisions;
Expand Down Expand Up @@ -173,7 +177,7 @@ class _QuestionSliderState extends State<_QuestionSlider> {

@override
void initState() {
_value = widget.initialValue.toDouble();
_value = widget.initialValue;
_onlyInt = widget.initialValue.ceilToDouble() ==
widget.initialValue.floorToDouble();
super.initState();
Expand All @@ -187,7 +191,7 @@ class _QuestionSliderState extends State<_QuestionSlider> {
_value = _value >= widget.minValue.toDouble() &&
_value <= widget.maxValue.toDouble()
? _value
: widget.initialValue.toDouble();
: widget.initialValue;
return SliderTheme(
data: SliderThemeData(
activeTrackColor: widget.theme.activeColor,
Expand Down
1 change: 1 addition & 0 deletions core/lib/src/presentation/survey/survey.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ class _SurveyState extends State<Survey> {
.map<Widget>(
(question) => DataToWidgetUtil.createWidget(
data: question,
answer: state.answers[question.index],
onSend: _cubit.saveAnswer,
onGoNext: _surveyController.onNext,
),
Expand Down
4 changes: 4 additions & 0 deletions core/lib/src/presentation/utils/data_to_widget_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ abstract class DataToWidgetUtil {
required QuestionData data,
required OnSendCallback onSend,
required VoidCallback onGoNext,
QuestionAnswer? answer,
}) {
void sendAndGoNext({required int index, required QuestionAnswer answer}) {
onSend(index: index, answer: answer);
Expand All @@ -30,16 +31,19 @@ abstract class DataToWidgetUtil {
case SliderQuestionData:
return SliderQuestionPage(
data: data as SliderQuestionData,
answer: answer as QuestionAnswer<double>?,
onSend: sendAndGoNext,
);
case ChoiceQuestionData:
return ChoiceQuestionPage(
data: data as ChoiceQuestionData,
answer: answer as QuestionAnswer<List<int>>?,
onSend: sendAndGoNext,
);
case InputQuestionData:
return InputQuestionPage(
data: data as InputQuestionData,
answer: answer,
onSend: sendAndGoNext,
);
case IntroQuestionData:
Expand Down