Skip to content

Commit

Permalink
Added a new Fortune wheel (#14)
Browse files Browse the repository at this point in the history
* Added a new Fortune wheel

* Fix missing import

* Fix issues blocking from running on web

* Updated spinner
  • Loading branch information
ikbendewilliam authored Jan 29, 2025
1 parent e9bd2e6 commit 73748c8
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 74 deletions.
3 changes: 3 additions & 0 deletions .fvm/fvm_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"flutterSdkVersion": "3.27.3"
}
2 changes: 1 addition & 1 deletion .fvmrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"flutter": "3.27.1",
"flutter": "3.27.3",
"flavors": {}
}
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"dart.flutterSdkPath": ".fvm/versions/3.27.1",
"dart.flutterSdkPath": ".fvm/versions/3.27.3",
"dart.sdkPath": ".fvm/flutter_sdk/bin/cache/dart-sdk",
"dart.lineLength": 180,
"search.exclude": {
Expand Down
7 changes: 6 additions & 1 deletion lib/repo/remote_config.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:io';

import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_belgium/model/data/remote_config/remote_config_data.dart';
import 'package:flutter_belgium/util/flavor/flavor_config.dart';
import 'package:impaktfull_architecture/impaktfull_architecture.dart';
Expand Down Expand Up @@ -34,7 +35,11 @@ class AppRemoteConfigRepository extends ImpaktfullRemoteConfigRepository<RemoteC
Future<RemoteConfigData> getDefault() async => RemoteConfigData(
latestVersionCode: 1,
minVersionCode: 1,
updateUrl: Platform.isAndroid ? 'https://play.google.com/store/apps/details?id=be.flutterbelgium.app' : 'https://apps.apple.com/us/app/flutter-belgium/id6479450596',
updateUrl: kIsWeb
? ''
: Platform.isAndroid
? 'https://play.google.com/store/apps/details?id=be.flutterbelgium.app'
: 'https://apps.apple.com/us/app/flutter-belgium/id6479450596',
adminIds: [],
);

Expand Down
7 changes: 4 additions & 3 deletions lib/screen/login/login_screen.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_belgium/di/injectable.dart';
import 'package:flutter_belgium/model/data/login/login_type.dart';
import 'package:flutter_belgium/theme/theme_assets.dart';
import 'package:flutter_belgium/viewmodel/login/login_viewmodel.dart';
import 'package:flutter_belgium/widget/provider/provider_widget.dart';
import 'package:flutter_belgium/widget/social_login/social_login_button.dart';
import 'package:flutter_navigation_generator_annotations/flutter_navigation_generator_annotations.dart';
import 'package:flutter_belgium/di/injectable.dart';
import 'package:flutter_belgium/widget/provider/provider_widget.dart';
import 'package:impaktfull_architecture/impaktfull_architecture.dart';

@FlutterRoute(
Expand Down Expand Up @@ -45,7 +46,7 @@ class LoginScreen extends StatelessWidget {
onTap: viewModel.onLoginTapped,
loginType: LoginType.github,
),
if (Platform.isIOS) ...[
if (!kIsWeb && Platform.isIOS) ...[
SocialLoginButton(
onTap: viewModel.onLoginTapped,
loginType: LoginType.apple,
Expand Down
16 changes: 11 additions & 5 deletions lib/screen/raffle/raffle_winner_picker_screen.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_belgium/di/injectable.dart';
import 'package:flutter_belgium/theme/theme_colors.dart';
import 'package:flutter_belgium/viewmodel/raffle/raffle_winner_picker_viewmodel.dart';
import 'package:flutter_belgium/widget/general/button.dart';
import 'package:flutter_belgium/widget/provider/provider_widget.dart';
import 'package:flutter_belgium/widget/raffle/custom_confetti.dart';
import 'package:flutter_belgium/widget/raffle/custom_fortune_wheel.dart';
import 'package:flutter_navigation_generator_annotations/flutter_navigation_generator_annotations.dart';
import 'package:flutter_belgium/di/injectable.dart';
import 'package:flutter_belgium/widget/provider/provider_widget.dart';
import 'package:impaktfull_architecture/impaktfull_architecture.dart';

@FlutterRoute(
navigationType: NavigationType.push,
)
class RaffleWinnerPickerScreen extends StatelessWidget {
class RaffleWinnerPickerScreen extends StatefulWidget {
const RaffleWinnerPickerScreen({
super.key,
});

@override
State<RaffleWinnerPickerScreen> createState() => _RaffleWinnerPickerScreenState();
}

class _RaffleWinnerPickerScreenState extends State<RaffleWinnerPickerScreen> with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return ProviderWidget<RaffleWinnerPickerViewModel>(
create: () => getIt()..init(),
create: () => getIt()..init(this),
builder: (context, viewModel) => ImpaktfullUiScreen(
child: Builder(builder: (context) {
if (viewModel.hasInactiveRaffle) {
Expand Down Expand Up @@ -57,7 +62,8 @@ class RaffleWinnerPickerScreen extends StatelessWidget {
);
}
return CustomFortuneWheel(
selected: viewModel.selectedIndexStream,
winnerIndex: viewModel.raffleWinnerIndex,
animation: viewModel.raffleAnimation,
participants: viewModel.participants,
);
},
Expand Down
3 changes: 1 addition & 2 deletions lib/theme/theme_duration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ class ThemeDuration {
const ThemeDuration._();

static const confettiDuration = Duration(seconds: 5);
static const raffleWheelDuration = Duration(seconds: 3);

static const nextRoundDelayDuration = Duration(seconds: 5);
static const raffleWheelDuration = Duration(seconds: 10);
}
27 changes: 17 additions & 10 deletions lib/viewmodel/raffle/raffle_winner_picker_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import 'dart:async';
import 'dart:math';

import 'package:confetti/confetti.dart';
import 'package:flutter/material.dart';
import 'package:flutter_belgium/model/data/raffle/participant.dart';
import 'package:flutter_belgium/model/data/raffle/raffle.dart';
import 'package:flutter_belgium/navigator/main_navigator.dart';
import 'package:flutter_belgium/repo/raffle/raffle_repo.dart';
import 'package:flutter_belgium/theme/theme_duration.dart';
import 'package:flutter_crazy_fortune_wheel/flutter_crazy_fortune_wheel.dart';
import 'package:impaktfull_architecture/impaktfull_architecture.dart';

@injectable
Expand All @@ -15,7 +17,9 @@ class RaffleWinnerPickerViewModel extends ChangeNotifierEx {
final MainNavigator _mainNavigator;

final _confettiController = ConfettiController();
final _selectedIndexStreamController = StreamController<int>.broadcast();
late final AnimationController _raffleAnimationController;
late final Animation<double> _raffleAnimation;
int _raffleWinnerIndex = 0;
StreamSubscription<Raffle?>? _subscription;

Raffle? _raffle;
Expand All @@ -27,7 +31,9 @@ class RaffleWinnerPickerViewModel extends ChangeNotifierEx {

ConfettiController get confettiController => _confettiController;

Stream<int> get selectedIndexStream => _selectedIndexStreamController.stream;
Animation<double> get raffleAnimation => _raffleAnimation;

int get raffleWinnerIndex => _raffleWinnerIndex;

int get minRequiredParticipants => 2;

Expand All @@ -46,7 +52,9 @@ class RaffleWinnerPickerViewModel extends ChangeNotifierEx {
this._mainNavigator,
);

void init() {
void init(TickerProvider vsync) {
_raffleAnimationController = AnimationController(vsync: vsync, duration: ThemeDuration.raffleWheelDuration);
_raffleAnimation = CurvedAnimation(parent: _raffleAnimationController, curve: FortuneWheelCurve());
_subscription?.cancel();
_subscription = _raffleRepository.getRaffle().listen((raffle) {
final winnerIds = raffle?.winners.map((e) => e.userUid) ?? [];
Expand All @@ -61,7 +69,7 @@ class RaffleWinnerPickerViewModel extends ChangeNotifierEx {
@override
void dispose() {
_subscription?.cancel();
_selectedIndexStreamController.close();
_raffleAnimationController.dispose();
super.dispose();
}

Expand All @@ -78,18 +86,17 @@ class RaffleWinnerPickerViewModel extends ChangeNotifierEx {
}
_winner = null;
_lockedParticipants = participants;
_raffleWinnerIndex = Random.secure().nextInt(participants.length);
notifyListeners();
final winnerIndex = Random().nextInt(participants.length);
final winner = participants[winnerIndex];
_selectedIndexStreamController.add(winnerIndex);
await Future.delayed(ThemeDuration.raffleWheelDuration);
final winner = participants[_raffleWinnerIndex];
await _raffleAnimationController.forward(from: 0);
_raffleRepository.setWinner(raffleId: raffleId, winner: winner);
_winner = winner;
notifyListeners();
_confettiController.play();
await Future.delayed(ThemeDuration.confettiDuration);
_confettiController.stop();
await Future.delayed(ThemeDuration.nextRoundDelayDuration);
_raffleAnimationController.reset();
_winner = null;
_lockedParticipants = null;
notifyListeners();
Expand All @@ -107,7 +114,7 @@ class RaffleWinnerPickerViewModel extends ChangeNotifierEx {
Future<void> onAddParticipantTapped() async {
final docId = _raffle?.id;
if (docId == null) {
_mainNavigator.showErrorMessage('Failed to make raffle active (no raffle available)');
_mainNavigator.showErrorMessage('Failed to add participant (no raffle available)');
return;
}
final name = await _mainNavigator.goToAddParticipantDialog();
Expand Down
2 changes: 2 additions & 0 deletions lib/widget/raffle/add_participant_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ class _AddParticipantDialogState extends State<AddParticipantDialog> {
],
child: TextField(
controller: textController,
onSubmitted: (_) => Navigator.of(context).pop(textController.text),
cursorColor: ThemeColors.primary,
focusNode: FocusNode()..requestFocus(),
decoration: InputDecoration(
hintText: localization.dialogRaffleNewParticipantInputName,
focusedBorder: UnderlineInputBorder(
Expand Down
52 changes: 16 additions & 36 deletions lib/widget/raffle/custom_fortune_wheel.dart
Original file line number Diff line number Diff line change
@@ -1,58 +1,38 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_belgium/model/data/raffle/participant.dart';
import 'package:flutter_belgium/theme/theme_colors.dart';
import 'package:flutter_belgium/theme/theme_duration.dart';
import 'package:flutter_fortune_wheel/flutter_fortune_wheel.dart';
import 'package:flutter_crazy_fortune_wheel/flutter_crazy_fortune_wheel.dart';

class CustomFortuneWheel extends StatelessWidget {
final Stream<int> selected;
final int winnerIndex;
final Animation<double> animation;
final List<RaffleParticipant> participants;

const CustomFortuneWheel({
required this.selected,
required this.winnerIndex,
required this.animation,
required this.participants,
super.key,
});

@override
Widget build(BuildContext context) {
return FortuneWheel(
selected: selected,
animateFirst: false,
duration: ThemeDuration.raffleWheelDuration,
rotationCount: Random().nextInt(10) + 20,
indicators: const [
FortuneIndicator(
alignment: Alignment.topCenter,
child: TriangleIndicator(
color: ThemeColors.primary,
),
),
],
physics: CircularPanPhysics(
duration: const Duration(seconds: 1),
curve: Curves.decelerate,
),
items: [
for (final participant in participants)
FortuneItem(
child: Text(
return RandomWheel(
animation: animation,
winnerIndex: winnerIndex,
wheelType: WheelType.values[participants.length % WheelType.values.length],
children: participants
.map(
(participant) => Text(
participant.name,
style: TextStyle(
style: const TextStyle(
color: ThemeColors.primary,
fontSize: participants.length > 30 ? 16 : 24,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
style: const FortuneItemStyle(
color: ThemeColors.primaryUltraLight, // <-- custom circle slice fill color
borderColor: ThemeColors.primary,
borderWidth: 2,
),
),
],
)
.toList(),
);
}
}
36 changes: 22 additions & 14 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -435,22 +435,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_fortune_wheel:
flutter_crazy_fortune_wheel:
dependency: "direct main"
description:
name: flutter_fortune_wheel
sha256: "8f93144fab448ec95579d320e77dcf189b71bc363545985e41d3ba57fa42664e"
name: flutter_crazy_fortune_wheel
sha256: a6b299961a4bed63a3244462bd2512296f78bd37170a350022c8c332d5fcf13f
url: "https://pub.dev"
source: hosted
version: "1.3.2"
flutter_hooks:
dependency: transitive
description:
name: flutter_hooks
sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70
url: "https://pub.dev"
source: hosted
version: "0.20.5"
version: "1.0.1"
flutter_lints:
dependency: "direct dev"
description:
Expand Down Expand Up @@ -536,6 +528,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_shader_snap:
dependency: transitive
description:
name: flutter_shader_snap
sha256: a97fd2767391ca49dce96b15643aafe34dd3823da5b996e18bf06c3f7f02b513
url: "https://pub.dev"
source: hosted
version: "0.0.3"
flutter_shaders:
dependency: transitive
description:
name: flutter_shaders
sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2"
url: "https://pub.dev"
source: hosted
version: "0.1.3"
flutter_svg:
dependency: transitive
description:
Expand Down Expand Up @@ -582,10 +590,10 @@ packages:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.3"
google_identity_services_web:
dependency: transitive
description:
Expand Down
4 changes: 3 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dependencies:
firebase_remote_config: ^5.3.0
flutter:
sdk: flutter
flutter_fortune_wheel: ^1.3.2
flutter_crazy_fortune_wheel: ^1.0.1
flutter_localizations:
sdk: flutter
flutter_navigation_generator_annotations: ^2.1.0
Expand Down Expand Up @@ -51,6 +51,8 @@ flutter:
- family: Ubuntu
fonts:
- asset: assets/font/ubuntu/ubuntu_regular.ttf
shaders:
- packages/flutter_crazy_fortune_wheel/shaders/sliced_wheel_shader.frag

impaktfull_translations:
api_key: caca1cf8-fd65-428e-bd5b-8f5e2a3fdf23
Expand Down

0 comments on commit 73748c8

Please sign in to comment.