Browse Source

finish work

master
mohsen zamani 2 years ago
parent
commit
ff1fdc72c4
  1. BIN
      assets/images/jpg/level-1.jpg
  2. BIN
      assets/images/jpg/level-2.jpg
  3. BIN
      assets/images/jpg/level-3.jpg
  4. BIN
      assets/images/jpg/level-4.jpg
  5. BIN
      assets/images/jpg/level-5.jpg
  6. BIN
      assets/images/jpg/level-6.jpg
  7. BIN
      assets/images/jpg/level-7.jpg
  8. BIN
      assets/images/jpg/level-8.jpg
  9. BIN
      assets/images/level_1_image.png
  10. BIN
      assets/images/level_2_image.png
  11. BIN
      assets/images/level_4_image.png
  12. BIN
      assets/images/png/avatar.png
  13. 0
      assets/images/png/default_image.png
  14. 0
      assets/images/png/level_screen_image.png
  15. 0
      assets/images/png/puzzle_side_image.png
  16. 0
      assets/images/png/splash_screen_image.png
  17. 0
      assets/images/png/start.png
  18. 3
      assets/images/svg/flash.svg
  19. 4
      assets/images/svg/refresh.svg
  20. 3
      assets/images/svg/timer.svg
  21. 3
      lib/application/notifiers/image_splitter_notifier.dart
  22. 10
      lib/cubits/count_down_timer_cubit.dart
  23. 15
      lib/main.dart
  24. 62
      lib/screens/level_list/screen/level_list_screen.dart
  25. 14
      lib/screens/photo/photo_screen.dart
  26. 123
      lib/screens/photo/photo_screen_large.dart
  27. 3
      lib/screens/puzzle/puzzle_starter_screen.dart
  28. 1
      lib/screens/splash/screen/splash_screen.dart
  29. 6
      lib/utils/extensions/string_extensions.dart
  30. 10
      lib/utils/image_splitter.dart
  31. 11
      lib/utils/puzzle_solver.dart
  32. 12
      lib/widgets/photo_screen/image_viewer.dart
  33. 15
      lib/widgets/solo_screen/animated_dash.dart
  34. 19
      lib/widgets/solo_screen/count_down_timer_widget.dart
  35. 7
      lib/widgets/solo_screen/countdown_widget.dart
  36. 4
      lib/widgets/solo_screen/moves_tiles_widget.dart
  37. 83
      lib/widgets/solo_screen/moves_tiles_widget/moves_tiles_text.dart
  38. 4
      lib/widgets/solo_screen/puzzle_widget/puzzle_board.dart
  39. 2
      pubspec.yaml
  40. 13
      test/my_queue_test.dart
  41. 16
      test/puzzle_solver_test.dart
  42. 29
      test/widget_test.dart

BIN
assets/images/jpg/level-1.jpg

After

Width: 600  |  Height: 600  |  Size: 71 KiB

BIN
assets/images/jpg/level-2.jpg

After

Width: 600  |  Height: 600  |  Size: 67 KiB

BIN
assets/images/jpg/level-3.jpg

After

Width: 600  |  Height: 600  |  Size: 89 KiB

BIN
assets/images/jpg/level-4.jpg

After

Width: 600  |  Height: 600  |  Size: 68 KiB

BIN
assets/images/jpg/level-5.jpg

After

Width: 600  |  Height: 600  |  Size: 84 KiB

BIN
assets/images/jpg/level-6.jpg

After

Width: 600  |  Height: 600  |  Size: 66 KiB

BIN
assets/images/jpg/level-7.jpg

After

Width: 600  |  Height: 600  |  Size: 59 KiB

BIN
assets/images/jpg/level-8.jpg

After

Width: 600  |  Height: 600  |  Size: 70 KiB

BIN
assets/images/level_1_image.png

Before

Width: 70  |  Height: 70  |  Size: 10 KiB

BIN
assets/images/level_2_image.png

Before

Width: 70  |  Height: 71  |  Size: 11 KiB

BIN
assets/images/level_4_image.png

Before

Width: 70  |  Height: 71  |  Size: 13 KiB

BIN
assets/images/png/avatar.png

After

Width: 126  |  Height: 251  |  Size: 37 KiB

0
assets/images/default_image.png → assets/images/png/default_image.png

Before

Width: 1006  |  Height: 1007  |  Size: 1.2 MiB

After

Width: 1006  |  Height: 1007  |  Size: 1.2 MiB

0
assets/images/level_screen_image.png → assets/images/png/level_screen_image.png

Before

Width: 214  |  Height: 296  |  Size: 54 KiB

After

Width: 214  |  Height: 296  |  Size: 54 KiB

0
assets/images/puzzle_side_image.png → assets/images/png/puzzle_side_image.png

Before

Width: 1061  |  Height: 1400  |  Size: 791 KiB

