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

Refactor: Lift sheet context up #201

Merged
merged 1 commit into from
Jul 23, 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
25 changes: 17 additions & 8 deletions package/lib/src/draggable/draggable_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_controller.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_gesture_tamperer.dart';
Expand All @@ -15,7 +16,7 @@ import 'sheet_draggable.dart';
///
/// Note that this widget does not work with scrollable widgets.
/// Instead, use [ScrollableSheet] for this usecase.
class DraggableSheet extends StatelessWidget {
class DraggableSheet extends StatefulWidget {
/// Creates a sheet that can be dragged.
///
/// The maximum height will be equal to the [child]'s height.
Expand Down Expand Up @@ -61,26 +62,34 @@ class DraggableSheet extends StatelessWidget {
/// This value will be passed to the constructor of internal [SheetDraggable].
final HitTestBehavior hitTestBehavior;

@override
State<DraggableSheet> createState() => _DraggableSheetState();
}

class _DraggableSheetState extends State<DraggableSheet>
with TickerProviderStateMixin, SheetContextStateMixin<DraggableSheet> {
@override
Widget build(BuildContext context) {
final theme = SheetTheme.maybeOf(context);
final physics = this.physics ?? theme?.physics ?? kDefaultSheetPhysics;
final physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics;
final gestureTamper = TamperSheetGesture.maybeOf(context);
final controller = this.controller ?? SheetControllerScope.maybeOf(context);
final controller =
widget.controller ?? SheetControllerScope.maybeOf(context);

return DraggableSheetExtentScope(
context: this,
controller: controller,
initialExtent: initialExtent,
minExtent: minExtent,
maxExtent: maxExtent,
initialExtent: widget.initialExtent,
minExtent: widget.minExtent,
maxExtent: widget.maxExtent,
physics: physics,
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'DraggableSheet' : null,
child: SheetViewport(
child: SheetContentViewport(
child: SheetDraggable(
behavior: hitTestBehavior,
child: child,
behavior: widget.hitTestBehavior,
child: widget.child,
),
),
),
Expand Down
2 changes: 2 additions & 0 deletions package/lib/src/draggable/draggable_sheet_extent_scope.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_extent_scope.dart';
import 'draggable_sheet_extent.dart';
Expand All @@ -10,6 +11,7 @@ class DraggableSheetExtentScope extends SheetExtentScope {
super.key,
super.controller,
super.isPrimary,
required super.context,
required this.initialExtent,
required super.minExtent,
required super.maxExtent,
Expand Down
23 changes: 23 additions & 0 deletions package/lib/src/foundation/sheet_context.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

import 'sheet_extent.dart';

/// An interface that provides a set of dependencies required by [SheetExtent].
@internal
abstract class SheetContext {
TickerProvider get vsync;
BuildContext? get notificationContext;
}

@internal
@optionalTypeArgs
mixin SheetContextStateMixin<T extends StatefulWidget>
on State<T>, TickerProviderStateMixin<T>
implements SheetContext {
@override
TickerProvider get vsync => this;

@override
BuildContext? get notificationContext => mounted ? context : null;
}
10 changes: 1 addition & 9 deletions package/lib/src/foundation/sheet_extent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:meta/meta.dart';

import '../internal/double_utils.dart';
import 'sheet_activity.dart';
import 'sheet_context.dart';
import 'sheet_controller.dart';
import 'sheet_drag.dart';
import 'sheet_extent_scope.dart';
Expand Down Expand Up @@ -744,12 +745,3 @@ class SheetMetrics {
viewportInsets: maybeViewportInsets,
).toString();
}

/// An interface that provides the necessary context to a [SheetExtent].
///
/// Typically, [State]s that host a [SheetExtent] will implement this interface.
@internal
abstract class SheetContext {
TickerProvider get vsync;
BuildContext? get notificationContext;
}
22 changes: 10 additions & 12 deletions package/lib/src/foundation/sheet_extent_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

import 'sheet_context.dart';
import 'sheet_controller.dart';
import 'sheet_extent.dart';
import 'sheet_gesture_tamperer.dart';
Expand Down Expand Up @@ -53,6 +54,7 @@ abstract class SheetExtentScope extends StatefulWidget {
/// Creates a widget that hosts a [SheetExtent].
const SheetExtentScope({
super.key,
required this.context,
this.controller,
this.isPrimary = true,
required this.minExtent,
Expand All @@ -62,6 +64,9 @@ abstract class SheetExtentScope extends StatefulWidget {
required this.child,
});

/// The context the extent object belongs to.
final SheetContext context;

/// The [SheetController] attached to the [SheetExtent].
final SheetController? controller;

Expand Down Expand Up @@ -120,18 +125,10 @@ abstract class SheetExtentScope extends StatefulWidget {

@internal
abstract class SheetExtentScopeState<E extends SheetExtent,
W extends SheetExtentScope> extends State<W>
with TickerProviderStateMixin
implements SheetContext {
W extends SheetExtentScope> extends State<W> {
late E _extent;
SheetController? _controller;

@override
TickerProvider get vsync => this;

@override
BuildContext? get notificationContext => mounted ? context : null;

SheetExtentScopeKey<E>? get _scopeKey {
assert(() {
if (widget.key != null && widget.key is! SheetExtentScopeKey<E>) {
Expand All @@ -152,7 +149,7 @@ abstract class SheetExtentScopeState<E extends SheetExtent,
@override
void initState() {
super.initState();
_extent = buildExtent(this);
_extent = buildExtent(widget.context);
_scopeKey?._notifySheetExtentCreation();
}

Expand All @@ -176,7 +173,7 @@ abstract class SheetExtentScopeState<E extends SheetExtent,
_rewireControllerAndScope();
if (shouldRebuildExtent(_extent)) {
final oldExtent = _extent;
_extent = buildExtent(this)..takeOver(oldExtent);
_extent = buildExtent(widget.context)..takeOver(oldExtent);
_scopeKey?._notifySheetExtentCreation();
_disposeExtent(oldExtent);
_rewireControllerAndExtent();
Expand All @@ -198,7 +195,8 @@ abstract class SheetExtentScopeState<E extends SheetExtent,
E buildExtent(SheetContext context);

@protected
bool shouldRebuildExtent(E oldExtent) => false;
@mustCallSuper
bool shouldRebuildExtent(E oldExtent) => widget.context != oldExtent.context;

void _disposeExtent(E extent) {
_controller?.detach(extent);
Expand Down
20 changes: 19 additions & 1 deletion package/lib/src/navigation/navigation_route.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_extent_scope.dart';
import '../foundation/sheet_viewport.dart';
Expand Down Expand Up @@ -125,6 +126,7 @@ abstract class NavigationSheetRoute<T, E extends SheetExtent>
}

typedef ExtentScopeBuilder = SheetExtentScope Function(
SheetContext context,
SheetExtentScopeKey key,
Widget child,
);
Expand All @@ -143,10 +145,15 @@ class NavigationSheetRouteContent extends StatelessWidget {
Widget build(BuildContext context) {
assert(_debugAssertDependencies(context));
final parentRoute = ModalRoute.of(context)! as NavigationSheetRoute;
final globalExtent = SheetExtentScope.of<NavigationSheetExtent>(context);
final routeViewport = NavigationSheetRouteViewport(
child: SheetContentViewport(child: child),
);
final localScope = scopeBuilder(parentRoute.scopeKey, routeViewport);
final localScope = scopeBuilder(
globalExtent.context,
parentRoute.scopeKey,
routeViewport,
);
assert(_debugAssertScope(localScope, parentRoute.scopeKey, routeViewport));
return localScope;
}
Expand Down Expand Up @@ -185,6 +192,17 @@ class NavigationSheetRouteContent extends StatelessWidget {
bool _debugAssertDependencies(BuildContext context) {
assert(
() {
final globalExtent =
SheetExtentScope.maybeOf<NavigationSheetExtent>(context);
if (globalExtent == null) {
throw FlutterError(
'A $SheetExtentScope that hosts a $NavigationSheetExtent '
'is not found in the given context. This is likely because '
'this $NavigationSheetRouteContent is not in the subtree of '
'the navigator enclosed by a $NavigationSheet.',
);
}

final parentRoute = ModalRoute.of(context);
if (parentRoute is NavigationSheetRoute) {
return true;
Expand Down
6 changes: 4 additions & 2 deletions package/lib/src/navigation/navigation_routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ class _ScrollableNavigationSheetRouteContent extends StatelessWidget {
final gestureTamper = TamperSheetGesture.maybeOf(context);

return NavigationSheetRouteContent(
scopeBuilder: (key, child) {
scopeBuilder: (context, key, child) {
return ScrollableSheetExtentScope(
key: key,
context: context,
isPrimary: false,
initialExtent: initialExtent,
minExtent: minExtent,
Expand Down Expand Up @@ -78,9 +79,10 @@ class _DraggableNavigationSheetRouteContent extends StatelessWidget {
final gestureTamper = TamperSheetGesture.maybeOf(context);

return NavigationSheetRouteContent(
scopeBuilder: (key, child) {
scopeBuilder: (context, key, child) {
return DraggableSheetExtentScope(
key: key,
context: context,
isPrimary: false,
initialExtent: initialExtent,
minExtent: minExtent,
Expand Down
7 changes: 6 additions & 1 deletion package/lib/src/navigation/navigation_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_controller.dart';
import '../foundation/sheet_extent_scope.dart';
import '../foundation/sheet_gesture_tamperer.dart';
Expand Down Expand Up @@ -31,7 +32,10 @@ class NavigationSheet extends StatefulWidget with TransitionAwareWidgetMixin {
}

class _NavigationSheetState extends State<NavigationSheet>
with TransitionAwareStateMixin {
with
TransitionAwareStateMixin,
TickerProviderStateMixin,
SheetContextStateMixin {
final _scopeKey = SheetExtentScopeKey<NavigationSheetExtent>(
debugLabel: kDebugMode ? 'NavigationSheet' : null,
);
Expand All @@ -49,6 +53,7 @@ class _NavigationSheetState extends State<NavigationSheet>

return NavigationSheetExtentScope(
key: _scopeKey,
context: this,
controller: controller,
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'NavigationSheet' : null,
Expand Down
2 changes: 2 additions & 0 deletions package/lib/src/navigation/navigation_sheet_extent_scope.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_extent_scope.dart';
import '../foundation/sheet_physics.dart';
Expand All @@ -11,6 +12,7 @@ class NavigationSheetExtentScope extends SheetExtentScope {
super.key,
super.controller,
super.gestureTamperer,
required super.context,
this.debugLabel,
required super.child,
}) : super(
Expand Down
23 changes: 16 additions & 7 deletions package/lib/src/scrollable/scrollable_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_controller.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_gesture_tamperer.dart';
Expand All @@ -12,7 +13,7 @@ import '../foundation/sheet_viewport.dart';
import 'scrollable_sheet_extent_scope.dart';
import 'sheet_scrollable.dart';

class ScrollableSheet extends StatelessWidget {
class ScrollableSheet extends StatefulWidget {
const ScrollableSheet({
super.key,
this.initialExtent = const Extent.proportional(1),
Expand Down Expand Up @@ -41,24 +42,32 @@ class ScrollableSheet extends StatelessWidget {
/// The content of the sheet.
final Widget child;

@override
State<ScrollableSheet> createState() => _ScrollableSheetState();
}

class _ScrollableSheetState extends State<ScrollableSheet>
with TickerProviderStateMixin, SheetContextStateMixin {
@override
Widget build(BuildContext context) {
final theme = SheetTheme.maybeOf(context);
final physics = this.physics ?? theme?.physics ?? kDefaultSheetPhysics;
final physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics;
final gestureTamper = TamperSheetGesture.maybeOf(context);
final controller = this.controller ?? SheetControllerScope.maybeOf(context);
final controller =
widget.controller ?? SheetControllerScope.maybeOf(context);

return ScrollableSheetExtentScope(
context: this,
controller: controller,
initialExtent: initialExtent,
minExtent: minExtent,
maxExtent: maxExtent,
initialExtent: widget.initialExtent,
minExtent: widget.minExtent,
maxExtent: widget.maxExtent,
physics: physics,
gestureTamperer: gestureTamper,
debugLabel: kDebugMode ? 'ScrollableSheet' : null,
child: SheetViewport(
child: SheetContentViewport(
child: ScrollableSheetContent(child: child),
child: ScrollableSheetContent(child: widget.child),
),
),
);
Expand Down
2 changes: 2 additions & 0 deletions package/lib/src/scrollable/scrollable_sheet_extent_scope.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../foundation/sheet_context.dart';
import '../foundation/sheet_extent.dart';
import '../foundation/sheet_extent_scope.dart';
import 'scrollable_sheet_extent.dart';
Expand All @@ -10,6 +11,7 @@ class ScrollableSheetExtentScope extends SheetExtentScope {
super.key,
super.controller,
super.isPrimary,
required super.context,
required this.initialExtent,
required super.minExtent,
required super.maxExtent,
Expand Down
1 change: 1 addition & 0 deletions package/test/foundation/sheet_viewport_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/src/foundation/sheet_activity.dart';
import 'package:smooth_sheets/src/foundation/sheet_context.dart';
import 'package:smooth_sheets/src/foundation/sheet_extent.dart';
import 'package:smooth_sheets/src/foundation/sheet_extent_scope.dart';
import 'package:smooth_sheets/src/foundation/sheet_physics.dart';
Expand Down
Loading