Browse Source

fix some bug

handle finish timer state
master
mohsen zamani 2 years ago
parent
commit
dcd6604420
  1. 27
      lib/application/notifiers/puzzle_notifier.dart
  2. 9
      lib/application/states/puzzle_state.dart
  3. 160
      lib/application/states/puzzle_state.freezed.dart
  4. 9
      lib/screens/photo/photo_screen.dart
  5. 232
      lib/screens/photo/photo_screen_large.dart
  6. 200
      lib/screens/photo/photo_screen_medium.dart
  7. 212
      lib/screens/photo/photo_screen_small.dart
  8. 59
      lib/screens/puzzle/puzzle_screen_large.dart
  9. 8
      lib/screens/puzzle/puzzle_starter_screen.dart
  10. 635
      lib/screens/solo/solo_large_screen_prev.dart
  11. 63
      lib/screens/solo/solo_screen.dart
  12. 164
      lib/screens/solo/solo_screen_large.dart
  13. 148
      lib/screens/solo/solo_screen_medium.dart
  14. 145
      lib/screens/solo/solo_screen_small.dart
  15. 198
      lib/utils/toast.dart
  16. 49
      lib/utils/utils.dart
  17. 29
      lib/widgets/puzzle_widgets/timer_test_screen.dart
  18. 3
      lib/widgets/solo_screen/count_down_timer_widget.dart
  19. 12
      lib/widgets/solo_screen/game_button_widget.dart
  20. 16
      lib/widgets/solo_screen/game_button_widget/puzzle_game_button.dart

27
lib/application/notifiers/puzzle_notifier.dart