After

Width: 1061  |  Height: 1400  |  Size: 791 KiB

0
assets/images/splash_screen_image.png → assets/images/png/splash_screen_image.png

Before

Width: 266  |  Height: 209  |  Size: 45 KiB

After

Width: 266  |  Height: 209  |  Size: 45 KiB

0
assets/images/start.png → assets/images/png/start.png

Before

Width: 63  |  Height: 63  |  Size: 1.8 KiB

After

Width: 63  |  Height: 63  |  Size: 1.8 KiB

3
assets/images/svg/flash.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8.669" height="15.172" viewBox="0 0 8.669 15.172">
<path id="Icon_ionic-ios-flash" data-name="Icon ionic-ios-flash" d="M17.4,8.748H13.738L15.512,2.42a.137.137,0,0,0-.244-.115L9.049,10.461a.286.286,0,0,0,.213.454h3.663l-1.774,6.328a.137.137,0,0,0,.244.115l6.219-8.153A.289.289,0,0,0,17.4,8.748Z" transform="translate(-8.996 -2.246)" fill="#fa6400"/>
</svg>

4
assets/images/svg/refresh.svg

@ -0,0 +1,4 @@
<svg id="icon-refresh" xmlns="http://www.w3.org/2000/svg" width="12.871" height="12.995" viewBox="0 0 12.871 12.995">
<path id="Path" d="M1.609,5.785a4.877,4.877,0,0,1,7.995-3l-.629.629a.54.54,0,0,0,.381.921h2.474a.54.54,0,0,0,.54-.54V1.325a.54.54,0,0,0-.921-.381l-.7.7A6.488,6.488,0,0,0,.011,5.549a.817.817,0,0,0,.8.948A.817.817,0,0,0,1.609,5.785Z" transform="translate(0 0)" fill="#ff9700"/>
<path id="Path-2" data-name="Path" d="M11.563,0a.817.817,0,0,0-.8.712,4.877,4.877,0,0,1-7.995,3L3.4,3.079a.54.54,0,0,0-.381-.921H.54A.54.54,0,0,0,0,2.7V5.172a.54.54,0,0,0,.921.381l.7-.7A6.488,6.488,0,0,0,12.36.949a.817.817,0,0,0-.8-.949Z" transform="translate(0.5 6.497)" fill="#ff9700"/>
</svg>

3
assets/images/svg/timer.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="13.763" height="17.144" viewBox="0 0 13.763 17.144">
<path id="Icon_material-timer" data-name="Icon material-timer" d="M13.342.5H8.921V1.974h4.421ZM10.394,11.25h1.474V7.408H10.394Zm5.917-4.87,1.046-1.046a8.14,8.14,0,0,0-1.039-1.039L15.272,5.341A6.631,6.631,0,1,0,16.311,6.38Zm-5.18,9.291a5.158,5.158,0,1,1,5.158-5.158A5.154,5.154,0,0,1,11.131,15.671Z" transform="translate(-4.25 -0.25)" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="0.5"/>
</svg>

3
lib/application/notifiers/image_splitter_notifier.dart

