diff --git a/lib/screens/bookmarks/ui/bookmarks.dart b/lib/screens/bookmarks/ui/bookmarks.dart index 44bfa27..8205aaa 100644 --- a/lib/screens/bookmarks/ui/bookmarks.dart +++ b/lib/screens/bookmarks/ui/bookmarks.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; @@ -9,6 +7,7 @@ import 'package:linkdy/screens/bookmarks/ui/bookmark_item.dart'; import 'package:linkdy/screens/bookmarks/ui/bookmark_form_modal.dart'; import 'package:linkdy/screens/bookmarks/ui/delete_bookmark_modal.dart'; import 'package:linkdy/screens/bookmarks/ui/search_bookmarks.dart'; +import 'package:linkdy/widgets/circle_page_transition.dart'; import 'package:linkdy/widgets/error_screen.dart'; import 'package:linkdy/widgets/no_data_screen.dart'; @@ -16,6 +15,8 @@ import 'package:linkdy/constants/enums.dart'; import 'package:linkdy/constants/global_keys.dart'; import 'package:linkdy/i18n/strings.g.dart'; +final GlobalKey _searchButtonKey = GlobalKey(); + class BookmarksScreen extends ConsumerWidget { const BookmarksScreen({super.key}); @@ -26,18 +27,14 @@ class BookmarksScreen extends ConsumerWidget { final width = MediaQuery.of(context).size.width; void openSearchModal() { - showGeneralDialog( - context: context, - barrierColor: !(width > 700 || !(Platform.isAndroid || Platform.isIOS)) ? Colors.transparent : Colors.black54, - transitionBuilder: (context, anim1, anim2, child) { - return SlideTransition( - position: Tween(begin: const Offset(0, 1), end: const Offset(0, 0)).animate( - CurvedAnimation(parent: anim1, curve: Curves.easeInOutCubicEmphasized), - ), - child: child, - ); - }, - pageBuilder: (context, animation, secondaryAnimation) => SearchBookmarksModal(fullscreen: width <= 700), + if (_searchButtonKey.currentContext == null) return; + RenderBox box = _searchButtonKey.currentContext!.findRenderObject() as RenderBox; + Offset position = box.localToGlobal(Offset.zero); + Navigator.of(context).push( + circlePageBuilder( + page: const SearchBookmarksModal(fullscreen: true), + beginPosition: Offset(position.dx + 20, position.dy + 20), + ), ); } @@ -66,6 +63,7 @@ class BookmarksScreen extends ConsumerWidget { title: Text(t.bookmarks.bookmarks), actions: [ IconButton( + key: _searchButtonKey, onPressed: openSearchModal, icon: const Icon(Icons.search_rounded), tooltip: t.bookmarks.search.searchBookmarks, diff --git a/lib/screens/bookmarks/ui/search_bookmarks.dart b/lib/screens/bookmarks/ui/search_bookmarks.dart index bb255be..475ec80 100644 --- a/lib/screens/bookmarks/ui/search_bookmarks.dart +++ b/lib/screens/bookmarks/ui/search_bookmarks.dart @@ -44,7 +44,9 @@ class SearchBookmarksModal extends ConsumerWidget { key: ScaffoldMessengerKeys.search, child: Dialog.fullscreen( child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.2), appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.primaryContainer.withOpacity(0), toolbarHeight: 68, leading: Padding( padding: const EdgeInsets.only(bottom: 8), diff --git a/lib/widgets/circle_page_transition.dart b/lib/widgets/circle_page_transition.dart new file mode 100644 index 0000000..d8f3190 --- /dev/null +++ b/lib/widgets/circle_page_transition.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +Route circlePageBuilder({ + required Widget page, + required Offset beginPosition, + bool? fullScreenDialog, + Duration? transitionDuration, +}) { + return PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => page, + transitionDuration: transitionDuration ?? const Duration(milliseconds: 250), + reverseTransitionDuration: transitionDuration ?? const Duration(milliseconds: 250), + fullscreenDialog: fullScreenDialog ?? false, + transitionsBuilder: (context, animation, secondaryAnimation, child) { + const double beganRadius = 0.0; + final double endRadius = MediaQuery.of(context).size.height * 1.2; + + final radiusTweenAnimation = animation.drive( + Tween(begin: beganRadius, end: endRadius), + ); + + return ClipPath( + clipper: _CircleTransitionClipper( + center: beginPosition, + radius: radiusTweenAnimation.value, + ), + child: child, + ); + }, + ); +} + +class _CircleTransitionClipper extends CustomClipper { + final Offset center; + final double radius; + + const _CircleTransitionClipper({ + required this.center, + required this.radius, + }); + + @override + Path getClip(Size size) { + return Path() + ..addOval( + Rect.fromCircle( + center: center, + radius: radius, + ), + ); + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) => true; +}