From 3b77ebda3d6b45dd6d302761dbc5914889b0fa87 Mon Sep 17 00:00:00 2001 From: mohsen zamani Date: Mon, 27 Feb 2023 00:04:02 +0330 Subject: [PATCH] fix show image bug add timer --- .../notifiers/image_splitter_notifier.dart | 8 +- lib/cubits/base_cubit_type.dart | 6 ++ lib/cubits/count_down_timer_cubit.dart | 18 ++++ lib/main.dart | 51 +++++---- lib/screens/photo/photo_screen_large.dart | 71 ++++++------ lib/screens/puzzle/puzzle_starter_screen.dart | 12 +++ .../solo_screen/count_down_timer_widget.dart | 102 ++++++++++++++++++ lib/widgets/solo_screen/countdown_widget.dart | 1 - .../solo_screen/game_button_widget.dart | 16 +-- pubspec.lock | 32 ++++++ pubspec.yaml | 1 + 11 files changed, 235 insertions(+), 83 deletions(-) create mode 100644 lib/cubits/base_cubit_type.dart create mode 100644 lib/cubits/count_down_timer_cubit.dart create mode 100644 lib/widgets/solo_screen/count_down_timer_widget.dart diff --git a/lib/application/notifiers/image_splitter_notifier.dart b/lib/application/notifiers/image_splitter_notifier.dart index a16ea3c..a029899 100644 --- a/lib/application/notifiers/image_splitter_notifier.dart +++ b/lib/application/notifiers/image_splitter_notifier.dart @@ -13,18 +13,18 @@ class ImageSplitterNotifier extends StateNotifier { ImageSplitterNotifier(this._splitter) : super(const ImageSplitterState()); - void getInitialImages({required int puzzleSize}) async { + void getInitialImages({required int puzzleSize, required String imagePath}) async { state = const ImageSplitterState.generating(); try { - final data = await rootBundle.load(defaultImagePath); + final data = await rootBundle.load(imagePath); final imageBytes = data.buffer.asUint8List(); final _splitter = ImageSplitter(); - final palette = await _splitter.getImagePalette(const AssetImage(defaultImagePath)); + final palette = await _splitter.getImagePalette(AssetImage(imagePath)); final images = await _splitter.runSplitterIsolate(imageBytes, puzzleSize); - state = ImageSplitterState.complete(Image.asset(defaultImagePath), images, palette); + state = ImageSplitterState.complete(Image.asset(imagePath), images, palette); } catch (e) { state = ImageSplitterState.error(message: e.toString()); } diff --git a/lib/cubits/base_cubit_type.dart b/lib/cubits/base_cubit_type.dart new file mode 100644 index 0000000..cae3095 --- /dev/null +++ b/lib/cubits/base_cubit_type.dart @@ -0,0 +1,6 @@ +class BaseCubitType { + BaseCubitType({this.eventName, this.data}); + + T? eventName; + dynamic data; +} diff --git a/lib/cubits/count_down_timer_cubit.dart b/lib/cubits/count_down_timer_cubit.dart new file mode 100644 index 0000000..22de9c3 --- /dev/null +++ b/lib/cubits/count_down_timer_cubit.dart @@ -0,0 +1,18 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:my_flutter_puzzle/cubits/base_cubit_type.dart'; + +class CountDownTimerCubit extends Cubit> { + 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)); +} + +enum CountDownTimerState { + empty, + start, + stop, +} diff --git a/lib/main.dart b/lib/main.dart index daa2141..cc4788e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.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/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/utils/color_brightness.dart'; -import 'package:my_flutter_puzzle/utils/extensions/string_extensions.dart'; import 'package:url_strategy/url_strategy.dart'; void main() async { @@ -25,31 +25,30 @@ class MyApp extends StatelessWidget { DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight, ]); - return MaterialApp( - title: 'Flutter Puzzle', - debugShowCheckedModeBanner: false, - theme: ThemeData( - primarySwatch: Colors.blue, - fontFamily: 'GoogleSans', - backgroundColor: Palette.blue.darken(0.3), - colorScheme: ColorScheme( - brightness: Theme.of(context).brightness, - primary: Palette.blue, - onPrimary: Colors.white, - secondary: Palette.blue.withOpacity(0.6), - onSecondary: Palette.blue.withOpacity(0.3), - error: Theme.of(context).colorScheme.error, - onError: Theme.of(context).colorScheme.onError, - background: Palette.blue.darken(0.3), - onBackground: Colors.white, - surface: Palette.crimson, - onSurface: Colors.white38, + return BlocProvider( + create: (context) => CountDownTimerCubit(), + child: MaterialApp( + title: 'Flutter Puzzle', + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + fontFamily: 'GoogleSans', + backgroundColor: Palette.blue.darken(0.3), + colorScheme: ColorScheme( + brightness: Theme.of(context).brightness, + primary: Palette.blue, + onPrimary: Colors.white, + secondary: Palette.blue.withOpacity(0.6), + onSecondary: Palette.blue.withOpacity(0.3), + error: Theme.of(context).colorScheme.error, + onError: Theme.of(context).colorScheme.onError, + background: Palette.blue.darken(0.3), + onBackground: Colors.white, + surface: Palette.crimson, + onSurface: Colors.white38, + ), ), - ), - home: PuzzleStarterScreen( - duration: 3, - puzzleSize: 5, - image: 'default_image'.pngPath, + home: const LevelListScreen(), ), ); } diff --git a/lib/screens/photo/photo_screen_large.dart b/lib/screens/photo/photo_screen_large.dart index 697b540..9e03078 100644 --- a/lib/screens/photo/photo_screen_large.dart +++ b/lib/screens/photo/photo_screen_large.dart @@ -1,14 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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/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/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/count_down_timer_widget.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'; @@ -39,7 +41,10 @@ class _SoloScreenLargeState extends ConsumerState { bool _isStartPressed = false; final _imagePicker = ImagePicker(); - + final double _fontSize = 70.0; + final double _boardSize = 280.0; + final int _spacing = 4; + late double _eachBoxSize; List? _previousImages; Image? _previousImage; PaletteGenerator? _previousPalette; @@ -48,6 +53,7 @@ class _SoloScreenLargeState extends ConsumerState { void initState() { _solverClient = widget.solverClient; _puzzleSize = widget.puzzleSize; + _eachBoxSize = (_boardSize / _puzzleSize) - (_spacing * (_puzzleSize - 1)); _initialPuzzleData = widget.initialPuzzleData; _riveController = widget.riveController; super.initState(); @@ -73,12 +79,6 @@ class _SoloScreenLargeState extends ConsumerState { } }); - var fontSize = 70.0; - var boardSize = 280.0; - - var spacing = 4; - var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); - return Scaffold( backgroundColor: Theme.of(context).colorScheme.background, body: SafeArea( @@ -105,7 +105,11 @@ class _SoloScreenLargeState extends ConsumerState { child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - const TimerWidget(fontSize: 30), + // const TimerWidget(fontSize: 30), + CountDownTimerWidget( + duration: 3, + finishCallback: () {}, + ), const SizedBox(height: 12), Consumer( builder: (context, ref, child) { @@ -114,10 +118,10 @@ class _SoloScreenLargeState extends ConsumerState { return state.maybeWhen( () => PuzzleWidget( solverClient: _solverClient, - boardSize: boardSize, - eachBoxSize: eachBoxSize, + boardSize: _boardSize, + eachBoxSize: _eachBoxSize, initialPuzzleData: _initialPuzzleData, - fontSize: fontSize, + fontSize: _fontSize, images: _previousImages, kInitialSpeed: kInitialSpeed, ), @@ -128,20 +132,20 @@ class _SoloScreenLargeState extends ConsumerState { return PuzzleWidget( solverClient: _solverClient, - boardSize: boardSize, - eachBoxSize: eachBoxSize, + boardSize: _boardSize, + eachBoxSize: _eachBoxSize, initialPuzzleData: _initialPuzzleData, - fontSize: fontSize, + fontSize: _fontSize, images: images, kInitialSpeed: kInitialSpeed, ); }, orElse: () => PuzzleWidget( solverClient: _solverClient, - boardSize: boardSize, - eachBoxSize: eachBoxSize, + boardSize: _boardSize, + eachBoxSize: _eachBoxSize, initialPuzzleData: _initialPuzzleData, - fontSize: fontSize, + fontSize: _fontSize, images: _previousImages, kInitialSpeed: kInitialSpeed, ), @@ -157,7 +161,7 @@ class _SoloScreenLargeState extends ConsumerState { child: Stack( children: [ AnimatedDash( - boardSize: boardSize * 0.8, + boardSize: _boardSize * 0.8, riveController: _riveController, onInit: (_) => setState(() {}), ), @@ -169,7 +173,8 @@ class _SoloScreenLargeState extends ConsumerState { CountdownWidget( isStartPressed: _isStartPressed, onFinish: () { - ref.read(timerNotifierProvider.notifier).startTimer(); + // ref.read(timerNotifierProvider.notifier).startTimer(); + BlocProvider.of(context).start(); setState(() { _isStartPressed = false; }); @@ -178,25 +183,12 @@ class _SoloScreenLargeState extends ConsumerState { ), Visibility( visible: !_isStartPressed, - child: Column( - children: [ - ImageViewer( - imagePicker: _imagePicker, - puzzleSize: _puzzleSize, - previousImage: _previousImage, - previousPalette: _previousPalette, - imageSize: 200, - ), - PickImageButton( - text: 'Pick Image', - onTap: ref.read(imageSplitterNotifierProvider) is ImageSplitterComplete - ? () => ref.read(imageSplitterNotifierProvider.notifier).generateImages( - picker: _imagePicker, - puzzleSize: _puzzleSize, - ) - : null, - ), - ], + child: ImageViewer( + imagePicker: _imagePicker, + puzzleSize: _puzzleSize, + previousImage: _previousImage, + previousPalette: _previousPalette, + imageSize: 200, ), ), ], @@ -204,7 +196,6 @@ class _SoloScreenLargeState extends ConsumerState { ], ), ), - // SizedBox(), ], ), ), diff --git a/lib/screens/puzzle/puzzle_starter_screen.dart b/lib/screens/puzzle/puzzle_starter_screen.dart index 933ab82..739835e 100644 --- a/lib/screens/puzzle/puzzle_starter_screen.dart +++ b/lib/screens/puzzle/puzzle_starter_screen.dart @@ -1,5 +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'; @@ -35,6 +36,17 @@ class _SoloScreenState extends ConsumerState { _riveController = SimpleAnimation('idle'); _solverClient = PuzzleSolverClient(size: _puzzleSize); _initialPuzzleData = ref.read(puzzleNotifierProvider(_solverClient).notifier).generateInitialPuzzle(); + final state = ref.read(imageSplitterNotifierProvider); + if (state is! ImageSplitterComplete) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + ref.read(imageSplitterNotifierProvider.notifier).getInitialImages( + puzzleSize: _puzzleSize, + imagePath: widget.image, + ); + }); + } + + ref.read(puzzleTypeNotifierProvider.notifier).changeToPhoto(); super.initState(); } diff --git a/lib/widgets/solo_screen/count_down_timer_widget.dart b/lib/widgets/solo_screen/count_down_timer_widget.dart new file mode 100644 index 0000000..be6b452 --- /dev/null +++ b/lib/widgets/solo_screen/count_down_timer_widget.dart @@ -0,0 +1,102 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:my_flutter_puzzle/cubits/base_cubit_type.dart'; +import 'package:my_flutter_puzzle/cubits/count_down_timer_cubit.dart'; + +class CountDownTimerWidget extends StatefulWidget { + final int duration; + final Function() finishCallback; + + const CountDownTimerWidget({Key? key, required this.duration, required this.finishCallback}) : super(key: key); + + @override + State createState() => _CountDownTimerWidgetState(); +} + +class _CountDownTimerWidgetState extends State { + Timer? _countdownTimer; + Duration? myDuration; + late final CountDownTimerCubit _cubit; + + @override + void initState() { + _cubit = BlocProvider.of(context); + myDuration = Duration(minutes: widget.duration); + super.initState(); + } + + void startTimer() { + myDuration = Duration(minutes: widget.duration); + _countdownTimer = Timer.periodic(const Duration(seconds: 1), (_) => setCountDown()); + } + + void stopTimer() { + setState(() => _countdownTimer!.cancel()); + } + + void resetTimer() { + stopTimer(); + setState(() => myDuration = const Duration(days: 5)); + } + + void setCountDown() { + const reduceSecondsBy = 1; + setState(() { + final seconds = myDuration!.inSeconds - reduceSecondsBy; + if (seconds < 0) { + widget.finishCallback.call(); + _countdownTimer!.cancel(); + } else { + myDuration = Duration(seconds: seconds); + } + }); + } + + @override + Widget build(BuildContext context) { + String strDigits(int n) => n.toString().padLeft(2, '0'); + final hours = strDigits(myDuration!.inHours.remainder(24)); + final minutes = strDigits(myDuration!.inMinutes.remainder(60)); + final seconds = strDigits(myDuration!.inSeconds.remainder(60)); + return BlocBuilder>( + builder: (context, state) { + switch (state.eventName!) { + case CountDownTimerState.empty: + break; + case CountDownTimerState.start: + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + startTimer(); + _cubit.empty(); + }); + break; + case CountDownTimerState.stop: + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + stopTimer(); + _cubit.empty(); + }); + break; + } + return Row( + children: [ + Text( + '$hours:$minutes:$seconds', + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(width: 8), + const Icon( + Icons.timer, + color: Colors.white, + size: 30, + ) + ], + ); + }, + ); + } +} diff --git a/lib/widgets/solo_screen/countdown_widget.dart b/lib/widgets/solo_screen/countdown_widget.dart index 7c15370..37254db 100644 --- a/lib/widgets/solo_screen/countdown_widget.dart +++ b/lib/widgets/solo_screen/countdown_widget.dart @@ -30,7 +30,6 @@ class CountdownWidget extends ConsumerWidget { fontFamily: 'GoogleSans', ), child: AnimatedTextKit( - // totalRepeatCount: 1, isRepeatingAnimation: false, pause: const Duration(milliseconds: 0), onFinished: _onFinish, diff --git a/lib/widgets/solo_screen/game_button_widget.dart b/lib/widgets/solo_screen/game_button_widget.dart index e985f55..b311f04 100644 --- a/lib/widgets/solo_screen/game_button_widget.dart +++ b/lib/widgets/solo_screen/game_button_widget.dart @@ -31,9 +31,7 @@ class GameButtonWidget extends StatelessWidget { return state.when( () => PuzzleGameButton( text: 'Start Game', - onTap: () => ref - .read(puzzleNotifierProvider(_solverClient).notifier) - .initializePuzzle( + onTap: () => ref.read(puzzleNotifierProvider(_solverClient).notifier).initializePuzzle( initialPuzzleData: _initialPuzzleData, ), padding: padding, @@ -55,9 +53,7 @@ class GameButtonWidget extends StatelessWidget { text: 'Restart', onTap: () { ref.read(timerNotifierProvider.notifier).stopTimer(); - ref - .read(puzzleNotifierProvider(_solverClient).notifier) - .restartPuzzle(); + ref.read(puzzleNotifierProvider(_solverClient).notifier).restartPuzzle(); }, padding: padding, width: width, @@ -76,9 +72,7 @@ class GameButtonWidget extends StatelessWidget { ), solved: (puzzleData) => PuzzleGameButton( text: 'Start Game', - onTap: () => ref - .read(puzzleNotifierProvider(_solverClient).notifier) - .initializePuzzle( + onTap: () => ref.read(puzzleNotifierProvider(_solverClient).notifier).initializePuzzle( initialPuzzleData: puzzleData, ), padding: padding, @@ -86,9 +80,7 @@ class GameButtonWidget extends StatelessWidget { ), error: (_) => PuzzleGameButton( text: 'Start Game', - onTap: () => ref - .read(puzzleNotifierProvider(_solverClient).notifier) - .initializePuzzle( + onTap: () => ref.read(puzzleNotifierProvider(_solverClient).notifier).initializePuzzle( initialPuzzleData: _initialPuzzleData, ), padding: padding, diff --git a/pubspec.lock b/pubspec.lock index 929745f..0c6a249 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" + url: "https://pub.dev" + source: hosted + version: "8.1.1" boolean_selector: dependency: transitive description: @@ -254,6 +262,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775" + url: "https://pub.dev" + source: hosted + version: "8.1.2" flutter_lints: dependency: "direct dev" description: @@ -488,6 +504,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_config: dependency: transitive description: @@ -608,6 +632,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" pub_semver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6128abe..d355e70 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: palette_generator: ^0.3.3 path_provider: ^2.0.8 image_picker: ^0.8.4+5 + flutter_bloc: ^8.1.1 font_awesome_flutter: ^9.2.0 crop: ^0.5.2