@ -131,32 +131,28 @@ class PuzzleNotifier extends StateNotifier<PuzzleState> {
int currentTileCol = index % _puzzleSize;
//current element moves up
if ((currentTileRow - 1 == emptyTilePosRow) &&
(currentTileCol == emptyTilePosCol)) {
if ((currentTileRow - 1 == emptyTilePosRow) && (currentTileCol == emptyTilePosCol)) {
board1D[emptyTilePosIndex] = board1D[index];
board1D[index] = 0;
_moves++;
}
//current element moves down
else if ((currentTileRow + 1 == emptyTilePosRow) &&
(currentTileCol == emptyTilePosCol)) {
else if ((currentTileRow + 1 == emptyTilePosRow) && (currentTileCol == emptyTilePosCol)) {
board1D[emptyTilePosIndex] = board1D[index];
board1D[index] = 0;
_moves++;
}
//current element moves left
else if ((currentTileRow == emptyTilePosRow) &&
(currentTileCol + 1 == emptyTilePosCol)) {
else if ((currentTileRow == emptyTilePosRow) && (currentTileCol + 1 == emptyTilePosCol)) {
board1D[emptyTilePosIndex] = board1D[index];
board1D[index] = 0;
_moves++;
}
//current element moves right
else if ((currentTileRow == emptyTilePosRow) &&
(currentTileCol - 1 == emptyTilePosCol)) {
else if ((currentTileRow == emptyTilePosRow) && (currentTileCol - 1 == emptyTilePosCol)) {
board1D[emptyTilePosIndex] = board1D[index];
board1D[index] = 0;
_moves++;
@ -172,8 +168,7 @@ class PuzzleNotifier extends StateNotifier<PuzzleState> {
int i = low;
while (i < high) {
board1D[(i * _puzzleSize) + emptyTilePosCol] =
board1D[(((i + 1) * _puzzleSize) + emptyTilePosCol)];
board1D[(i * _puzzleSize) + emptyTilePosCol] = board1D[(((i + 1) * _puzzleSize) + emptyTilePosCol)];
i += 1;
}
@ -189,8 +184,7 @@ class PuzzleNotifier extends StateNotifier<PuzzleState> {
int i = low;
while (i > high) {
board1D[(i * _puzzleSize) + emptyTilePosCol] =
board1D[(((i - 1) * _puzzleSize) + emptyTilePosCol)];
board1D[(i * _puzzleSize) + emptyTilePosCol] = board1D[(((i - 1) * _puzzleSize) + emptyTilePosCol)];
i -= 1;
}
@ -212,8 +206,7 @@ class PuzzleNotifier extends StateNotifier<PuzzleState> {
int i = low;
while (i < high) {
board1D[(emptyTilePosRow * _puzzleSize) + i] =
board1D[(emptyTilePosRow * _puzzleSize) + (i + 1)];
board1D[(emptyTilePosRow * _puzzleSize) + i] = board1D[(emptyTilePosRow * _puzzleSize) + (i + 1)];
i += 1;
}
@ -229,8 +222,7 @@ class PuzzleNotifier extends StateNotifier<PuzzleState> {
int i = low;
while (i > high) {
board1D[(i + (emptyTilePosRow * _puzzleSize))] =
board1D[(i - 1) + (emptyTilePosRow * _puzzleSize)];
board1D[(i + (emptyTilePosRow * _puzzleSize))] = board1D[(i - 1) + (emptyTilePosRow * _puzzleSize)];
i -= 1;
}
@ -258,9 +250,6 @@ class PuzzleNotifier extends StateNotifier<PuzzleState> {
if (listEquals(board1D, _solvedList)) {
state = PuzzleState.solved(updatedData);
}
log('List: $board1D');
log('-----------------------');
}
int calculateCorrectTiles({required List<int> board}) {

9
lib/application/states/puzzle_state.dart

@ -6,11 +6,18 @@ part 'puzzle_state.freezed.dart';
@freezed
class PuzzleState with _$PuzzleState {
const factory PuzzleState() = PuzzleIdle;
const factory PuzzleState.initializing() = PuzzleInitializing;
const factory PuzzleState.scrambling(PuzzleData puzzleData) = PuzzleScrambling;
const factory PuzzleState.current(PuzzleData puzzleData) = PuzzleCurrent;
const factory PuzzleState.computingSolution(PuzzleData puzzleData) = PuzzleComputingSolution;
const factory PuzzleState.autoSolving(PuzzleData puzzleData) = PuzzleAutoSolving;
const factory PuzzleState.solved(PuzzleData puzzleData) = PuzzleSolved;
const factory PuzzleState.error({String? message}) = PuzzleError;
}
}

160
lib/application/states/puzzle_state.freezed.dart

@ -80,6 +80,7 @@ mixin _$PuzzleState {
required TResult Function(String? message) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult Function()? $default, {
@ -105,6 +106,7 @@ mixin _$PuzzleState {
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(PuzzleIdle value) $default, {
@ -117,6 +119,7 @@ mixin _$PuzzleState {
required TResult Function(PuzzleError value) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult Function(PuzzleIdle value)? $default, {
@ -146,9 +149,7 @@ mixin _$PuzzleState {
/// @nodoc
abstract class $PuzzleStateCopyWith<$Res> {
factory $PuzzleStateCopyWith(
PuzzleState value, $Res Function(PuzzleState) then) =
_$PuzzleStateCopyWithImpl<$Res>;
factory $PuzzleStateCopyWith(PuzzleState value, $Res Function(PuzzleState) then) = _$PuzzleStateCopyWithImpl<$Res>;
}
/// @nodoc
@ -156,20 +157,18 @@ class _$PuzzleStateCopyWithImpl<$Res> implements $PuzzleStateCopyWith<$Res> {
_$PuzzleStateCopyWithImpl(this._value, this._then);
final PuzzleState _value;
// ignore: unused_field
final $Res Function(PuzzleState) _then;
}
/// @nodoc
abstract class $PuzzleIdleCopyWith<$Res> {
factory $PuzzleIdleCopyWith(
PuzzleIdle value, $Res Function(PuzzleIdle) then) =
_$PuzzleIdleCopyWithImpl<$Res>;
factory $PuzzleIdleCopyWith(PuzzleIdle value, $Res Function(PuzzleIdle) then) = _$PuzzleIdleCopyWithImpl<$Res>;
}
/// @nodoc
class _$PuzzleIdleCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res>
implements $PuzzleIdleCopyWith<$Res> {
class _$PuzzleIdleCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res> implements $PuzzleIdleCopyWith<$Res> {
_$PuzzleIdleCopyWithImpl(PuzzleIdle _value, $Res Function(PuzzleIdle) _then)
: super(_value, (v) => _then(v as PuzzleIdle));
@ -189,8 +188,7 @@ class _$PuzzleIdle implements PuzzleIdle {
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is PuzzleIdle);
return identical(this, other) || (other.runtimeType == runtimeType && other is PuzzleIdle);
}
@override
@ -301,17 +299,14 @@ abstract class PuzzleIdle implements PuzzleState {
/// @nodoc
abstract class $PuzzleInitializingCopyWith<$Res> {
factory $PuzzleInitializingCopyWith(
PuzzleInitializing value, $Res Function(PuzzleInitializing) then) =
factory $PuzzleInitializingCopyWith(PuzzleInitializing value, $Res Function(PuzzleInitializing) then) =
_$PuzzleInitializingCopyWithImpl<$Res>;
}
/// @nodoc
class _$PuzzleInitializingCopyWithImpl<$Res>
extends _$PuzzleStateCopyWithImpl<$Res>
class _$PuzzleInitializingCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res>
implements $PuzzleInitializingCopyWith<$Res> {
_$PuzzleInitializingCopyWithImpl(
PuzzleInitializing _value, $Res Function(PuzzleInitializing) _then)
_$PuzzleInitializingCopyWithImpl(PuzzleInitializing _value, $Res Function(PuzzleInitializing) _then)
: super(_value, (v) => _then(v as PuzzleInitializing));
@override
@ -330,8 +325,7 @@ class _$PuzzleInitializing implements PuzzleInitializing {
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is PuzzleInitializing);
return identical(this, other) || (other.runtimeType == runtimeType && other is PuzzleInitializing);
}
@override
@ -442,18 +436,16 @@ abstract class PuzzleInitializing implements PuzzleState {
/// @nodoc
abstract class $PuzzleScramblingCopyWith<$Res> {
factory $PuzzleScramblingCopyWith(
PuzzleScrambling value, $Res Function(PuzzleScrambling) then) =
factory $PuzzleScramblingCopyWith(PuzzleScrambling value, $Res Function(PuzzleScrambling) then) =
_$PuzzleScramblingCopyWithImpl<$Res>;
$Res call({PuzzleData puzzleData});
}
/// @nodoc
class _$PuzzleScramblingCopyWithImpl<$Res>
extends _$PuzzleStateCopyWithImpl<$Res>
class _$PuzzleScramblingCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res>
implements $PuzzleScramblingCopyWith<$Res> {
_$PuzzleScramblingCopyWithImpl(
PuzzleScrambling _value, $Res Function(PuzzleScrambling) _then)
_$PuzzleScramblingCopyWithImpl(PuzzleScrambling _value, $Res Function(PuzzleScrambling) _then)
: super(_value, (v) => _then(v as PuzzleScrambling));
@override
@ -490,13 +482,11 @@ class _$PuzzleScrambling implements PuzzleScrambling {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is PuzzleScrambling &&
const DeepCollectionEquality()
.equals(other.puzzleData, puzzleData));
const DeepCollectionEquality().equals(other.puzzleData, puzzleData));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
@JsonKey(ignore: true)
@override
@ -606,24 +596,23 @@ abstract class PuzzleScrambling implements PuzzleState {
const factory PuzzleScrambling(PuzzleData puzzleData) = _$PuzzleScrambling;
PuzzleData get puzzleData;
@JsonKey(ignore: true)
$PuzzleScramblingCopyWith<PuzzleScrambling> get copyWith =>
throw _privateConstructorUsedError;
$PuzzleScramblingCopyWith<PuzzleScrambling> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PuzzleCurrentCopyWith<$Res> {
factory $PuzzleCurrentCopyWith(
PuzzleCurrent value, $Res Function(PuzzleCurrent) then) =
factory $PuzzleCurrentCopyWith(PuzzleCurrent value, $Res Function(PuzzleCurrent) then) =
_$PuzzleCurrentCopyWithImpl<$Res>;
$Res call({PuzzleData puzzleData});
}
/// @nodoc
class _$PuzzleCurrentCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res>
implements $PuzzleCurrentCopyWith<$Res> {
_$PuzzleCurrentCopyWithImpl(
PuzzleCurrent _value, $Res Function(PuzzleCurrent) _then)
_$PuzzleCurrentCopyWithImpl(PuzzleCurrent _value, $Res Function(PuzzleCurrent) _then)
: super(_value, (v) => _then(v as PuzzleCurrent));
@override
@ -660,18 +649,15 @@ class _$PuzzleCurrent implements PuzzleCurrent {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is PuzzleCurrent &&
const DeepCollectionEquality()
.equals(other.puzzleData, puzzleData));
const DeepCollectionEquality().equals(other.puzzleData, puzzleData));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
@JsonKey(ignore: true)
@override
$PuzzleCurrentCopyWith<PuzzleCurrent> get copyWith =>
_$PuzzleCurrentCopyWithImpl<PuzzleCurrent>(this, _$identity);
$PuzzleCurrentCopyWith<PuzzleCurrent> get copyWith => _$PuzzleCurrentCopyWithImpl<PuzzleCurrent>(this, _$identity);
@override
@optionalTypeArgs
@ -776,25 +762,23 @@ abstract class PuzzleCurrent implements PuzzleState {
const factory PuzzleCurrent(PuzzleData puzzleData) = _$PuzzleCurrent;
PuzzleData get puzzleData;
@JsonKey(ignore: true)
$PuzzleCurrentCopyWith<PuzzleCurrent> get copyWith =>
throw _privateConstructorUsedError;
$PuzzleCurrentCopyWith<PuzzleCurrent> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PuzzleComputingSolutionCopyWith<$Res> {
factory $PuzzleComputingSolutionCopyWith(PuzzleComputingSolution value,
$Res Function(PuzzleComputingSolution) then) =
factory $PuzzleComputingSolutionCopyWith(PuzzleComputingSolution value, $Res Function(PuzzleComputingSolution) then) =
_$PuzzleComputingSolutionCopyWithImpl<$Res>;
$Res call({PuzzleData puzzleData});
}
/// @nodoc
class _$PuzzleComputingSolutionCopyWithImpl<$Res>
extends _$PuzzleStateCopyWithImpl<$Res>
class _$PuzzleComputingSolutionCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res>
implements $PuzzleComputingSolutionCopyWith<$Res> {
_$PuzzleComputingSolutionCopyWithImpl(PuzzleComputingSolution _value,
$Res Function(PuzzleComputingSolution) _then)
_$PuzzleComputingSolutionCopyWithImpl(PuzzleComputingSolution _value, $Res Function(PuzzleComputingSolution) _then)
: super(_value, (v) => _then(v as PuzzleComputingSolution));
@override
@ -831,19 +815,16 @@ class _$PuzzleComputingSolution implements PuzzleComputingSolution {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is PuzzleComputingSolution &&
const DeepCollectionEquality()
.equals(other.puzzleData, puzzleData));
const DeepCollectionEquality().equals(other.puzzleData, puzzleData));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
@JsonKey(ignore: true)
@override
$PuzzleComputingSolutionCopyWith<PuzzleComputingSolution> get copyWith =>
_$PuzzleComputingSolutionCopyWithImpl<PuzzleComputingSolution>(
this, _$identity);
_$PuzzleComputingSolutionCopyWithImpl<PuzzleComputingSolution>(this, _$identity);
@override
@optionalTypeArgs
@ -945,29 +926,26 @@ class _$PuzzleComputingSolution implements PuzzleComputingSolution {
}
abstract class PuzzleComputingSolution implements PuzzleState {
const factory PuzzleComputingSolution(PuzzleData puzzleData) =
_$PuzzleComputingSolution;
const factory PuzzleComputingSolution(PuzzleData puzzleData) = _$PuzzleComputingSolution;
PuzzleData get puzzleData;
@JsonKey(ignore: true)
$PuzzleComputingSolutionCopyWith<PuzzleComputingSolution> get copyWith =>
throw _privateConstructorUsedError;
$PuzzleComputingSolutionCopyWith<PuzzleComputingSolution> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PuzzleAutoSolvingCopyWith<$Res> {
factory $PuzzleAutoSolvingCopyWith(
PuzzleAutoSolving value, $Res Function(PuzzleAutoSolving) then) =
factory $PuzzleAutoSolvingCopyWith(PuzzleAutoSolving value, $Res Function(PuzzleAutoSolving) then) =
_$PuzzleAutoSolvingCopyWithImpl<$Res>;
$Res call({PuzzleData puzzleData});
}
/// @nodoc
class _$PuzzleAutoSolvingCopyWithImpl<$Res>
extends _$PuzzleStateCopyWithImpl<$Res>
class _$PuzzleAutoSolvingCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res>
implements $PuzzleAutoSolvingCopyWith<$Res> {
_$PuzzleAutoSolvingCopyWithImpl(
PuzzleAutoSolving _value, $Res Function(PuzzleAutoSolving) _then)
_$PuzzleAutoSolvingCopyWithImpl(PuzzleAutoSolving _value, $Res Function(PuzzleAutoSolving) _then)
: super(_value, (v) => _then(v as PuzzleAutoSolving));
@override
@ -1004,13 +982,11 @@ class _$PuzzleAutoSolving implements PuzzleAutoSolving {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is PuzzleAutoSolving &&
const DeepCollectionEquality()
.equals(other.puzzleData, puzzleData));
const DeepCollectionEquality().equals(other.puzzleData, puzzleData));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
@JsonKey(ignore: true)
@override
@ -1120,24 +1096,22 @@ abstract class PuzzleAutoSolving implements PuzzleState {
const factory PuzzleAutoSolving(PuzzleData puzzleData) = _$PuzzleAutoSolving;
PuzzleData get puzzleData;
@JsonKey(ignore: true)
$PuzzleAutoSolvingCopyWith<PuzzleAutoSolving> get copyWith =>
throw _privateConstructorUsedError;
$PuzzleAutoSolvingCopyWith<PuzzleAutoSolving> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PuzzleSolvedCopyWith<$Res> {
factory $PuzzleSolvedCopyWith(
PuzzleSolved value, $Res Function(PuzzleSolved) then) =
factory $PuzzleSolvedCopyWith(PuzzleSolved value, $Res Function(PuzzleSolved) then) =
_$PuzzleSolvedCopyWithImpl<$Res>;
$Res call({PuzzleData puzzleData});
}
/// @nodoc
class _$PuzzleSolvedCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res>
implements $PuzzleSolvedCopyWith<$Res> {
_$PuzzleSolvedCopyWithImpl(
PuzzleSolved _value, $Res Function(PuzzleSolved) _then)
class _$PuzzleSolvedCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res> implements $PuzzleSolvedCopyWith<$Res> {
_$PuzzleSolvedCopyWithImpl(PuzzleSolved _value, $Res Function(PuzzleSolved) _then)
: super(_value, (v) => _then(v as PuzzleSolved));
@override
@ -1174,18 +1148,15 @@ class _$PuzzleSolved implements PuzzleSolved {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is PuzzleSolved &&
const DeepCollectionEquality()
.equals(other.puzzleData, puzzleData));
const DeepCollectionEquality().equals(other.puzzleData, puzzleData));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData));
@JsonKey(ignore: true)
@override
$PuzzleSolvedCopyWith<PuzzleSolved> get copyWith =>
_$PuzzleSolvedCopyWithImpl<PuzzleSolved>(this, _$identity);
$PuzzleSolvedCopyWith<PuzzleSolved> get copyWith => _$PuzzleSolvedCopyWithImpl<PuzzleSolved>(this, _$identity);
@override
@optionalTypeArgs
@ -1290,24 +1261,21 @@ abstract class PuzzleSolved implements PuzzleState {
const factory PuzzleSolved(PuzzleData puzzleData) = _$PuzzleSolved;
PuzzleData get puzzleData;
@JsonKey(ignore: true)
$PuzzleSolvedCopyWith<PuzzleSolved> get copyWith =>
throw _privateConstructorUsedError;
$PuzzleSolvedCopyWith<PuzzleSolved> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PuzzleErrorCopyWith<$Res> {
factory $PuzzleErrorCopyWith(
PuzzleError value, $Res Function(PuzzleError) then) =
_$PuzzleErrorCopyWithImpl<$Res>;
factory $PuzzleErrorCopyWith(PuzzleError value, $Res Function(PuzzleError) then) = _$PuzzleErrorCopyWithImpl<$Res>;
$Res call({String? message});
}
/// @nodoc
class _$PuzzleErrorCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res>
implements $PuzzleErrorCopyWith<$Res> {
_$PuzzleErrorCopyWithImpl(
PuzzleError _value, $Res Function(PuzzleError) _then)
class _$PuzzleErrorCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res> implements $PuzzleErrorCopyWith<$Res> {
_$PuzzleErrorCopyWithImpl(PuzzleError _value, $Res Function(PuzzleError) _then)
: super(_value, (v) => _then(v as PuzzleError));
@override
@ -1348,13 +1316,11 @@ class _$PuzzleError implements PuzzleError {
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(message));
int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(message));
@JsonKey(ignore: true)
@override
$PuzzleErrorCopyWith<PuzzleError> get copyWith =>
_$PuzzleErrorCopyWithImpl<PuzzleError>(this, _$identity);
$PuzzleErrorCopyWith<PuzzleError> get copyWith => _$PuzzleErrorCopyWithImpl<PuzzleError>(this, _$identity);
@override
@optionalTypeArgs
@ -1459,7 +1425,7 @@ abstract class PuzzleError implements PuzzleState {
const factory PuzzleError({String? message}) = _$PuzzleError;
String? get message;
@JsonKey(ignore: true)
$PuzzleErrorCopyWith<PuzzleError> get copyWith =>
throw _privateConstructorUsedError;
$PuzzleErrorCopyWith<PuzzleError> get copyWith => throw _privateConstructorUsedError;
}

9
lib/screens/photo/photo_screen.dart

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart';
import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/cubits/count_down_timer_cubit.dart';
import 'package:my_flutter_puzzle/models/puzzle_data.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/res/palette.dart';
@ -16,11 +18,13 @@ class PhotoScreen extends ConsumerStatefulWidget {
required this.initialPuzzleData,
required this.puzzleSize,
required this.riveController,
required this.duration,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int duration;
final int puzzleSize;
final RiveAnimationController riveController;
@ -48,7 +52,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
if (next is PuzzleSolved) {
ref.read(timerNotifierProvider.notifier).stopTimer();
BlocProvider.of<CountDownTimerCubit>(context).stop();
}
});
@ -85,6 +89,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
child: PhotoScreenLarge(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
duration: widget.duration,
puzzleSize: _puzzleSize,
riveController: _riveController,
),
@ -113,6 +118,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
initialPuzzleData: _initialPuzzleData,
puzzleSize: _puzzleSize,
riveController: _riveController,
duration: widget.duration,
),
);
},
@ -121,6 +127,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
child: PhotoScreenLarge(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
duration: widget.duration,
puzzleSize: _puzzleSize,
riveController: _riveController,
),

232
lib/screens/photo/photo_screen_large.dart

@ -9,6 +9,7 @@ import 'package:my_flutter_puzzle/models/puzzle_data.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/res/puzzle_constants.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:my_flutter_puzzle/utils/utils.dart';
import 'package:my_flutter_puzzle/widgets/photo_screen/image_viewer.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/count_down_timer_widget.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart';
@ -21,12 +22,14 @@ class PhotoScreenLarge extends ConsumerStatefulWidget {
required this.initialPuzzleData,
required this.puzzleSize,
required this.riveController,
required this.duration,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int puzzleSize;
final int duration;
final RiveAnimationController riveController;
@override
@ -38,7 +41,7 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
late final int _puzzleSize;
late final PuzzleData _initialPuzzleData;
late final RiveAnimationController _riveController;
bool _puzzleSolved = false;
bool _isStartPressed = false;
final _imagePicker = ImagePicker();
final double _fontSize = 70.0;
@ -67,6 +70,9 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
_isStartPressed = true;
});
}
if (next is PuzzleSolved) {
_puzzleSolved = true;
}
});
ref.listen(imageSplitterNotifierProvider, (previous, next) {
@ -79,124 +85,136 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
}
});
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 32),
MovesTilesWidget(solverClient: _solverClient, fontSize: 16),
const SizedBox(height: 32),
GameButtonWidget(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
),
],
return WillPopScope(
onWillPop: () async {
BlocProvider.of<CountDownTimerCubit>(context).stop();
return true;
},
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 32),
MovesTilesWidget(solverClient: _solverClient, fontSize: 16),
const SizedBox(height: 32),
GameButtonWidget(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
),
],
),
),
),
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// const TimerWidget(fontSize: 30),
CountDownTimerWidget(
duration: 3,
finishCallback: () {},
),
const SizedBox(height: 12),
Consumer(
builder: (context, ref, child) {
final state = ref.watch(imageSplitterNotifierProvider);
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CountDownTimerWidget(
duration: widget.duration,
finishCallback: () {
if (!_puzzleSolved) {
Utils.instance.showToast(context, 'TimeOut');
Future.delayed(const Duration(milliseconds: 1500), () {
Navigator.pop(context);
});
}
},
),
const SizedBox(height: 12),
Consumer(
builder: (context, ref, child) {
final state = ref.watch(imageSplitterNotifierProvider);
return state.maybeWhen(
() => PuzzleWidget(
solverClient: _solverClient,
boardSize: _boardSize,
eachBoxSize: _eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: _fontSize,
images: _previousImages,
kInitialSpeed: kInitialSpeed,
),
complete: (image, images, palette) {
_previousImages = images;
_previousImage = image;
_previousPalette = palette;
return state.maybeWhen(
() => PuzzleWidget(
solverClient: _solverClient,
boardSize: _boardSize,
eachBoxSize: _eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: _fontSize,
images: _previousImages,
kInitialSpeed: kInitialSpeed,
),
complete: (image, images, palette) {
_previousImages = images;
_previousImage = image;
_previousPalette = palette;
return PuzzleWidget(
return PuzzleWidget(
solverClient: _solverClient,
boardSize: _boardSize,
eachBoxSize: _eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: _fontSize,
images: images,
kInitialSpeed: kInitialSpeed,
);
},
orElse: () => PuzzleWidget(
solverClient: _solverClient,
boardSize: _boardSize,
eachBoxSize: _eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: _fontSize,
images: images,
images: _previousImages,
kInitialSpeed: kInitialSpeed,
);
},
orElse: () => PuzzleWidget(
solverClient: _solverClient,
boardSize: _boardSize,
eachBoxSize: _eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: _fontSize,
images: _previousImages,
kInitialSpeed: kInitialSpeed,
),
);
},
),
const SizedBox(height: 30),
],
),
);
},
),
const SizedBox(height: 30),
],
),
),
),
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: Stack(
children: [
AnimatedDash(
boardSize: _boardSize * 0.8,
riveController: _riveController,
onInit: (_) => setState(() {}),
),
Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: [
CountdownWidget(
isStartPressed: _isStartPressed,
onFinish: () {
// ref.read(timerNotifierProvider.notifier).startTimer();
BlocProvider.of<CountDownTimerCubit>(context).start();
setState(() {
_isStartPressed = false;
});
},
initialSpeed: kInitialSpeed,
),
Visibility(
visible: !_isStartPressed,
child: ImageViewer(
imagePicker: _imagePicker,
puzzleSize: _puzzleSize,
previousImage: _previousImage,
previousPalette: _previousPalette,
imageSize: 200,
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: Stack(
children: [
AnimatedDash(
boardSize: _boardSize * 0.8,
riveController: _riveController,
onInit: (_) => setState(() {}),
),
Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: [
CountdownWidget(
isStartPressed: _isStartPressed,
onFinish: () {
// ref.read(timerNotifierProvider.notifier).startTimer();
BlocProvider.of<CountDownTimerCubit>(context).start();
setState(() {
_isStartPressed = false;
});
},
initialSpeed: kInitialSpeed,
),
Visibility(
visible: !_isStartPressed,
child: ImageViewer(
imagePicker: _imagePicker,
puzzleSize: _puzzleSize,
previousImage: _previousImage,
previousPalette: _previousPalette,
imageSize: 200,
),
),
),
],
),
],
],
),
],
),
),
),
],
],
),
),
),
);

