Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Cupertino style modal transition not working with NavigationSheet #182

Merged
merged 9 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
"program": "lib/tutorial/draggable_sheet.dart",
"cwd": "./cookbook"
},
{
"name": "iOS-Style Modal Navigation Sheet (with go_router)",
"request": "launch",
"type": "dart",
"program": "lib/tutorial/ios_style_declarative_modal_navigation_sheet.dart",
"cwd": "./cookbook"
},
{
"name": "Declarative Navigation Sheet",
"request": "launch",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:smooth_sheets/smooth_sheets.dart';

/// Example code of iOS style modal `NavigationSheet` with go_router.
void main() {
runApp(const _App());
}

final transitionObserver = NavigationSheetTransitionObserver();

final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) {
return const _Home();
},
routes: [
ShellRoute(
observers: [transitionObserver],
pageBuilder: (context, state, child) {
return CupertinoModalSheetPage(
key: state.pageKey,
child: _Modal(nestedNavigator: child),
);
},
routes: [
GoRoute(
path: 'modal',
pageBuilder: (context, state) {
return DraggableNavigationSheetPage(
key: state.pageKey,
child: Container(color: Colors.white),
);
},
),
],
)
]),
],
);

class _App extends StatelessWidget {
const _App();

@override
Widget build(BuildContext context) {
return MaterialApp.router(routerConfig: router);
}
}

class _Home extends StatelessWidget {
const _Home();

@override
Widget build(BuildContext context) {
return CupertinoStackedTransition(
cornerRadius: Tween(begin: 0, end: 20),
child: Scaffold(
body: Center(
child: TextButton(
onPressed: () => context.go('/modal'),
child: const Text('Show Sheet'),
),
),
),
);
}
}

class _Modal extends StatelessWidget {
const _Modal({
required this.nestedNavigator,
});

final Widget nestedNavigator;

@override
Widget build(BuildContext context) {
return NavigationSheet(
transitionObserver: transitionObserver,
child: Material(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
clipBehavior: Clip.antiAlias,
child: nestedNavigator,
),
);
}
}
4 changes: 4 additions & 0 deletions package/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.8.1 Jun 23, 2024