@ -1,10 +1,7 @@
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.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/image_splitter_state.dart';
import 'package:my_flutter_puzzle/res/strings.dart';
import 'package:my_flutter_puzzle/utils/image_splitter.dart'; import 'package:my_flutter_puzzle/utils/image_splitter.dart';
class ImageSplitterNotifier extends StateNotifier<ImageSplitterState> { class ImageSplitterNotifier extends StateNotifier<ImageSplitterState> {

10
lib/cubits/count_down_timer_cubit.dart

@ -2,17 +2,25 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:my_flutter_puzzle/cubits/base_cubit_type.dart'; import 'package:my_flutter_puzzle/cubits/base_cubit_type.dart';
class CountDownTimerCubit extends Cubit<BaseCubitType<CountDownTimerState>> { class CountDownTimerCubit extends Cubit<BaseCubitType<CountDownTimerState>> {
CountDownTimerCubit() : super(BaseCubitType(eventName: CountDownTimerState.empty));
CountDownTimerCubit()
: super(
BaseCubitType(
eventName: CountDownTimerState.empty,
),
);
void empty() => emit(BaseCubitType(eventName: CountDownTimerState.empty)); void empty() => emit(BaseCubitType(eventName: CountDownTimerState.empty));
void start() => emit(BaseCubitType(eventName: CountDownTimerState.start)); void start() => emit(BaseCubitType(eventName: CountDownTimerState.start));
void stop() => emit(BaseCubitType(eventName: CountDownTimerState.stop)); void stop() => emit(BaseCubitType(eventName: CountDownTimerState.stop));
void reset() => emit(BaseCubitType(eventName: CountDownTimerState.reset));
} }
enum CountDownTimerState { enum CountDownTimerState {
empty, empty,
start, start,
stop, stop,
reset,
} }

15
lib/main.dart

@ -5,8 +5,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/cubits/count_down_timer_cubit.dart'; import 'package:my_flutter_puzzle/cubits/count_down_timer_cubit.dart';
import 'package:my_flutter_puzzle/res/palette.dart'; import 'package:my_flutter_puzzle/res/palette.dart';
import 'package:my_flutter_puzzle/screens/level_list/screen/level_list_screen.dart'; import 'package:my_flutter_puzzle/screens/level_list/screen/level_list_screen.dart';
import 'package:my_flutter_puzzle/screens/puzzle/puzzle_starter_screen.dart';
import 'package:my_flutter_puzzle/screens/splash/screen/splash_screen.dart'; import 'package:my_flutter_puzzle/screens/splash/screen/splash_screen.dart';
import 'package:my_flutter_puzzle/utils/color_brightness.dart'; import 'package:my_flutter_puzzle/utils/color_brightness.dart';
import 'package:my_flutter_puzzle/utils/extensions/string_extensions.dart';
import 'package:url_strategy/url_strategy.dart'; import 'package:url_strategy/url_strategy.dart';
void main() async { void main() async {
@ -26,6 +28,12 @@ class MyApp extends StatelessWidget {
DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight, DeviceOrientation.landscapeRight,
]); ]);
Level level = Level(
image: 'level-1'.jpgPath,
duration: 4,
puzzleSize: 3,
level: 1,
);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
return BlocProvider( return BlocProvider(
create: (context) => CountDownTimerCubit(), create: (context) => CountDownTimerCubit(),
@ -50,7 +58,12 @@ class MyApp extends StatelessWidget {
onSurface: Colors.white38, onSurface: Colors.white38,
), ),
), ),
home: const SplashScreen(),
home: PuzzleStarterScreen(
duration: level.duration,
puzzleSize: level.puzzleSize,
image: level.image,
level: level.level,
),
), ),
); );
} }

62
lib/screens/level_list/screen/level_list_screen.dart