200
lib/screens/photo/photo_screen_medium.dart

@ -1,200 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart';
import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/models/puzzle_data.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/res/palette.dart';
import 'package:my_flutter_puzzle/res/puzzle_constants.dart';
import 'package:my_flutter_puzzle/utils/color_brightness.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:my_flutter_puzzle/widgets/photo_screen/image_viewer.dart';
import 'package:my_flutter_puzzle/widgets/photo_screen/pick_image_button.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/countdown_overlay.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart';
import 'package:palette_generator/palette_generator.dart';
import 'package:rive/rive.dart';
class PhotoScreenMedium extends ConsumerStatefulWidget {
const PhotoScreenMedium({
required this.solverClient,
required this.initialPuzzleData,
required this.puzzleSize,
required this.riveController,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int puzzleSize;
final RiveAnimationController riveController;
@override
ConsumerState<ConsumerStatefulWidget> createState() => _PhotoScreenMediumState();
}
class _PhotoScreenMediumState extends ConsumerState<PhotoScreenMedium> {
late final PuzzleSolverClient _solverClient;
late final int _puzzleSize;
late final PuzzleData _initialPuzzleData;
late final RiveAnimationController _riveController;
bool _isStartPressed = false;
final _imagePicker = ImagePicker();
List<Image>? _previousImages;
Image? _previousImage;
PaletteGenerator? _previousPalette;
@override
void initState() {
_solverClient = widget.solverClient;
_puzzleSize = widget.puzzleSize;
_initialPuzzleData = widget.initialPuzzleData;
_riveController = widget.riveController;
super.initState();
}
@override
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
if (next is PuzzleSolved) {}
if (next is PuzzleInitializing) {
setState(() {
_isStartPressed = true;
});
}
});
ref.listen(imageSplitterNotifierProvider, (previous, next) {
if (next is ImageSplitterComplete) {
setState(() {
_previousImages = next.images;
_previousImage = next.image;
_previousPalette = next.palette;
});
}
});
var fontSize = 64.0;
var boardSize = 400.0;
var spacing = 5;
var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1));
return Stack(
children: [
Scaffold(
backgroundColor: Palette.blue.darken(0.3),
body: Stack(
children: [
Row(
children: [
const Spacer(),
AnimatedDash(
boardSize: boardSize / 1.5,
riveController: _riveController,
onInit: (_) => setState(() {}),
),
],
),
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(),
const SizedBox(height: 16),
MovesTilesWidget(solverClient: _solverClient),
const SizedBox(height: 16),
const TimerWidget(fontSize: 36),
const SizedBox(height: 36),
Consumer(
builder: (context, ref, child) {
final state = ref.watch(imageSplitterNotifierProvider);
return state.maybeWhen(
() => PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
images: _previousImages,
kInitialSpeed: kInitialSpeed,
),
complete: (image, images, palette) {
_previousImages = images;
_previousImage = image;
_previousPalette = palette;
return PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
images: images,
kInitialSpeed: kInitialSpeed,
);
},
orElse: () => PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
images: _previousImages,
kInitialSpeed: kInitialSpeed,
),
);
},
),
const SizedBox(height: 36),
Row(
mainAxisSize: MainAxisSize.min,
children: [
GameButtonWidget(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
),
const SizedBox(width: 36),
PickImageButton(
text: 'Pick Image',
onTap: ref.read(imageSplitterNotifierProvider) is ImageSplitterComplete
? () => ref.read(imageSplitterNotifierProvider.notifier).generateImages(
picker: _imagePicker,
puzzleSize: _puzzleSize,
)
: null,
),
],
),
const SizedBox(height: 36),
ImageViewer(
imagePicker: _imagePicker,
puzzleSize: _puzzleSize,
previousImage: _previousImage,
previousPalette: _previousPalette,
imageSize: 150,
),
],
),
),
],
),
),
CountdownOverlay(
isStartPressed: _isStartPressed,
onFinish: () {
ref.read(timerNotifierProvider.notifier).startTimer();
setState(() {
_isStartPressed = false;
});
},
initialSpeed: kInitialSpeed,
),
],
);
}
}

