From 757c0f867d8ba0d2176dc91866b6a3598da98ce6 Mon Sep 17 00:00:00 2001 From: ahmed Date: Fri, 23 Aug 2024 18:47:37 +0300 Subject: [PATCH 01/23] create SortingPage --- lib/features/sorting/view/sorting_page.dart | 81 +++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 lib/features/sorting/view/sorting_page.dart diff --git a/lib/features/sorting/view/sorting_page.dart b/lib/features/sorting/view/sorting_page.dart new file mode 100644 index 0000000..c6c5ae9 --- /dev/null +++ b/lib/features/sorting/view/sorting_page.dart @@ -0,0 +1,81 @@ +import 'package:algorithm_visualizer/core/helpers/random_int.dart'; +import 'package:algorithm_visualizer/core/resources/strings_manager.dart'; +import 'package:algorithm_visualizer/core/resources/theme_manager.dart'; +import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_dialog.dart'; +import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_icon.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class SortingPage extends StatelessWidget { + const SortingPage({super.key}); + + @override + Widget build(BuildContext context) { + final list = CustomRandom.generateRandomList(150, 70); + + return Scaffold( + appBar: AppBar( + elevation: 1, + actions: [ + Consumer( + builder: (context, ref, _) { + return TextButton( + onPressed: () { + CustomAlertDialog(context).solidDialog( + parameters: [ + ListDialogParameters( + text: StringsManager.generateMaze, + onTap: () {}, + ), + ListDialogParameters( + text: "Dijkstra", + onTap: () {}, + ), + ListDialogParameters( + text: "BFS", + onTap: () {}, + ), + ListDialogParameters( + text: StringsManager.clearPath, + color: ThemeEnum.redColor, + onTap: () {}, + ), + ListDialogParameters( + text: StringsManager.clearAll, + color: ThemeEnum.redColor, + onTap: () {}, + ), + ], + ); + }, + child: const CustomIcon(Icons.menu_rounded), + ); + }, + ), + ], + ), + body: Padding( + padding: REdgeInsets.only(top: 15), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: List.generate( + list.length, + (index) => Flexible( + child: Container( + margin: REdgeInsets.symmetric(horizontal: 1), + height: (1 + (list[index])).toDouble(), + decoration: BoxDecoration( + color: context.getColor(ThemeEnum.darkBlueColor), + borderRadius: const BorderRadius.vertical( + top: Radius.circular(1), + ), + ), + ), + ), + ), + ), + ), + ); + } +} From bc811c466c2a4f36814ddf55e393b5d603d64322 Mon Sep 17 00:00:00 2001 From: ahmed Date: Fri, 23 Aug 2024 18:48:22 +0300 Subject: [PATCH 02/23] create CustomRandom --- lib/core/helpers/random_int.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 lib/core/helpers/random_int.dart diff --git a/lib/core/helpers/random_int.dart b/lib/core/helpers/random_int.dart new file mode 100644 index 0000000..3c6a971 --- /dev/null +++ b/lib/core/helpers/random_int.dart @@ -0,0 +1,13 @@ +import 'dart:math'; + +class CustomRandom { + static List generateRandomList(int maxNum, int length) { + if (length > maxNum + 1) length = maxNum; + + List numbers = List.generate(maxNum + 1, (index) => index); + + numbers.shuffle(Random()); + + return numbers.sublist(0, length); + } +} \ No newline at end of file From 4423a24ed0aaf8cbdf4c0ce41dcbf55c462a73c8 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 08:33:47 +0300 Subject: [PATCH 03/23] refactoring grid page --- .../{grid => searching}/view/grid_page.dart | 106 +++++++++--------- 1 file changed, 55 insertions(+), 51 deletions(-) rename lib/features/{grid => searching}/view/grid_page.dart (70%) diff --git a/lib/features/grid/view/grid_page.dart b/lib/features/searching/view/grid_page.dart similarity index 70% rename from lib/features/grid/view/grid_page.dart rename to lib/features/searching/view/grid_page.dart index f697958..06f82c5 100644 --- a/lib/features/grid/view/grid_page.dart +++ b/lib/features/searching/view/grid_page.dart @@ -1,15 +1,17 @@ import 'package:algorithm_visualizer/core/resources/color_manager.dart'; import 'package:algorithm_visualizer/core/resources/strings_manager.dart'; +import 'package:algorithm_visualizer/core/resources/theme_manager.dart'; import 'package:algorithm_visualizer/core/widgets/adaptive/text/adaptive_text.dart'; +import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_dialog.dart'; import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_icon.dart'; -import 'package:algorithm_visualizer/features/grid/view_model/grid_notifier.dart'; +import 'package:algorithm_visualizer/features/searching/view_model/grid_notifier.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; part '../widgets/searcher_grid.dart'; -final gridNotifierProvider = StateNotifierProvider( - (ref) => GridNotifierCubit(), +final _gridNotifierProvider = StateNotifierProvider( + (ref) => SearchingNotifier(), ); BorderSide _borderSide([bool isWhite = false]) => @@ -24,8 +26,8 @@ BorderDirectional _thineVerticalBorder() => BorderDirectional( bottom: _borderSide(true), ); -class GridPage extends StatelessWidget { - const GridPage({super.key}); +class SearchingPage extends StatelessWidget { + const SearchingPage({super.key}); @override Widget build(BuildContext context) { @@ -36,42 +38,44 @@ class GridPage extends StatelessWidget { builder: (context, ref, _) { return TextButton( onPressed: () { - ref.read(gridNotifierProvider.notifier).generateMaze(); + CustomAlertDialog(context).solidDialog( + parameters: [ + ListDialogParameters( + text: StringsManager.generateMaze, + onTap: () { + ref.read(_gridNotifierProvider.notifier).generateMaze(); + }, + ), + ListDialogParameters( + text: "Dijkstra", + onTap: () { + ref.read(_gridNotifierProvider.notifier).performDijkstra(); + }, + ), + ListDialogParameters( + text: "BFS", + onTap: () { + ref.read(_gridNotifierProvider.notifier).performBFS(); + }, + ), + ListDialogParameters( + text: StringsManager.clearPath, + color: ThemeEnum.redColor, + onTap: () { + ref.read(_gridNotifierProvider.notifier).clearTheGrid(keepWall: true); + }, + ), + ListDialogParameters( + text: StringsManager.clearAll, + color: ThemeEnum.redColor, + onTap: () { + ref.read(_gridNotifierProvider.notifier).clearTheGrid(); + }, + ), + ], + ); }, - child: const RegularText(StringsManager.generateMaze), - ); - }, - ), - Consumer( - builder: (context, ref, _) { - return TextButton( - onPressed: () { - ref.read(gridNotifierProvider.notifier).performDijkstra(); - }, - child: const RegularText("Dijkstra"), - ); - }, - ), - Consumer( - builder: (context, ref, _) { - return TextButton( - onPressed: () { - ref.read(gridNotifierProvider.notifier).performBFS(); - }, - child: const RegularText("BFS"), - ); - }, - ), - Consumer( - builder: (context, ref, _) { - return TextButton( - onLongPress: () { - ref.read(gridNotifierProvider.notifier).clearTheGrid(keepWall: true); - }, - onPressed: () { - ref.read(gridNotifierProvider.notifier).clearTheGrid(); - }, - child: const RegularText(StringsManager.clear), + child: const CustomIcon(Icons.menu_rounded), ); }, ), @@ -112,7 +116,7 @@ class _BuildLayoutState extends ConsumerState<_BuildLayout> { } void _updateLayout() { - ref.read(gridNotifierProvider.notifier).updateGridLayout(widget.size); + ref.read(_gridNotifierProvider.notifier).updateGridLayout(widget.size); } @override @@ -126,14 +130,14 @@ class _BuildGridItems extends ConsumerWidget { @override Widget build(BuildContext context, ref) { - final gridCount = ref.watch(gridNotifierProvider.select((it) => it.gridCount)); - final watchColumnCrossAxisCount = ref.watch(gridNotifierProvider.select((it) => it.columnCrossAxisCount)); + final gridCount = ref.watch(_gridNotifierProvider.select((it) => it.gridCount)); + final watchColumnCrossAxisCount = ref.watch(_gridNotifierProvider.select((it) => it.columnCrossAxisCount)); if (gridCount == 0) { return const Center(child: MediumText(StringsManager.notInitializeGridYet)); } - final read = ref.read(gridNotifierProvider.notifier); + final read = ref.read(_gridNotifierProvider.notifier); return Listener( onPointerDown: read.onPointerDownOnGrid, @@ -165,7 +169,7 @@ class _Square extends ConsumerStatefulWidget { class _SquareState extends ConsumerState<_Square> { @override Widget build(BuildContext context) { - final isSelected = ref.watch(gridNotifierProvider.select((it) => it.gridData[widget.index])); + final isSelected = ref.watch(_gridNotifierProvider.select((it) => it.gridData[widget.index])); final isColored = isSelected != GridStatus.empty; final showBorder = isSelected != GridStatus.empty && @@ -178,7 +182,7 @@ class _SquareState extends ConsumerState<_Square> { ), child: AnimatedScale( scale: isColored ? 1.0 : 0.1, - duration: GridNotifierCubit.scaleAppearDurationForWall, + duration: SearchingNotifier.scaleAppearDurationForWall, curve: Curves.elasticOut, child: Builder( builder: (context) { @@ -210,7 +214,7 @@ class _PathGrid extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, ref, _) { - final size = ref.watch(gridNotifierProvider.select((it) => it.gridSize)); + final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); return Container( width: size, @@ -229,7 +233,7 @@ class _WallGrid extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, ref, _) { - final size = ref.watch(gridNotifierProvider.select((it) => it.gridSize)); + final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); return Container( width: size, @@ -248,7 +252,7 @@ class _StartPointGrid extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, ref, _) { - final size = ref.watch(gridNotifierProvider.select((it) => it.gridSize)); + final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); return Container( width: size, @@ -274,7 +278,7 @@ class _TargetPointGrid extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, ref, _) { - final size = ref.watch(gridNotifierProvider.select((it) => it.gridSize)); + final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); return Container( width: size, @@ -323,7 +327,7 @@ class _DefaultGrid extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, ref, _) { - final size = ref.watch(gridNotifierProvider.select((it) => it.gridSize)); + final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); return Container( width: size, From bfa4014a95a68216e498672b88f648c34ef8204b Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:02:23 +0300 Subject: [PATCH 04/23] create SortingNotifier --- .../sorting/view_model/sorting_notifier.dart | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 lib/features/sorting/view_model/sorting_notifier.dart diff --git a/lib/features/sorting/view_model/sorting_notifier.dart b/lib/features/sorting/view_model/sorting_notifier.dart new file mode 100644 index 0000000..021a105 --- /dev/null +++ b/lib/features/sorting/view_model/sorting_notifier.dart @@ -0,0 +1,81 @@ +import 'package:algorithm_visualizer/core/helpers/screen_size.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +part 'sorting_state.dart'; + +class SortableItem { + final int id; + final int value; + + SortableItem(this.id, this.value); +} + +class SortingNotifier extends StateNotifier { + SortingNotifier() + : super(SortingNotifierState( + list: List.generate(_listSize, (index) => SortableItem(index, index + 1))..shuffle(), + )) { + _initializePositions(); + } + + static const int _listSize = 50; + static double maxListItemHeight = 250.h; + static double itemsPadding = 1.w; + static const Duration swipeDuration = Duration(milliseconds: 5); + + static double calculateItemWidth(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + final availableWidth = screenWidth - (itemsPadding * (_listSize - 1)); + + // Ensure a positive width + return availableWidth / _listSize > 0 ? availableWidth / _listSize : 1.0; + } + + static double calculateItemHeight(int itemIndex) { + final value = (maxListItemHeight / _listSize) * (itemIndex + 1); + return value.h; + } + + static Color getColor(int itemIndex) { + double step = (itemIndex * 2) / 100; + return Colors.indigo.withOpacity(step); + } + + void _initializePositions() { + final positions = {}; + final itemWidth = calculateItemWidth(ScreenSize.context!); + + for (int i = 0; i < state.list.length; i++) { + positions[state.list[i].id] = Offset(i * (itemWidth + itemsPadding), 0); + } + state = state.copyWith(positions: positions); + } + + Future bubbleSort() async { + final list = List.from(state.list); + for (int i = 0; i < list.length - 1; i++) { + for (int j = 0; j < list.length - i - 1; j++) { + if (list[j].value > list[j + 1].value) { + // Swap items in the list + final temp = list[j]; + list[j] = list[j + 1]; + list[j + 1] = temp; + + // Update positions for swap + final positions = Map.from(state.positions); + final tempPosition = positions[list[j].id]!; + positions[list[j].id] = positions[list[j + 1].id]!; + positions[list[j + 1].id] = tempPosition; + + // Update state + state = state.copyWith(list: list, positions: positions); + + // Delay for the animation to complete + await Future.delayed(swipeDuration); + } + } + } + } +} From aab0f17bf7bd1227bc1d11b764743066a81f7e12 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:02:35 +0300 Subject: [PATCH 05/23] create SortingNotifierState --- .../sorting/view_model/sorting_state.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 lib/features/sorting/view_model/sorting_state.dart diff --git a/lib/features/sorting/view_model/sorting_state.dart b/lib/features/sorting/view_model/sorting_state.dart new file mode 100644 index 0000000..de0f6f5 --- /dev/null +++ b/lib/features/sorting/view_model/sorting_state.dart @@ -0,0 +1,18 @@ +part of 'sorting_notifier.dart'; + +class SortingNotifierState { + final List list; + final Map positions; + + SortingNotifierState({required this.list, this.positions = const {}}); + + SortingNotifierState copyWith({ + List? list, + Map? positions, + }) { + return SortingNotifierState( + list: list ?? this.list, + positions: positions ?? this.positions, + ); + } +} From d23f728dd41aba00257d26031b3124a453278bd3 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:02:51 +0300 Subject: [PATCH 06/23] handle grid page --- lib/features/searching/view/grid_page.dart | 114 +++++++++------------ 1 file changed, 46 insertions(+), 68 deletions(-) diff --git a/lib/features/searching/view/grid_page.dart b/lib/features/searching/view/grid_page.dart index 06f82c5..e86f58e 100644 --- a/lib/features/searching/view/grid_page.dart +++ b/lib/features/searching/view/grid_page.dart @@ -131,7 +131,8 @@ class _BuildGridItems extends ConsumerWidget { @override Widget build(BuildContext context, ref) { final gridCount = ref.watch(_gridNotifierProvider.select((it) => it.gridCount)); - final watchColumnCrossAxisCount = ref.watch(_gridNotifierProvider.select((it) => it.columnCrossAxisCount)); + final watchColumnCrossAxisCount = + ref.watch(_gridNotifierProvider.select((it) => it.columnCrossAxisCount)); if (gridCount == 0) { return const Center(child: MediumText(StringsManager.notInitializeGridYet)); @@ -212,16 +213,8 @@ class _PathGrid extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, ref, _) { - final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); - - return Container( - width: size, - height: size, - decoration: const BoxDecoration(color: ColorManager.light2Yellow), - ); - }, + return const _WidgetSize( + decoration: BoxDecoration(color: ColorManager.light2Yellow), ); } } @@ -231,16 +224,8 @@ class _WallGrid extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, ref, _) { - final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); - - return Container( - width: size, - height: size, - decoration: const BoxDecoration(color: ColorManager.wallBlack), - ); - }, + return const _WidgetSize( + decoration: BoxDecoration(color: ColorManager.wallBlack), ); } } @@ -250,23 +235,15 @@ class _StartPointGrid extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, ref, _) { - final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); - - return Container( - width: size, - height: size, - decoration: const BoxDecoration(shape: BoxShape.circle), - child: const FittedBox( - child: CustomIcon( - Icons.arrow_forward_ios_rounded, - size: 50, - color: ColorManager.darkPurple, - ), - ), - ); - }, + return const _WidgetSize( + decoration: BoxDecoration(shape: BoxShape.circle), + child: FittedBox( + child: CustomIcon( + Icons.arrow_forward_ios_rounded, + size: 50, + color: ColorManager.darkPurple, + ), + ), ); } } @@ -276,27 +253,19 @@ class _TargetPointGrid extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, ref, _) { - final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); - - return Container( - width: size, - height: size, - decoration: const BoxDecoration(shape: BoxShape.circle), - child: const FittedBox( - child: _Circle( - radius: 30, - backgroundColor: ColorManager.darkPurple, - child: _Circle( - radius: 20, - backgroundColor: ColorManager.white, - child: _Circle(radius: 12, backgroundColor: ColorManager.darkPurple), - ), - ), + return const _WidgetSize( + decoration: BoxDecoration(shape: BoxShape.circle), + child: FittedBox( + child: _Circle( + radius: 30, + backgroundColor: ColorManager.darkPurple, + child: _Circle( + radius: 20, + backgroundColor: ColorManager.white, + child: _Circle(radius: 12, backgroundColor: ColorManager.darkPurple), ), - ); - }, + ), + ), ); } } @@ -325,16 +294,25 @@ class _DefaultGrid extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, ref, _) { - final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); - - return Container( - width: size, - height: size, - decoration: const BoxDecoration(color: ColorManager.transparent), - ); - }, + return const _WidgetSize( + decoration: BoxDecoration(color: ColorManager.transparent), + ); + } +} + +class _WidgetSize extends ConsumerWidget { + const _WidgetSize({this.child, this.decoration}); + final Widget? child; + final Decoration? decoration; + @override + Widget build(BuildContext context, ref) { + final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); + + return Container( + width: size, + height: size, + decoration: decoration, + child: child, ); } } From e279ba32302df696cfc692ef954ce1dccb497347 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:03:03 +0300 Subject: [PATCH 07/23] create BasePage --- lib/features/base/view/base_page.dart | 56 +++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 lib/features/base/view/base_page.dart diff --git a/lib/features/base/view/base_page.dart b/lib/features/base/view/base_page.dart new file mode 100644 index 0000000..ab2bf9d --- /dev/null +++ b/lib/features/base/view/base_page.dart @@ -0,0 +1,56 @@ +import 'package:algorithm_visualizer/config/routes/route_app.dart'; +import 'package:algorithm_visualizer/core/extensions/navigators.dart'; +import 'package:algorithm_visualizer/core/helpers/screen_size.dart'; +import 'package:algorithm_visualizer/core/resources/strings_manager.dart'; +import 'package:algorithm_visualizer/core/resources/theme_manager.dart'; +import 'package:algorithm_visualizer/core/widgets/adaptive/text/adaptive_text.dart'; +import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_rounded_elevated_button.dart'; +import 'package:flutter/material.dart'; + +class BasePage extends StatefulWidget { + const BasePage({super.key}); + + @override + State createState() => _BasePageState(); +} + +class _BasePageState extends State { + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScreenSize.initContext(context); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + CustomRoundedElevatedButton( + roundedRadius: 3, + backgroundColor: ThemeEnum.whiteD5Color, + child: const RegularText(StringsManager.searching), + onPressed: () { + context.pushTo(Routes.searching); + }, + ), + CustomRoundedElevatedButton( + roundedRadius: 3, + backgroundColor: ThemeEnum.whiteD5Color, + child: const RegularText(StringsManager.sorting), + onPressed: () { + context.pushTo(Routes.sorting); + }, + ), + ], + ), + ), + ), + ); + } +} From 05d05dfff21c8deeb7bb429e6c7b06188a249ad0 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:03:21 +0300 Subject: [PATCH 08/23] create ScreenSize --- lib/core/helpers/screen_size.dart | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 lib/core/helpers/screen_size.dart diff --git a/lib/core/helpers/screen_size.dart b/lib/core/helpers/screen_size.dart new file mode 100644 index 0000000..a0030bf --- /dev/null +++ b/lib/core/helpers/screen_size.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class ScreenSize { + static BuildContext? context; + static initContext(BuildContext? ctx) { + context = ctx; + } +} From ec88b7b898b72431a5e16590b2ef008e127b1d37 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:03:30 +0300 Subject: [PATCH 09/23] add some texts --- lib/core/resources/strings_manager.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/resources/strings_manager.dart b/lib/core/resources/strings_manager.dart index 17e7532..b706121 100644 --- a/lib/core/resources/strings_manager.dart +++ b/lib/core/resources/strings_manager.dart @@ -24,6 +24,10 @@ class StringsManager { static const String unknownPage = "Unknown page"; static const String notInitializeGridYet = "Not initialize grid yet."; static const String clear = "Clear"; + static const String clearAll = "Clear all"; + static const String clearPath = "Clear path"; static const String generateMaze = "Generate maze"; + static const String searching = "Searching"; + static const String sorting = "Sorting"; } From d40c81417def068a2141fbf2eac93f30219d52c7 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:03:40 +0300 Subject: [PATCH 10/23] create SortingPage --- lib/features/sorting/view/sorting_page.dart | 117 ++++++++++---------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/lib/features/sorting/view/sorting_page.dart b/lib/features/sorting/view/sorting_page.dart index c6c5ae9..06945dd 100644 --- a/lib/features/sorting/view/sorting_page.dart +++ b/lib/features/sorting/view/sorting_page.dart @@ -1,79 +1,84 @@ -import 'package:algorithm_visualizer/core/helpers/random_int.dart'; import 'package:algorithm_visualizer/core/resources/strings_manager.dart'; import 'package:algorithm_visualizer/core/resources/theme_manager.dart'; import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_dialog.dart'; import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_icon.dart'; +import 'package:algorithm_visualizer/features/sorting/view_model/sorting_notifier.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; +part '../widgets/sorting_app_bar.dart'; + +final _notifierProvider = StateNotifierProvider( + (ref) => SortingNotifier(), +); class SortingPage extends StatelessWidget { const SortingPage({super.key}); @override Widget build(BuildContext context) { - final list = CustomRandom.generateRandomList(150, 70); - return Scaffold( appBar: AppBar( elevation: 1, - actions: [ - Consumer( - builder: (context, ref, _) { - return TextButton( - onPressed: () { - CustomAlertDialog(context).solidDialog( - parameters: [ - ListDialogParameters( - text: StringsManager.generateMaze, - onTap: () {}, - ), - ListDialogParameters( - text: "Dijkstra", - onTap: () {}, - ), - ListDialogParameters( - text: "BFS", - onTap: () {}, - ), - ListDialogParameters( - text: StringsManager.clearPath, - color: ThemeEnum.redColor, - onTap: () {}, - ), - ListDialogParameters( - text: StringsManager.clearAll, - color: ThemeEnum.redColor, - onTap: () {}, - ), - ], - ); - }, - child: const CustomIcon(Icons.menu_rounded), - ); + title: Consumer(builder: (context, ref, _) { + return InkWell( + onTap: () { + ref.read(_notifierProvider.notifier).bubbleSort(); }, - ), - ], + child: const Text("Sort"), + ); + }), ), - body: Padding( - padding: REdgeInsets.only(top: 15), - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: List.generate( - list.length, - (index) => Flexible( - child: Container( - margin: REdgeInsets.symmetric(horizontal: 1), - height: (1 + (list[index])).toDouble(), - decoration: BoxDecoration( - color: context.getColor(ThemeEnum.darkBlueColor), - borderRadius: const BorderRadius.vertical( - top: Radius.circular(1), - ), - ), + body: Consumer(builder: (context, ref, _) { + final items = ref.watch(_notifierProvider).list; + + return Padding( + padding: const EdgeInsets.only(top: 15), + child: SizedBox( + height: SortingNotifier.maxListItemHeight*1.2, + width: double.infinity, + child: Stack( + alignment: AlignmentDirectional.bottomCenter, + children: List.generate( + items.length, + (index) { + final item = items[index]; + final position = + ref.watch(_notifierProvider.select((state) => state.positions[item.id]!)); + return AnimatedPositioned( + key: ValueKey(item.id), + left: position.dx, + bottom: position.dy, + duration: SortingNotifier.swipeDuration, + child: _BuildItem(item: item), + ); + }, ), ), ), + ); + }), + ); + } +} + +class _BuildItem extends ConsumerWidget { + const _BuildItem({required this.item}); + + final SortableItem item; + + @override + Widget build(BuildContext context, ref) { + final itemWidth = SortingNotifier.calculateItemWidth(context); + return Padding( + padding: EdgeInsets.symmetric(horizontal: SortingNotifier.itemsPadding / 2), + child: Container( + height: SortingNotifier.calculateItemHeight(item.value), + width: itemWidth, + decoration: BoxDecoration( + color: SortingNotifier.getColor(item.value), + borderRadius: const BorderRadius.vertical( + top: Radius.circular(1), + ), ), ), ); From c11dfe74d114a2909aad8691ffe19e58fa356508 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:04:03 +0300 Subject: [PATCH 11/23] reformat the code --- lib/core/helpers/random_int.dart | 2 +- .../widgets/adaptive/text/regular_text.dart | 2 +- .../widgets/searcher_grid.dart | 2 +- .../sorting/widgets/sorting_app_bar.dart | 44 +++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) rename lib/features/{grid => searching}/widgets/searcher_grid.dart (97%) create mode 100644 lib/features/sorting/widgets/sorting_app_bar.dart diff --git a/lib/core/helpers/random_int.dart b/lib/core/helpers/random_int.dart index 3c6a971..d0cbbe3 100644 --- a/lib/core/helpers/random_int.dart +++ b/lib/core/helpers/random_int.dart @@ -1,7 +1,7 @@ import 'dart:math'; class CustomRandom { - static List generateRandomList(int maxNum, int length) { + static List generateList(int maxNum, int length) { if (length > maxNum + 1) length = maxNum; List numbers = List.generate(maxNum + 1, (index) => index); diff --git a/lib/core/widgets/adaptive/text/regular_text.dart b/lib/core/widgets/adaptive/text/regular_text.dart index dbf6ab4..8a5cf02 100644 --- a/lib/core/widgets/adaptive/text/regular_text.dart +++ b/lib/core/widgets/adaptive/text/regular_text.dart @@ -6,7 +6,7 @@ class RegularText extends _AdaptiveText { super.fontSize = 16, super.decoration = TextDecoration.none, super.fontStyle = FontStyle.normal, - super.color, + super.color = ThemeEnum.focusColor, super.shadows, super.maxLines = 2, super.textAlign, diff --git a/lib/features/grid/widgets/searcher_grid.dart b/lib/features/searching/widgets/searcher_grid.dart similarity index 97% rename from lib/features/grid/widgets/searcher_grid.dart rename to lib/features/searching/widgets/searcher_grid.dart index 628ce06..ddda9fa 100644 --- a/lib/features/grid/widgets/searcher_grid.dart +++ b/lib/features/searching/widgets/searcher_grid.dart @@ -69,7 +69,7 @@ class _SearcherGridState extends State<_SearcherGrid> with SingleTickerProviderS @override Widget build(BuildContext context) { return Consumer(builder: (context, ref, _) { - final size = ref.watch(gridNotifierProvider.select((it) => it.gridSize)); + final size = ref.watch(_gridNotifierProvider.select((it) => it.gridSize)); return AnimatedBuilder( animation: _controller, diff --git a/lib/features/sorting/widgets/sorting_app_bar.dart b/lib/features/sorting/widgets/sorting_app_bar.dart new file mode 100644 index 0000000..f84e27b --- /dev/null +++ b/lib/features/sorting/widgets/sorting_app_bar.dart @@ -0,0 +1,44 @@ +part of '../view/sorting_page.dart'; +// +// class _MenuButton extends StatelessWidget { +// const _MenuButton(); +// +// @override +// Widget build(BuildContext context) { +// return Consumer( +// builder: (context, ref, _) { +// return TextButton( +// onPressed: () { +// CustomAlertDialog(context).solidDialog( +// parameters: [ +// ListDialogParameters( +// text: StringsManager.generateMaze, +// onTap: () {}, +// ), +// ListDialogParameters( +// text: "Dijkstra", +// onTap: () {}, +// ), +// ListDialogParameters( +// text: "BFS", +// onTap: () {}, +// ), +// ListDialogParameters( +// text: StringsManager.clearPath, +// color: ThemeEnum.redColor, +// onTap: () {}, +// ), +// ListDialogParameters( +// text: StringsManager.clearAll, +// color: ThemeEnum.redColor, +// onTap: () {}, +// ), +// ], +// ); +// }, +// child: const CustomIcon(Icons.menu_rounded), +// ); +// }, +// ); +// } +// } From 3d887565b8980fc60b0e5d768a443d177874bed9 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:04:09 +0300 Subject: [PATCH 12/23] reformat the code --- .../view_model/grid_notifier.dart | 11 +++++++---- .../view_model/grid_notifier_state.dart | 17 +++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) rename lib/features/{grid => searching}/view_model/grid_notifier.dart (97%) rename lib/features/{grid => searching}/view_model/grid_notifier_state.dart (53%) diff --git a/lib/features/grid/view_model/grid_notifier.dart b/lib/features/searching/view_model/grid_notifier.dart similarity index 97% rename from lib/features/grid/view_model/grid_notifier.dart rename to lib/features/searching/view_model/grid_notifier.dart index 45de5af..605458b 100644 --- a/lib/features/grid/view_model/grid_notifier.dart +++ b/lib/features/searching/view_model/grid_notifier.dart @@ -18,9 +18,9 @@ class MazeDirection { static const right = MazeDirection._(0, 1); } -class GridNotifierCubit extends StateNotifier { - GridNotifierCubit() : super(GridNotifierState()); - final int columnSquares = 20; +class SearchingNotifier extends StateNotifier { + SearchingNotifier() : super(GridNotifierState()); + final int columnSquares = 40; static const Duration scaleAppearDurationForWall = Duration(milliseconds: 700); static const Duration clearDuration = Duration(microseconds: 1); static const Duration drawFindingPathDuration = Duration(milliseconds: 2); @@ -359,8 +359,11 @@ class GridNotifierCubit extends StateNotifier { final newRow = row + direction.rowDelta * 2; final newCol = col + direction.colDelta * 2; + final currentGrid = gridData[newRow * state.columnCrossAxisCount + newCol]; if (_isValidCell(newRow, newCol) && - gridData[newRow * state.columnCrossAxisCount + newCol] == GridStatus.empty) { + currentGrid == GridStatus.empty && + currentGrid != GridStatus.startPoint && + currentGrid != GridStatus.targetPoint) { gridData[(row + direction.rowDelta) * state.columnCrossAxisCount + (col + direction.colDelta)] = GridStatus.wall; gridData[newRow * state.columnCrossAxisCount + newCol] = GridStatus.wall; diff --git a/lib/features/grid/view_model/grid_notifier_state.dart b/lib/features/searching/view_model/grid_notifier_state.dart similarity index 53% rename from lib/features/grid/view_model/grid_notifier_state.dart rename to lib/features/searching/view_model/grid_notifier_state.dart index ea53886..621118f 100644 --- a/lib/features/grid/view_model/grid_notifier_state.dart +++ b/lib/features/searching/view_model/grid_notifier_state.dart @@ -42,15 +42,16 @@ final class GridNotifierState { List? gridData, int? currentTappedIndex, }) { + return GridNotifierState( - columnCrossAxisCount: columnCrossAxisCount ?? this.columnCrossAxisCount, - rowMainAxisCount: rowMainAxisCount ?? this.rowMainAxisCount, - gridSize: gridSize ?? this.gridSize, - gridCount: gridCount ?? this.gridCount, - screenHeight: screenHeight ?? this.screenHeight, - screenWidth: screenWidth ?? this.screenWidth, - gridData: gridData ?? this.gridData, - currentTappedIndex: currentTappedIndex ?? this.currentTappedIndex, + columnCrossAxisCount: columnCrossAxisCount!=this.columnCrossAxisCount?(columnCrossAxisCount ?? this.columnCrossAxisCount):this.columnCrossAxisCount, + rowMainAxisCount: rowMainAxisCount!=this.rowMainAxisCount?(rowMainAxisCount ?? this.rowMainAxisCount):this.rowMainAxisCount, + gridSize: gridSize!=this.gridSize?(gridSize ?? this.gridSize):this.gridSize, + gridCount: gridCount!=this.gridCount?(gridCount ?? this.gridCount):this.gridCount, + screenHeight: screenHeight!=this.screenHeight?(screenHeight ?? this.screenHeight):this.screenHeight, + screenWidth: screenWidth!=this.screenWidth?(screenWidth ?? this.screenWidth):this.screenWidth, + gridData: gridData!=this.gridData?(gridData ?? this.gridData):this.gridData, + currentTappedIndex: currentTappedIndex!=this.currentTappedIndex?(currentTappedIndex ?? this.currentTappedIndex):this.currentTappedIndex, ); } } From b6059862f0c730ed85c44d9ef2b08a7d727803d8 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:04:11 +0300 Subject: [PATCH 13/23] reformat the code --- .../custom_widgets/custom_rounded_elevated_button.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/widgets/custom_widgets/custom_rounded_elevated_button.dart b/lib/core/widgets/custom_widgets/custom_rounded_elevated_button.dart index 9e7fecd..2dbb418 100644 --- a/lib/core/widgets/custom_widgets/custom_rounded_elevated_button.dart +++ b/lib/core/widgets/custom_widgets/custom_rounded_elevated_button.dart @@ -9,14 +9,14 @@ class CustomRoundedElevatedButton extends StatelessWidget { final Widget child; final VoidCallback onPressed; final bool fitToContent; - final bool smallRounded; + final double roundedRadius; final double fixedSize; const CustomRoundedElevatedButton({ super.key, this.backgroundColor = ThemeEnum.focusColor, this.shadowColor = ThemeEnum.transparentColor, this.fitToContent = true, - this.smallRounded = false, + this.roundedRadius = 50, this.fixedSize = 35, required this.child, required this.onPressed, @@ -37,7 +37,7 @@ class CustomRoundedElevatedButton extends StatelessWidget { fixedSize: fitToContent ? Size.fromHeight(fixedSize.r) : null, padding: EdgeInsets.symmetric(horizontal: 15.r), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(smallRounded ? 10 : 50).r), + borderRadius: BorderRadius.circular(roundedRadius).r), surfaceTintColor: background, foregroundColor: context.getColor(ThemeEnum.hintColor), ), From b2d99b6770d0e743d2a3da0bda0627936c3e777a Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:04:19 +0300 Subject: [PATCH 14/23] add CustomAlertDialog --- .../widgets/custom_widgets/custom_dialog.dart | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 lib/core/widgets/custom_widgets/custom_dialog.dart diff --git a/lib/core/widgets/custom_widgets/custom_dialog.dart b/lib/core/widgets/custom_widgets/custom_dialog.dart new file mode 100644 index 0000000..99c7aa0 --- /dev/null +++ b/lib/core/widgets/custom_widgets/custom_dialog.dart @@ -0,0 +1,96 @@ +import 'package:algorithm_visualizer/core/extensions/navigators.dart'; +import 'package:algorithm_visualizer/core/resources/theme_manager.dart'; +import 'package:algorithm_visualizer/core/widgets/adaptive/text/adaptive_text.dart'; +import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_divider.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +const double _borderRadius = 15; + +class ListDialogParameters { + final String text; + final VoidCallback onTap; + final ThemeEnum? color; + ListDialogParameters({ + required this.text, + required this.onTap, + this.color, + }); +} + +class CustomAlertDialog { + final BuildContext context; + CustomAlertDialog(this.context); + + Future solidDialog( + {required List parameters, bool barrierDismissible = true}) { + return showDialog( + context: context, + barrierDismissible: barrierDismissible, + builder: (context) { + return AlertDialog( + contentPadding: const EdgeInsetsDirectional.all(0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_borderRadius.r), + ), + content: _SolidContent(parameters), + ); + }, + ); + } +} + +class _SolidContent extends StatelessWidget { + const _SolidContent(this.parameters); + final List parameters; + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ...List.generate( + parameters.length, + (index) { + final isLast = index == parameters.length - 1; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + borderRadius: index == 0 + ? BorderRadius.only( + topLeft: Radius.circular(_borderRadius.r), + topRight: Radius.circular(_borderRadius.r), + ) + : (isLast + ? BorderRadius.only( + bottomLeft: Radius.circular(_borderRadius.r), + bottomRight: Radius.circular(_borderRadius.r), + ) + : null), + onTap: () { + parameters[index].onTap(); + context.pop(); + }, + child: SizedBox( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: REdgeInsets.symmetric(vertical: 10), + child: RegularText(parameters[index].text, + color: parameters[index].color ?? ThemeEnum.focusColor), + ), + ], + ), + ), + ), + if (!isLast) const CustomDivider(withHeight: false, color: ThemeEnum.whiteD4Color), + ], + ); + }, + ), + ], + ); + } +} From fc5f60aba68cc86f08aaa9e5e13ca8f260061531 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:04:28 +0300 Subject: [PATCH 15/23] add some routes --- lib/config/routes/route_app.dart | 41 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/lib/config/routes/route_app.dart b/lib/config/routes/route_app.dart index 2e17fdc..a196771 100644 --- a/lib/config/routes/route_app.dart +++ b/lib/config/routes/route_app.dart @@ -1,20 +1,25 @@ import 'package:algorithm_visualizer/core/helpers/app_bar/app_bar.dart'; import 'package:algorithm_visualizer/core/resources/strings_manager.dart'; import 'package:algorithm_visualizer/core/widgets/adaptive/text/adaptive_text.dart'; -import 'package:algorithm_visualizer/features/grid/view/grid_page.dart'; +import 'package:algorithm_visualizer/features/base/view/base_page.dart'; +import 'package:algorithm_visualizer/features/searching/view/grid_page.dart'; +import 'package:algorithm_visualizer/features/sorting/view/sorting_page.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; class Routes { - static const RouteConfig grid = RouteConfig( - name: 'grid', + static const RouteConfig base = RouteConfig( + name: 'base', path: '/', ); - - // name: 'hashtag', - // path: '/hashtag/:hashtagId', - // pathParamsName: "hashtagId", - // queryParamsName: 'mid', + static const RouteConfig searching = RouteConfig( + name: 'searching', + path: '/searching', + ); + static const RouteConfig sorting = RouteConfig( + name: 'sorting', + path: '/sorting', + ); } class RouteConfig { @@ -34,18 +39,30 @@ class RouteConfig { class AppRoutes { static final router = GoRouter( debugLogDiagnostics: true, - initialLocation: Routes.grid.path, + initialLocation: Routes.base.path, errorBuilder: (context, state) => const _UnknownPage(), routes: [ ShellRoute( builder: (context, state, child) { return child; }, + routes: [ + + GoRoute( + path: Routes.base.path, + name: Routes.base.name, + builder: (context, state) => const BasePage(), + ), + GoRoute( + path: Routes.searching.path, + name: Routes.searching.name, + builder: (context, state) => const SearchingPage(), + ), GoRoute( - path: Routes.grid.path, - name: Routes.grid.name, - builder: (context, state) => const GridPage(), + path: Routes.sorting.path, + name: Routes.sorting.name, + builder: (context, state) => const SortingPage(), ), ], ), From 346bc421a4da16da48534af5d11c2093f2f56c53 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 16:04:51 +0300 Subject: [PATCH 16/23] reformat the code --- lib/features/sorting/view/sorting_page.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/features/sorting/view/sorting_page.dart b/lib/features/sorting/view/sorting_page.dart index 06945dd..c6e428f 100644 --- a/lib/features/sorting/view/sorting_page.dart +++ b/lib/features/sorting/view/sorting_page.dart @@ -1,7 +1,3 @@ -import 'package:algorithm_visualizer/core/resources/strings_manager.dart'; -import 'package:algorithm_visualizer/core/resources/theme_manager.dart'; -import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_dialog.dart'; -import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_icon.dart'; import 'package:algorithm_visualizer/features/sorting/view_model/sorting_notifier.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; From 920dd44555edd66a39d2ef73cae0bdde0f035025 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 17:00:21 +0300 Subject: [PATCH 17/23] add play, stop, reset controls for sorting --- .../sorting/view_model/sorting_notifier.dart | 72 +++++++++++++++---- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/lib/features/sorting/view_model/sorting_notifier.dart b/lib/features/sorting/view_model/sorting_notifier.dart index 021a105..c2d3203 100644 --- a/lib/features/sorting/view_model/sorting_notifier.dart +++ b/lib/features/sorting/view_model/sorting_notifier.dart @@ -1,10 +1,14 @@ import 'package:algorithm_visualizer/core/helpers/screen_size.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:async/async.dart'; part 'sorting_state.dart'; +enum SortingEnum { played, stopped, none } + class SortableItem { final int id; final int value; @@ -13,10 +17,7 @@ class SortableItem { } class SortingNotifier extends StateNotifier { - SortingNotifier() - : super(SortingNotifierState( - list: List.generate(_listSize, (index) => SortableItem(index, index + 1))..shuffle(), - )) { + SortingNotifier() : super(SortingNotifierState(list: generateList())) { _initializePositions(); } @@ -24,6 +25,12 @@ class SortingNotifier extends StateNotifier { static double maxListItemHeight = 250.h; static double itemsPadding = 1.w; static const Duration swipeDuration = Duration(milliseconds: 5); + SortingEnum _operation = SortingEnum.none; + CancelableOperation? _cancelableBubbleSort; + + static List generateList() { + return List.generate(_listSize, (index) => SortableItem(index, index + 1))..shuffle(); + } static double calculateItemWidth(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; @@ -40,7 +47,9 @@ class SortingNotifier extends StateNotifier { static Color getColor(int itemIndex) { double step = (itemIndex * 2) / 100; - return Colors.indigo.withOpacity(step); + final value = step + 0.1 > 1 ? 1.0 : step + 0.1; + + return Colors.indigo.withOpacity(value); } void _initializePositions() { @@ -53,27 +62,62 @@ class SortingNotifier extends StateNotifier { state = state.copyWith(positions: positions); } + void stopSorting() { + _cancelableBubbleSort?.cancel(); + _operation = SortingEnum.stopped; + } + + void playSorting() { + if (_operation == SortingEnum.played) return; + _operation = SortingEnum.played; + + bubbleSort(); + } + + void generateAgain() { + _operation = SortingEnum.stopped; + + i = 0; + j = 0; + + state = state.copyWith(list: generateList()); + _initializePositions(); + } + + int i = 0; + int j = 0; + Future bubbleSort() async { + _cancelableBubbleSort = CancelableOperation.fromFuture(_bubbleSort()); + try { + await _cancelableBubbleSort?.value; + } catch (e) { + debugPrint("something wrong with bubbleSort: $e"); + } + } + + Future _bubbleSort() async { final list = List.from(state.list); - for (int i = 0; i < list.length - 1; i++) { - for (int j = 0; j < list.length - i - 1; j++) { + + for (i = 0; i < list.length - 1; i++) { + if (_operation != SortingEnum.played) return; + + for (j = 0; j < list.length - i - 1; j++) { + if (_operation != SortingEnum.played) return; + if (list[j].value > list[j + 1].value) { - // Swap items in the list - final temp = list[j]; - list[j] = list[j + 1]; - list[j + 1] = temp; + if (_operation != SortingEnum.played) return; + list.swap(j, j + 1); - // Update positions for swap final positions = Map.from(state.positions); final tempPosition = positions[list[j].id]!; positions[list[j].id] = positions[list[j + 1].id]!; positions[list[j + 1].id] = tempPosition; - // Update state state = state.copyWith(list: list, positions: positions); - // Delay for the animation to complete await Future.delayed(swipeDuration); + if (_operation != SortingEnum.played) return; } } } From 43f3ebf0f95e19b2484893505f45ee1a4ef27449 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 17:00:31 +0300 Subject: [PATCH 18/23] add play, stop, reset controls for sorting widgets --- lib/features/sorting/view/sorting_page.dart | 103 +++++++++++++------- 1 file changed, 68 insertions(+), 35 deletions(-) diff --git a/lib/features/sorting/view/sorting_page.dart b/lib/features/sorting/view/sorting_page.dart index c6e428f..ba5fc33 100644 --- a/lib/features/sorting/view/sorting_page.dart +++ b/lib/features/sorting/view/sorting_page.dart @@ -1,7 +1,12 @@ +import 'package:algorithm_visualizer/core/resources/strings_manager.dart'; +import 'package:algorithm_visualizer/core/resources/theme_manager.dart'; +import 'package:algorithm_visualizer/core/widgets/adaptive/text/adaptive_text.dart'; +import 'package:algorithm_visualizer/core/widgets/custom_widgets/custom_rounded_elevated_button.dart'; import 'package:algorithm_visualizer/features/sorting/view_model/sorting_notifier.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; part '../widgets/sorting_app_bar.dart'; +part '../widgets/control_buttons.dart'; final _notifierProvider = StateNotifierProvider( (ref) => SortingNotifier(), @@ -15,44 +20,72 @@ class SortingPage extends StatelessWidget { return Scaffold( appBar: AppBar( elevation: 1, - title: Consumer(builder: (context, ref, _) { - return InkWell( - onTap: () { - ref.read(_notifierProvider.notifier).bubbleSort(); - }, - child: const Text("Sort"), - ); - }), + title: Consumer( + builder: (context, ref, _) { + return InkWell( + onTap: () { + ref.read(_notifierProvider.notifier).bubbleSort(); + }, + child: const Text("Sort"), + ); + }, + ), + ), + body: const Stack( + alignment: AlignmentDirectional.bottomCenter, + children: [ + Align(alignment: AlignmentDirectional.topCenter, child: _BuildList()), + _InteractionButton(), + ], ), - body: Consumer(builder: (context, ref, _) { - final items = ref.watch(_notifierProvider).list; + ); + } +} - return Padding( - padding: const EdgeInsets.only(top: 15), - child: SizedBox( - height: SortingNotifier.maxListItemHeight*1.2, - width: double.infinity, - child: Stack( - alignment: AlignmentDirectional.bottomCenter, - children: List.generate( - items.length, - (index) { - final item = items[index]; - final position = - ref.watch(_notifierProvider.select((state) => state.positions[item.id]!)); - return AnimatedPositioned( - key: ValueKey(item.id), - left: position.dx, - bottom: position.dy, - duration: SortingNotifier.swipeDuration, - child: _BuildItem(item: item), - ); - }, - ), - ), +class _InteractionButton extends ConsumerWidget { + const _InteractionButton(); + + @override + Widget build(BuildContext context, ref) { + return const Column( + mainAxisSize: MainAxisSize.min, + children: [ + _ControlButtons(), + ], + ); + } +} + +class _BuildList extends ConsumerWidget { + const _BuildList(); + + @override + Widget build(BuildContext context, ref) { + final items = ref.watch(_notifierProvider).list; + + return Padding( + padding: const EdgeInsets.only(top: 15), + child: SizedBox( + height: SortingNotifier.maxListItemHeight * 1.2, + width: double.infinity, + child: Stack( + alignment: AlignmentDirectional.bottomCenter, + children: List.generate( + items.length, + (index) { + final item = items[index]; + final position = ref.watch(_notifierProvider.select((state) => state.positions[item.id]!)); + return AnimatedPositioned( + key: ValueKey(item.id), + left: position.dx, + bottom: position.dy, + duration: SortingNotifier.swipeDuration, + child: _BuildItem(item: item), + ); + }, ), - ); - }), + ), + ), ); } } From f5edca989c52778eba3e64451d404557d68fa6ef Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 17:00:39 +0300 Subject: [PATCH 19/23] create _ControlButtons --- .../sorting/widgets/control_buttons.dart | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 lib/features/sorting/widgets/control_buttons.dart diff --git a/lib/features/sorting/widgets/control_buttons.dart b/lib/features/sorting/widgets/control_buttons.dart new file mode 100644 index 0000000..d860e22 --- /dev/null +++ b/lib/features/sorting/widgets/control_buttons.dart @@ -0,0 +1,48 @@ +part of '../view/sorting_page.dart'; + +class _ControlButtons extends ConsumerWidget { + const _ControlButtons(); + + @override + Widget build(BuildContext context, ref) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + CustomRoundedElevatedButton( + roundedRadius: 3, + backgroundColor: ThemeEnum.blackOp10, + child: const RegularText( + StringsManager.play, + fontSize: 14, + ), + onPressed: () { + ref.read(_notifierProvider.notifier).playSorting(); + }, + ), + CustomRoundedElevatedButton( + roundedRadius: 3, + backgroundColor: ThemeEnum.blackOp10, + child: const RegularText( + StringsManager.stop, + fontSize: 14, + ), + onPressed: () { + ref.read(_notifierProvider.notifier).stopSorting(); + }, + ), + CustomRoundedElevatedButton( + roundedRadius: 3, + backgroundColor: ThemeEnum.redColor, + child: const RegularText( + StringsManager.reset, + color: ThemeEnum.whiteColor, + fontSize: 14, + ), + onPressed: () { + ref.read(_notifierProvider.notifier).generateAgain(); + }, + ), + ], + ); + } +} From f1459f425e567f7973561c9a2d9067c77599f130 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 17:00:47 +0300 Subject: [PATCH 20/23] add some texts --- lib/core/resources/strings_manager.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/resources/strings_manager.dart b/lib/core/resources/strings_manager.dart index b706121..83d3c19 100644 --- a/lib/core/resources/strings_manager.dart +++ b/lib/core/resources/strings_manager.dart @@ -29,5 +29,8 @@ class StringsManager { static const String generateMaze = "Generate maze"; static const String searching = "Searching"; static const String sorting = "Sorting"; + static const String stop = "Stop"; + static const String play = "Play"; + static const String reset = "Reset"; } From bbaa7741f30d3117ac663c6ef8f9b9bd616aed13 Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 17:00:56 +0300 Subject: [PATCH 21/23] add async --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 76393a6..0e48812 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: get_storage: ^2.1.1 animations: ^2.0.11 # not used yet collection: ^1.18.0 + async: ^2.11.0 cupertino_icons: ^1.0.6 From 80a07b696832f6c9166bc039a9681a038e8da91f Mon Sep 17 00:00:00 2001 From: ahmed Date: Sat, 24 Aug 2024 17:00:59 +0300 Subject: [PATCH 22/23] add async --- pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index b2defac..b38bea7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -18,7 +18,7 @@ packages: source: hosted version: "2.5.0" async: - dependency: transitive + dependency: "direct main" description: name: async sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" From fcc752b493c99754a7bf95f951f62aa16657eb6f Mon Sep 17 00:00:00 2001 From: ahmed Date: Tue, 26 Nov 2024 20:30:26 +0200 Subject: [PATCH 23/23] handle sorting --- lib/features/sorting/view/sorting_page.dart | 59 ++++++++++++++++--- .../sorting/view_model/sorting_notifier.dart | 40 +++++++++++++ .../sorting/view_model/sorting_state.dart | 5 +- pubspec.lock | 4 +- 4 files changed, 96 insertions(+), 12 deletions(-) diff --git a/lib/features/sorting/view/sorting_page.dart b/lib/features/sorting/view/sorting_page.dart index ba5fc33..0aef091 100644 --- a/lib/features/sorting/view/sorting_page.dart +++ b/lib/features/sorting/view/sorting_page.dart @@ -31,11 +31,16 @@ class SortingPage extends StatelessWidget { }, ), ), - body: const Stack( + body: Stack( alignment: AlignmentDirectional.bottomCenter, children: [ - Align(alignment: AlignmentDirectional.topCenter, child: _BuildList()), - _InteractionButton(), + const Align(alignment: AlignmentDirectional.topCenter, child: _BuildList()), + // _ControlButtons(), + const Align(alignment: AlignmentDirectional.bottomCenter, child: _InteractionButton()), + ...List.generate( + SortingNotifier.sortingAlgorithms.length, + (index) => _SelectedOperation(index), + ), ], ), ); @@ -47,11 +52,47 @@ class _InteractionButton extends ConsumerWidget { @override Widget build(BuildContext context, ref) { - return const Column( - mainAxisSize: MainAxisSize.min, - children: [ - _ControlButtons(), - ], + return const _ControlButtons(); + } +} + +class _SelectedOperation extends ConsumerWidget { + const _SelectedOperation(this.index); + final int index; + @override + Widget build(BuildContext context, ref) { + final algo = SortingNotifier.sortingAlgorithms[index]; + final isChanged = ref.watch(_notifierProvider).selectedAlgorithms.contains(algo); + final width = SortingNotifier.calculateButtonWidth(index); + + return AnimatedPositionedDirectional( + duration: const Duration(milliseconds: 300), + start: width, + bottom: isChanged ? 100 : 0, + // width: width, + child: CustomRoundedElevatedButton( + roundedRadius: 3, + backgroundColor: ThemeEnum.whiteD7Color, + child: RegularText(algo.name, fontSize: 14), + onPressed: () { + ref.read(_notifierProvider.notifier).selectAlgorithm(index); + }, + ), + ); + } +} + +class Item extends StatelessWidget { + final String text; + + const Item({super.key, required this.text}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(10), + color: context.getColor(ThemeEnum.whiteD7Color), + child: RegularText(text, color: ThemeEnum.primaryColor), ); } } @@ -61,7 +102,7 @@ class _BuildList extends ConsumerWidget { @override Widget build(BuildContext context, ref) { - final items = ref.watch(_notifierProvider).list; + final items = ref.watch(_notifierProvider.select((state) => state.list)); return Padding( padding: const EdgeInsets.only(top: 15), diff --git a/lib/features/sorting/view_model/sorting_notifier.dart b/lib/features/sorting/view_model/sorting_notifier.dart index c2d3203..5d25cb5 100644 --- a/lib/features/sorting/view_model/sorting_notifier.dart +++ b/lib/features/sorting/view_model/sorting_notifier.dart @@ -1,4 +1,6 @@ import 'package:algorithm_visualizer/core/helpers/screen_size.dart'; +import 'package:algorithm_visualizer/core/resources/theme_manager.dart'; +import 'package:algorithm_visualizer/core/widgets/adaptive/text/adaptive_text.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -9,6 +11,9 @@ part 'sorting_state.dart'; enum SortingEnum { played, stopped, none } +enum SortingAlgorithm { bubble, selection, merge, quick } + +//SortingAlgorithm class SortableItem { final int id; final int value; @@ -28,10 +33,45 @@ class SortingNotifier extends StateNotifier { SortingEnum _operation = SortingEnum.none; CancelableOperation? _cancelableBubbleSort; + static const List widthButtons = [ + SortingAlgorithm.bubble, + SortingAlgorithm.selection, + SortingAlgorithm.merge, + SortingAlgorithm.quick, + ]; + + static const List sortingAlgorithms = [ + SortingAlgorithm.bubble, + SortingAlgorithm.selection, + SortingAlgorithm.merge, + SortingAlgorithm.quick, + ]; + + void selectAlgorithm(int index) { + final target = sortingAlgorithms[index]; + final selected = [...state.selectedAlgorithms]; + final targetIndex = selected.indexOf(target); + + if (targetIndex != -1) { + selected.removeAt(targetIndex); + } else { + selected.add(target); + } + state = state.copyWith(selectedAlgorithms: selected); + } + static List generateList() { return List.generate(_listSize, (index) => SortableItem(index, index + 1))..shuffle(); } + static double calculateButtonWidth(int index) { + double width = 0; + for (int i = 0; i <= index; i++) { + width += sortingAlgorithms[index].name.length *5; + } + return width; + } + static double calculateItemWidth(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final availableWidth = screenWidth - (itemsPadding * (_listSize - 1)); diff --git a/lib/features/sorting/view_model/sorting_state.dart b/lib/features/sorting/view_model/sorting_state.dart index de0f6f5..ae72650 100644 --- a/lib/features/sorting/view_model/sorting_state.dart +++ b/lib/features/sorting/view_model/sorting_state.dart @@ -3,16 +3,19 @@ part of 'sorting_notifier.dart'; class SortingNotifierState { final List list; final Map positions; + final List selectedAlgorithms; - SortingNotifierState({required this.list, this.positions = const {}}); + SortingNotifierState({required this.list, this.positions = const {}, this.selectedAlgorithms = const []}); SortingNotifierState copyWith({ List? list, Map? positions, + List? selectedAlgorithms, }) { return SortingNotifierState( list: list ?? this.list, positions: positions ?? this.positions, + selectedAlgorithms: selectedAlgorithms ?? this.selectedAlgorithms, ); } } diff --git a/pubspec.lock b/pubspec.lock index b38bea7..bb9b3d0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -441,10 +441,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" web: dependency: transitive description: