Skip to content

Commit

Permalink
Merge pull request #21 from leancodepl/mobile/refactor-star-dialog-flow
Browse files Browse the repository at this point in the history
Let rate star dialog to be more customizable
  • Loading branch information
mkucharski17 authored Jan 9, 2025
2 parents 025d8cf + 207b363 commit 4f82371
Show file tree
Hide file tree
Showing 13 changed files with 411 additions and 238 deletions.
22 changes: 20 additions & 2 deletions mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,27 @@ void showStarDialog(

The showStarDialog function displays a dialog box allowing the user to provide a star rating. If the user rates the app with fewer than 5 stars, the dialog expands to include a text field for additional comments. For high ratings (5 stars), the dialog changes its appearance by showing a button that directs the user to the app store to submit a review.

#### Customization
#### Customizable five star rating dialog

In the current version of this package, you're not able to have a strong impact on how the dialogs look and how the flow works. You can apply your own texts and labels into `showStarDialog` and `showSingleAnswerDialog` methods. But at this moment, that's it.
```dart
void showCustomizableStarDialog(
BuildContext context, {
required WidgetBuilder headerBuilder,
required WidgetBuilder subtitleBuilder,
required ButtonBuilder primaryButtonBuilder,
required ButtonBuilder secondaryButtonBuilder,
required RatedWidgetBuilder ratedHeaderBuilder,
required RatedWidgetBuilder ratedSubtitleBuilder,
required RatedButtonBuilder ratedPrimaryButtonBuilder,
required RatedButtonBuilder ratedSecondaryButtonBuilder,
required TextFieldBuilder additionalCommentBuilder,
required RatingBuilder ratingBuilder,
EdgeInsets padding = EdgeInsets.zero,
})
```
The showCustomizableStarDialog function enables customization of the star rating flow. Texts, buttons, the text field for additional comments, and the rating widget are all fully customizable.

To replicate the behavior of the showStarDialog function, ensure that you call the onPressed functions for the button builders. In this flow, all secondary buttons terminate the rating process and close the dialog. The primary buttons have distinct behaviors: the primary button in the first dialog leads the user to the second dialog, while the primary button in the second dialog redirects the user to the app store to submit a review.

[pub-badge]: https://img.shields.io/pub/v/leancode_app_rating.svg?logo=dart

Expand Down
2 changes: 1 addition & 1 deletion mobile/lib/leancode_app_rating.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export 'src/app_rating.dart';
export 'src/l10n/app_localizations.dart' show AppRatingLocalizations;
export 'src/l10n/app_localizations.dart' show AppRatingLocalizations;
42 changes: 40 additions & 2 deletions mobile/lib/src/app_rating.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:leancode_app_rating/src/widgets/single_answer_dialog/single_answ
import 'package:leancode_app_rating/src/widgets/star_dialog/rate_star_dialog.dart';
import 'package:flutter/material.dart';
import 'package:in_app_review/in_app_review.dart';
import 'package:leancode_app_rating/src/widgets/star_dialog/simple_rate_star_dialog.dart';
import 'package:leancode_contracts/leancode_contracts.dart';

class AppRating {
Expand Down Expand Up @@ -30,10 +31,10 @@ class AppRating {
showDialog(
context: context,
barrierDismissible: false,
builder: (ctx) => RateStarDialog(
builder: (ctx) => SimpleRateStarDialog(
cqrs: cqrs,
inAppReview: inAppReview,
appleStoreId: appleStoreId,
inAppReview: inAppReview,
appVersion: appVersion,
starDialogHeader: starDialogHeader,
starDialogSubtitle: starDialogSubtitle,
Expand Down Expand Up @@ -76,4 +77,41 @@ class AppRating {
),
);
}

void showCustomizableStarDialog(
BuildContext context, {
required WidgetBuilder headerBuilder,
required WidgetBuilder subtitleBuilder,
required ButtonBuilder primaryButtonBuilder,
required ButtonBuilder secondaryButtonBuilder,
required RatedWidgetBuilder ratedHeaderBuilder,
required RatedWidgetBuilder ratedSubtitleBuilder,
required RatedButtonBuilder ratedPrimaryButtonBuilder,
required RatedButtonBuilder ratedSecondaryButtonBuilder,
required TextFieldBuilder additionalCommentBuilder,
required RatingBuilder ratingBuilder,
EdgeInsets padding = EdgeInsets.zero,
}) {
showDialog(
context: context,
barrierDismissible: false,
builder: (ctx) => RateStarDialog(
cqrs: cqrs,
inAppReview: inAppReview,
appleStoreId: appleStoreId,
appVersion: appVersion,
headerBuilder: headerBuilder,
subtitleBuilder: subtitleBuilder,
primaryButtonBuilder: primaryButtonBuilder,
secondaryButtonBuilder: secondaryButtonBuilder,
ratedHeaderBuilder: ratedHeaderBuilder,
ratedSubtitleBuilder: ratedSubtitleBuilder,
ratedPrimaryButtonBuilder: ratedPrimaryButtonBuilder,
ratedSecondaryButtonBuilder: ratedSecondaryButtonBuilder,
additionalCommentBuilder: additionalCommentBuilder,
ratingBuilder: ratingBuilder,
padding: padding,
),
);
}
}
2 changes: 1 addition & 1 deletion mobile/lib/src/package_name.dart
Original file line number Diff line number Diff line change
@@ -1 +1 @@
const packageName = 'leancode_app_rating';
const packageName = 'leancode_app_rating';
3 changes: 2 additions & 1 deletion mobile/lib/src/utils/strings.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:leancode_app_rating/src/l10n/app_localizations.dart';
import 'package:flutter/material.dart';

AppRatingLocalizations l10n(BuildContext context) => AppRatingLocalizations.of(context);
AppRatingLocalizations l10n(BuildContext context) =>
AppRatingLocalizations.of(context);
3 changes: 1 addition & 2 deletions mobile/lib/src/widgets/common/base_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
class BaseDialog extends StatelessWidget {
const BaseDialog({super.key, required this.child});


final Widget child;

@override
Expand All @@ -17,4 +16,4 @@ class BaseDialog extends StatelessWidget {
child: child,
);
}
}
}
5 changes: 1 addition & 4 deletions mobile/lib/src/widgets/common/feedback_text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ class FeedbackTextField extends HookWidget {
AnimatedOpacity(
opacity: showLabel.value ? 1 : 0,
duration: const Duration(milliseconds: 60),
child: Text(
s.textFieldHint,
style: hintTextStyle
),
child: Text(s.textFieldHint, style: hintTextStyle),
),
const SizedBox(height: 4),
TextField(
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
enum RateOptions { yes, no, later }
enum RateOptions { yes, no, later }
32 changes: 17 additions & 15 deletions mobile/lib/src/widgets/star_dialog/rate_star_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,34 @@ class RatingCubit extends Cubit<RatingState>
inAppReview.openStoreListing(
appStoreId: appStoreId,
);

emitPresentation(const CloseDialogEvent());
}

Future<void> submit({String? additionalComment}) async {
emit(state.copyWith(inProgress: true));
await cqrs.run(
SubmitAppRating(
rating: state.rating.toDouble(),
metadata: {},
platform: operatingSystem,
systemVersion: systemVersion,
appVersion: appVersion,
additionalComment: additionalComment,
),
);
emit(state.copyWith(inProgress: false, rateUs: true));
void submit({String? additionalComment}) {
emit(state.copyWith(rated: true));

cqrs
.run(
SubmitAppRating(
rating: state.rating.toDouble(),
metadata: {},
platform: operatingSystem,
systemVersion: systemVersion,
appVersion: appVersion,
additionalComment: additionalComment,
),
)
.ignore();
}
}

@freezed
abstract class RatingState with _$RatingState {
const factory RatingState({
@Default(0) int rating,
@Default(false) bool inProgress,
@Default(false) bool expanded,
@Default(false) bool rateUs,
@Default(false) bool rated,
}) = _RatingState;
}

Expand Down
59 changes: 18 additions & 41 deletions mobile/lib/src/widgets/star_dialog/rate_star_cubit.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc
mixin _$RatingState {
int get rating => throw _privateConstructorUsedError;
bool get inProgress => throw _privateConstructorUsedError;
bool get expanded => throw _privateConstructorUsedError;
bool get rateUs => throw _privateConstructorUsedError;
bool get rated => throw _privateConstructorUsedError;

/// Create a copy of RatingState
/// with the given fields replaced by the non-null parameter values.
Expand All @@ -34,7 +33,7 @@ abstract class $RatingStateCopyWith<$Res> {
RatingState value, $Res Function(RatingState) then) =
_$RatingStateCopyWithImpl<$Res, RatingState>;
@useResult
$Res call({int rating, bool inProgress, bool expanded, bool rateUs});
$Res call({int rating, bool expanded, bool rated});
}

/// @nodoc
Expand All @@ -53,26 +52,21 @@ class _$RatingStateCopyWithImpl<$Res, $Val extends RatingState>
@override
$Res call({
Object? rating = null,
Object? inProgress = null,
Object? expanded = null,
Object? rateUs = null,
Object? rated = null,
}) {
return _then(_value.copyWith(
rating: null == rating
? _value.rating
: rating // ignore: cast_nullable_to_non_nullable
as int,
inProgress: null == inProgress
? _value.inProgress
: inProgress // ignore: cast_nullable_to_non_nullable
as bool,
expanded: null == expanded
? _value.expanded
: expanded // ignore: cast_nullable_to_non_nullable
as bool,
rateUs: null == rateUs
? _value.rateUs
: rateUs // ignore: cast_nullable_to_non_nullable
rated: null == rated
? _value.rated
: rated // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
Expand All @@ -86,7 +80,7 @@ abstract class _$$RatingStateImplCopyWith<$Res>
__$$RatingStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int rating, bool inProgress, bool expanded, bool rateUs});
$Res call({int rating, bool expanded, bool rated});
}

/// @nodoc
Expand All @@ -103,26 +97,21 @@ class __$$RatingStateImplCopyWithImpl<$Res>
@override
$Res call({
Object? rating = null,
Object? inProgress = null,
Object? expanded = null,
Object? rateUs = null,
Object? rated = null,
}) {
return _then(_$RatingStateImpl(
rating: null == rating
? _value.rating
: rating // ignore: cast_nullable_to_non_nullable
as int,
inProgress: null == inProgress
? _value.inProgress
: inProgress // ignore: cast_nullable_to_non_nullable
as bool,
expanded: null == expanded
? _value.expanded
: expanded // ignore: cast_nullable_to_non_nullable
as bool,
rateUs: null == rateUs
? _value.rateUs
: rateUs // ignore: cast_nullable_to_non_nullable
rated: null == rated
? _value.rated
: rated // ignore: cast_nullable_to_non_nullable
as bool,
));
}
Expand All @@ -132,27 +121,21 @@ class __$$RatingStateImplCopyWithImpl<$Res>
class _$RatingStateImpl implements _RatingState {
const _$RatingStateImpl(
{this.rating = 0,
this.inProgress = false,
this.expanded = false,
this.rateUs = false});
{this.rating = 0, this.expanded = false, this.rated = false});

@override
@JsonKey()
final int rating;
@override
@JsonKey()
final bool inProgress;
@override
@JsonKey()
final bool expanded;
@override
@JsonKey()
final bool rateUs;
final bool rated;

@override
String toString() {
return 'RatingState(rating: $rating, inProgress: $inProgress, expanded: $expanded, rateUs: $rateUs)';
return 'RatingState(rating: $rating, expanded: $expanded, rated: $rated)';
}

@override
Expand All @@ -161,16 +144,13 @@ class _$RatingStateImpl implements _RatingState {
(other.runtimeType == runtimeType &&
other is _$RatingStateImpl &&
(identical(other.rating, rating) || other.rating == rating) &&
(identical(other.inProgress, inProgress) ||
other.inProgress == inProgress) &&
(identical(other.expanded, expanded) ||
other.expanded == expanded) &&
(identical(other.rateUs, rateUs) || other.rateUs == rateUs));
(identical(other.rated, rated) || other.rated == rated));
}

@override
int get hashCode =>
Object.hash(runtimeType, rating, inProgress, expanded, rateUs);
int get hashCode => Object.hash(runtimeType, rating, expanded, rated);

/// Create a copy of RatingState
/// with the given fields replaced by the non-null parameter values.
Expand All @@ -184,18 +164,15 @@ class _$RatingStateImpl implements _RatingState {
abstract class _RatingState implements RatingState {
const factory _RatingState(
{final int rating,
final bool inProgress,
final bool expanded,
final bool rateUs}) = _$RatingStateImpl;
final bool rated}) = _$RatingStateImpl;

@override
int get rating;
@override
bool get inProgress;
@override
bool get expanded;
@override
bool get rateUs;
bool get rated;

/// Create a copy of RatingState
/// with the given fields replaced by the non-null parameter values.
Expand Down
Loading

0 comments on commit 4f82371

Please sign in to comment.