212
lib/screens/photo/photo_screen_small.dart

@ -1,212 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart';
import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/models/puzzle_data.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/res/puzzle_constants.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:my_flutter_puzzle/widgets/photo_screen/image_viewer.dart';
import 'package:my_flutter_puzzle/widgets/photo_screen/pick_image_button.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/countdown_overlay.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart';
import 'package:palette_generator/palette_generator.dart';
import 'package:rive/rive.dart';
class PhotoScreenSmall extends ConsumerStatefulWidget {
const PhotoScreenSmall({
required this.solverClient,
required this.initialPuzzleData,
required this.puzzleSize,
required this.riveController,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int puzzleSize;
final RiveAnimationController riveController;
@override
ConsumerState<ConsumerStatefulWidget> createState() => _SoloScreenLargeState();
}
class _SoloScreenLargeState extends ConsumerState<PhotoScreenSmall> {
late final PuzzleSolverClient _solverClient;
late final int _puzzleSize;
late final PuzzleData _initialPuzzleData;
late final RiveAnimationController _riveController;
bool _isStartPressed = false;
final _imagePicker = ImagePicker();
List<Image>? _previousImages;
Image? _previousImage;
PaletteGenerator? _previousPalette;
@override
void initState() {
_solverClient = widget.solverClient;
_puzzleSize = widget.puzzleSize;
_initialPuzzleData = widget.initialPuzzleData;
_riveController = widget.riveController;
super.initState();
}
@override
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
if (next is PuzzleInitializing) {
setState(() {
_isStartPressed = true;
});
}
});
ref.listen(imageSplitterNotifierProvider, (previous, next) {
if (next is ImageSplitterComplete) {
setState(() {
_previousImages = next.images;
_previousImage = next.image;
_previousPalette = next.palette;
});
}
});
var fontSize = 48.0;
var boardSize = 300.0;
var spacing = 2;
var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1));
return Stack(
children: [
Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
// appBar: PreferredSize(
// child: Container(
// color: Theme.of(context).colorScheme.background,
// ),
// preferredSize: const Size(double.maxFinite, 30),
// ),
body: Stack(
children: [
Row(
children: [
const Spacer(),
AnimatedDash(
boardSize: boardSize / 1.6,
riveController: _riveController,
onInit: (_) => setState(() {}),
padding: const EdgeInsets.only(right: 16.0, bottom: 30),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(),
const SizedBox(height: 60),
MovesTilesWidget(
solverClient: _solverClient,
fontSize: 22,
),
const SizedBox(height: 20),
const TimerWidget(fontSize: 24),
const SizedBox(height: 60),
Consumer(
builder: (context, ref, child) {
final state = ref.watch(imageSplitterNotifierProvider);
return state.maybeWhen(
() => PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
images: _previousImages,
kInitialSpeed: kInitialSpeed,
borderRadius: 16,
),
complete: (image, images, palette) {
_previousImages = images;
_previousImage = image;
_previousPalette = palette;
return PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
images: images,
kInitialSpeed: kInitialSpeed,
borderRadius: 16,
);
},
orElse: () => PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
images: _previousImages,
kInitialSpeed: kInitialSpeed,
borderRadius: 16,
),
);
},
),
const SizedBox(height: 24),
Row(
mainAxisSize: MainAxisSize.min,
children: [
GameButtonWidget(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
padding: const EdgeInsets.only(top: 10.0, bottom: 9.0),
width: 130,
),
const SizedBox(width: 16),
PickImageButton(
text: 'Pick Image',
onTap: ref.read(imageSplitterNotifierProvider) is ImageSplitterComplete
? () => ref.read(imageSplitterNotifierProvider.notifier).generateImages(
picker: _imagePicker,
puzzleSize: _puzzleSize,
)
: null,
padding: const EdgeInsets.only(top: 10.0, bottom: 9.0),
width: 130,
),
],
),
const SizedBox(height: 16),
ImageViewer(
imagePicker: _imagePicker,
puzzleSize: _puzzleSize,
previousImage: _previousImage,
previousPalette: _previousPalette,
imageSize: 110,
),
const SizedBox(height: 50),
],
),
],
),
),
CountdownOverlay(
isStartPressed: _isStartPressed,
onFinish: () {
ref.read(timerNotifierProvider.notifier).startTimer();
setState(() {
_isStartPressed = false;
});
},
initialSpeed: kInitialSpeed,
),
],
);
}
}