@ -18,15 +18,54 @@ class _LevelListScreenState extends State<LevelListScreen> {
@override @override
void initState() { void initState() {
_levelList.add(Level(image: 'level_1_image'.pngPath, duration: 4, puzzleSize: 3, level: 1));
_levelList.add(Level(image: 'level_1_image'.pngPath, duration: 4, puzzleSize: 3, level: 2));
_levelList.add(Level(image: 'level_1_image'.pngPath, duration: 4, puzzleSize: 3, level: 3));
_levelList.add(Level(image: 'level_2_image'.pngPath, duration: 6, puzzleSize: 4, level: 4));
_levelList.add(Level(image: 'level_2_image'.pngPath, duration: 6, puzzleSize: 4, level: 5));
_levelList.add(Level(image: 'level_2_image'.pngPath, duration: 6, puzzleSize: 4, level: 6));
_levelList.add(Level(image: 'level_4_image'.pngPath, duration: 8, puzzleSize: 5, level: 7));
_levelList.add(Level(image: 'level_4_image'.pngPath, duration: 8, puzzleSize: 5, level: 8));
_levelList.add(Level(image: 'level_4_image'.pngPath, duration: 8, puzzleSize: 5, level: 9));
_levelList.add(Level(
image: 'level-1'.jpgPath,
duration: 4,
puzzleSize: 3,
level: 1,
));
_levelList.add(Level(
image: 'level-2'.jpgPath,
duration: 4,
puzzleSize: 3,
level: 2,
));
_levelList.add(Level(
image: 'level-3'.jpgPath,
duration: 4,
puzzleSize: 3,
level: 3,
));
_levelList.add(Level(
image: 'level-4'.jpgPath,
duration: 4,
puzzleSize: 3,
level: 4,
));
_levelList.add(Level(
image: 'level-5'.jpgPath,
duration: 4,
puzzleSize: 3,
level: 5,
));
_levelList.add(Level(
image: 'level-6'.jpgPath,
duration: 4,
puzzleSize: 3,
level: 6,
));
_levelList.add(Level(
image: 'level-7'.jpgPath,
duration: 6,
puzzleSize: 4,
level: 7,
));
_levelList.add(Level(
image: 'level-8'.jpgPath,
duration: 6,
puzzleSize: 4,
level: 8,
));
super.initState(); super.initState();
} }
@ -51,7 +90,9 @@ class _LevelListScreenState extends State<LevelListScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( Padding(
padding: EdgeInsets.only(top: context.height * 33 / 540, bottom: context.height * 12 / 540),
padding: EdgeInsets.only(
top: context.height * 33 / 540,
bottom: context.height * 12 / 540),
child: const Text( child: const Text(
'Levels', 'Levels',
style: TextStyle( style: TextStyle(
@ -103,6 +144,7 @@ class _LevelListScreenState extends State<LevelListScreen> {
duration: level.duration, duration: level.duration,
puzzleSize: level.puzzleSize, puzzleSize: level.puzzleSize,
image: level.image, image: level.image,
level: level.level,
); );
}, },
), ),

14
lib/screens/photo/photo_screen.dart

@ -19,6 +19,7 @@ class PhotoScreen extends ConsumerStatefulWidget {
required this.puzzleSize, required this.puzzleSize,
required this.riveController, required this.riveController,
required this.duration, required this.duration,
required this.level,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -26,6 +27,7 @@ class PhotoScreen extends ConsumerStatefulWidget {
final PuzzleData initialPuzzleData; final PuzzleData initialPuzzleData;
final int duration; final int duration;
final int puzzleSize; final int puzzleSize;
final int level;
final RiveAnimationController riveController; final RiveAnimationController riveController;
@override @override
@ -50,7 +52,8 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
ref.listen(puzzleNotifierProvider(_solverClient),
(previous, PuzzleState next) {
if (next is PuzzleSolved) { if (next is PuzzleSolved) {
BlocProvider.of<CountDownTimerCubit>(context).stop(); BlocProvider.of<CountDownTimerCubit>(context).stop();
} }
@ -67,7 +70,8 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
onSecondary: Theme.of(context).colorScheme.onSecondary, onSecondary: Theme.of(context).colorScheme.onSecondary,
error: Theme.of(context).colorScheme.error, error: Theme.of(context).colorScheme.error,
onError: Theme.of(context).colorScheme.onError, onError: Theme.of(context).colorScheme.onError,
background: next.palette.darkMutedColor?.color ?? Palette.blue.darken(0.3),
background:
next.palette.darkMutedColor?.color ?? Palette.blue.darken(0.3),
onBackground: Colors.white, onBackground: Colors.white,
surface: Theme.of(context).colorScheme.surface, surface: Theme.of(context).colorScheme.surface,
onSurface: Theme.of(context).colorScheme.onSurface, onSurface: Theme.of(context).colorScheme.onSurface,
@ -88,6 +92,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
data: _themeData, data: _themeData,
child: PhotoScreenLarge( child: PhotoScreenLarge(
solverClient: _solverClient, solverClient: _solverClient,
level: widget.level,
initialPuzzleData: _initialPuzzleData, initialPuzzleData: _initialPuzzleData,
duration: widget.duration, duration: widget.duration,
puzzleSize: _puzzleSize, puzzleSize: _puzzleSize,
@ -105,7 +110,8 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
onSecondary: Theme.of(context).colorScheme.onSecondary, onSecondary: Theme.of(context).colorScheme.onSecondary,
error: Theme.of(context).colorScheme.error, error: Theme.of(context).colorScheme.error,
onError: Theme.of(context).colorScheme.onError, onError: Theme.of(context).colorScheme.onError,
background: palette.darkMutedColor?.color ?? Palette.blue.darken(0.3),
background:
palette.darkMutedColor?.color ?? Palette.blue.darken(0.3),
onBackground: Colors.white, onBackground: Colors.white,
surface: Theme.of(context).colorScheme.surface, surface: Theme.of(context).colorScheme.surface,
onSurface: Theme.of(context).colorScheme.onSurface, onSurface: Theme.of(context).colorScheme.onSurface,
@ -117,6 +123,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
solverClient: _solverClient, solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData, initialPuzzleData: _initialPuzzleData,
puzzleSize: _puzzleSize, puzzleSize: _puzzleSize,
level: widget.level,
riveController: _riveController, riveController: _riveController,
duration: widget.duration, duration: widget.duration,
), ),
@ -128,6 +135,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
solverClient: _solverClient, solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData, initialPuzzleData: _initialPuzzleData,
duration: widget.duration, duration: widget.duration,
level: widget.level,
puzzleSize: _puzzleSize, puzzleSize: _puzzleSize,
riveController: _riveController, riveController: _riveController,
), ),

123
lib/screens/photo/photo_screen_large.dart

@ -1,12 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:my_flutter_puzzle/application/states/image_splitter_state.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/application/states/puzzle_state.dart';
import 'package:my_flutter_puzzle/cubits/count_down_timer_cubit.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/models/puzzle_data.dart';
import 'package:my_flutter_puzzle/providers.dart'; import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/res/puzzle_constants.dart'; import 'package:my_flutter_puzzle/res/puzzle_constants.dart';
import 'package:my_flutter_puzzle/utils/extensions/context_extension.dart';
import 'package:my_flutter_puzzle/utils/extensions/string_extensions.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'package:my_flutter_puzzle/utils/utils.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/photo_screen/image_viewer.dart';
@ -25,6 +28,7 @@ class PhotoScreenLarge extends ConsumerStatefulWidget {
required this.puzzleSize, required this.puzzleSize,
required this.riveController, required this.riveController,
required this.duration, required this.duration,
required this.level,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -32,10 +36,12 @@ class PhotoScreenLarge extends ConsumerStatefulWidget {
final PuzzleData initialPuzzleData; final PuzzleData initialPuzzleData;
final int puzzleSize; final int puzzleSize;
final int duration; final int duration;
final int level;
final RiveAnimationController riveController; final RiveAnimationController riveController;
@override @override
ConsumerState<ConsumerStatefulWidget> createState() => _SoloScreenLargeState();
ConsumerState<ConsumerStatefulWidget> createState() =>
_SoloScreenLargeState();
} }
class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> { class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
@ -65,7 +71,8 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
ref.listen(puzzleNotifierProvider(_solverClient),
(previous, PuzzleState next) {
if (next is PuzzleInitializing) { if (next is PuzzleInitializing) {
setState(() { setState(() {
_isStartPressed = true; _isStartPressed = true;
@ -83,7 +90,9 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
_previousImage = next.image; _previousImage = next.image;
_previousPalette = next.palette; _previousPalette = next.palette;
}); });
ref.read(puzzleNotifierProvider(_solverClient).notifier).initializePuzzle(
ref
.read(puzzleNotifierProvider(_solverClient).notifier)
.initializePuzzle(
initialPuzzleData: _initialPuzzleData, initialPuzzleData: _initialPuzzleData,
); );
} }
@ -95,52 +104,77 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
return true; return true;
}, },
child: Scaffold( child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor: const Color(0xff4400CE),
body: SafeArea( body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: context.width * 30 / 812,
vertical: context.height * 20 / 375,
),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: Column(
Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Visibility(
visible: !_isStartPressed,
child: ImageViewer(
Row(
children: [
SvgPicture.asset('flash'.svgPath),
const SizedBox(width: 8),
Text(
'Level ${widget.level}',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(width: 18),
GestureDetector(
onTap: _resetTimer,
child: Container(
width: context.width * 22 / 540,
height: context.width * 22 / 540,
padding: const EdgeInsets.all(5),
margin: const EdgeInsets.symmetric(vertical: 3),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: SvgPicture.asset('refresh'.svgPath),
),
),
],
),
const SizedBox(height: 21),
ImageViewer(
puzzleSize: _puzzleSize, puzzleSize: _puzzleSize,
previousImage: _previousImage, previousImage: _previousImage,
previousPalette: _previousPalette, previousPalette: _previousPalette,
imageSize: 200, imageSize: 200,
), ),
const SizedBox(height: 12),
MovesTilesWidget(
solverClient: _solverClient,
fontSize: 16,
), ),
const SizedBox(height: 32),
MovesTilesWidget(solverClient: _solverClient, fontSize: 16),
const SizedBox(height: 32),
], ],
), ),
),
SizedBox(width: context.width * 60 / 812),
SingleChildScrollView( SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
CountDownTimerWidget( CountDownTimerWidget(
duration: widget.duration, duration: widget.duration,
finishCallback: () {
if (!_puzzleSolved) {
Utils.instance.showToast(context, 'TimeOut');
Future.delayed(const Duration(milliseconds: 1500), () {
Navigator.pop(context);
});
}
},
finishCallback: _finishTime,
), ),
const SizedBox(height: 12),
const SizedBox(height: 23),
Consumer( Consumer(
builder: (context, ref, child) { builder: (context, ref, child) {
final state = ref.watch(imageSplitterNotifierProvider);
final state = ref.watch(
imageSplitterNotifierProvider,
);
return state.maybeWhen( return state.maybeWhen(
() => PuzzleWidget( () => PuzzleWidget(
solverClient: _solverClient, solverClient: _solverClient,
@ -178,18 +212,19 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
); );
}, },
), ),
const SizedBox(height: 30),
], ],
), ),
), ),
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: Stack(
SizedBox(width: context.width * 38 / 812),
Stack(
alignment: Alignment.center,
children: [ children: [
AnimatedDash(
boardSize: _boardSize * 0.8,
riveController: _riveController,
onInit: (_) => setState(() {}),
SizedBox(
width: context.width * 126 / 812,
height: context.height * 251 / 375,
child: Image(
image: AssetImage('avatar'.pngPath),
),
), ),
Column( Column(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@ -199,7 +234,8 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
CountdownWidget( CountdownWidget(
isStartPressed: _isStartPressed, isStartPressed: _isStartPressed,
onFinish: () { onFinish: () {
BlocProvider.of<CountDownTimerCubit>(context).start();
BlocProvider.of<CountDownTimerCubit>(context)
.start();
setState(() { setState(() {
_isStartPressed = false; _isStartPressed = false;
}); });
@ -210,11 +246,24 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
), ),
], ],
), ),
),
], ],
), ),
), ),
), ),
),
); );
} }
void _finishTime() {
if (!_puzzleSolved) {
Utils.instance.showToast(context, 'TimeOut');
Future.delayed(const Duration(milliseconds: 1500), () {
Navigator.pop(context);
});
}
}
void _resetTimer() {
BlocProvider.of<CountDownTimerCubit>(context).reset();
}
} }

