diff --git a/example/lib/pages/welcome/bottom_sheet_page.dart b/example/lib/pages/welcome/bottom_sheet_page.dart new file mode 100644 index 0000000..471529f --- /dev/null +++ b/example/lib/pages/welcome/bottom_sheet_page.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class BottomSheetPage extends StatelessWidget { + const BottomSheetPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Center( + child: Text('BottomSheetPage Content'), + ), + ); + } +} diff --git a/example/lib/pages/welcome/welcome_view.dart b/example/lib/pages/welcome/welcome_view.dart index eb1a15d..1b70788 100644 --- a/example/lib/pages/welcome/welcome_view.dart +++ b/example/lib/pages/welcome/welcome_view.dart @@ -18,42 +18,48 @@ class WelcomeView extends StatelessWidget { title: const Text('Welcome to Qlevar Router'), centerTitle: true, ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - NodeWidget( - name: 'Store', - // Navigate with route name - onPressed: () => QR.toName(StoreRoutes.store), - ), - NodeWidget( - name: 'Dashboard', - onPressed: () => QR.toName(DashboardRoutes.dashboard), - ), - NodeWidget( - name: 'Mobile', - onPressed: () => QR.toName(MobileRoutes.mobile), - ), - NodeWidget( - name: 'Middleware', - // Navigate with route path - onPressed: () => QR.to('/parent'), - ), - NodeWidget( - name: 'Await routes results', - onPressed: () => QR.to('/await-result'), - ), - NodeWidget( - name: 'Declarative', - onPressed: () => QR.to('/declarative'), - ), - NodeWidget( - name: 'Editable Routes', - onPressed: () => QR.to('/editable-routes'), - ), - const SingleNavigatorRouterExample(), - ], + body: SingleChildScrollView( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + NodeWidget( + name: 'Store', + // Navigate with route name + onPressed: () => QR.toName(StoreRoutes.store), + ), + NodeWidget( + name: 'Dashboard', + onPressed: () => QR.toName(DashboardRoutes.dashboard), + ), + NodeWidget( + name: 'Mobile', + onPressed: () => QR.toName(MobileRoutes.mobile), + ), + NodeWidget( + name: 'Middleware', + // Navigate with route path + onPressed: () => QR.to('/parent'), + ), + NodeWidget( + name: 'Await routes results', + onPressed: () => QR.to('/await-result'), + ), + NodeWidget( + name: 'Declarative', + onPressed: () => QR.to('/declarative'), + ), + NodeWidget( + name: 'Editable Routes', + onPressed: () => QR.to('/editable-routes'), + ), + const SingleNavigatorRouterExample(), + NodeWidget( + name: 'BottomSheet Page', + onPressed: () => QR.push('/bottom-sheet'), + ), + ], + ), ), ), floatingActionButton: const DebugTools(), diff --git a/example/lib/routes/app_routes.dart b/example/lib/routes/app_routes.dart index c61d600..0cda48c 100644 --- a/example/lib/routes/app_routes.dart +++ b/example/lib/routes/app_routes.dart @@ -5,6 +5,7 @@ import 'package:qlevar_router/qlevar_router.dart'; import '../pages/declarative/declarative_view.dart'; import '../pages/login/login_view.dart'; import '../pages/not_found/not_found_view.dart'; +import '../pages/welcome/bottom_sheet_page.dart'; import '../pages/welcome/welcome_view.dart'; import '../services/auth_service.dart'; import 'await_result_routes.dart'; @@ -17,6 +18,7 @@ import 'store_routes.dart'; class AppRoutes { static const String root = 'root'; static const String login = 'login'; + static const String bottomSheetPage = 'bottom_sheet_page'; void setup() { // enable debug logging for all routes @@ -97,5 +99,15 @@ class AppRoutes { path: '/declarative', declarativeBuilder: (k) => DeclarativePage(k), ), + // Add bottom sheet page route + QRoute( + path: '/bottom-sheet', + name: bottomSheetPage, + pageType: const QModalBottomSheetPage( + showDragHandle: true, + isDismissible: true, + anchorPoint: Offset(100, 200)), + builder: () => const BottomSheetPage(), + ), ]; } diff --git a/lib/src/controllers/qrouter_controller.dart b/lib/src/controllers/qrouter_controller.dart index d73ae0d..534e043 100644 --- a/lib/src/controllers/qrouter_controller.dart +++ b/lib/src/controllers/qrouter_controller.dart @@ -71,7 +71,7 @@ abstract class QNavigator extends ChangeNotifier { Future popUntilOrPush(String path); /// {@template q.navigator.popUntilOrPushName} - /// Push the page with this [name] on the top of the stack, or pop unit it if it's already + /// Push the page with this [name] on the top of the stack, or pop until it if it's already /// in the stack /// {@endtemplate} Future popUntilOrPushName(String name, {Map? params}); diff --git a/lib/src/pages/page_creator.dart b/lib/src/pages/page_creator.dart index c14fd3e..d74b0e4 100644 --- a/lib/src/pages/page_creator.dart +++ b/lib/src/pages/page_creator.dart @@ -27,6 +27,9 @@ abstract class _PageConverter { if (pageType is QCustomPage) { return _getCustomPage(child); } + if (pageType is QModalBottomSheetPage) { + return _getModalBottomSheetPage(child); + } return _getMaterialPage(child); } @@ -83,6 +86,25 @@ abstract class _PageConverter { ); } + QModalBottomSheetPageInternal _getModalBottomSheetPage(Widget child) { + final page = pageType as QModalBottomSheetPage; + return QModalBottomSheetPageInternal( + name: pageName, + child: child, + restorationId: _getRestorationId(), + key: key, + matchKey: matchKey, + isScrollControlled: page.isScrollControlled, + isDismissible: page.isDismissible, + enableDrag: page.enableDrag, + showDragHandle: page.showDragHandle, + useSafeArea: page.useSafeArea, + barrierOnTapHint: page.barrierOnTapHint, + barrierLabel: page.barrierOnTapHint, + anchorPoint: page.anchorPoint, + ); + } + Widget _buildTransaction(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) => _getTransaction(pageType as QCustomPage, child, animation); diff --git a/lib/src/pages/qpage_internal.dart b/lib/src/pages/qpage_internal.dart index a853444..d57c9ae 100644 --- a/lib/src/pages/qpage_internal.dart +++ b/lib/src/pages/qpage_internal.dart @@ -166,3 +166,48 @@ class QCustomPageInternal extends QPageInternal { ); } } + +class QModalBottomSheetPageInternal extends QPageInternal { + const QModalBottomSheetPageInternal({ + required this.child, + required super.matchKey, + this.isDismissible = true, + this.isScrollControlled = false, + this.enableDrag = true, + this.showDragHandle, + this.useSafeArea = false, + this.barrierLabel, + this.barrierOnTapHint, + this.anchorPoint, + super.key, + super.restorationId, + super.name, + super.arguments, + }); + + final Widget child; + final bool isScrollControlled; + final bool? showDragHandle; + final bool isDismissible; + final bool enableDrag; + final bool useSafeArea; + final String? barrierLabel; + final String? barrierOnTapHint; + final Offset? anchorPoint; + + @override + Route createRoute(BuildContext context) { + return ModalBottomSheetRoute( + settings: this, + builder: (_) => child, + isScrollControlled: isScrollControlled, + barrierLabel: barrierLabel, + barrierOnTapHint: barrierOnTapHint, + isDismissible: isDismissible, + showDragHandle: showDragHandle, + enableDrag: enableDrag, + useSafeArea: useSafeArea, + anchorPoint: anchorPoint, + ); + } +} diff --git a/lib/src/pages/qpages.dart b/lib/src/pages/qpages.dart index 8c4d512..8a4c7d1 100644 --- a/lib/src/pages/qpages.dart +++ b/lib/src/pages/qpages.dart @@ -117,3 +117,27 @@ class QFadePage extends QCustomPage { final Curve? curve; } + +class QModalBottomSheetPage extends QPage { + const QModalBottomSheetPage({ + this.isScrollControlled = false, + this.isDismissible = true, + this.barrierDismissible = true, + this.enableDrag = true, + this.useSafeArea = false, + this.showDragHandle, + this.barrierLabel, + this.barrierOnTapHint, + this.anchorPoint, + String? restorationId, + }) : super(false, false, restorationId); + final bool isScrollControlled; + final bool isDismissible; + final bool barrierDismissible; + final bool enableDrag; + final bool useSafeArea; + final bool? showDragHandle; + final String? barrierLabel; + final String? barrierOnTapHint; + final Offset? anchorPoint; +}