59
lib/screens/puzzle/puzzle_screen_large.dart

@ -1,59 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/application/notifiers/puzzle_type_notifier.dart';
import 'package:my_flutter_puzzle/models/puzzle_data.dart';
import 'package:my_flutter_puzzle/screens/photo/photo_screen_large.dart';
import 'package:my_flutter_puzzle/screens/solo/solo_screen_large.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:rive/rive.dart';
import '../../providers.dart';
class PuzzleScreenLarge extends ConsumerWidget {
const PuzzleScreenLarge({
required this.solverClient,
required this.initialPuzzleData,
required this.puzzleSize,
required this.riveController,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int puzzleSize;
final RiveAnimationController riveController;
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentPuzzleType = ref.watch(puzzleTypeNotifierProvider);
final name = currentPuzzleType.name[0].toUpperCase() + currentPuzzleType.name.substring(1);
switch (currentPuzzleType) {
case PuzzleType.normal:
return SoloScreenLarge(
solverClient: solverClient,
initialPuzzleData: initialPuzzleData,
puzzleSize: puzzleSize,
puzzleType: name,
riveController: riveController,
);
case PuzzleType.photo:
return PhotoScreenLarge(
solverClient: solverClient,
initialPuzzleData: initialPuzzleData,
puzzleSize: puzzleSize,
riveController: riveController,
);
case PuzzleType.multiplayer:
default:
return SoloScreenLarge(
solverClient: solverClient,
initialPuzzleData: initialPuzzleData,
puzzleSize: puzzleSize,
puzzleType: name,
riveController: riveController,
);
}
}
}

8
lib/screens/puzzle/puzzle_starter_screen.dart

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart';
import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/models/puzzle_data.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/screens/photo/photo_screen.dart';
@ -36,6 +35,7 @@ class _SoloScreenState extends ConsumerState<PuzzleStarterScreen> {
_riveController = SimpleAnimation('idle');
_solverClient = PuzzleSolverClient(size: _puzzleSize);
_initialPuzzleData = ref.read(puzzleNotifierProvider(_solverClient).notifier).generateInitialPuzzle();
ref.refresh(imageSplitterNotifierProvider);
final state = ref.read(imageSplitterNotifierProvider);
if (state is! ImageSplitterComplete) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
@ -52,16 +52,12 @@ class _SoloScreenState extends ConsumerState<PuzzleStarterScreen> {
@override
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
if (next is PuzzleSolved) {
ref.read(timerNotifierProvider.notifier).stopTimer();
}
});
return AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
child: PhotoScreen(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
duration: widget.duration,
puzzleSize: _puzzleSize,
riveController: _riveController,
),

635
lib/screens/solo/solo_large_screen_prev.dart

@ -1,635 +0,0 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter_puzzle/res/palette.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:rive/rive.dart';
class SoloLargeScreen extends StatefulWidget {
const SoloLargeScreen({
Key? key,
}) : super(key: key);
@override
_SoloLargeScreenState createState() => _SoloLargeScreenState();
}
class _SoloLargeScreenState extends State<SoloLargeScreen> {
late PuzzleSolverClient _solverClient;
late RiveAnimationController _riveController;
List<List<int>>? _board2D;
List<int>? myList;
int _moves = 0;
final _puzzleSize = 3;
final int _animationSpeedInMilliseconds = 300;
bool _isComputing = false;
bool _isAutoSolving = false;
bool _isSolved = false;
Map<int, FractionalOffset>? _offsetMap;
List<int> _solvedList = [];
@override
void initState() {
super.initState();
_solverClient = PuzzleSolverClient(size: _puzzleSize);
_riveController = SimpleAnimation('idle');
initBoard();
generateSolvedList();
}
generateSolvedList() {
for (int i = 1; i < _puzzleSize * _puzzleSize; i++) {
_solvedList.add(i);
}
_solvedList.add(0);
}
scrambleBoard() {
final generated2DBoard = _solverClient.createRandomBoard();
final generated1DBoard = _solverClient.convertTo1D(generated2DBoard);
updateOffset(generated1DBoard);
setState(() {
_board2D = generated2DBoard;
myList = generated1DBoard;
_moves = 0;
_isSolved = false;
});
}
initBoard() {
final generated2DBoard = _solverClient.createRandomBoard();
final generated1DBoard = _solverClient.convertTo1D(generated2DBoard);
createOffset(generated1DBoard);
setState(() {
_board2D = generated2DBoard;
myList = generated1DBoard;
_moves = 0;
});
}
startAutoSolver() async {
if (_board2D != null) {
setState(() {
_isComputing = true;
});
List<List<int>>? boardStates = await compute(
_solverClient.runner, _solverClient.convertTo2D(myList!));
setState(() {
_isComputing = false;
_isAutoSolving = true;
});
if (boardStates != null) {
for (var board in boardStates) {
await Future.delayed(Duration(
milliseconds: _animationSpeedInMilliseconds,
));
setState(() {
myList = board;
_moves++;
});
updateOffset(myList!);
}
}
}
setState(() {
_isAutoSolving = false;
_isSolved = true;
});
showCompleteDialogBox(context);
}
isSolved(List<int> currentBoard) {
if (listEquals(currentBoard, _solvedList)) {
setState(() {
_isSolved = true;
});
return true;
}
setState(() {
_isSolved = false;
});
return false;
}
onClick(index) {
log('-----------------------');
log('Tapped index: $index');
if (myList != null) {
int emptyTilePosIndex = myList!.indexOf(0);
int emptyTilePosRow = emptyTilePosIndex ~/ _puzzleSize;
int emptyTilePosCol = emptyTilePosIndex % _puzzleSize;
int currentTileRow = index ~/ _puzzleSize;
int currentTileCol = index % _puzzleSize;
//current element moves up
if ((currentTileRow - 1 == emptyTilePosRow) &&
(currentTileCol == emptyTilePosCol)) {
myList![emptyTilePosIndex] = myList![index];
myList![index] = 0;
_moves++;
}
//current element moves down
else if ((currentTileRow + 1 == emptyTilePosRow) &&
(currentTileCol == emptyTilePosCol)) {
myList![emptyTilePosIndex] = myList![index];
myList![index] = 0;
_moves++;
}
//current element moves left
else if ((currentTileRow == emptyTilePosRow) &&
(currentTileCol + 1 == emptyTilePosCol)) {
myList![emptyTilePosIndex] = myList![index];
myList![index] = 0;
_moves++;
}
//current element moves right
else if ((currentTileRow == emptyTilePosRow) &&
(currentTileCol - 1 == emptyTilePosCol)) {
myList![emptyTilePosIndex] = myList![index];
myList![index] = 0;
_moves++;
} else {
if (currentTileCol == emptyTilePosCol) {
int low;
int high;
// multiple elements move up
if (emptyTilePosRow < currentTileRow) {
low = emptyTilePosRow;
high = currentTileRow;
int i = low;
while (i < high) {
myList![(i * _puzzleSize) + emptyTilePosCol] =
myList![(((i + 1) * _puzzleSize) + emptyTilePosCol)];
i += 1;
}
myList![(high * _puzzleSize) + emptyTilePosCol] = 0;
_moves++;
}
//multiple elements move down
else {
low = emptyTilePosRow;
high = currentTileRow;
int i = low;
while (i > high) {
myList![(i * _puzzleSize) + emptyTilePosCol] =
myList![(((i - 1) * _puzzleSize) + emptyTilePosCol)];
i -= 1;
}
myList![(high * _puzzleSize) + emptyTilePosCol] = 0;
_moves++;
}
}
// multiple elements move left or right
if (currentTileRow == emptyTilePosRow) {
int low;
int high;
// multiple elements move left
if (emptyTilePosCol < currentTileCol) {
low = emptyTilePosCol;
high = currentTileCol;
int i = low;
while (i < high) {
myList![(emptyTilePosRow * _puzzleSize) + i] =
myList![(emptyTilePosRow * _puzzleSize) + (i + 1)];
i += 1;
}
myList![high + (emptyTilePosRow * _puzzleSize)] = 0;
_moves++;
}
//multiple elements move right
else {
low = emptyTilePosCol;
high = currentTileCol;
int i = low;
while (i > high) {
myList![(i + (emptyTilePosRow * _puzzleSize))] =
myList![(i - 1) + (emptyTilePosRow * _puzzleSize)];
i -= 1;
}
myList![high + (emptyTilePosRow * _puzzleSize)] = 0;
_moves++;
}
}
}
// Update Offset list
// setState(() {
// updateOffset(myList!);
// });
updateOffset(myList!);
setState(() {});
if (isSolved(myList!)) {
showCompleteDialogBox(context);
}
log('List: $myList');
log('-----------------------');
}
}
createOffset(List<int> board) {
Map<int, FractionalOffset> offsetMap = {};
int j = 0;
log('BOARD: $board');
for (int i = 0; i < board.length; i++) {
final xMod = i % _puzzleSize;
double x = xMod / (_puzzleSize - 1);
if (x % i == 0 && i != 0) j++;
int yMod = j % _puzzleSize;
double y = yMod / (_puzzleSize - 1);
offsetMap.addEntries([
MapEntry<int, FractionalOffset>(
board[i],
FractionalOffset(x, y),
)
]);
}
log('INITIAL OFFSET MAP: $offsetMap');
setState(() {
_offsetMap = offsetMap;
});
}
updateOffset(List<int> board) {
int j = 0;
for (int i = 0; i < board.length; i++) {
final xMod = i % _puzzleSize;
double x = xMod / (_puzzleSize - 1);
if (x % i == 0 && i != 0) j++;
int yMod = j % _puzzleSize;
double y = yMod / (_puzzleSize - 1);
_offsetMap![board[i]] = FractionalOffset(x, y);
}
log('OFFSET MAP: $_offsetMap');
}
showCompleteDialogBox(BuildContext context) {
showDialog(
context: context,
builder: (context) => Dialog(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Solved successfully!',
style: TextStyle(fontSize: 22),
),
const SizedBox(height: 16),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Palette.violet,
onSurface: Palette.violet,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
),
onPressed: () {
Navigator.of(context).pop();
},
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'OK',
style: TextStyle(fontSize: 22),
),
),
),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
var shortestSide = screenSize.shortestSide;
var fontSize = shortestSide * 0.08;
var boardSize = shortestSide * 0.45;
var spacing = 5;
var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1));
return Scaffold(
backgroundColor: Colors.black,
appBar: PreferredSize(
child: Container(
color: Colors.black,
),
preferredSize: Size(double.maxFinite, shortestSide * 0.1),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 56.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(),
const Text(
'Photo',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const SizedBox(height: 8),
const Text(
'Puzzle',
style: TextStyle(
fontSize: 58,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const Text(
'Challenge',
style: TextStyle(
fontSize: 58,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const SizedBox(height: 32),
RichText(
text: TextSpan(
style: const TextStyle(
fontSize: 24,
// fontWeight: FontWeight.w500,
color: Colors.white,
),
children: [
TextSpan(
text: _moves.toString(),
style: const TextStyle(
fontWeight: FontWeight.w600,
),
),
const TextSpan(text: ' Moves | '),
const TextSpan(
text: '15',
style: TextStyle(
fontWeight: FontWeight.w600,
),
),
const TextSpan(text: ' Tiles'),
],
),
),
const SizedBox(height: 32),
SizedBox(
width: 145,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
onPressed: () {},
child: const Padding(
padding: EdgeInsets.only(top: 13.0, bottom: 12.0),
child: Text(
'Start Game',
// 'Restart',
// 'Get ready...',
style: TextStyle(
fontSize: 16,
),
),
),
),
),
],
),
),
myList != null && _offsetMap != null
? Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// Row(),
// MovesText(
// moves: _moves,
// fontSize: fontSize,
// ),
Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text(
'00:00:00',
style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(width: 8),
Icon(
Icons.timer,
color: Colors.white,
size: 40,
)
],
),
SizedBox(
height: boardSize,
width: boardSize,
child: Stack(
children: [
for (int i = 0; i < _offsetMap!.length; i++)
_offsetMap!.entries.toList()[i].key != 0
? AnimatedAlign(
alignment:
_offsetMap!.entries.toList()[i].value,
duration: Duration(
milliseconds:
_animationSpeedInMilliseconds,
),
curve: Curves.easeInOut,
child: GestureDetector(
onTap: () => onClick(myList!.indexOf(
_offsetMap!.entries.toList()[i].key)),
child: Card(
elevation: 4,
color: const Color(0xFF2868d7),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20),
),
child: SizedBox(
height: eachBoxSize,
width: eachBoxSize,
child: Center(
child: Text(
_offsetMap!.entries
.toList()[i]
.key
.toString(),
style: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
),
),
)
: const SizedBox(),
],
),
),
const SizedBox(height: 30),
// Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// Padding(
// padding: const EdgeInsets.only(bottom: 30.0),
// child: SizedBox(
// width: MediaQuery.of(context).size.width * 0.2,
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// primary: Palette.violet,
// onSurface: Palette.violet,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(50),
// ),
// ),
// onPressed: _isComputing || _isAutoSolving || _isSolved
// ? null
// : () {
// startAutoSolver();
// },
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: _isComputing || _isAutoSolving
// ? Row(
// children: [
// const SizedBox(
// width: 25,
// height: 25,
// child: CircularProgressIndicator(
// color: Palette.violet,
// strokeWidth: 2,
// ),
// ),
// const SizedBox(width: 16),
// Text(
// _isComputing
// ? 'Computing ...'
// : 'Solving ...',
// style: const TextStyle(fontSize: 20),
// ),
// ],
// )
// : const Text(
// 'Start Auto Solver',
// style: TextStyle(fontSize: 22),
// ),
// ),
// ),
// ),
// ),
// const SizedBox(width: 16.0),
// Padding(
// padding: const EdgeInsets.only(bottom: 30.0),
// child: SizedBox(
// width: MediaQuery.of(context).size.width * 0.2,
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// primary: Palette.crimson,
// onSurface: Palette.crimson,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(50),
// ),
// ),
// onPressed: _isComputing || _isAutoSolving
// ? null
// : () {
// scrambleBoard();
// },
// child: const Padding(
// padding: EdgeInsets.all(16.0),
// child: Text(
// 'Scramble',
// style: TextStyle(fontSize: 22),
// ),
// ),
// ),
// ),
// ),
// ],
// ),
],
)
: const SizedBox(),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(right: 56.0, bottom: 56),
child: SizedBox(
width: boardSize * 0.75,
height: boardSize * 0.75,
child: RiveAnimation.asset(
'assets/rive/dash.riv',
fit: BoxFit.contain,
antialiasing: true,
controllers: [_riveController],
onInit: (_) => setState(() {}),
),
),
),
),
// SizedBox(),
],
),
);
}
}