3
lib/screens/puzzle/puzzle_starter_screen.dart

@ -11,12 +11,14 @@ class PuzzleStarterScreen extends ConsumerStatefulWidget {
final int puzzleSize; final int puzzleSize;
final String image; final String image;
final int duration; final int duration;
final int level;
const PuzzleStarterScreen({ const PuzzleStarterScreen({
Key? key, Key? key,
required this.duration, required this.duration,
required this.puzzleSize, required this.puzzleSize,
required this.image, required this.image,
required this.level,
}) : super(key: key); }) : super(key: key);
@override @override
@ -57,6 +59,7 @@ class _SoloScreenState extends ConsumerState<PuzzleStarterScreen> {
child: PhotoScreen( child: PhotoScreen(
solverClient: _solverClient, solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData, initialPuzzleData: _initialPuzzleData,
level: widget.level,
duration: widget.duration, duration: widget.duration,
puzzleSize: _puzzleSize, puzzleSize: _puzzleSize,
riveController: _riveController, riveController: _riveController,

1
lib/screens/splash/screen/splash_screen.dart

@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:my_flutter_puzzle/screens/level_list/screen/level_list_screen.dart'; import 'package:my_flutter_puzzle/screens/level_list/screen/level_list_screen.dart';
import 'package:my_flutter_puzzle/utils/extensions/string_extensions.dart'; import 'package:my_flutter_puzzle/utils/extensions/string_extensions.dart';

6
lib/utils/extensions/string_extensions.dart

@ -1,6 +1,10 @@
extension StringExtension on String { extension StringExtension on String {
String get pngPath { String get pngPath {
return 'assets/images/$this.png';
return 'assets/images/png/$this.png';
}
String get jpgPath {
return 'assets/images/jpg/$this.jpg';
} }
String get rivePath { String get rivePath {

10
lib/utils/image_splitter.dart

@ -1,23 +1,16 @@
import 'dart:developer';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image/image.dart' as image_lib; import 'package:image/image.dart' as image_lib;
import 'package:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
import 'package:tuple/tuple.dart';
class ImageSplitter { class ImageSplitter {
List<Image> splitImage(Map<String, dynamic> mapData) { List<Image> splitImage(Map<String, dynamic> mapData) {
List<int> input = mapData['input']; List<int> input = mapData['input'];
int size = mapData['size']; int size = mapData['size'];
// convert image to image from image package
image_lib.Image image = image_lib.decodeImage(input)!; image_lib.Image image = image_lib.decodeImage(input)!;
int x = 0, y = 0; int x = 0, y = 0;
int width = (image.width / size).round(); int width = (image.width / size).round();
int height = (image.height / size).round(); int height = (image.height / size).round();
// split image to parts
List<image_lib.Image> parts = []; List<image_lib.Image> parts = [];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) { for (int j = 0; j < size; j++) {
@ -38,7 +31,8 @@ class ImageSplitter {
} }
Future<PaletteGenerator> getImagePalette(ImageProvider imageProvider) async { Future<PaletteGenerator> getImagePalette(ImageProvider imageProvider) async {
final paletteGenerator = await PaletteGenerator.fromImageProvider(imageProvider);
final paletteGenerator =
await PaletteGenerator.fromImageProvider(imageProvider);
return paletteGenerator; return paletteGenerator;
} }

11
lib/utils/puzzle_solver.dart

@ -128,14 +128,6 @@ class PuzzleSolverClient {
return board; return board;
} }
void plainPrint(List<List<int>> b) {
for (var i in b) {
for (var j in i) {
print('$j ');
}
}
}
int nodeManhattan(List<List<int>> board) { int nodeManhattan(List<List<int>> board) {
int sum = 0; int sum = 0;
int n = board.length; int n = board.length;
@ -153,7 +145,6 @@ class PuzzleSolverClient {
int manhattan(List<List<int>> board, Set<int> goalNums) { int manhattan(List<List<int>> board, Set<int> goalNums) {
int sum = 0; int sum = 0;
int count = 0;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) { for (int j = 0; j < size; j++) {
if (goalNums.contains(board[i][j])) { if (goalNums.contains(board[i][j])) {
@ -310,7 +301,6 @@ class PuzzleSolverClient {
} }
List<Set<int>> goalStates = []; List<Set<int>> goalStates = [];
int count = 1;
String flag = 'A_STAR'; String flag = 'A_STAR';
if (flag == 'A_STAR') { if (flag == 'A_STAR') {
@ -330,7 +320,6 @@ class PuzzleSolverClient {
); );
queue.add(Tuple2<int, Node>(root.depth + hScaleFactor * root.heuristic, root)); queue.add(Tuple2<int, Node>(root.depth + hScaleFactor * root.heuristic, root));
while (queue.isNotEmpty) { while (queue.isNotEmpty) {
count += 1;
final node = queue.removeFirst().item2; final node = queue.removeFirst().item2;
if (isGoal(node.board, goalStates[currGoal])) { if (isGoal(node.board, goalStates[currGoal])) {
queue.clear(); queue.clear();

12
lib/widgets/photo_screen/image_viewer.dart

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/providers.dart'; import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/utils/extensions/context_extension.dart';
import 'package:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
class ImageViewer extends ConsumerWidget { class ImageViewer extends ConsumerWidget {
@ -24,18 +25,17 @@ class ImageViewer extends ConsumerWidget {
final state = ref.watch(imageSplitterNotifierProvider); final state = ref.watch(imageSplitterNotifierProvider);
return state.maybeWhen( return state.maybeWhen(
() => const SizedBox(), () => const SizedBox(),
complete: (image, images, palette) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(20),
complete: (image, images, palette) => SizedBox(
width: context.width * 202 / 812,
height: context.width * 202 / 812,
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image( child: Image(
image: image.image, image: image.image,
height: imageSize, height: imageSize,
width: imageSize, width: imageSize,
), ),
), ),
],
), ),
orElse: () => previousImage != null orElse: () => previousImage != null
? Padding( ? Padding(

15
lib/widgets/solo_screen/animated_dash.dart

@ -22,20 +22,7 @@ class AnimatedDash extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Align( return Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Padding(
padding: _padding,
// 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: _onInit,
// ),
// ),
),
child: Padding(padding: _padding),
); );
} }
} }

19
lib/widgets/solo_screen/count_down_timer_widget.dart

@ -2,8 +2,10 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:my_flutter_puzzle/cubits/base_cubit_type.dart'; import 'package:my_flutter_puzzle/cubits/base_cubit_type.dart';
import 'package:my_flutter_puzzle/cubits/count_down_timer_cubit.dart'; import 'package:my_flutter_puzzle/cubits/count_down_timer_cubit.dart';
import 'package:my_flutter_puzzle/utils/extensions/string_extensions.dart';
class CountDownTimerWidget extends StatefulWidget { class CountDownTimerWidget extends StatefulWidget {
final int duration; final int duration;
@ -41,7 +43,8 @@ class _CountDownTimerWidgetState extends State<CountDownTimerWidget> {
void resetTimer() { void resetTimer() {
stopTimer(); stopTimer();
setState(() => myDuration = const Duration(days: 5));
setState(() => myDuration = Duration(minutes: widget.duration));
startTimer();
} }
void setCountDown() { void setCountDown() {
@ -80,23 +83,25 @@ class _CountDownTimerWidgetState extends State<CountDownTimerWidget> {
_cubit.empty(); _cubit.empty();
}); });
break; break;
case CountDownTimerState.reset:
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
resetTimer();
_cubit.empty();
});
break;
} }
return Row( return Row(
children: [ children: [
Text( Text(
'$hours:$minutes:$seconds', '$hours:$minutes:$seconds',
style: const TextStyle( style: const TextStyle(
fontSize: 30,
fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.white, color: Colors.white,
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
const Icon(
Icons.timer,
color: Colors.white,
size: 30,
)
SvgPicture.asset('timer'.svgPath),
], ],
); );
}, },

7
lib/widgets/solo_screen/countdown_widget.dart

@ -21,11 +21,9 @@ class CountdownWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return Visibility( return Visibility(
visible: _isStartPressed, visible: _isStartPressed,
child: SizedBox(
width: 250.0,
child: DefaultTextStyle( child: DefaultTextStyle(
style: const TextStyle( style: const TextStyle(
fontSize: 150.0,
fontSize: 80,
color: Colors.white, color: Colors.white,
fontFamily: 'GoogleSans', fontFamily: 'GoogleSans',
), ),
@ -61,7 +59,7 @@ class CountdownWidget extends ConsumerWidget {
RotateAnimatedText( RotateAnimatedText(
'GO!', 'GO!',
textStyle: const TextStyle( textStyle: const TextStyle(
fontSize: 120.0,
fontSize: 60,
color: Colors.white, color: Colors.white,
), ),
transitionHeight: 120 * 2.5, transitionHeight: 120 * 2.5,
@ -70,7 +68,6 @@ class CountdownWidget extends ConsumerWidget {
], ],
), ),
), ),
),
); );
} }
} }

4
lib/widgets/solo_screen/moves_tiles_widget.dart

@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_flutter_puzzle/providers.dart'; import 'package:my_flutter_puzzle/providers.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
import 'moves_tiles_widget/moves_tiles_text.dart';
import 'package:my_flutter_puzzle/widgets/solo_screen/moves_tiles_widget/moves_tiles_text.dart';
class MovesTilesWidget extends StatelessWidget { class MovesTilesWidget extends StatelessWidget {
const MovesTilesWidget({ const MovesTilesWidget({
@ -21,7 +20,6 @@ class MovesTilesWidget extends StatelessWidget {
return Consumer( return Consumer(
builder: (context, ref, child) { builder: (context, ref, child) {
final state = ref.watch(puzzleNotifierProvider(_solverClient)); final state = ref.watch(puzzleNotifierProvider(_solverClient));
return state.when( return state.when(
() => MovesTilesText( () => MovesTilesText(
moves: 0, moves: 0,

83
lib/widgets/solo_screen/moves_tiles_widget/moves_tiles_text.dart

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:my_flutter_puzzle/utils/extensions/context_extension.dart';
class MovesTilesText extends StatelessWidget { class MovesTilesText extends StatelessWidget {
final int moves; final int moves;
@ -14,30 +15,86 @@ class MovesTilesText extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RichText(
text: TextSpan(
return Row(
children: [
Container(
height: context.height * 32 / 375,
width: context.width * 94 / 812,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
border: Border.all(color: const Color(0xff6236FF), width: 1),
borderRadius: BorderRadius.circular(26),
gradient: const LinearGradient(
colors: [
Color(0xff4824CB),
Color(0xff6236FF),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Tiles',
style: TextStyle( style: TextStyle(
fontSize: fontSize,
// fontWeight: FontWeight.w500,
color: Colors.white, color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
), ),
children: [
TextSpan(
text: moves.toString(),
),
Text(
'$tiles',
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.w600,
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
), ),
), ),
const TextSpan(text: ' Moves | '),
TextSpan(
text: tiles.toString(),
const SizedBox(width: 12),
Container(
height: context.height * 32 / 375,
width: context.width * 94 / 812,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
border: Border.all(color: const Color(0xff6236FF), width: 1),
borderRadius: BorderRadius.circular(26),
gradient: const LinearGradient(
colors: [
Color(0xff4824CB),
Color(0xff6236FF),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Move',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
Text(
'$moves',
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.w600,
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
), ),
), ),
const TextSpan(text: ' Tiles'),
], ],
), ),
),
],
); );
} }
} }

4
lib/widgets/solo_screen/puzzle_widget/puzzle_board.dart

@ -59,7 +59,7 @@ class PuzzleBoard extends ConsumerWidget {
elevation: 4, elevation: 4,
color: Theme.of(context).colorScheme.primary.withOpacity(isEnabled ? 1 : 0.5), color: Theme.of(context).colorScheme.primary.withOpacity(isEnabled ? 1 : 0.5),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(8),
), ),
child: SizedBox( child: SizedBox(
height: eachBoxSize, height: eachBoxSize,
@ -82,7 +82,7 @@ class PuzzleBoard extends ConsumerWidget {
child: Opacity( child: Opacity(
opacity: isEnabled ? 1 : 0.5, opacity: isEnabled ? 1 : 0.5,
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(8),
child: child:
images![int.parse(puzzleData.offsetMap.entries.toList()[i].key.toString()) - 1], images![int.parse(puzzleData.offsetMap.entries.toList()[i].key.toString()) - 1],
), ),

2
pubspec.yaml

@ -36,6 +36,8 @@ flutter:
assets: assets:
- assets/rive/dash.riv - assets/rive/dash.riv
- assets/images/ - assets/images/
- assets/images/jpg/
- assets/images/png/
- assets/images/svg/ - assets/images/svg/
fonts: fonts:
- family: GoogleSans - family: GoogleSans

13
test/my_queue_test.dart

@ -1,13 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:tuple/tuple.dart';
void main() {
test('queue test', () {
final queue = HeapPriorityQueue<Tuple2<int, int>>((a, b) => a.item1.compareTo(b.item1));
queue.add(const Tuple2(5, 515));
queue.add(const Tuple2(3, 245));
queue.add(const Tuple2(8, 645));
queue.add(const Tuple2(2, 345));
});
}

16
test/puzzle_solver_test.dart

@ -1,16 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:my_flutter_puzzle/utils/puzzle_solver.dart';
void main() {
test('solver logic test', () {
final solver = PuzzleSolverClient(size: 4);
final board = solver.createRandomBoard();
final boardStates = solver.runner(board);
if (boardStates != null) {
for (var board in boardStates) {
print(board);
}
}
});
}

29
test/widget_test.dart

@ -1,29 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_flutter_puzzle/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
Loading…
Cancel
Save