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