63
lib/screens/solo/solo_screen.dart

@ -1,63 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/screens/solo/solo_screen_large.dart';
import 'package:my_flutter_puzzle/screens/solo/solo_screen_small.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:my_flutter_puzzle/utils/responsive_layout.dart';
import 'package:rive/rive.dart';
import '../../models/puzzle_data.dart';
import 'solo_screen_medium.dart';
class SoloScreen extends ConsumerWidget {
const SoloScreen({
required this.solverClient,
required this.initialPuzzleData,
required this.puzzleSize,
required this.puzzleType,
required this.riveController,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int puzzleSize;
final String puzzleType;
final RiveAnimationController riveController;
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen(puzzleNotifierProvider(solverClient),
(previous, PuzzleState next) {
if (next is PuzzleSolved) {
ref.read(timerNotifierProvider.notifier).stopTimer();
}
});
return ResponsiveLayout(
largeChild: SoloScreenLarge(
solverClient: solverClient,
puzzleType: puzzleType,
initialPuzzleData: initialPuzzleData,
puzzleSize: puzzleSize,
riveController: riveController,
),
mediumChild: SoloScreenMedium(
solverClient: solverClient,
initialPuzzleData: initialPuzzleData,
puzzleSize: puzzleSize,
puzzleType: puzzleType,
riveController: riveController,
),
smallChild: SoloScreenSmall(
solverClient: solverClient,
initialPuzzleData: initialPuzzleData,
puzzleSize: puzzleSize,
puzzleType: puzzleType,
riveController: riveController,
),
);
}
}

164
lib/screens/solo/solo_screen_large.dart

@ -1,164 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/res/puzzle_constants.dart';
import 'package:my_flutter_puzzle/screens/puzzle/top_bar.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart';
import 'package:rive/rive.dart';
import '../../models/puzzle_data.dart';
class SoloScreenLarge extends ConsumerStatefulWidget {
const SoloScreenLarge({
required this.solverClient,
required this.initialPuzzleData,
required this.puzzleSize,
required this.puzzleType,
required this.riveController,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int puzzleSize;
final String puzzleType;
final RiveAnimationController riveController;
@override
ConsumerState<ConsumerStatefulWidget> createState() => _SoloScreenLargeState();
}
class _SoloScreenLargeState extends ConsumerState<SoloScreenLarge> {
late final PuzzleSolverClient _solverClient;
late final int _puzzleSize;
late final PuzzleData _initialPuzzleData;
late final String _puzzleType;
late final RiveAnimationController _riveController;
bool _isStartPressed = false;
@override
void initState() {
_solverClient = widget.solverClient;
_puzzleSize = widget.puzzleSize;
_initialPuzzleData = widget.initialPuzzleData;
_puzzleType = widget.puzzleType;
_riveController = widget.riveController;
super.initState();
}
@override
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
if (next is PuzzleSolved) {
// TODO: Add celebration
}
if (next is PuzzleInitializing) {
setState(() {
_isStartPressed = true;
});
}
});
var fontSize = 70.0;
var boardSize = 450.0;
var spacing = 5;
var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1));
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 56.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(),
Text(
_puzzleType,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const SizedBox(height: 8),
const Text(
'Puzzle',
style: TextStyle(
fontSize: 58,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const Text(
'Challenge',
style: TextStyle(
fontSize: 58,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const SizedBox(height: 32),
MovesTilesWidget(solverClient: _solverClient),
const SizedBox(height: 32),
GameButtonWidget(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
),
],
),
),
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const TimerWidget(
fontSize: 40,
),
const SizedBox(height: 36),
PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
kInitialSpeed: kInitialSpeed,
),
const SizedBox(height: 30),
],
),
),
Column(
mainAxisSize: MainAxisSize.max,
children: [
const Spacer(),
CountdownWidget(
isStartPressed: _isStartPressed,
onFinish: () {
ref.read(timerNotifierProvider.notifier).startTimer();
setState(() {
_isStartPressed = false;
});
},
initialSpeed: kInitialSpeed,
),
const Spacer(),
AnimatedDash(
boardSize: boardSize * 0.8,
riveController: _riveController,
onInit: (_) => setState(() {}),
),
],
),
// SizedBox(),
],
),
);
}
}

