From 4d80ffc3a89b8221393180b33c9cb642ba671d32 Mon Sep 17 00:00:00 2001 From: Tim Lutz Date: Sat, 19 Jul 2025 09:56:44 +0200 Subject: [PATCH] new questiontype star-rating There was no star-rating feature. So I modified a copy of the choose image feature I tested for android (I did not test the key instead of url feature). It is used like the choice image feature, for every choice there is an image for an active and not-active star. A function tests, which image should be displayed according to the selected item. Because it is a derivate of choose image feature (no visual changes, but to display active or not-active picture) it should work on other devices as good as on android. You can use the starActive and starNotActive images in example to test it and for the offical example assets. These files are CC0 created by me with inkscape. What has to be done to get multilang-support: add new string to other "lang" json-files Best regards Tim Lutz, Tyrol To test the new questiontype, just copy this to the LinearSurveyPage example: final imagesstars = [ RPStarChoice( starActiveUrl: 'assets/images/starActive.png', starNotActiveUrl:'assets/images/starNotActive.png' , value: -2, description: '1 star', ), RPStarChoice( starActiveUrl: 'assets/images/starActive.png', starNotActiveUrl: 'assets/images/starNotActive.png', value: -1, description: '2 stars', ), RPStarChoice( starActiveUrl: 'assets/images/starActive.png', starNotActiveUrl: 'assets/images/starNotActive.png', value: 0, description: '3 stars', ), RPStarChoice( starActiveUrl: 'assets/images/starActive.png', starNotActiveUrl: 'assets/images/starNotActive.png', value: 1, description: '4 stars', ), RPStarChoice( starActiveUrl: 'assets/images/starActive.png', starNotActiveUrl: 'assets/images/starNotActive.png', value: 2, description: '5 stars', ), ]; final imageChoiceAnswerStars = RPStarChoiceAnswerFormat(choices: imagesstars); --- assets/lang/en.json | 1 + example/assets/images/starActive.png | Bin 0 -> 7758 bytes example/assets/images/starNotActive.png | Bin 0 -> 6738 bytes lib/model.dart | 1 + lib/model.g.dart | 40 ++++++++ lib/model.json.dart | 2 + lib/src/model/answerformat/answer_format.dart | 1 + .../star_choice_answer_format.dart | 61 +++++++++++ lib/src/ui/question_step.dart | 5 + .../questions/star_choice_question_body.dart | 97 ++++++++++++++++++ lib/ui.dart | 1 + 11 files changed, 209 insertions(+) create mode 100644 example/assets/images/starActive.png create mode 100644 example/assets/images/starNotActive.png create mode 100644 lib/src/model/answerformat/star_choice_answer_format.dart create mode 100644 lib/src/ui/questions/star_choice_question_body.dart diff --git a/assets/lang/en.json b/assets/lang/en.json index 6ad2a42d4..5432fa6cb 100644 --- a/assets/lang/en.json +++ b/assets/lang/en.json @@ -53,5 +53,6 @@ "title.consent_summary": "Consent Summary", "title.consent_sections": "Consent Sections", "title.consents_section": "Consent Section", + "select_star": "Select how many", "": "" } \ No newline at end of file diff --git a/example/assets/images/starActive.png b/example/assets/images/starActive.png new file mode 100644 index 0000000000000000000000000000000000000000..80d8be2e5b8c93582afd4ffea39e05d5b4446dd4 GIT binary patch literal 7758 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 HvU>YJbvZJU literal 0 HcmV?d00001 diff --git a/example/assets/images/starNotActive.png b/example/assets/images/starNotActive.png new file mode 100644 index 0000000000000000000000000000000000000000..d0243073d5ded0137a86cf60b66bebab651e1417 GIT binary patch literal 6738 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 _$RPImageChoiceToJson(RPImageChoice instance) => 'description': instance.description, }; +RPStarChoiceAnswerFormat _$RPStarChoiceAnswerFormatFromJson( + Map json) => + RPStarChoiceAnswerFormat( + choices: (json['choices'] as List) + .map((e) => RPStarChoice.fromJson(e as Map)) + .toList(), + ) + ..$type = json['__type'] as String? + ..questionType = + $enumDecode(_$RPQuestionTypeEnumMap, json['questionType']); + +Map _$RPStarChoiceAnswerFormatToJson( + RPStarChoiceAnswerFormat instance) => + { + if (instance.$type case final value?) '__type': value, + 'choices': instance.choices.map((e) => e.toJson()).toList(), + 'questionType': _$RPQuestionTypeEnumMap[instance.questionType]!, + }; + +RPStarChoice _$RPStarChoiceFromJson(Map json) => + RPStarChoice( + starActiveUrl: json['starActiveUrl'] as String, + starNotActiveUrl: json['starNotActiveUrl'] as String, + keyActive: json['keyActive'] as String?, + keyNotActive: json['keyNotActive'] as String?, + value: json['value'], + description: json['description'] as String, + )..$type = json['__type'] as String?; + +Map _$RPStarChoiceToJson(RPStarChoice instance) => + { + if (instance.$type case final value?) '__type': value, + 'starActiveUrl': instance.starActiveUrl, + 'starNotActiveUrl': instance.starNotActiveUrl, + if (instance.keyActive case final value?) 'keyActive': value, + if (instance.keyNotActive case final value?) 'keyNotActive': value, + if (instance.value case final value?) 'value': value, + 'description': instance.description, + }; + RPDateTimeAnswerFormat _$RPDateTimeAnswerFormatFromJson( Map json) => RPDateTimeAnswerFormat( diff --git a/lib/model.json.dart b/lib/model.json.dart index f1fd5ae93..c770418a4 100644 --- a/lib/model.json.dart +++ b/lib/model.json.dart @@ -19,6 +19,8 @@ void registerFromJsonFunctions() { RPFormAnswerFormat(), RPImageChoiceAnswerFormat(choices: []), RPImageChoice(description: '', imageUrl: ''), + RPStarChoiceAnswerFormat(choices: []), + RPStarChoice(starActiveUrl: '', starNotActiveUrl: '', description: ''), RPIntegerAnswerFormat(maxValue: 1, minValue: 1), RPDoubleAnswerFormat(maxValue: 1, minValue: 1), RPSliderAnswerFormat(divisions: 1, maxValue: 1, minValue: 1), diff --git a/lib/src/model/answerformat/answer_format.dart b/lib/src/model/answerformat/answer_format.dart index 6ac6b535e..765abde4d 100644 --- a/lib/src/model/answerformat/answer_format.dart +++ b/lib/src/model/answerformat/answer_format.dart @@ -43,6 +43,7 @@ enum RPQuestionType { Date, Duration, ImageChoice, + StarChoice, Double, // Eligibility, // TimeInterval, diff --git a/lib/src/model/answerformat/star_choice_answer_format.dart b/lib/src/model/answerformat/star_choice_answer_format.dart new file mode 100644 index 000000000..4d0aaff32 --- /dev/null +++ b/lib/src/model/answerformat/star_choice_answer_format.dart @@ -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 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 json) => + FromJsonFactory().fromJson(json); + @override + Map 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 json) => + FromJsonFactory().fromJson(json); + @override + Map toJson() => _$RPStarChoiceToJson(this); +} diff --git a/lib/src/ui/question_step.dart b/lib/src/ui/question_step.dart index ffd3fddbd..8bbd008d0 100644 --- a/lib/src/ui/question_step.dart +++ b/lib/src/ui/question_step.dart @@ -111,6 +111,11 @@ class RPUIQuestionStepState extends State 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) { diff --git a/lib/src/ui/questions/star_choice_question_body.dart b/lib/src/ui/questions/star_choice_question_body.dart new file mode 100644 index 000000000..095f1e56c --- /dev/null +++ b/lib/src/ui/questions/star_choice_question_body.dart @@ -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 + with AutomaticKeepAliveClientMixin { + RPStarChoice? _selectedItem; + + bool isLeftOfSelected(RPStarChoice item,List 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: [ + _buildList(context, widget.answerFormat.choices), + Text( + text, + style: Theme.of(context).textTheme.headlineSmall, + ) + ], + )); + } + + Row _buildList(BuildContext context, List items) { + List 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; +} diff --git a/lib/ui.dart b/lib/ui.dart index a7f949216..198d26cb5 100644 --- a/lib/ui.dart +++ b/lib/ui.dart @@ -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';