Showing
16 changed files
with
704 additions
and
239 deletions
@@ -13,7 +13,7 @@ jobs: | @@ -13,7 +13,7 @@ jobs: | ||
13 | uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 | 13 | uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 |
14 | with: | 14 | with: |
15 | working_directory: sheet | 15 | working_directory: sheet |
16 | - min_coverage: 60 # Working to reach 100% XD | 16 | + min_coverage: 70 # Working to reach 100% XD |
17 | 17 | ||
18 | pana: | 18 | pana: |
19 | uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/pana.yml@v1 | 19 | uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/pana.yml@v1 |
@@ -21,6 +21,6 @@ | @@ -21,6 +21,6 @@ | ||
21 | <key>CFBundleVersion</key> | 21 | <key>CFBundleVersion</key> |
22 | <string>1.0</string> | 22 | <string>1.0</string> |
23 | <key>MinimumOSVersion</key> | 23 | <key>MinimumOSVersion</key> |
24 | - <string>9.0</string> | 24 | + <string>11.0</string> |
25 | </dict> | 25 | </dict> |
26 | </plist> | 26 | </plist> |
@@ -272,7 +272,7 @@ | @@ -272,7 +272,7 @@ | ||
272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; |
273 | GCC_WARN_UNUSED_FUNCTION = YES; | 273 | GCC_WARN_UNUSED_FUNCTION = YES; |
274 | GCC_WARN_UNUSED_VARIABLE = YES; | 274 | GCC_WARN_UNUSED_VARIABLE = YES; |
275 | - IPHONEOS_DEPLOYMENT_TARGET = 9.0; | 275 | + IPHONEOS_DEPLOYMENT_TARGET = 11.0; |
276 | MTL_ENABLE_DEBUG_INFO = NO; | 276 | MTL_ENABLE_DEBUG_INFO = NO; |
277 | SDKROOT = iphoneos; | 277 | SDKROOT = iphoneos; |
278 | SUPPORTED_PLATFORMS = iphoneos; | 278 | SUPPORTED_PLATFORMS = iphoneos; |
@@ -358,7 +358,7 @@ | @@ -358,7 +358,7 @@ | ||
358 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | 358 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; |
359 | GCC_WARN_UNUSED_FUNCTION = YES; | 359 | GCC_WARN_UNUSED_FUNCTION = YES; |
360 | GCC_WARN_UNUSED_VARIABLE = YES; | 360 | GCC_WARN_UNUSED_VARIABLE = YES; |
361 | - IPHONEOS_DEPLOYMENT_TARGET = 9.0; | 361 | + IPHONEOS_DEPLOYMENT_TARGET = 11.0; |
362 | MTL_ENABLE_DEBUG_INFO = YES; | 362 | MTL_ENABLE_DEBUG_INFO = YES; |
363 | ONLY_ACTIVE_ARCH = YES; | 363 | ONLY_ACTIVE_ARCH = YES; |
364 | SDKROOT = iphoneos; | 364 | SDKROOT = iphoneos; |
@@ -407,7 +407,7 @@ | @@ -407,7 +407,7 @@ | ||
407 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | 407 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; |
408 | GCC_WARN_UNUSED_FUNCTION = YES; | 408 | GCC_WARN_UNUSED_FUNCTION = YES; |
409 | GCC_WARN_UNUSED_VARIABLE = YES; | 409 | GCC_WARN_UNUSED_VARIABLE = YES; |
410 | - IPHONEOS_DEPLOYMENT_TARGET = 9.0; | 410 | + IPHONEOS_DEPLOYMENT_TARGET = 11.0; |
411 | MTL_ENABLE_DEBUG_INFO = NO; | 411 | MTL_ENABLE_DEBUG_INFO = NO; |
412 | SDKROOT = iphoneos; | 412 | SDKROOT = iphoneos; |
413 | SUPPORTED_PLATFORMS = iphoneos; | 413 | SUPPORTED_PLATFORMS = iphoneos; |
@@ -4,41 +4,25 @@ import 'package:flutter/material.dart'; | @@ -4,41 +4,25 @@ import 'package:flutter/material.dart'; | ||
4 | import 'package:flutter/widgets.dart'; | 4 | import 'package:flutter/widgets.dart'; |
5 | import 'package:sheet/sheet.dart'; | 5 | import 'package:sheet/sheet.dart'; |
6 | 6 | ||
7 | -class FitSheet extends StatefulWidget { | ||
8 | - @override | ||
9 | - _FitSheetState createState() => _FitSheetState(); | ||
10 | -} | ||
11 | - | ||
12 | -class _FitSheetState extends State<FitSheet> { | ||
13 | - final SheetController controller = SheetController(); | ||
14 | - | ||
15 | - @override | ||
16 | - void initState() { | ||
17 | - Future<void>.delayed(const Duration(milliseconds: 400), animateSheet); | ||
18 | - | ||
19 | - super.initState(); | ||
20 | - } | ||
21 | - | ||
22 | - void animateSheet() { | ||
23 | - controller.relativeAnimateTo(1, | ||
24 | - duration: const Duration(milliseconds: 400), curve: Curves.easeOut); | ||
25 | - } | ||
26 | - | ||
27 | - @override | ||
28 | - void dispose() { | ||
29 | - controller.dispose(); | ||
30 | - super.dispose(); | ||
31 | - } | ||
32 | - | 7 | +class FitSheet extends StatelessWidget { |
33 | @override | 8 | @override |
34 | Widget build(BuildContext context) { | 9 | Widget build(BuildContext context) { |
35 | - return Sheet( | ||
36 | - elevation: 4, | ||
37 | - child: Container( | ||
38 | - height: 400, | ||
39 | - child: const Text('hello'), | 10 | + return DefaultSheetController( |
11 | + onCreated: (controller) async { | ||
12 | + await Future<void>.delayed(const Duration(milliseconds: 400)); | ||
13 | + controller.relativeAnimateTo( | ||
14 | + 1, | ||
15 | + duration: const Duration(milliseconds: 400), | ||
16 | + curve: Curves.easeOut, | ||
17 | + ); | ||
18 | + }, | ||
19 | + child: Sheet( | ||
20 | + elevation: 4, | ||
21 | + child: Container( | ||
22 | + height: 400, | ||
23 | + child: const Text('hello'), | ||
24 | + ), | ||
40 | ), | 25 | ), |
41 | - controller: controller, | ||
42 | ); | 26 | ); |
43 | } | 27 | } |
44 | } | 28 | } |
@@ -7,7 +7,7 @@ packages: | @@ -7,7 +7,7 @@ packages: | ||
7 | name: async | 7 | name: async |
8 | url: "https://pub.dartlang.org" | 8 | url: "https://pub.dartlang.org" |
9 | source: hosted | 9 | source: hosted |
10 | - version: "2.8.2" | 10 | + version: "2.9.0" |
11 | boolean_selector: | 11 | boolean_selector: |
12 | dependency: transitive | 12 | dependency: transitive |
13 | description: | 13 | description: |
@@ -21,21 +21,14 @@ packages: | @@ -21,21 +21,14 @@ packages: | ||
21 | name: characters | 21 | name: characters |
22 | url: "https://pub.dartlang.org" | 22 | url: "https://pub.dartlang.org" |
23 | source: hosted | 23 | source: hosted |
24 | - version: "1.2.0" | ||
25 | - charcode: | ||
26 | - dependency: transitive | ||
27 | - description: | ||
28 | - name: charcode | ||
29 | - url: "https://pub.dartlang.org" | ||
30 | - source: hosted | ||
31 | - version: "1.3.1" | 24 | + version: "1.2.1" |
32 | clock: | 25 | clock: |
33 | dependency: transitive | 26 | dependency: transitive |
34 | description: | 27 | description: |
35 | name: clock | 28 | name: clock |
36 | url: "https://pub.dartlang.org" | 29 | url: "https://pub.dartlang.org" |
37 | source: hosted | 30 | source: hosted |
38 | - version: "1.1.0" | 31 | + version: "1.1.1" |
39 | collection: | 32 | collection: |
40 | dependency: transitive | 33 | dependency: transitive |
41 | description: | 34 | description: |
@@ -63,7 +56,7 @@ packages: | @@ -63,7 +56,7 @@ packages: | ||
63 | name: fake_async | 56 | name: fake_async |
64 | url: "https://pub.dartlang.org" | 57 | url: "https://pub.dartlang.org" |
65 | source: hosted | 58 | source: hosted |
66 | - version: "1.3.0" | 59 | + version: "1.3.1" |
67 | flutter: | 60 | flutter: |
68 | dependency: "direct main" | 61 | dependency: "direct main" |
69 | description: flutter | 62 | description: flutter |
@@ -106,21 +99,21 @@ packages: | @@ -106,21 +99,21 @@ packages: | ||
106 | name: matcher | 99 | name: matcher |
107 | url: "https://pub.dartlang.org" | 100 | url: "https://pub.dartlang.org" |
108 | source: hosted | 101 | source: hosted |
109 | - version: "0.12.11" | 102 | + version: "0.12.12" |
110 | material_color_utilities: | 103 | material_color_utilities: |
111 | dependency: transitive | 104 | dependency: transitive |
112 | description: | 105 | description: |
113 | name: material_color_utilities | 106 | name: material_color_utilities |
114 | url: "https://pub.dartlang.org" | 107 | url: "https://pub.dartlang.org" |
115 | source: hosted | 108 | source: hosted |
116 | - version: "0.1.4" | 109 | + version: "0.1.5" |
117 | meta: | 110 | meta: |
118 | dependency: transitive | 111 | dependency: transitive |
119 | description: | 112 | description: |
120 | name: meta | 113 | name: meta |
121 | url: "https://pub.dartlang.org" | 114 | url: "https://pub.dartlang.org" |
122 | source: hosted | 115 | source: hosted |
123 | - version: "1.7.0" | 116 | + version: "1.8.0" |
124 | nested: | 117 | nested: |
125 | dependency: transitive | 118 | dependency: transitive |
126 | description: | 119 | description: |
@@ -134,7 +127,7 @@ packages: | @@ -134,7 +127,7 @@ packages: | ||
134 | name: path | 127 | name: path |
135 | url: "https://pub.dartlang.org" | 128 | url: "https://pub.dartlang.org" |
136 | source: hosted | 129 | source: hosted |
137 | - version: "1.8.1" | 130 | + version: "1.8.2" |
138 | provider: | 131 | provider: |
139 | dependency: "direct main" | 132 | dependency: "direct main" |
140 | description: | 133 | description: |
@@ -160,7 +153,7 @@ packages: | @@ -160,7 +153,7 @@ packages: | ||
160 | name: source_span | 153 | name: source_span |
161 | url: "https://pub.dartlang.org" | 154 | url: "https://pub.dartlang.org" |
162 | source: hosted | 155 | source: hosted |
163 | - version: "1.8.2" | 156 | + version: "1.9.0" |
164 | stack_trace: | 157 | stack_trace: |
165 | dependency: transitive | 158 | dependency: transitive |
166 | description: | 159 | description: |
@@ -181,21 +174,21 @@ packages: | @@ -181,21 +174,21 @@ packages: | ||
181 | name: string_scanner | 174 | name: string_scanner |
182 | url: "https://pub.dartlang.org" | 175 | url: "https://pub.dartlang.org" |
183 | source: hosted | 176 | source: hosted |
184 | - version: "1.1.0" | 177 | + version: "1.1.1" |
185 | term_glyph: | 178 | term_glyph: |
186 | dependency: transitive | 179 | dependency: transitive |
187 | description: | 180 | description: |
188 | name: term_glyph | 181 | name: term_glyph |
189 | url: "https://pub.dartlang.org" | 182 | url: "https://pub.dartlang.org" |
190 | source: hosted | 183 | source: hosted |
191 | - version: "1.2.0" | 184 | + version: "1.2.1" |
192 | test_api: | 185 | test_api: |
193 | dependency: transitive | 186 | dependency: transitive |
194 | description: | 187 | description: |
195 | name: test_api | 188 | name: test_api |
196 | url: "https://pub.dartlang.org" | 189 | url: "https://pub.dartlang.org" |
197 | source: hosted | 190 | source: hosted |
198 | - version: "0.4.9" | 191 | + version: "0.4.12" |
199 | vector_math: | 192 | vector_math: |
200 | dependency: transitive | 193 | dependency: transitive |
201 | description: | 194 | description: |
@@ -205,4 +198,4 @@ packages: | @@ -205,4 +198,4 @@ packages: | ||
205 | version: "2.1.2" | 198 | version: "2.1.2" |
206 | sdks: | 199 | sdks: |
207 | dart: ">=2.17.0 <3.0.0" | 200 | dart: ">=2.17.0 <3.0.0" |
208 | - flutter: ">=3.0.0" | 201 | + flutter: ">=3.3.0" |
@@ -2,3 +2,5 @@ export 'src/physics.dart'; | @@ -2,3 +2,5 @@ export 'src/physics.dart'; | ||
2 | export 'src/scroll_controller.dart'; | 2 | export 'src/scroll_controller.dart'; |
3 | export 'src/scrollable.dart'; | 3 | export 'src/scrollable.dart'; |
4 | export 'src/sheet.dart'; | 4 | export 'src/sheet.dart'; |
5 | +export 'src/widgets/default_sheet_controller.dart'; | ||
6 | +export 'src/widgets/sheet_media_query.dart'; |
@@ -272,7 +272,7 @@ class Sheet extends StatelessWidget { | @@ -272,7 +272,7 @@ class Sheet extends StatelessWidget { | ||
272 | offset: offset, | 272 | offset: offset, |
273 | minExtent: minResizableExtent ?? 0, | 273 | minExtent: minResizableExtent ?? 0, |
274 | child: Builder( | 274 | child: Builder( |
275 | - key: const Key('_sheet_builder'), | 275 | + key: const Key('_sheet_child'), |
276 | builder: (BuildContext context) { | 276 | builder: (BuildContext context) { |
277 | return decorationBuilder( | 277 | return decorationBuilder( |
278 | context, | 278 | context, |
@@ -290,45 +290,6 @@ class Sheet extends StatelessWidget { | @@ -290,45 +290,6 @@ class Sheet extends StatelessWidget { | ||
290 | } | 290 | } |
291 | } | 291 | } |
292 | 292 | ||
293 | -class SheetMediaQuery extends StatelessWidget { | ||
294 | - const SheetMediaQuery({Key? key, required this.child}) : super(key: key); | ||
295 | - | ||
296 | - final Widget child; | ||
297 | - | ||
298 | - @override | ||
299 | - Widget build(BuildContext context) { | ||
300 | - final SheetPosition position = Sheet.of(context)!.controller.position; | ||
301 | - final EdgeInsets padding = MediaQuery.of(context).padding; | ||
302 | - return LayoutBuilder( | ||
303 | - builder: ( | ||
304 | - BuildContext context, | ||
305 | - BoxConstraints constraints, | ||
306 | - ) { | ||
307 | - return AnimatedBuilder( | ||
308 | - animation: position, | ||
309 | - builder: (BuildContext context, Widget? child) { | ||
310 | - final double pixels = position.hasPixels ? position.pixels : 0; | ||
311 | - final double viewportDimension = position.hasViewportDimension | ||
312 | - ? position.viewportDimension | ||
313 | - : double.infinity; | ||
314 | - final double top = math.max( | ||
315 | - 0.0, | ||
316 | - padding.top - (viewportDimension - pixels), | ||
317 | - ); | ||
318 | - return MediaQuery( | ||
319 | - data: MediaQuery.of(context).copyWith( | ||
320 | - padding: padding.copyWith(top: top), | ||
321 | - ), | ||
322 | - child: child!, | ||
323 | - ); | ||
324 | - }, | ||
325 | - child: child, | ||
326 | - ); | ||
327 | - }, | ||
328 | - ); | ||
329 | - } | ||
330 | -} | ||
331 | - | ||
332 | class _DefaultSheetScrollController extends StatelessWidget { | 293 | class _DefaultSheetScrollController extends StatelessWidget { |
333 | const _DefaultSheetScrollController({Key? key, required this.child}) | 294 | const _DefaultSheetScrollController({Key? key, required this.child}) |
334 | : super(key: key); | 295 | : super(key: key); |
@@ -436,61 +397,6 @@ class SheetController extends ScrollController { | @@ -436,61 +397,6 @@ class SheetController extends ScrollController { | ||
436 | } | 397 | } |
437 | } | 398 | } |
438 | 399 | ||
439 | -typedef SheetControllerCallback = void Function(SheetController controller); | ||
440 | - | ||
441 | -class DefaultSheetController extends StatefulWidget { | ||
442 | - const DefaultSheetController({Key? key, required this.child, this.onCreated}) | ||
443 | - : super(key: key); | ||
444 | - | ||
445 | - final Widget child; | ||
446 | - | ||
447 | - final SheetControllerCallback? onCreated; | ||
448 | - | ||
449 | - static SheetController? of(BuildContext context) { | ||
450 | - return context | ||
451 | - .dependOnInheritedWidgetOfExactType<_InheritedSheetController>() | ||
452 | - ?.controller; | ||
453 | - } | ||
454 | - | ||
455 | - @override | ||
456 | - State<DefaultSheetController> createState() => _DefaultSheetControllerState(); | ||
457 | -} | ||
458 | - | ||
459 | -class _DefaultSheetControllerState extends State<DefaultSheetController> { | ||
460 | - late final SheetController controller = SheetController(); | ||
461 | - | ||
462 | - @override | ||
463 | - void initState() { | ||
464 | - widget.onCreated?.call(controller); | ||
465 | - super.initState(); | ||
466 | - } | ||
467 | - | ||
468 | - @override | ||
469 | - Widget build(BuildContext context) { | ||
470 | - return _InheritedSheetController( | ||
471 | - child: widget.child, controller: controller); | ||
472 | - } | ||
473 | - | ||
474 | - @override | ||
475 | - void dispose() { | ||
476 | - controller.dispose(); | ||
477 | - super.dispose(); | ||
478 | - } | ||
479 | -} | ||
480 | - | ||
481 | -class _InheritedSheetController extends InheritedWidget { | ||
482 | - const _InheritedSheetController( | ||
483 | - {Key? key, required super.child, required this.controller}) | ||
484 | - : super(key: key); | ||
485 | - | ||
486 | - final SheetController controller; | ||
487 | - | ||
488 | - @override | ||
489 | - bool updateShouldNotify(_InheritedSheetController oldWidget) { | ||
490 | - return false; | ||
491 | - } | ||
492 | -} | ||
493 | - | ||
494 | /// A scroll position that manages scroll activities for | 400 | /// A scroll position that manages scroll activities for |
495 | /// [_SheetScrollController]. | 401 | /// [_SheetScrollController]. |
496 | /// | 402 | /// |
@@ -528,8 +434,11 @@ class SheetPosition extends ScrollPositionWithSingleContext { | @@ -528,8 +434,11 @@ class SheetPosition extends ScrollPositionWithSingleContext { | ||
528 | Future<void> relativeAnimateTo(double to, | 434 | Future<void> relativeAnimateTo(double to, |
529 | {required Duration duration, required Curve curve}) { | 435 | {required Duration duration, required Curve curve}) { |
530 | assert(to >= 0 && to <= 1); | 436 | assert(to >= 0 && to <= 1); |
531 | - return super | ||
532 | - .animateTo(to * maxScrollExtent, duration: duration, curve: curve); | 437 | + return super.animateTo( |
438 | + pixelsFromRelativeOffset(to, minScrollExtent, maxScrollExtent), | ||
439 | + duration: duration, | ||
440 | + curve: curve, | ||
441 | + ); | ||
533 | } | 442 | } |
534 | 443 | ||
535 | @override | 444 | @override |
@@ -541,7 +450,9 @@ class SheetPosition extends ScrollPositionWithSingleContext { | @@ -541,7 +450,9 @@ class SheetPosition extends ScrollPositionWithSingleContext { | ||
541 | 450 | ||
542 | void relativeJumpTo(double to) { | 451 | void relativeJumpTo(double to) { |
543 | assert(to >= 0 && to <= 1); | 452 | assert(to >= 0 && to <= 1); |
544 | - return super.jumpTo(to * maxScrollExtent); | 453 | + final value = |
454 | + pixelsFromRelativeOffset(to, minScrollExtent, maxScrollExtent); | ||
455 | + return super.jumpTo(value); | ||
545 | } | 456 | } |
546 | 457 | ||
547 | @override | 458 | @override |
@@ -567,13 +478,21 @@ class SheetPosition extends ScrollPositionWithSingleContext { | @@ -567,13 +478,21 @@ class SheetPosition extends ScrollPositionWithSingleContext { | ||
567 | 478 | ||
568 | @override | 479 | @override |
569 | double setPixels(double newPixels) { | 480 | double setPixels(double newPixels) { |
570 | - _controller.value = (newPixels / maxScrollExtent).clamp(0, 1); | 481 | + _controller.value = relativeOffsetFromPixels( |
482 | + newPixels, | ||
483 | + minScrollExtent, | ||
484 | + maxScrollExtent, | ||
485 | + ); | ||
571 | return super.setPixels(newPixels); | 486 | return super.setPixels(newPixels); |
572 | } | 487 | } |
573 | 488 | ||
574 | @override | 489 | @override |
575 | void forcePixels(double value) { | 490 | void forcePixels(double value) { |
576 | - _controller.value = (value / maxScrollExtent).clamp(0, 1); | 491 | + _controller.value = relativeOffsetFromPixels( |
492 | + value, | ||
493 | + minScrollExtent, | ||
494 | + maxScrollExtent, | ||
495 | + ); | ||
577 | super.forcePixels(value); | 496 | super.forcePixels(value); |
578 | } | 497 | } |
579 | 498 | ||
@@ -589,9 +508,34 @@ class SheetPosition extends ScrollPositionWithSingleContext { | @@ -589,9 +508,34 @@ class SheetPosition extends ScrollPositionWithSingleContext { | ||
589 | // Clamp initial extent to maxScrollExtent | 508 | // Clamp initial extent to maxScrollExtent |
590 | if (!hasContentDimensions) { | 509 | if (!hasContentDimensions) { |
591 | correctPixels(pixels.clamp(minScrollExtent, maxScrollExtent)); | 510 | correctPixels(pixels.clamp(minScrollExtent, maxScrollExtent)); |
511 | + _controller.value = relativeOffsetFromPixels( | ||
512 | + pixels, | ||
513 | + minScrollExtent, | ||
514 | + maxScrollExtent, | ||
515 | + ); | ||
592 | } | 516 | } |
593 | return super.applyContentDimensions(minScrollExtent, maxScrollExtent); | 517 | return super.applyContentDimensions(minScrollExtent, maxScrollExtent); |
594 | } | 518 | } |
519 | + | ||
520 | + static double relativeOffsetFromPixels( | ||
521 | + double pixels, | ||
522 | + double minScrollExtent, | ||
523 | + double maxScrollExtent, | ||
524 | + ) { | ||
525 | + if (minScrollExtent == maxScrollExtent) return 1; | ||
526 | + final value = | ||
527 | + ((pixels - minScrollExtent) / (maxScrollExtent - minScrollExtent)) | ||
528 | + .clamp(0.0, 1.0); | ||
529 | + return value; | ||
530 | + } | ||
531 | + | ||
532 | + static double pixelsFromRelativeOffset( | ||
533 | + double offset, | ||
534 | + double minScrollExtent, | ||
535 | + double maxScrollExtent, | ||
536 | + ) { | ||
537 | + return minScrollExtent + offset * (maxScrollExtent - minScrollExtent); | ||
538 | + } | ||
595 | } | 539 | } |
596 | 540 | ||
597 | class SheetViewport extends SingleChildRenderObjectWidget { | 541 | class SheetViewport extends SingleChildRenderObjectWidget { |
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:sheet/sheet.dart'; | ||
3 | + | ||
4 | +typedef SheetControllerCallback = void Function(SheetController controller); | ||
5 | + | ||
6 | +/// A widget that injets a [SheetController] that can be used by | ||
7 | +/// any [Sheet] children | ||
8 | +/// | ||
9 | +/// It is useful for creating initial animations | ||
10 | +/// ```dart | ||
11 | +/// DefaultSheetController( | ||
12 | +/// onCreated: (controller) => controller.play | ||
13 | +/// ) | ||
14 | +/// | ||
15 | +/// | ||
16 | +class DefaultSheetController extends StatefulWidget { | ||
17 | + const DefaultSheetController({Key? key, required this.child, this.onCreated}) | ||
18 | + : super(key: key); | ||
19 | + | ||
20 | + final Widget child; | ||
21 | + | ||
22 | + /// A callback called when the controller is created | ||
23 | + final SheetControllerCallback? onCreated; | ||
24 | + | ||
25 | + static SheetController? of(BuildContext context) { | ||
26 | + return context | ||
27 | + .dependOnInheritedWidgetOfExactType<_InheritedSheetController>() | ||
28 | + ?.controller; | ||
29 | + } | ||
30 | + | ||
31 | + @override | ||
32 | + State<DefaultSheetController> createState() => _DefaultSheetControllerState(); | ||
33 | +} | ||
34 | + | ||
35 | +class _DefaultSheetControllerState extends State<DefaultSheetController> { | ||
36 | + late final SheetController controller = SheetController(); | ||
37 | + | ||
38 | + @override | ||
39 | + void initState() { | ||
40 | + widget.onCreated?.call(controller); | ||
41 | + super.initState(); | ||
42 | + } | ||
43 | + | ||
44 | + @override | ||
45 | + Widget build(BuildContext context) { | ||
46 | + return _InheritedSheetController( | ||
47 | + child: widget.child, controller: controller); | ||
48 | + } | ||
49 | + | ||
50 | + @override | ||
51 | + void dispose() { | ||
52 | + controller.dispose(); | ||
53 | + super.dispose(); | ||
54 | + } | ||
55 | +} | ||
56 | + | ||
57 | +class _InheritedSheetController extends InheritedWidget { | ||
58 | + const _InheritedSheetController( | ||
59 | + {Key? key, required super.child, required this.controller}) | ||
60 | + : super(key: key); | ||
61 | + | ||
62 | + final SheetController controller; | ||
63 | + | ||
64 | + @override | ||
65 | + bool updateShouldNotify(_InheritedSheetController oldWidget) { | ||
66 | + return false; | ||
67 | + } | ||
68 | +} |
sheet/lib/src/widgets/sheet_media_query.dart
0 → 100644
1 | +import 'package:flutter/widgets.dart'; | ||
2 | +import 'package:sheet/sheet.dart'; | ||
3 | +import 'dart:math' as math; | ||
4 | + | ||
5 | +/// A widget that updates the top safe area proportionally to the position | ||
6 | +/// of the sheet | ||
7 | +/// | ||
8 | +/// If the sheet is below the top safe area the inner top padding will be 0. | ||
9 | +/// Once the sheet enters the top safe area the inner top padding will increase | ||
10 | +/// proportionally to the sheet offset. | ||
11 | +/// If the sheet is fully expanded to the top of the screen the top padding | ||
12 | +/// will be the same as the parent top safe area. | ||
13 | +class SheetMediaQuery extends StatelessWidget { | ||
14 | + const SheetMediaQuery({Key? key, required this.child}) : super(key: key); | ||
15 | + | ||
16 | + final Widget child; | ||
17 | + | ||
18 | + @override | ||
19 | + Widget build(BuildContext context) { | ||
20 | + final controller = Sheet.of(context)!.controller; | ||
21 | + final MediaQueryData data = MediaQuery.of(context); | ||
22 | + return LayoutBuilder(builder: (context, constraints) { | ||
23 | + return AnimatedBuilder( | ||
24 | + animation: controller.animation, | ||
25 | + builder: (BuildContext context, Widget? child) { | ||
26 | + final position = controller.position; | ||
27 | + final viewportDimension = position.hasViewportDimension | ||
28 | + ? position.viewportDimension | ||
29 | + : double.infinity; | ||
30 | + final pixels = position.hasPixels ? position.pixels : 0; | ||
31 | + final offset = viewportDimension - pixels; | ||
32 | + final topPadding = math.max(0.0, data.padding.top - offset); | ||
33 | + return MediaQuery( | ||
34 | + data: data.copyWith( | ||
35 | + padding: data.padding.copyWith( | ||
36 | + top: topPadding, | ||
37 | + ), | ||
38 | + ), | ||
39 | + child: child!, | ||
40 | + ); | ||
41 | + }, | ||
42 | + child: child, | ||
43 | + ); | ||
44 | + }); | ||
45 | + } | ||
46 | +} |
sheet/test/cases/animation_value_test.dart
0 → 100644
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:flutter_test/flutter_test.dart'; | ||
3 | +import 'package:sheet/sheet.dart'; | ||
4 | + | ||
5 | +import '../helpers.dart'; | ||
6 | + | ||
7 | +void main() { | ||
8 | + group('SheetController.animation', () { | ||
9 | + testWidgets('is 0 when starts in minExtent', (tester) async { | ||
10 | + await tester.pumpWidget( | ||
11 | + MaterialApp( | ||
12 | + home: Sheet( | ||
13 | + minExtent: 100, | ||
14 | + maxExtent: 400, | ||
15 | + initialExtent: 100, | ||
16 | + child: SizedBox(height: 400), | ||
17 | + ), | ||
18 | + ), | ||
19 | + ); | ||
20 | + expect(tester.getSheetController().animation.value, 0); | ||
21 | + }); | ||
22 | + | ||
23 | + testWidgets('is 1 when starts in maxExtent', (tester) async { | ||
24 | + await tester.pumpWidget( | ||
25 | + MaterialApp( | ||
26 | + home: Sheet( | ||
27 | + minExtent: 100, | ||
28 | + maxExtent: 400, | ||
29 | + initialExtent: 400, | ||
30 | + child: SizedBox(height: 400), | ||
31 | + ), | ||
32 | + ), | ||
33 | + ); | ||
34 | + expect(tester.getSheetController().animation.value, 1); | ||
35 | + }); | ||
36 | + testWidgets('is 0.5 when is between minExtent and maxExtent', | ||
37 | + (tester) async { | ||
38 | + await tester.pumpWidget( | ||
39 | + MaterialApp( | ||
40 | + home: Sheet( | ||
41 | + minExtent: 100, | ||
42 | + maxExtent: 300, | ||
43 | + initialExtent: 200, | ||
44 | + child: SizedBox(height: 300), | ||
45 | + ), | ||
46 | + ), | ||
47 | + ); | ||
48 | + | ||
49 | + expect(tester.getSheetController().animation.value, 0.5); | ||
50 | + }); | ||
51 | + | ||
52 | + testWidgets('is 1 when minExtent equals maxExtent', (tester) async { | ||
53 | + await tester.pumpWidget( | ||
54 | + MaterialApp( | ||
55 | + home: Sheet( | ||
56 | + minExtent: 100, | ||
57 | + maxExtent: 100, | ||
58 | + child: SizedBox(height: 100), | ||
59 | + ), | ||
60 | + ), | ||
61 | + ); | ||
62 | + expect(tester.getSheetController().animation.value, 1); | ||
63 | + }); | ||
64 | + | ||
65 | + testWidgets('updates to 0 when it goes to minExtent', (tester) async { | ||
66 | + await tester.pumpWidget( | ||
67 | + MaterialApp( | ||
68 | + home: Sheet( | ||
69 | + minExtent: 100, | ||
70 | + maxExtent: 400, | ||
71 | + initialExtent: 400, | ||
72 | + fit: SheetFit.expand, | ||
73 | + child: SizedBox(), | ||
74 | + ), | ||
75 | + ), | ||
76 | + ); | ||
77 | + expect(tester.getSheetController().animation.value, 1); | ||
78 | + tester.getSheetController().relativeJumpTo(0); | ||
79 | + await tester.pumpAndSettle(); | ||
80 | + expect(tester.getSheetController().animation.value, 0); | ||
81 | + }); | ||
82 | + | ||
83 | + testWidgets('updates to 1 when it goes to maxExtent', (tester) async { | ||
84 | + await tester.pumpWidget( | ||
85 | + MaterialApp( | ||
86 | + home: Sheet( | ||
87 | + minExtent: 100, | ||
88 | + maxExtent: 400, | ||
89 | + initialExtent: 100, | ||
90 | + child: SizedBox(height: 400), | ||
91 | + ), | ||
92 | + ), | ||
93 | + ); | ||
94 | + expect(tester.getSheetController().animation.value, 0); | ||
95 | + tester.getSheetController().relativeJumpTo(1); | ||
96 | + expect(tester.getSheetController().animation.value, 1); | ||
97 | + }); | ||
98 | + | ||
99 | + testWidgets('updates linearly', (tester) async { | ||
100 | + await tester.pumpWidget( | ||
101 | + MaterialApp( | ||
102 | + home: Sheet( | ||
103 | + minExtent: 100, | ||
104 | + maxExtent: 300, | ||
105 | + initialExtent: 100, | ||
106 | + child: SizedBox(height: 300), | ||
107 | + ), | ||
108 | + ), | ||
109 | + ); | ||
110 | + tester.getSheetController().relativeJumpTo(0.5); | ||
111 | + await tester.pumpAndSettle(); | ||
112 | + expect(tester.getSheetController().animation.value, 0.5); | ||
113 | + }); | ||
114 | + }); | ||
115 | +} |
@@ -9,132 +9,184 @@ import '../screen_size_test.dart'; | @@ -9,132 +9,184 @@ import '../screen_size_test.dart'; | ||
9 | 9 | ||
10 | void main() { | 10 | void main() { |
11 | group('SheetController', () { | 11 | group('SheetController', () { |
12 | - testWidgets('jumpTo', (WidgetTester tester) async { | ||
13 | - final GlobalKey key = GlobalKey(); | ||
14 | - | ||
15 | - await tester.pumpApp( | ||
16 | - Sheet( | ||
17 | - initialExtent: 200, | ||
18 | - child: Container( | ||
19 | - key: key, | ||
20 | - height: kScreenRect.height, | 12 | + const curve = Curves.easeInOut; |
13 | + const duration = Duration(milliseconds: 1); | ||
14 | + const child = SizedBox(height: kScreenHeight); | ||
15 | + | ||
16 | + group('jumpTo', () { | ||
17 | + testWidgets('a custom extent - 400', (WidgetTester tester) async { | ||
18 | + await tester.pumpApp( | ||
19 | + Sheet(child: child), | ||
20 | + ); | ||
21 | + | ||
22 | + tester.getSheetController().jumpTo(400); | ||
23 | + expect(tester.getSheetExtent(), 400); | ||
24 | + }); | ||
25 | + | ||
26 | + testWidgets('minExtent', (WidgetTester tester) async { | ||
27 | + await tester.pumpApp( | ||
28 | + Sheet( | ||
29 | + minExtent: 100, | ||
30 | + child: child, | ||
21 | ), | 31 | ), |
22 | - ), | ||
23 | - ); | ||
24 | - | ||
25 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
26 | - expect(tester.getSheetTop(), equals(kScreenRect.bottom - 200)); | ||
27 | - | ||
28 | - tester.getSheetController().jumpTo(400); | ||
29 | - | ||
30 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
31 | - expect(tester.getSheetTop(), kScreenRect.height - 400); | ||
32 | - | ||
33 | - tester.getSheetController().jumpTo(-100); | ||
34 | - | ||
35 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
36 | - expect(tester.getSheetTop(), equals(kScreenRect.height)); | ||
37 | - | ||
38 | - tester.getSheetController().jumpTo(kScreenRect.height + 100); | 32 | + ); |
33 | + | ||
34 | + tester.getSheetController().jumpTo(100); | ||
35 | + expect(tester.getSheetExtent(), 100); | ||
36 | + }); | ||
37 | + testWidgets('maxExtent', (WidgetTester tester) async { | ||
38 | + await tester.pumpApp( | ||
39 | + Sheet( | ||
40 | + maxExtent: 400, | ||
41 | + child: child, | ||
42 | + ), | ||
43 | + ); | ||
44 | + | ||
45 | + tester.getSheetController().jumpTo(400); | ||
46 | + expect(tester.getSheetExtent(), 400); | ||
47 | + }); | ||
48 | + | ||
49 | + testWidgets('less than 0 clamps to 0', (WidgetTester tester) async { | ||
50 | + await tester.pumpApp( | ||
51 | + Sheet(child: child), | ||
52 | + ); | ||
53 | + | ||
54 | + tester.getSheetController().jumpTo(-100); | ||
55 | + expect(tester.getSheetExtent(), equals(0)); | ||
56 | + }); | ||
57 | + testWidgets('less than minExtent clamps to minExtent', | ||
58 | + (WidgetTester tester) async { | ||
59 | + await tester.pumpApp( | ||
60 | + Sheet( | ||
61 | + minExtent: 100, | ||
62 | + initialExtent: 200, | ||
63 | + child: child, | ||
64 | + ), | ||
65 | + ); | ||
66 | + expect(tester.getSheetExtent(), equals(200)); | ||
67 | + | ||
68 | + tester.getSheetController().jumpTo(100); | ||
69 | + expect(tester.getSheetExtent(), equals(100)); | ||
70 | + }); | ||
71 | + | ||
72 | + testWidgets( | ||
73 | + 'more than than screenHeight clamps to screenHeight ' | ||
74 | + 'if maxExtent is null', (WidgetTester tester) async { | ||
75 | + await tester.pumpApp( | ||
76 | + Sheet( | ||
77 | + child: child, | ||
78 | + ), | ||
79 | + ); | ||
80 | + | ||
81 | + tester.getSheetController().jumpTo(kScreenHeight + 100); | ||
82 | + expect(tester.getSheetExtent(), equals(kScreenHeight)); | ||
83 | + }); | ||
84 | + testWidgets('more than maxExtent clamps to maxExtent', | ||
85 | + (WidgetTester tester) async { | ||
86 | + await tester.pumpApp( | ||
87 | + Sheet( | ||
88 | + maxExtent: 400, | ||
89 | + child: child, | ||
90 | + ), | ||
91 | + ); | ||
39 | 92 | ||
40 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
41 | - expect(tester.getSheetTop(), equals(0)); | 93 | + tester.getSheetController().jumpTo(500); |
94 | + expect(tester.getSheetExtent(), 400); | ||
95 | + }); | ||
42 | }); | 96 | }); |
43 | 97 | ||
44 | testWidgets('relativeJumpTo', (WidgetTester tester) async { | 98 | testWidgets('relativeJumpTo', (WidgetTester tester) async { |
45 | - final GlobalKey key = GlobalKey(); | ||
46 | - | ||
47 | await tester.pumpApp( | 99 | await tester.pumpApp( |
48 | Sheet( | 100 | Sheet( |
49 | initialExtent: 200, | 101 | initialExtent: 200, |
50 | child: Container( | 102 | child: Container( |
51 | - key: key, | ||
52 | - height: kScreenRect.height, | 103 | + height: kScreenHeight, |
53 | ), | 104 | ), |
54 | ), | 105 | ), |
55 | ); | 106 | ); |
56 | 107 | ||
57 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
58 | - expect(tester.getSheetTop(), equals(kScreenRect.bottom - 200)); | 108 | + expect(tester.getSheetExtent(), equals(200)); |
59 | 109 | ||
60 | tester.getSheetController().relativeJumpTo(1); | 110 | tester.getSheetController().relativeJumpTo(1); |
61 | - | ||
62 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
63 | - expect(tester.getSheetTop(), equals(0)); | 111 | + expect(tester.getSheetExtent(), equals(kScreenHeight)); |
64 | 112 | ||
65 | tester.getSheetController().relativeJumpTo(0.5); | 113 | tester.getSheetController().relativeJumpTo(0.5); |
66 | - | ||
67 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
68 | - expect(tester.getSheetTop(), equals(kScreenRect.bottom / 2)); | 114 | + expect(tester.getSheetExtent(), equals(kScreenHeight / 2)); |
69 | }); | 115 | }); |
70 | 116 | ||
71 | testWidgets('animateTo', (WidgetTester tester) async { | 117 | testWidgets('animateTo', (WidgetTester tester) async { |
72 | - final GlobalKey key = GlobalKey(); | ||
73 | - | ||
74 | await tester.pumpApp( | 118 | await tester.pumpApp( |
75 | Sheet( | 119 | Sheet( |
76 | initialExtent: 200, | 120 | initialExtent: 200, |
77 | child: Container( | 121 | child: Container( |
78 | - key: key, | ||
79 | - height: kScreenRect.height, | 122 | + height: kScreenHeight, |
80 | ), | 123 | ), |
81 | ), | 124 | ), |
82 | ); | 125 | ); |
83 | 126 | ||
84 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
85 | - expect(tester.getSheetTop(), equals(kScreenRect.height - 200)); | 127 | + expect(tester.getSheetExtent(), equals(200)); |
86 | 128 | ||
87 | - tester.getSheetController().animateTo(400, | ||
88 | - curve: Curves.easeInOut, duration: const Duration(milliseconds: 1)); | 129 | + tester |
130 | + .getSheetController() | ||
131 | + .animateTo(400, curve: curve, duration: duration); | ||
89 | await tester.pumpAndSettle(); | 132 | await tester.pumpAndSettle(); |
133 | + final controller = tester.getSheetController(); | ||
134 | + expect(tester.getSheetExtent(), equals(400)); | ||
90 | 135 | ||
91 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
92 | - expect(tester.getSheetTop(), equals(kScreenRect.bottom - 400)); | ||
93 | - | ||
94 | - tester.getSheetController().animateTo(-100, | ||
95 | - curve: Curves.easeInOut, duration: const Duration(milliseconds: 1)); | 136 | + controller.animateTo(-100, curve: curve, duration: duration); |
96 | await tester.pumpAndSettle(); | 137 | await tester.pumpAndSettle(); |
97 | 138 | ||
98 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
99 | - expect(tester.getSheetTop(), equals(kScreenRect.bottom)); | ||
100 | - tester.getSheetController().animateTo(kScreenRect.height + 100, | ||
101 | - curve: Curves.easeInOutCirc, | ||
102 | - duration: const Duration(milliseconds: 1)); | 139 | + expect(tester.getSheetExtent(), equals(0)); |
140 | + controller.animateTo( | ||
141 | + kScreenHeight + 100, | ||
142 | + curve: curve, | ||
143 | + duration: duration, | ||
144 | + ); | ||
103 | await tester.pumpAndSettle(); | 145 | await tester.pumpAndSettle(); |
104 | - | ||
105 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
106 | - expect(tester.getSheetTop(), equals(0)); | 146 | + expect(tester.getSheetExtent(), equals(kScreenHeight)); |
107 | }); | 147 | }); |
108 | 148 | ||
109 | testWidgets('relativeAnimateTo', (WidgetTester tester) async { | 149 | testWidgets('relativeAnimateTo', (WidgetTester tester) async { |
110 | - final GlobalKey key = GlobalKey(); | ||
111 | - | ||
112 | await tester.pumpApp( | 150 | await tester.pumpApp( |
113 | Sheet( | 151 | Sheet( |
114 | initialExtent: 200, | 152 | initialExtent: 200, |
115 | child: Container( | 153 | child: Container( |
116 | - key: key, | ||
117 | - height: kScreenRect.height, | 154 | + height: kScreenHeight, |
118 | ), | 155 | ), |
119 | ), | 156 | ), |
120 | ); | 157 | ); |
158 | + final controller = tester.getSheetController(); | ||
159 | + expect(tester.getSheetExtent(), equals(200)); | ||
121 | 160 | ||
122 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
123 | - expect(tester.getSheetTop(), equals(kScreenRect.bottom - 200)); | 161 | + controller.relativeAnimateTo(1, curve: curve, duration: duration); |
162 | + await tester.pumpAndSettle(); | ||
163 | + expect(tester.getSheetExtent(), equals(kScreenHeight)); | ||
124 | 164 | ||
125 | - tester.getSheetController().relativeAnimateTo(1, | ||
126 | - curve: Curves.easeInOut, duration: const Duration(milliseconds: 1)); | 165 | + controller.relativeAnimateTo(0.5, curve: curve, duration: duration); |
127 | await tester.pumpAndSettle(); | 166 | await tester.pumpAndSettle(); |
167 | + expect(tester.getSheetExtent(), equals(kScreenHeight * 0.5)); | ||
168 | + }); | ||
128 | 169 | ||
129 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
130 | - expect(tester.getSheetTop(), equals(0)); | 170 | + testWidgets('relativeAnimateTo with min xtent', |
171 | + (WidgetTester tester) async { | ||
172 | + await tester.pumpApp( | ||
173 | + Sheet( | ||
174 | + initialExtent: 200, | ||
175 | + child: Container( | ||
176 | + height: kScreenHeight, | ||
177 | + ), | ||
178 | + ), | ||
179 | + ); | ||
180 | + final controller = tester.getSheetController(); | ||
181 | + expect(tester.getSheetExtent(), equals(200)); | ||
131 | 182 | ||
132 | - tester.getSheetController().relativeAnimateTo(0.5, | ||
133 | - curve: Curves.easeInOut, duration: const Duration(milliseconds: 1)); | 183 | + controller.relativeAnimateTo(1, curve: curve, duration: duration); |
134 | await tester.pumpAndSettle(); | 184 | await tester.pumpAndSettle(); |
185 | + expect(tester.getSheetExtent(), equals(kScreenHeight)); | ||
135 | 186 | ||
136 | - expect(tester.getSheetSize(), equals(kScreenRect.size)); | ||
137 | - expect(tester.getSheetTop(), equals(kScreenRect.bottom / 2)); | 187 | + controller.relativeAnimateTo(0.5, curve: curve, duration: duration); |
188 | + await tester.pumpAndSettle(); | ||
189 | + expect(tester.getSheetExtent(), equals(kScreenHeight * 0.5)); | ||
138 | }); | 190 | }); |
139 | }); | 191 | }); |
140 | } | 192 | } |
sheet/test/cases/sheet_position_test.dart
0 → 100644
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:flutter_test/flutter_test.dart'; | ||
3 | +import 'package:sheet/sheet.dart'; | ||
4 | + | ||
5 | +import '../helpers.dart'; | ||
6 | + | ||
7 | +void main() { | ||
8 | + group('SheetPosition', () { | ||
9 | + testWidgets('can prevent drag', (WidgetTester tester) async { | ||
10 | + await tester.pumpApp( | ||
11 | + Sheet( | ||
12 | + child: SizedBox(height: 200), | ||
13 | + ), | ||
14 | + ); | ||
15 | + final position = tester.getSheetPosition(); | ||
16 | + expect(position.preventingDrag, false); | ||
17 | + position.preventDrag(); | ||
18 | + expect(position.preventingDrag, true); | ||
19 | + }); | ||
20 | + | ||
21 | + testWidgets('can stops preventing drag', (WidgetTester tester) async { | ||
22 | + await tester.pumpApp( | ||
23 | + Sheet( | ||
24 | + child: SizedBox(height: 200), | ||
25 | + ), | ||
26 | + ); | ||
27 | + final position = tester.getSheetPosition(); | ||
28 | + position.preventDrag(); | ||
29 | + expect(position.preventingDrag, true); | ||
30 | + position.stopPreventingDrag(); | ||
31 | + expect(position.preventingDrag, false); | ||
32 | + }); | ||
33 | + }); | ||
34 | +} |
@@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; | @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; | ||
2 | import 'package:flutter_test/flutter_test.dart'; | 2 | import 'package:flutter_test/flutter_test.dart'; |
3 | import 'package:sheet/sheet.dart'; | 3 | import 'package:sheet/sheet.dart'; |
4 | 4 | ||
5 | -const Key _key = Key('_sheet_builder'); | 5 | +const Key _childKey = Key('_sheet_child'); |
6 | 6 | ||
7 | -Finder findSheet() => find.byKey(_key); | 7 | +Finder findSheet() => find.byKey(_childKey); |
8 | 8 | ||
9 | extension SheetTester on WidgetTester { | 9 | extension SheetTester on WidgetTester { |
10 | Future<void> pumpApp(Widget sheet, {VoidCallback? onButtonPressed}) async { | 10 | Future<void> pumpApp(Widget sheet, {VoidCallback? onButtonPressed}) async { |
@@ -41,26 +41,37 @@ extension SheetTester on WidgetTester { | @@ -41,26 +41,37 @@ extension SheetTester on WidgetTester { | ||
41 | } | 41 | } |
42 | 42 | ||
43 | double getSheetTop() { | 43 | double getSheetTop() { |
44 | - final Offset offset = getTopLeft(find.byKey(_key)); | 44 | + final Offset offset = getTopLeft(find.byKey(_childKey)); |
45 | return offset.dy; | 45 | return offset.dy; |
46 | } | 46 | } |
47 | 47 | ||
48 | + double getSheetExtent() { | ||
49 | + final Rect rootRect = getRect(find.byType(Sheet)); | ||
50 | + final Offset offset = getTopLeft(find.byKey(_childKey)); | ||
51 | + return rootRect.bottom - offset.dy; | ||
52 | + } | ||
53 | + | ||
48 | SheetController getSheetController() { | 54 | SheetController getSheetController() { |
49 | - final Element context = element(find.byKey(_key)); | 55 | + final BuildContext context = element(find.byKey(_childKey)); |
50 | return Sheet.of(context)!.controller; | 56 | return Sheet.of(context)!.controller; |
51 | } | 57 | } |
52 | 58 | ||
59 | + SheetPosition getSheetPosition() { | ||
60 | + final BuildContext context = element(find.byKey(_childKey)); | ||
61 | + return Sheet.of(context)!.position; | ||
62 | + } | ||
63 | + | ||
53 | double getSheetHeight() { | 64 | double getSheetHeight() { |
54 | - final Size rect = getSize(find.byKey(_key)); | 65 | + final Size rect = getSize(find.byKey(_childKey)); |
55 | return rect.height; | 66 | return rect.height; |
56 | } | 67 | } |
57 | 68 | ||
58 | Size getSheetSize() { | 69 | Size getSheetSize() { |
59 | - return getSize(find.byKey(_key)); | 70 | + return getSize(find.byKey(_childKey)); |
60 | } | 71 | } |
61 | 72 | ||
62 | Future<void> dragSheet(double offset) { | 73 | Future<void> dragSheet(double offset) { |
63 | - return drag(find.byKey(_key), Offset(0, offset)); | 74 | + return drag(find.byKey(_childKey), Offset(0, offset)); |
64 | } | 75 | } |
65 | } | 76 | } |
66 | 77 |
sheet/test/src/sheet_test.dart
0 → 100644
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:flutter_test/flutter_test.dart'; | ||
3 | +import 'package:sheet/sheet.dart'; | ||
4 | + | ||
5 | +void main() { | ||
6 | + group('Sheet', () { | ||
7 | + testWidgets('default has material appareance', (tester) async { | ||
8 | + await tester.pumpWidget( | ||
9 | + MaterialApp( | ||
10 | + home: Sheet( | ||
11 | + child: SizedBox(), | ||
12 | + ), | ||
13 | + ), | ||
14 | + ); | ||
15 | + expect(find.byType(Material), findsOneWidget); | ||
16 | + final material = tester.widget<Material>(find.byType(Material)); | ||
17 | + expect( | ||
18 | + material.color, | ||
19 | + ThemeData.fallback().bottomSheetTheme.backgroundColor, | ||
20 | + ); | ||
21 | + }); | ||
22 | + | ||
23 | + testWidgets('Sheet uses bottomSheetTheme by default', (tester) async { | ||
24 | + await tester.pumpWidget( | ||
25 | + MaterialApp( | ||
26 | + home: Sheet( | ||
27 | + child: SizedBox(), | ||
28 | + ), | ||
29 | + ), | ||
30 | + ); | ||
31 | + | ||
32 | + final material = tester.widget<Material>(find.byType(Material)); | ||
33 | + expect( | ||
34 | + material.color, | ||
35 | + ThemeData.fallback().bottomSheetTheme.backgroundColor, | ||
36 | + ); | ||
37 | + | ||
38 | + expect( | ||
39 | + material.shape, | ||
40 | + ThemeData.fallback().bottomSheetTheme.shape, | ||
41 | + ); | ||
42 | + expect(material.elevation, 0); | ||
43 | + expect(material.clipBehavior, Clip.none); | ||
44 | + }); | ||
45 | + | ||
46 | + testWidgets('.raw has no material appareance', (tester) async { | ||
47 | + await tester.pumpWidget( | ||
48 | + MaterialApp( | ||
49 | + home: Sheet.raw( | ||
50 | + child: SizedBox(), | ||
51 | + ), | ||
52 | + ), | ||
53 | + ); | ||
54 | + expect(find.byType(Material), findsNothing); | ||
55 | + }); | ||
56 | + }); | ||
57 | +} |
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:flutter_test/flutter_test.dart'; | ||
3 | +import 'package:sheet/sheet.dart'; | ||
4 | + | ||
5 | +void main() { | ||
6 | + group('DefaultSheetController', () { | ||
7 | + testWidgets('DefaultSheetController injects a SheetController', | ||
8 | + (tester) async { | ||
9 | + final childKey = UniqueKey(); | ||
10 | + await tester.pumpWidget( | ||
11 | + MaterialApp( | ||
12 | + home: DefaultSheetController( | ||
13 | + child: SizedBox(key: childKey), | ||
14 | + ), | ||
15 | + ), | ||
16 | + ); | ||
17 | + await tester.pumpAndSettle(); | ||
18 | + final context = tester.element(find.byKey(childKey)); | ||
19 | + expect(DefaultSheetController.of(context), isA<SheetController>()); | ||
20 | + }); | ||
21 | + | ||
22 | + testWidgets('Sheet uses its SheetController', (tester) async { | ||
23 | + final childKey = UniqueKey(); | ||
24 | + await tester.pumpWidget( | ||
25 | + MaterialApp( | ||
26 | + home: DefaultSheetController( | ||
27 | + child: Sheet( | ||
28 | + child: SizedBox(key: childKey), | ||
29 | + ), | ||
30 | + ), | ||
31 | + ), | ||
32 | + ); | ||
33 | + await tester.pumpAndSettle(); | ||
34 | + final context = tester.element(find.byKey(childKey)); | ||
35 | + final controller = DefaultSheetController.of(context); | ||
36 | + expect(Sheet.of(context)!.controller, controller); | ||
37 | + }); | ||
38 | + | ||
39 | + testWidgets('SheetController is the same between rebuilds', (tester) async { | ||
40 | + final childKey = UniqueKey(); | ||
41 | + late StateSetter setState; | ||
42 | + await tester.pumpWidget( | ||
43 | + MaterialApp( | ||
44 | + home: StatefulBuilder(builder: (context, stateSetter) { | ||
45 | + setState = stateSetter; | ||
46 | + return DefaultSheetController( | ||
47 | + child: Sheet( | ||
48 | + child: SizedBox(key: childKey), | ||
49 | + ), | ||
50 | + ); | ||
51 | + }), | ||
52 | + ), | ||
53 | + ); | ||
54 | + await tester.pumpAndSettle(); | ||
55 | + final context = tester.element(find.byKey(childKey)); | ||
56 | + final controller = DefaultSheetController.of(context); | ||
57 | + setState(() {}); | ||
58 | + await tester.pumpAndSettle(); | ||
59 | + expect(DefaultSheetController.of(context), controller); | ||
60 | + }); | ||
61 | + }); | ||
62 | +} |
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:flutter_test/flutter_test.dart'; | ||
3 | +import 'package:sheet/sheet.dart'; | ||
4 | + | ||
5 | +import '../../helpers.dart'; | ||
6 | + | ||
7 | +void main() { | ||
8 | + group('SheetMediaQuery', () { | ||
9 | + testWidgets('top padding is zero if sheet is not inside top safe area', | ||
10 | + (tester) async { | ||
11 | + final childKey = UniqueKey(); | ||
12 | + await tester.pumpWidget( | ||
13 | + MaterialApp( | ||
14 | + home: MediaQuery( | ||
15 | + data: MediaQueryData(padding: EdgeInsets.only(top: 20)), | ||
16 | + child: Sheet( | ||
17 | + child: SheetMediaQuery( | ||
18 | + child: SizedBox.expand(key: childKey), | ||
19 | + ), | ||
20 | + ), | ||
21 | + ), | ||
22 | + ), | ||
23 | + ); | ||
24 | + await tester.pumpAndSettle(); | ||
25 | + final context = tester.element(find.byKey(childKey)); | ||
26 | + expect(MediaQuery.of(context).padding.top, 0); | ||
27 | + }); | ||
28 | + | ||
29 | + testWidgets( | ||
30 | + 'top padding is same as top safe area if sheet is fully open ' | ||
31 | + 'before viewportDimension is rendered', (tester) async { | ||
32 | + // viewportDimension is not available in first frame | ||
33 | + final childKey = UniqueKey(); | ||
34 | + await tester.pumpWidget( | ||
35 | + MaterialApp( | ||
36 | + home: MediaQuery( | ||
37 | + data: MediaQueryData(padding: EdgeInsets.only(top: 20)), | ||
38 | + child: Sheet( | ||
39 | + initialExtent: 600, | ||
40 | + child: SheetMediaQuery( | ||
41 | + child: SizedBox.expand(key: childKey), | ||
42 | + ), | ||
43 | + ), | ||
44 | + ), | ||
45 | + ), | ||
46 | + ); | ||
47 | + await tester.pumpAndSettle(); | ||
48 | + final context = tester.element(find.byKey(childKey)); | ||
49 | + expect(MediaQuery.of(context).padding.top, 20); | ||
50 | + }); | ||
51 | + | ||
52 | + testWidgets( | ||
53 | + 'top padding is same as top safe area if sheet is fully open ' | ||
54 | + 'after viewportDimension is rendered', (tester) async { | ||
55 | + final childKey = UniqueKey(); | ||
56 | + await tester.pumpWidget( | ||
57 | + MaterialApp( | ||
58 | + home: MediaQuery( | ||
59 | + data: MediaQueryData(padding: EdgeInsets.only(top: 20)), | ||
60 | + child: Sheet( | ||
61 | + child: SheetMediaQuery( | ||
62 | + child: SizedBox.expand(key: childKey), | ||
63 | + ), | ||
64 | + ), | ||
65 | + ), | ||
66 | + ), | ||
67 | + ); | ||
68 | + tester.getSheetController().relativeJumpTo(1); | ||
69 | + await tester.pumpAndSettle(); | ||
70 | + final context = tester.element(find.byKey(childKey)); | ||
71 | + expect(MediaQuery.of(context).padding.top, 20); | ||
72 | + }); | ||
73 | + | ||
74 | + testWidgets('top padding increase is lineal', (tester) async { | ||
75 | + final offsetToTest = 10.0; | ||
76 | + final childKey = UniqueKey(); | ||
77 | + await tester.pumpWidget( | ||
78 | + MaterialApp( | ||
79 | + home: MediaQuery( | ||
80 | + data: MediaQueryData(padding: EdgeInsets.only(top: 20)), | ||
81 | + child: Sheet( | ||
82 | + initialExtent: 600 - offsetToTest, | ||
83 | + child: SizedBox( | ||
84 | + child: SheetMediaQuery( | ||
85 | + child: SizedBox.expand(key: childKey), | ||
86 | + ), | ||
87 | + ), | ||
88 | + ), | ||
89 | + ), | ||
90 | + ), | ||
91 | + ); | ||
92 | + await tester.pumpAndSettle(); | ||
93 | + final context = tester.element(find.byKey(childKey)); | ||
94 | + expect(MediaQuery.of(context).padding.top, 20 - offsetToTest); | ||
95 | + }); | ||
96 | + }); | ||
97 | +} |
-
Please register or login to post a comment