148
lib/screens/solo/solo_screen_medium.dart

@ -1,148 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/res/puzzle_constants.dart';
import 'package:my_flutter_puzzle/screens/puzzle/top_bar.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/countdown_overlay.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart';
import 'package:rive/rive.dart';
import '../../models/puzzle_data.dart';
class SoloScreenMedium extends ConsumerStatefulWidget {
const SoloScreenMedium({
required this.solverClient,
required this.initialPuzzleData,
required this.puzzleSize,
required this.puzzleType,
required this.riveController,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int puzzleSize;
final String puzzleType;
final RiveAnimationController riveController;
@override
ConsumerState<ConsumerStatefulWidget> createState() => _SoloScreenLargeState();
}
class _SoloScreenLargeState extends ConsumerState<SoloScreenMedium> {
late final PuzzleSolverClient _solverClient;
late final int _puzzleSize;
late final PuzzleData _initialPuzzleData;
late final String _puzzleType;
late final RiveAnimationController _riveController;
bool _isStartPressed = false;
@override
void initState() {
_solverClient = widget.solverClient;
_puzzleSize = widget.puzzleSize;
_initialPuzzleData = widget.initialPuzzleData;
_puzzleType = widget.puzzleType;
_riveController = widget.riveController;
super.initState();
}
@override
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
if (next is PuzzleSolved) {
// TODO: Add celebration
}
if (next is PuzzleInitializing) {
setState(() {
_isStartPressed = true;
});
}
});
var fontSize = 64.0;
var boardSize = 400.0;
var spacing = 5;
var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1));
return Stack(
children: [
Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: Stack(
children: [
Row(
children: [
const Spacer(),
AnimatedDash(
boardSize: boardSize / 1.5,
riveController: _riveController,
onInit: (_) => setState(() {}),
),
],
),
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(),
// Text(
// _puzzleType,
// style: const TextStyle(
// fontSize: 18,
// fontWeight: FontWeight.w500,
// color: Colors.white,
// ),
// ),
const SizedBox(height: 8),
const Text(
'Puzzle Challenge',
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const SizedBox(height: 16),
MovesTilesWidget(solverClient: _solverClient),
const SizedBox(height: 16),
const TimerWidget(fontSize: 36),
const SizedBox(height: 36),
PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
kInitialSpeed: kInitialSpeed,
),
const SizedBox(height: 36),
GameButtonWidget(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
),
const SizedBox(height: 100),
],
),
),
],
),
),
CountdownOverlay(
isStartPressed: _isStartPressed,
onFinish: () {
ref.read(timerNotifierProvider.notifier).startTimer();
setState(() {
_isStartPressed = false;
});
},
initialSpeed: kInitialSpeed,
),
],
);
}
}

145
lib/screens/solo/solo_screen_small.dart

@ -1,145 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/res/puzzle_constants.dart';
import 'package:my_flutter_puzzle/screens/puzzle/top_bar.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/countdown_overlay.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart';
import 'package:rive/rive.dart';
import '../../models/puzzle_data.dart';
class SoloScreenSmall extends ConsumerStatefulWidget {
const SoloScreenSmall({
required this.solverClient,
required this.initialPuzzleData,
required this.puzzleSize,
required this.puzzleType,
required this.riveController,
Key? key,
}) : super(key: key);
final PuzzleSolverClient solverClient;
final PuzzleData initialPuzzleData;
final int puzzleSize;
final String puzzleType;
final RiveAnimationController riveController;
@override
ConsumerState<ConsumerStatefulWidget> createState() => _SoloScreenLargeState();
}
class _SoloScreenLargeState extends ConsumerState<SoloScreenSmall> {
late final PuzzleSolverClient _solverClient;
late final int _puzzleSize;
late final PuzzleData _initialPuzzleData;
late final String _puzzleType;
late final RiveAnimationController _riveController;
bool _isStartPressed = false;
@override
void initState() {
_solverClient = widget.solverClient;
_puzzleSize = widget.puzzleSize;
_initialPuzzleData = widget.initialPuzzleData;
_puzzleType = widget.puzzleType;
_riveController = widget.riveController;
super.initState();
}
@override
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
if (next is PuzzleSolved) {
// TODO: Add celebration
}
if (next is PuzzleInitializing) {
setState(() {
_isStartPressed = true;
});
}
});
var fontSize = 48.0;
var boardSize = 300.0;
var spacing = 3;
var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1));
return Stack(
children: [
Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: Stack(
children: [
Row(
children: [
const Spacer(),
AnimatedDash(
boardSize: boardSize / 1.5,
riveController: _riveController,
onInit: (_) => setState(() {}),
padding: const EdgeInsets.only(right: 16.0, bottom: 30),
),
],
),
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(),
const SizedBox(height: 30),
const Text(
'Puzzle Challenge',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
const SizedBox(height: 8),
MovesTilesWidget(
solverClient: _solverClient,
fontSize: 22,
),
const SizedBox(height: 8),
const TimerWidget(fontSize: 24),
PuzzleWidget(
solverClient: _solverClient,
boardSize: boardSize,
eachBoxSize: eachBoxSize,
initialPuzzleData: _initialPuzzleData,
fontSize: fontSize,
kInitialSpeed: kInitialSpeed,
borderRadius: 16,
),
const SizedBox(height: 24),
GameButtonWidget(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
padding: const EdgeInsets.only(top: 10.0, bottom: 9.0),
width: 130,
),
const SizedBox(height: 100),
],
),
),
],
),
),
CountdownOverlay(
isStartPressed: _isStartPressed,
onFinish: () {
ref.read(timerNotifierProvider.notifier).startTimer();
setState(() {
_isStartPressed = false;
});
},
initialSpeed: kInitialSpeed,
),
],
);
}
}

