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. 225
      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. 22
      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. 85
      lib/widgets/solo_screen/countdown_widget.dart
  36. 4
      lib/widgets/solo_screen/moves_tiles_widget.dart
  37. 97
      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/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.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';
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';
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 start() => emit(BaseCubitType(eventName: CountDownTimerState.start));
void stop() => emit(BaseCubitType(eventName: CountDownTimerState.stop));
void reset() => emit(BaseCubitType(eventName: CountDownTimerState.reset));
}
enum CountDownTimerState {
empty,
start,
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/res/palette.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/utils/color_brightness.dart';
import 'package:my_flutter_puzzle/utils/extensions/string_extensions.dart';
import 'package:url_strategy/url_strategy.dart';
void main() async {
@ -26,6 +28,12 @@ class MyApp extends StatelessWidget {
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
Level level = Level(
image: 'level-1'.jpgPath,
duration: 4,
puzzleSize: 3,
level: 1,
);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
return BlocProvider(
create: (context) => CountDownTimerCubit(),
@ -50,7 +58,12 @@ class MyApp extends StatelessWidget {
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
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();
}
@ -51,7 +90,9 @@ class _LevelListScreenState extends State<LevelListScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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(
'Levels',
style: TextStyle(
@ -103,6 +144,7 @@ class _LevelListScreenState extends State<LevelListScreen> {
duration: level.duration,
puzzleSize: level.puzzleSize,
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.riveController,
required this.duration,
required this.level,
Key? key,
}) : super(key: key);
@ -26,6 +27,7 @@ class PhotoScreen extends ConsumerStatefulWidget {
final PuzzleData initialPuzzleData;
final int duration;
final int puzzleSize;
final int level;
final RiveAnimationController riveController;
@override
@ -50,7 +52,8 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
@override
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
ref.listen(puzzleNotifierProvider(_solverClient),
(previous, PuzzleState next) {
if (next is PuzzleSolved) {
BlocProvider.of<CountDownTimerCubit>(context).stop();
}
@ -67,7 +70,8 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
onSecondary: Theme.of(context).colorScheme.onSecondary,
error: Theme.of(context).colorScheme.error,
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,
surface: Theme.of(context).colorScheme.surface,
onSurface: Theme.of(context).colorScheme.onSurface,
@ -88,6 +92,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
data: _themeData,
child: PhotoScreenLarge(
solverClient: _solverClient,
level: widget.level,
initialPuzzleData: _initialPuzzleData,
duration: widget.duration,
puzzleSize: _puzzleSize,
@ -105,7 +110,8 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
onSecondary: Theme.of(context).colorScheme.onSecondary,
error: Theme.of(context).colorScheme.error,
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,
surface: Theme.of(context).colorScheme.surface,
onSurface: Theme.of(context).colorScheme.onSurface,
@ -117,6 +123,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
puzzleSize: _puzzleSize,
level: widget.level,
riveController: _riveController,
duration: widget.duration,
),
@ -128,6 +135,7 @@ class _PhotoScreenState extends ConsumerState<PhotoScreen> {
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
duration: widget.duration,
level: widget.level,
puzzleSize: _puzzleSize,
riveController: _riveController,
),

225
lib/screens/photo/photo_screen_large.dart

@ -1,12 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/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/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/utils.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.riveController,
required this.duration,
required this.level,
Key? key,
}) : super(key: key);
@ -32,10 +36,12 @@ class PhotoScreenLarge extends ConsumerStatefulWidget {
final PuzzleData initialPuzzleData;
final int puzzleSize;
final int duration;
final int level;
final RiveAnimationController riveController;
@override
ConsumerState<ConsumerStatefulWidget> createState() => _SoloScreenLargeState();
ConsumerState<ConsumerStatefulWidget> createState() =>
_SoloScreenLargeState();
}
class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
@ -65,7 +71,8 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
@override
Widget build(BuildContext context) {
ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) {
ref.listen(puzzleNotifierProvider(_solverClient),
(previous, PuzzleState next) {
if (next is PuzzleInitializing) {
setState(() {
_isStartPressed = true;
@ -83,9 +90,11 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
_previousImage = next.image;
_previousPalette = next.palette;
});
ref.read(puzzleNotifierProvider(_solverClient).notifier).initializePuzzle(
initialPuzzleData: _initialPuzzleData,
);
ref
.read(puzzleNotifierProvider(_solverClient).notifier)
.initializePuzzle(
initialPuzzleData: _initialPuzzleData,
);
}
});
@ -95,101 +104,127 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
return true;
},
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor: const Color(0xff4400CE),
body: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: Column(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: context.width * 30 / 812,
vertical: context.height * 20 / 375,
),
child: Row(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Visibility(
visible: !_isStartPressed,
child: ImageViewer(
puzzleSize: _puzzleSize,
previousImage: _previousImage,
previousPalette: _previousPalette,
imageSize: 200,
),
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: 32),
MovesTilesWidget(solverClient: _solverClient, fontSize: 16),
const SizedBox(height: 32),
],
),
),
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: 21),
ImageViewer(
puzzleSize: _puzzleSize,
previousImage: _previousImage,
previousPalette: _previousPalette,
imageSize: 200,
),
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;
MovesTilesWidget(
solverClient: _solverClient,
fontSize: 16,
),
],
),
SizedBox(width: context.width * 60 / 812),
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CountDownTimerWidget(
duration: widget.duration,
finishCallback: _finishTime,
),
const SizedBox(height: 23),
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(
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),
],
),
);
},
),
],
),
),
),
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: Stack(
SizedBox(width: context.width * 38 / 812),
Stack(
alignment: Alignment.center,
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(
mainAxisSize: MainAxisSize.max,
@ -199,7 +234,8 @@ class _SoloScreenLargeState extends ConsumerState<PhotoScreenLarge> {
CountdownWidget(
isStartPressed: _isStartPressed,
onFinish: () {
BlocProvider.of<CountDownTimerCubit>(context).start();
BlocProvider.of<CountDownTimerCubit>(context)
.start();
setState(() {
_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 String image;
final int duration;
final int level;
const PuzzleStarterScreen({
Key? key,
required this.duration,
required this.puzzleSize,
required this.image,
required this.level,
}) : super(key: key);
@override
@ -57,6 +59,7 @@ class _SoloScreenState extends ConsumerState<PuzzleStarterScreen> {
child: PhotoScreen(
solverClient: _solverClient,
initialPuzzleData: _initialPuzzleData,
level: widget.level,
duration: widget.duration,
puzzleSize: _puzzleSize,
riveController: _riveController,

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

@ -1,7 +1,6 @@
import 'dart:async';
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/utils/extensions/string_extensions.dart';

6
lib/utils/extensions/string_extensions.dart

@ -1,6 +1,10 @@
extension StringExtension on String {
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 {

10
lib/utils/image_splitter.dart

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

11
lib/utils/puzzle_solver.dart

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

22
lib/widgets/photo_screen/image_viewer.dart

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

15
lib/widgets/solo_screen/animated_dash.dart

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

85
lib/widgets/solo_screen/countdown_widget.dart

@ -21,54 +21,51 @@ class CountdownWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
return Visibility(
visible: _isStartPressed,
child: SizedBox(
width: 250.0,
child: DefaultTextStyle(
style: const TextStyle(
fontSize: 150.0,
color: Colors.white,
fontFamily: 'GoogleSans',
),
child: AnimatedTextKit(
isRepeatingAnimation: false,
pause: const Duration(milliseconds: 0),
onFinished: _onFinish,
animatedTexts: [
RotateAnimatedText(
'3',
transitionHeight: 100 * 2.5,
textStyle: const TextStyle(
color: Colors.white,
),
duration: Duration(milliseconds: _kInitialSpeed),
child: DefaultTextStyle(
style: const TextStyle(
fontSize: 80,
color: Colors.white,
fontFamily: 'GoogleSans',
),
child: AnimatedTextKit(
isRepeatingAnimation: false,
pause: const Duration(milliseconds: 0),
onFinished: _onFinish,
animatedTexts: [
RotateAnimatedText(
'3',
transitionHeight: 100 * 2.5,
textStyle: const TextStyle(
color: Colors.white,
),
RotateAnimatedText(
'2',
transitionHeight: 100 * 2.5,
textStyle: const TextStyle(
color: Colors.white,
),
duration: Duration(milliseconds: _kInitialSpeed),
duration: Duration(milliseconds: _kInitialSpeed),
),
RotateAnimatedText(
'2',
transitionHeight: 100 * 2.5,
textStyle: const TextStyle(
color: Colors.white,
),
RotateAnimatedText(
'1',
transitionHeight: 100 * 2.5,
textStyle: const TextStyle(
color: Colors.white,
),
duration: Duration(milliseconds: _kInitialSpeed),
duration: Duration(milliseconds: _kInitialSpeed),
),
RotateAnimatedText(
'1',
transitionHeight: 100 * 2.5,
textStyle: const TextStyle(
color: Colors.white,
),
RotateAnimatedText(
'GO!',
textStyle: const TextStyle(
fontSize: 120.0,
color: Colors.white,
),
transitionHeight: 120 * 2.5,
duration: Duration(milliseconds: _kInitialSpeed),
duration: Duration(milliseconds: _kInitialSpeed),
),
RotateAnimatedText(
'GO!',
textStyle: const TextStyle(
fontSize: 60,
color: Colors.white,
),
],
),
transitionHeight: 120 * 2.5,
duration: Duration(milliseconds: _kInitialSpeed),
),
],
),
),
);

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:my_flutter_puzzle/providers.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 {
const MovesTilesWidget({
@ -21,7 +20,6 @@ class MovesTilesWidget extends StatelessWidget {
return Consumer(
builder: (context, ref, child) {
final state = ref.watch(puzzleNotifierProvider(_solverClient));
return state.when(
() => MovesTilesText(
moves: 0,

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

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:my_flutter_puzzle/utils/extensions/context_extension.dart';
class MovesTilesText extends StatelessWidget {
final int moves;
@ -14,30 +15,86 @@ class MovesTilesText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RichText(
text: TextSpan(
style: TextStyle(
fontSize: fontSize,
// fontWeight: FontWeight.w500,
color: Colors.white,
),
children: [
TextSpan(
text: moves.toString(),
style: const TextStyle(
fontWeight: FontWeight.w600,
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,
),
),
const TextSpan(text: ' Moves | '),
TextSpan(
text: tiles.toString(),
style: const TextStyle(
fontWeight: FontWeight.w600,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Tiles',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
Text(
'$tiles',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
),
),
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,
),
),
const TextSpan(text: ' Tiles'),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Move',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
Text(
'$moves',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
),
),
],
);
}
}

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

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

2
pubspec.yaml

@ -36,6 +36,8 @@ flutter:
assets:
- assets/rive/dash.riv
- assets/images/
- assets/images/jpg/
- assets/images/png/
- assets/images/svg/
fonts:
- 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