Skip to content
Open
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: 1 addition & 0 deletions assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@
"title.consent_summary": "Consent Summary",
"title.consent_sections": "Consent Sections",
"title.consents_section": "Consent Section",
"select_star": "Select how many",
"": ""
}
Binary file added example/assets/images/starActive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/assets/images/starNotActive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions lib/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ part 'src/model/answerformat/choice_answer_format.dart';
part 'src/model/answerformat/form_answer_format.dart';
part 'src/model/answerformat/slider_answer_format.dart';
part 'src/model/answerformat/image_choice_answer_format.dart';
part 'src/model/answerformat/star_choice_answer_format.dart';
part 'src/model/answerformat/date_time_answer_format.dart';
part 'src/model/answerformat/text_answer_format.dart';

Expand Down
40 changes: 40 additions & 0 deletions lib/model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lib/model.json.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/src/model/answerformat/answer_format.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum RPQuestionType {
Date,
Duration,
ImageChoice,
StarChoice,
Double,
// Eligibility,
// TimeInterval,
Expand Down
61 changes: 61 additions & 0 deletions lib/src/model/answerformat/star_choice_answer_format.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
part of '../../../model.dart';

/// Class representing an Answer Format that lets participants choose a
/// star rating
@JsonSerializable(includeIfNull: false, explicitToJson: true)
class RPStarChoiceAnswerFormat extends RPAnswerFormat {
/// A list of available [RPStarChoice] objects which represent the choices to
/// the participants.
List<RPStarChoice> choices;

/// Returns an initialized [RPStarChoiceAnswerFormat] with the given list of
/// [RPStarChoice]s.
RPStarChoiceAnswerFormat({required this.choices}) : super();

@override
RPQuestionType get questionType => RPQuestionType.ImageChoice;

@override
Function get fromJsonFunction => _$RPStarChoiceAnswerFormatFromJson;
factory RPStarChoiceAnswerFormat.fromJson(Map<String, dynamic> json) =>
FromJsonFactory().fromJson<RPStarChoiceAnswerFormat>(json);
@override
Map<String, dynamic> toJson() => _$RPStarChoiceAnswerFormatToJson(this);
}

/// The image choice object which the participants can choose from, during a
/// [RPQuestionStep] with [RPStarChoiceAnswerFormat]
@JsonSerializable(includeIfNull: false, explicitToJson: true)
class RPStarChoice extends Serializable {
/// The image portraying the choice.
String starActiveUrl;
String starNotActiveUrl;

/// The key of the image if this is to be loaded from the images
/// in the assets on the phone.
/// Specify either the [image] or the [key].
String? keyActive;
String? keyNotActive;

/// The value of the choice. Can be any type but MUST be serializable if this feature is used.
dynamic value;

/// The description fitting the image. Is displayed when selected.
String description;

RPStarChoice({
required this.starActiveUrl,
required this.starNotActiveUrl,
this.keyActive,
this.keyNotActive,
this.value,
required this.description,
}) : super();

@override
Function get fromJsonFunction => _$RPStarChoiceFromJson;
factory RPStarChoice.fromJson(Map<String, dynamic> json) =>
FromJsonFactory().fromJson<RPStarChoice>(json);
@override
Map<String, dynamic> toJson() => _$RPStarChoiceToJson(this);
}
5 changes: 5 additions & 0 deletions lib/src/ui/question_step.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ class RPUIQuestionStepState extends State<RPUIQuestionStep> with CanSaveResult {
(answerFormat as RPImageChoiceAnswerFormat), (result) {
currentQuestionBodyResult = result;
});
case const (RPStarChoiceAnswerFormat):
return RPUIStarChoiceQuestionBody(
(answerFormat as RPStarChoiceAnswerFormat), (result) {
currentQuestionBodyResult = result;
});
case const (RPDateTimeAnswerFormat):
return RPUIDateTimeQuestionBody(
(answerFormat as RPDateTimeAnswerFormat), (result) {
Expand Down
97 changes: 97 additions & 0 deletions lib/src/ui/questions/star_choice_question_body.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
part of '../../../ui.dart';

class RPUIStarChoiceQuestionBody extends StatefulWidget {
final RPStarChoiceAnswerFormat answerFormat;
final void Function(dynamic) onResultChance;

const RPUIStarChoiceQuestionBody(
this.answerFormat,
this.onResultChance, {
super.key,
});

@override
RPUIStarChoiceQuestionBodyState createState() =>
RPUIStarChoiceQuestionBodyState();
}

class RPUIStarChoiceQuestionBodyState
extends State<RPUIStarChoiceQuestionBody>
with AutomaticKeepAliveClientMixin<RPUIStarChoiceQuestionBody> {
RPStarChoice? _selectedItem;

bool isLeftOfSelected(RPStarChoice item,List<RPStarChoice> items){
return (_selectedItem==null)?false:(items.indexOf(_selectedItem!)>=items.indexOf(item));
//items.indexOf(_selectedItem);
}
@override
Widget build(BuildContext context) {
super.build(context);
RPLocalizations? locale = RPLocalizations.of(context);
String text = (_selectedItem == null)
? (locale?.translate('select_star') ?? 'Select how many')
: (locale?.translate(_selectedItem!.description) ??
_selectedItem!.description);
return SizedBox(
height: 160,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_buildList(context, widget.answerFormat.choices),
Text(
text,
style: Theme.of(context).textTheme.headlineSmall,
)
],
));
}

Row _buildList(BuildContext context, List<RPStarChoice> items) {
List<Widget> list = [];
for (var item in items) {
list.add(
InkWell(
borderRadius: BorderRadius.circular(15),
onTap: () {
setState(() {
_selectedItem = item == _selectedItem ? null : item;
});
widget.onResultChance(_selectedItem);
},
child: Container(
// Highlighting of chosen answer
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5 * 25 / items.length)),
border: Border.all(
color: _selectedItem == item
? Theme.of(context).dividerColor
: Colors.transparent,
width: 3,
),
),
// Scaling item size with number of choices
// Max size is 125
padding: EdgeInsets.all(10 / items.length),
width:
(MediaQuery.of(context).size.width * 0.8) / items.length > 125
? 125
: MediaQuery.of(context).size.width * 0.8 / items.length,
height:
(MediaQuery.of(context).size.width * 0.8) / items.length > 125
? 125
: MediaQuery.of(context).size.width * 0.8 / items.length,
child: isLeftOfSelected(item,items)?Image.asset(item.starActiveUrl):Image.asset(item.starNotActiveUrl),
),
),
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: list,
);
}

@override
bool get wantKeepAlive => true;
}
1 change: 1 addition & 0 deletions lib/ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ part 'src/loggers/activity_event_logger.dart';
part 'src/ui/questions/choice_question_body.dart';
part 'src/ui/questions/date_time_question_body.dart';
part 'src/ui/questions/image_choice_question_body.dart';
part 'src/ui/questions/star_choice_question_body.dart';
part 'src/ui/questions/integer_question_body.dart';
part 'src/ui/questions/double_question_body.dart';
part 'src/ui/questions/slider_question_body.dart';
Expand Down