198
lib/utils/toast.dart

@ -0,0 +1,198 @@
import 'dart:async';
import 'package:flutter/material.dart';
enum Toast {
short,
long,
}
enum ToastGravity {
top,
bottom,
center,
topLeft,
topRight,
bottomLeft,
bottomRight,
centerLEft,
centerRight,
snackBar,
}
typedef PositionedToastBuilder = Widget Function(BuildContext context, Widget child);
class FToast {
late BuildContext context;
static final FToast _instance = FToast._internal();
factory FToast() {
return _instance;
}
FToast init(BuildContext context) {
_instance.context = context;
return _instance;
}
FToast._internal();
OverlayEntry? _entry;
final List<_ToastEntry> _overlayQueue = [];
Timer? _timer;
void _showOverlay() {
if (_overlayQueue.isEmpty) {
_entry = null;
return;
}
_ToastEntry toastEntry = _overlayQueue.removeAt(0);
_entry = toastEntry.entry;
Overlay.of(context)?.insert(_entry!);
_timer = Timer(toastEntry.duration, () {
Future.delayed(const Duration(milliseconds: 360), () {
removeCustomToast();
});
});
}
void removeCustomToast() {
_timer!.cancel();
_timer = null;
if (_entry != null) _entry!.remove();
_entry = null;
_showOverlay();
}
void removeQueuedCustomToasts() {
_timer?.cancel();
_timer = null;
_overlayQueue.clear();
if (_entry != null) _entry!.remove();
_entry = null;
}
void showToast({
required Widget child,
PositionedToastBuilder? positionedToastBuilder,
required Duration? toastDuration,
required ToastGravity gravity,
int fadeDuration = 350,
}) {
Widget newChild = _ToastStateFul(child, toastDuration ?? const Duration(seconds: 2), fadeDuration: fadeDuration);
if (gravity == ToastGravity.bottom) {
if (MediaQuery.of(context).viewInsets.bottom != 0) {
gravity = ToastGravity.center;
}
}
OverlayEntry newEntry = OverlayEntry(builder: (context) {
if (positionedToastBuilder != null) return positionedToastBuilder(context, newChild);
return _getPositionWidgetBasedOnGravity(newChild, gravity);
});
_overlayQueue.add(_ToastEntry(entry: newEntry, duration: toastDuration ?? const Duration(seconds: 2)));
if (_timer == null) _showOverlay();
}
Positioned _getPositionWidgetBasedOnGravity(Widget child, ToastGravity gravity) {
switch (gravity) {
case ToastGravity.top:
return Positioned(top: 100.0, left: 24.0, right: 24.0, child: child);
case ToastGravity.topLeft:
return Positioned(top: 100.0, left: 24.0, child: child);
case ToastGravity.topRight:
return Positioned(top: 100.0, right: 24.0, child: child);
case ToastGravity.center:
return Positioned(top: 50.0, bottom: 50.0, left: 24.0, right: 24.0, child: child);
case ToastGravity.centerLEft:
return Positioned(top: 50.0, bottom: 50.0, left: 24.0, child: child);
case ToastGravity.centerRight:
return Positioned(top: 50.0, bottom: 50.0, right: 24.0, child: child);
case ToastGravity.bottomLeft:
return Positioned(bottom: 50.0, left: 24.0, child: child);
case ToastGravity.bottomRight:
return Positioned(bottom: 50.0, right: 24.0, child: child);
case ToastGravity.snackBar:
return Positioned(bottom: MediaQuery.of(context).viewInsets.bottom, left: 0, right: 0, child: child);
case ToastGravity.bottom:
default:
return Positioned(bottom: 50.0, left: 24.0, right: 24.0, child: child);
}
}
}
class _ToastEntry {
final OverlayEntry entry;
final Duration duration;
_ToastEntry({required this.entry, required this.duration});
}
class _ToastStateFul extends StatefulWidget {
const _ToastStateFul(this.child, this.duration, {Key? key, this.fadeDuration = 350}) : super(key: key);
final Widget child;
final Duration duration;
final int fadeDuration;
@override
ToastStateFulState createState() => ToastStateFulState();
}
class ToastStateFulState extends State<_ToastStateFul> with SingleTickerProviderStateMixin {
void showIt() {
_animationController.forward();
}
void hideIt() {
_animationController.reverse();
_timer.cancel();
}
late AnimationController _animationController;
late Animation _fadeAnimation;
late Timer _timer;
@override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: widget.fadeDuration),
);
_fadeAnimation = CurvedAnimation(parent: _animationController, curve: Curves.easeIn);
super.initState();
showIt();
_timer = Timer(widget.duration, () {
hideIt();
});
}
@override
void deactivate() {
_timer.cancel();
_animationController.stop();
super.deactivate();
}
@override
void dispose() {
_timer.cancel();
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _fadeAnimation as Animation<double>,
child: Center(
child: Material(
color: Colors.transparent,
child: widget.child,
),
),
);
}
}

49
lib/utils/utils.dart

@ -0,0 +1,49 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter_puzzle/utils/toast.dart';
class Utils {
Utils.privateConstructor();
static final Utils instance = Utils.privateConstructor();
factory Utils() {
return instance;
}
void showToast(BuildContext? context, String? txt, {bool isError = true, ToastGravity gravity = ToastGravity.top}) {
try {
if (context == null) return;
if (txt == null || txt.isEmpty) {
if (isError == false) {
return;
}
txt = 'Error';
}
FToast fToast = FToast();
fToast.init(context);
Widget toast = Container(
padding: const EdgeInsetsDirectional.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: isError ? Colors.red : Colors.lightBlue,
),
child: Text(
txt,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
);
fToast.showToast(child: toast, gravity: gravity, toastDuration: const Duration(milliseconds: 1500));
} catch (e) {
assert(() {
if (kDebugMode) {
print(e);
}
return true;
}());
}
}
}

29
lib/widgets/puzzle_widgets/timer_test_screen.dart

@ -1,29 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/providers.dart';
class TimerTestScreen extends ConsumerWidget {
const TimerTestScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(timerNotifierProvider);
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(state),
ElevatedButton(
onPressed: () => ref.read(timerNotifierProvider.notifier).startTimer(),
child: const Text('Start timer'),
),
ElevatedButton(
onPressed: () => ref.read(timerNotifierProvider.notifier).stopTimer(),
child: const Text('Stop timer'),
),
],
),
);
}
}

3
lib/widgets/solo_screen/count_down_timer_widget.dart

@ -33,6 +33,9 @@ class _CountDownTimerWidgetState extends State<CountDownTimerWidget> {
}
void stopTimer() {
if (_countdownTimer == null) {
return;
}
setState(() => _countdownTimer!.cancel());
}

12
lib/widgets/solo_screen/game_button_widget.dart

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/cubits/count_down_timer_cubit.dart';
import 'package:my_flutter_puzzle/models/puzzle_data.dart';
import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
@ -49,15 +51,7 @@ class GameButtonWidget extends StatelessWidget {
padding: padding,
width: width,
),
current: (puzzleData) => PuzzleGameButton(
text: 'Restart',
onTap: () {
ref.read(timerNotifierProvider.notifier).stopTimer();
ref.read(puzzleNotifierProvider(_solverClient).notifier).restartPuzzle();
},
padding: padding,
width: width,
),
current: (puzzleData) => const SizedBox(),
computingSolution: (puzzleData) => PuzzleGameButton(
text: 'Processing...',
onTap: null,

16
lib/widgets/solo_screen/game_button_widget/puzzle_game_button.dart

@ -28,24 +28,14 @@ class PuzzleGameButton extends ConsumerWidget {
(Set<MaterialState> states) {
if (states.contains(MaterialState.pressed)) {
return Theme.of(context).colorScheme.primary.withOpacity(0.5);
} else if (states.contains(MaterialState.disabled)) {
}
if (states.contains(MaterialState.disabled)) {
return Theme.of(context).colorScheme.primary.withOpacity(0.5);
}
return Theme.of(context)
.colorScheme
.primary; // Use the component's default.
return Theme.of(context).colorScheme.primary;
},
),
),
// style: ElevatedButton.styleFrom(
// onPrimary: Palette.blue,
// onSurface: Palette.blue,
// primary: Palette.blue,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(30),
// ),
// ),
onPressed: onTap,
child: Padding(
padding: padding,

Loading…
Cancel
Save