-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
1,092 additions
and
6 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:go_router/go_router.dart'; | ||
import 'package:smooth_sheets/smooth_sheets.dart'; | ||
|
||
// The code may seem verbose, but the core principle is straightforward. | ||
// In this tutorial, you only need to learn the following: | ||
// | ||
// 1. Create a Navigator and wrap it in a NavigationSheet. | ||
// 2. Use *NavigationSheetPage to create a page that belongs to the navigator. | ||
// 3. Do not forget to register a NavigationSheetTransitionObserver to the navigator. | ||
void main() { | ||
runApp(const _DeclarativeNavigationSheetExample()); | ||
} | ||
|
||
// NavigationSheet requires a special NavigatorObserver in order to | ||
// smoothly change its extent during a route transition. | ||
final transitionObserver = NavigationSheetTransitionObserver(); | ||
|
||
// To use declarative navigation, we utilize the 'go_router' package. | ||
// However, any other package that works with Navigator 2.0 | ||
// or even your own implementation can also be used. | ||
final router = GoRouter( | ||
initialLocation: '/a', | ||
routes: [ | ||
// We use ShellRoute to create a Navigator | ||
// that will be used for nested navigation in the sheet. | ||
ShellRoute( | ||
// Do not forget this line! | ||
observers: [transitionObserver], | ||
builder: (context, state, child) { | ||
return _ExampleHome(nestedNavigator: child); | ||
}, | ||
routes: [ | ||
GoRoute( | ||
path: '/a', | ||
pageBuilder: (context, state) { | ||
return DraggableNavigationSheetPage( | ||
key: state.pageKey, | ||
child: const _ExampleSheetContent( | ||
title: '/a', | ||
size: 0.5, | ||
destinations: ['/a/details', '/b'], | ||
), | ||
); | ||
}, | ||
routes: [ | ||
GoRoute( | ||
path: 'details', | ||
pageBuilder: (context, state) { | ||
return DraggableNavigationSheetPage( | ||
key: state.pageKey, | ||
child: const _ExampleSheetContent( | ||
title: '/a/details', | ||
size: 0.75, | ||
destinations: ['/a/details/info'], | ||
), | ||
); | ||
}, | ||
routes: [ | ||
GoRoute( | ||
path: 'info', | ||
pageBuilder: (context, state) { | ||
return DraggableNavigationSheetPage( | ||
key: state.pageKey, | ||
child: const _ExampleSheetContent( | ||
title: '/a/details/info', | ||
size: 1.0, | ||
destinations: ['/a', '/b', '/b/details'], | ||
), | ||
); | ||
}, | ||
), | ||
], | ||
), | ||
], | ||
), | ||
GoRoute( | ||
path: '/b', | ||
pageBuilder: (context, state) { | ||
return DraggableNavigationSheetPage( | ||
key: state.pageKey, | ||
child: const _ExampleSheetContent( | ||
title: 'B', | ||
size: 0.6, | ||
destinations: ['/b/details', '/a'], | ||
), | ||
); | ||
}, | ||
routes: [ | ||
GoRoute( | ||
path: 'details', | ||
pageBuilder: (context, state) { | ||
return DraggableNavigationSheetPage( | ||
key: state.pageKey, | ||
child: const _ExampleSheetContent( | ||
title: 'B Details', | ||
size: 0.5, | ||
destinations: ['/a'], | ||
), | ||
); | ||
}, | ||
), | ||
], | ||
), | ||
], | ||
), | ||
], | ||
); | ||
|
||
class _DeclarativeNavigationSheetExample extends StatelessWidget { | ||
const _DeclarativeNavigationSheetExample(); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return MaterialApp.router(routerConfig: router); | ||
} | ||
} | ||
|
||
class _ExampleHome extends StatelessWidget { | ||
const _ExampleHome({ | ||
required this.nestedNavigator, | ||
}); | ||
|
||
final Widget nestedNavigator; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
body: Stack( | ||
children: [ | ||
const Placeholder(), | ||
_ExampleSheet(nestedNavigator: nestedNavigator), | ||
], | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _ExampleSheet extends StatelessWidget { | ||
const _ExampleSheet({ | ||
required this.nestedNavigator, | ||
}); | ||
|
||
final Widget nestedNavigator; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return NavigationSheet( | ||
transitionObserver: transitionObserver, | ||
child: Material( | ||
color: Theme.of(context).colorScheme.primary, | ||
borderRadius: BorderRadius.circular(16), | ||
clipBehavior: Clip.antiAlias, | ||
child: nestedNavigator, | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _ExampleSheetContent extends StatelessWidget { | ||
const _ExampleSheetContent({ | ||
required this.title, | ||
required this.size, | ||
required this.destinations, | ||
}); | ||
|
||
final String title; | ||
final double size; | ||
final List<String> destinations; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final textColor = Theme.of(context).colorScheme.onSecondaryContainer; | ||
final textStyle = Theme.of(context).textTheme.displayMedium?.copyWith( | ||
fontWeight: FontWeight.bold, | ||
color: textColor, | ||
); | ||
|
||
return LayoutBuilder( | ||
builder: (context, constraints) { | ||
return Container( | ||
color: Theme.of(context).colorScheme.secondaryContainer, | ||
width: constraints.maxWidth, | ||
height: constraints.maxHeight * size, | ||
child: SafeArea( | ||
child: Column( | ||
children: [ | ||
Expanded( | ||
child: Center( | ||
child: Text(title, style: textStyle), | ||
), | ||
), | ||
Column( | ||
mainAxisSize: MainAxisSize.min, | ||
children: [ | ||
for (final dest in destinations) | ||
TextButton( | ||
style: TextButton.styleFrom( | ||
foregroundColor: textColor, | ||
), | ||
onPressed: () => context.go(dest), | ||
child: Text('Go To $dest'), | ||
), | ||
], | ||
), | ||
], | ||
), | ||
), | ||
); | ||
}, | ||
); | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:smooth_sheets/smooth_sheets.dart'; | ||
|
||
void main() { | ||
runApp(const _SheetContentScaffoldExample()); | ||
} | ||
|
||
class _SheetContentScaffoldExample extends StatelessWidget { | ||
const _SheetContentScaffoldExample(); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return const MaterialApp( | ||
home: Scaffold( | ||
body: Stack( | ||
children: [ | ||
Placeholder(), | ||
_ExampleSheet(), | ||
], | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _ExampleSheet extends StatelessWidget { | ||
const _ExampleSheet(); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final content = SheetContentScaffold( | ||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer, | ||
requiredMinExtentForBottomBar: const Extent.proportional(0.5), | ||
body: Container(height: 500), | ||
appBar: buildAppBar(context), | ||
bottomBar: buildBottomBar(), | ||
); | ||
|
||
const physics = StretchingSheetPhysics( | ||
parent: SnappingSheetPhysics( | ||
snappingBehavior: SnapToNearest( | ||
snapTo: [ | ||
Extent.proportional(0.2), | ||
Extent.proportional(0.5), | ||
Extent.proportional(1), | ||
], | ||
), | ||
), | ||
); | ||
|
||
return DraggableSheet( | ||
physics: physics, | ||
minExtent: const Extent.pixels(0), | ||
child: Card( | ||
clipBehavior: Clip.antiAlias, | ||
margin: EdgeInsets.zero, | ||
shape: RoundedRectangleBorder( | ||
borderRadius: BorderRadius.circular(16), | ||
), | ||
child: content, | ||
), | ||
); | ||
} | ||
|
||
PreferredSizeWidget buildAppBar(BuildContext context) { | ||
return AppBar( | ||
title: const Text('Appbar'), | ||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer, | ||
); | ||
} | ||
|
||
Widget buildBottomBar() { | ||
return BottomAppBar( | ||
child: Row( | ||
children: [ | ||
Flexible( | ||
fit: FlexFit.tight, | ||
child: TextButton( | ||
onPressed: () {}, | ||
child: const Text('Cancel'), | ||
), | ||
), | ||
const SizedBox(width: 16), | ||
Flexible( | ||
fit: FlexFit.tight, | ||
child: FilledButton( | ||
onPressed: () {}, | ||
child: const Text('OK'), | ||
), | ||
) | ||
], | ||
), | ||
); | ||
} | ||
} |
Oops, something went wrong.