- Fix: Cupertino style modal transition not working with NavigationSheet (#182)

## 0.8.0 Jun 22, 2024

This version contains some breaking changes. See the [migration guide](https://github.com/fujidaiti/smooth_sheets/blob/main/docs/migration-guide-0.8.x.md) for more details.
Expand Down
1 change: 1 addition & 0 deletions package/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ See also:
- [declarative_modal_sheet.dart](https://github.com/fujidaiti/smooth_sheets/blob/main/cookbook/lib/tutorial/declarative_modal_sheet.dart), a tutorial of integration with declarative navigation using [go_router](https://pub.dev/packages/go_router) package.
- [imperative_modal_sheet.dart](https://github.com/fujidaiti/smooth_sheets/blob/main/cookbook/lib/tutorial/imperative_modal_sheet.dart), a tutorial of integration with imperative Navigator API.
- [cupertino_modal_sheet.dart](https://github.com/fujidaiti/smooth_sheets/blob/main/cookbook/lib/tutorial/cupertino_modal_sheet.dart), a tutorial of iOS style modal sheets.
- [ios_style_declarative_modal_navigation_sheet.dart](https://github.com/fujidaiti/smooth_sheets/blob/main/cookbook/lib/tutorial/ios_style_declarative_modal_navigation_sheet.dart), an example of iOS-style modal NavigationSheet with go_router.
- [showcase/todo_list](https://github.com/fujidaiti/smooth_sheets/blob/main/cookbook/lib/showcase/todo_list), which uses SheetDismissible to show a confirmation dialog when the user tries to discard the todo editing sheet without saving the content.

<br/>
Expand Down
1 change: 1 addition & 0 deletions package/lib/src/draggable/draggable_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class DraggableSheet extends StatelessWidget {
final keyboardDismissBehavior =
this.keyboardDismissBehavior ?? theme?.keyboardDismissBehavior;
final gestureTamper = TamperSheetGesture.maybeOf(context);
final controller = this.controller ?? SheetControllerScope.maybeOf(context);

Widget result = DraggableSheetExtentScope(
controller: controller,
Expand Down
3 changes: 3 additions & 0 deletions package/lib/src/foundation/sheet_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class SheetController extends ChangeNotifier

SheetStatus? get status => _client?.status;

/// Whether a [SheetExtent] is attached to this controller.
bool get hasClient => _client != null;

@override
void addListener(VoidCallback listener, {bool fireImmediately = false}) {
if (fireImmediately) {
Expand Down
6 changes: 2 additions & 4 deletions package/lib/src/foundation/sheet_extent_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,9 @@ abstract class SheetExtentScopeState<E extends SheetExtent,
}

void _rewireControllerAndScope() {
final newController =
widget.controller ?? SheetControllerScope.maybeOf(context);
if (_controller != newController) {
if (_controller != widget.controller) {
_controller?.detach(_extent);
_controller = newController?..attach(_extent);
_controller = widget.controller?..attach(_extent);
}
}

Expand Down
4 changes: 3 additions & 1 deletion package/lib/src/navigation/navigation_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ class _NavigationSheetState extends State<NavigationSheet>
final keyboardDismissBehavior =
widget.keyboardDismissBehavior ?? theme?.keyboardDismissBehavior;
final gestureTamper = TamperSheetGesture.maybeOf(context);
final controller =
widget.controller ?? SheetControllerScope.maybeOf(context);

Widget result = NavigationSheetExtentScope(
key: _scopeKey,
controller: widget.controller,
controller: controller,
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'NavigationSheet' : null,
child: NavigationSheetViewport(
Expand Down
1 change: 1 addition & 0 deletions package/lib/src/scrollable/scrollable_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class ScrollableSheet extends StatelessWidget {
final keyboardDismissBehavior =
this.keyboardDismissBehavior ?? theme?.keyboardDismissBehavior;
final gestureTamper = TamperSheetGesture.maybeOf(context);
final controller = this.controller ?? SheetControllerScope.maybeOf(context);

Widget result = ScrollableSheetExtentScope(
controller: controller,
Expand Down
2 changes: 1 addition & 1 deletion package/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: smooth_sheets
description: Sheet widgets with smooth motion and great flexibility. Also supports nested navigation in both imperative and declarative ways.
version: 0.8.0
version: 0.8.1
repository: https://github.com/fujidaiti/smooth_sheets
screenshots:
- description: Practical examples of smooth_sheets.
Expand Down
49 changes: 49 additions & 0 deletions package/test/draggable/draggable_sheet_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:smooth_sheets/smooth_sheets.dart';
import 'package:smooth_sheets/src/foundation/sheet_controller.dart';

class _TestWidget extends StatelessWidget {
const _TestWidget({
this.contentKey,
this.contentHeight = 500,
});

final Key? contentKey;
final double contentHeight;

@override
Widget build(BuildContext context) {
final content = Container(
key: contentKey,
color: Colors.white,
height: contentHeight,
width: double.infinity,
);

return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: DraggableSheet(
child: content,
),
),
);
}
}

void main() {
testWidgets('Inherited controller should be attached', (tester) async {
final controller = SheetController();
await tester.pumpWidget(
SheetControllerScope(
controller: controller,
child: const _TestWidget(),
),
);

expect(controller.hasClient, isTrue,
reason: 'The controller should have a client.');
});
}
27 changes: 27 additions & 0 deletions package/test/navigation/navigation_sheet_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:smooth_sheets/smooth_sheets.dart';
import 'package:smooth_sheets/src/foundation/sheet_controller.dart';

class _TestWidget extends StatelessWidget {
const _TestWidget(
Expand Down Expand Up @@ -244,6 +245,32 @@ void main() {
},
);

// Regression test for https://github.com/fujidaiti/smooth_sheets/issues/168
testWidgets('Inherited controller should be attached', (tester) async {
final controller = SheetController();
await tester.pumpWidget(
SheetControllerScope(
controller: controller,
child: _TestWidget(
transitionObserver,
initialRoute: 'first',
routes: {
'first': () => _TestDraggablePageWidget.createRoute(
key: const Key('First'),
label: 'First',
height: 300,
minExtent: const Extent.pixels(0),
physics: const ClampingSheetPhysics(),
),
},
),
),
);

expect(controller.hasClient, isTrue,
reason: 'The controller should have a client.');
});

// Regression test for https://github.com/fujidaiti/smooth_sheets/issues/139
testWidgets(
'Works with DropdownButton without crashing',
Expand Down
62 changes: 62 additions & 0 deletions package/test/scrollable/scrollable_sheet_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:smooth_sheets/smooth_sheets.dart';
import 'package:smooth_sheets/src/foundation/sheet_controller.dart';

class _TestWidget extends StatelessWidget {
const _TestWidget({
this.contentKey,
this.contentHeight,
this.scrollPhysics = const ClampingScrollPhysics(),
});

final Key? contentKey;
final double? contentHeight;
final ScrollPhysics scrollPhysics;

@override
Widget build(BuildContext context) {
Widget content = Material(
color: Colors.white,
child: ListView(
key: contentKey,
physics: scrollPhysics,
children: List.generate(
30,
(index) => ListTile(
title: Text('Item $index'),
),
),
),
);

if (contentHeight case final height?) {
content = SizedBox(height: height, child: content);
}

return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: ScrollableSheet(
child: content,
),
),
);
}
}

void main() {
testWidgets('Inherited controller should be attached', (tester) async {
final controller = SheetController();
await tester.pumpWidget(
SheetControllerScope(
controller: controller,
child: const _TestWidget(),
),
);

expect(controller.hasClient, isTrue,
reason: 'The controller should have a client.');
});
}
Loading