Jaime Blasco
Committed by GitHub

feat: bring up to date (#391)

* fix: lints

* feat: fix inner not resizing

* fix: expanded
Showing 60 changed files with 561 additions and 323 deletions
enable-experiment:
- extension-methods
\ No newline at end of file
include: package:lints/recommended.yaml
... ...
... ... @@ -15,8 +15,8 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
url_launcher_ios: 68d46cc9766d0c41dbdc884310529557e3cd7a86
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.13.0
COCOAPODS: 1.14.3
... ...
... ... @@ -93,7 +93,7 @@ class CupertinoSharePage extends StatelessWidget {
}
class PhotoShareBottomSheet extends StatelessWidget {
const PhotoShareBottomSheet({Key? key}) : super(key: key);
const PhotoShareBottomSheet({super.key});
@override
Widget build(BuildContext context) {
... ... @@ -539,9 +539,9 @@ final actions2 = [
];
extension ListUtils<T> on List<T> {
List<T> addItemInBetween<A extends T>(A item) => this.length == 0
List<T> addItemInBetween<A extends T>(A item) => isEmpty
? this
: (this.fold([], (r, element) => [...r, element, item])..removeLast());
: (fold([], (r, element) => [...r, element, item])..removeLast());
}
class SimpleSliverDelegate extends SliverPersistentHeaderDelegate {
... ...
... ... @@ -92,12 +92,12 @@ class MyApp extends StatelessWidget {
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
MyHomePage({super.key, required this.title});
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
... ...
... ... @@ -11,11 +11,10 @@ class AvatarBottomSheet extends StatelessWidget {
final SystemUiOverlayStyle? overlayStyle;
const AvatarBottomSheet(
{Key? key,
{super.key,
required this.child,
required this.animation,
this.overlayStyle})
: super(key: key);
this.overlayStyle});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -5,8 +5,7 @@ class FloatingModal extends StatelessWidget {
final Widget child;
final Color? backgroundColor;
const FloatingModal({Key? key, required this.child, this.backgroundColor})
: super(key: key);
const FloatingModal({super.key, required this.child, this.backgroundColor});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class ComplexModal extends StatelessWidget {
const ComplexModal({Key? key}) : super(key: key);
const ComplexModal({super.key});
@override
Widget build(BuildContext context) {
... ...
import 'package:flutter/material.dart';
class ModalFit extends StatelessWidget {
const ModalFit({Key? key}) : super(key: key);
const ModalFit({super.key});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -5,7 +5,7 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class ModalInsideModal extends StatelessWidget {
final bool reverse;
const ModalInsideModal({Key? key, this.reverse = false}) : super(key: key);
const ModalInsideModal({super.key, this.reverse = false});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SimpleModal extends StatelessWidget {
const SimpleModal({Key? key}) : super(key: key);
const SimpleModal({super.key});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ModalWillScope extends StatelessWidget {
const ModalWillScope({Key? key}) : super(key: key);
const ModalWillScope({super.key});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -3,30 +3,30 @@ import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class ModalWithNavigator extends StatelessWidget {
const ModalWithNavigator({Key? key}) : super(key: key);
const ModalWithNavigator({super.key});
@override
Widget build(BuildContext rootContext) {
Widget build(BuildContext context) {
return Material(
child: Navigator(
onGenerateRoute: (_) => MaterialPageRoute(
builder: (context2) => Builder(
builder: (context) => CupertinoPageScaffold(
builder: (childContext) => Builder(
builder: (childContext2) => CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
leading: Container(), middle: Text('Modal Page')),
child: SafeArea(
bottom: false,
child: ListView(
shrinkWrap: true,
controller: ModalScrollController.of(context),
controller: ModalScrollController.of(childContext2),
children: ListTile.divideTiles(
context: context,
context: childContext2,
tiles: List.generate(
100,
(index) => ListTile(
title: Text('Item'),
onTap: () {
Navigator.of(context).push(
Navigator.of(childContext2).push(
MaterialPageRoute(
builder: (context) => CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
... ... @@ -37,7 +37,7 @@ class ModalWithNavigator extends StatelessWidget {
children: <Widget>[
MaterialButton(
onPressed: () =>
Navigator.of(rootContext).pop(),
Navigator.of(context).pop(),
child: Text('touch here'),
)
],
... ...
... ... @@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class NestedScrollModal extends StatelessWidget {
const NestedScrollModal({Key? key}) : super(key: key);
const NestedScrollModal({super.key});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class ModalWithPageView extends StatelessWidget {
const ModalWithPageView({Key? key}) : super(key: key);
const ModalWithPageView({super.key});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
class ModalWithScroll extends StatelessWidget {
const ModalWithScroll({Key? key}) : super(key: key);
const ModalWithScroll({super.key});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -6,7 +6,7 @@ import 'package:url_launcher/url_launcher_string.dart';
class WebFrame extends StatelessWidget {
final Widget child;
const WebFrame({Key? key, required this.child}) : super(key: key);
const WebFrame({super.key, required this.child});
@override
Widget build(BuildContext context) {
... ...
... ... @@ -37,10 +37,10 @@ packages:
dependency: transitive
description:
name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.17.2"
version: "1.18.0"
cupertino_icons:
dependency: "direct main"
description:
... ... @@ -72,6 +72,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
lints:
dependency: "direct dev"
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "3.0.0"
matcher:
dependency: transitive
description:
... ... @@ -92,10 +100,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.10.0"
modal_bottom_sheet:
dependency: "direct main"
description:
... ... @@ -136,18 +144,18 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
string_scanner:
dependency: transitive
description:
... ... @@ -168,10 +176,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
version: "0.6.1"
url_launcher:
dependency: "direct main"
description:
... ... @@ -248,10 +256,10 @@ packages:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
version: "0.3.0"
sdks:
dart: ">=3.1.0 <4.0.0"
dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.13.0"
... ...
name: example
description: A new Flutter project.
publish_to: none
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: '>=2.17.0 <3.0.0'
flutter: ">=3.7.0"
sdk: ">=3.0.0 <4.0.0"
dependencies:
cupertino_icons: ^1.0.5
cupertino_icons: ^1.0.6
flutter:
sdk: flutter
modal_bottom_sheet:
path: '../'
url_launcher: ^6.1.5
url_launcher: ^6.2.1
dev_dependencies:
flutter_test:
sdk: flutter
lints: ^3.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- assets/
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
- assets/
\ No newline at end of file
... ...
... ... @@ -47,7 +47,7 @@ class ModalBottomSheet extends StatefulWidget {
this.minFlingVelocity = _minFlingVelocity,
double? closeProgressThreshold,
this.willPopThreshold = _willPopThreshold,
}) : closeProgressThreshold =
}) : closeProgressThreshold =
closeProgressThreshold ?? _closeProgressThreshold;
/// The closeProgressThreshold parameter
... ... @@ -231,7 +231,6 @@ class ModalBottomSheetState extends State<ModalBottomSheet>
if (_dismissUnderway || !isDragging) return;
isDragging = false;
// ignore: unawaited_futures
_bounceDragController.reverse();
Future<void> tryClose() async {
... ... @@ -251,7 +250,6 @@ class ModalBottomSheetState extends State<ModalBottomSheet>
tryClose();
} else if (hasReachedCloseThreshold) {
if (widget.animationController.value > 0.0) {
// ignore: unawaited_futures
widget.animationController.fling(velocity: -1.0);
}
tryClose();
... ... @@ -273,13 +271,11 @@ class ModalBottomSheetState extends State<ModalBottomSheet>
if (!_scrollController.hasClients) return;
ScrollPosition scrollPosition;
// ignore: invalid_use_of_protected_member
if (_scrollController.positions.length > 1) {
// ignore: invalid_use_of_protected_member
scrollPosition = _scrollController.positions
.firstWhere((p) => p.isScrollingNotifier.value,
// ignore: invalid_use_of_protected_member
orElse: () => _scrollController.positions.first);
scrollPosition = _scrollController.positions.firstWhere(
(p) => p.isScrollingNotifier.value,
orElse: () => _scrollController.positions.first);
} else {
scrollPosition = _scrollController.position;
}
... ... @@ -412,9 +408,15 @@ class ModalBottomSheetState extends State<ModalBottomSheet>
child: RepaintBoundary(child: child),
);
return ScrollToTopStatusBarHandler(
return StatusBarGestureDetector(
child: child,
scrollController: _scrollController,
onTap: (context) {
_scrollController.animateTo(
0.0,
duration: const Duration(milliseconds: 1000),
curve: Curves.easeOutCirc,
);
},
);
}
}
... ...
... ... @@ -395,7 +395,7 @@ class CupertinoScaffoldInheirted extends InheritedWidget {
required super.child,
this.topRadius,
required this.transitionBackgroundColor,
}) : super();
});
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
... ...
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
/// Widget that that will scroll to the top the ScrollController
typedef StatusBarGestureDetectorCallback = void Function(BuildContext context);
/// Widget that that will make the [scrollController] to scroll the top
/// when tapped on the status bar
///
/// Extracted from Scaffold and used in modal bottom sheet
class ScrollToTopStatusBarHandler extends StatefulWidget {
final Widget child;
final ScrollController scrollController;
const ScrollToTopStatusBarHandler({
/// Extracted from [Scaffold] and used in [Sheet]
class StatusBarGestureDetector extends StatefulWidget {
const StatusBarGestureDetector({
super.key,
required this.child,
required this.scrollController,
required this.onTap,
});
final Widget child;
final StatusBarGestureDetectorCallback onTap;
@override
ScrollToTopStatusBarState createState() => ScrollToTopStatusBarState();
State<StatusBarGestureDetector> createState() =>
_StatusBarGestureDetectorState();
}
class ScrollToTopStatusBarState extends State<ScrollToTopStatusBarHandler> {
class _StatusBarGestureDetectorState extends State<StatusBarGestureDetector> {
final OverlayPortalController controller = OverlayPortalController();
@override
void initState() {
controller.show();
super.initState();
}
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: [
widget.child,
Positioned(
top: 0,
left: 0,
right: 0,
height: MediaQuery.of(context).padding.top,
child: Builder(
builder: (context) => GestureDetector(
final view = View.of(context);
return OverlayPortal.targetsRootOverlay(
controller: controller,
overlayChildBuilder: (context) {
return Align(
alignment: Alignment.topCenter,
child: SizedBox(
height: view.padding.top / view.devicePixelRatio,
width: double.infinity,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _handleStatusBarTap(context),
onTap: () => widget.onTap(context),
// iOS accessibility automatically adds scroll-to-top to the clock in the status bar
excludeFromSemantics: true,
),
),
),
],
);
},
child: widget.child,
);
}
void _handleStatusBarTap(BuildContext context) {
final controller = widget.scrollController;
if (controller.hasClients) {
controller.animateTo(
0.0,
duration: const Duration(milliseconds: 300),
curve: Curves.linear, // TODO(ianh): Use a more appropriate curve.
);
}
}
}
... ...
include: package:lints/recommended.yaml
\ No newline at end of file
... ...
... ... @@ -127,7 +127,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
... ...
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
... ...
... ... @@ -3,15 +3,14 @@ import 'package:flutter/material.dart';
class ExampleTile extends StatelessWidget {
const ExampleTile({
Key? key,
super.key,
required this.title,
required this.page,
this.leading,
}) : super(key: key);
});
ExampleTile.sheet(this.title, Widget sheet, {this.leading})
: page = BaseScaffold(title: Text(title), sheet: sheet),
super();
: page = BaseScaffold(title: Text(title), sheet: sheet);
final String title;
final Widget page;
... ... @@ -33,11 +32,11 @@ class ExampleTile extends StatelessWidget {
class BaseScaffold extends StatelessWidget {
const BaseScaffold({
Key? key,
super.key,
this.sheet,
this.title,
this.appBarTrailingButton,
}) : super(key: key);
});
final Widget? sheet;
final Widget? title;
... ...
... ... @@ -142,7 +142,7 @@ class FilterEditor extends StatelessWidget {
),
),
],
).toList(),
),
const SectionTitle('Appareance'),
...ListTile.divideTiles(
context: context,
... ... @@ -221,7 +221,7 @@ class FilterEditor extends StatelessWidget {
}),
),
],
).toList(),
),
const SectionTitle('Physics'),
...ListTile.divideTiles(
context: context,
... ... @@ -251,7 +251,7 @@ class FilterEditor extends StatelessWidget {
),
),
],
).toList(),
),
]),
),
);
... ... @@ -259,8 +259,7 @@ class FilterEditor extends StatelessWidget {
}
class NumTextField extends StatelessWidget {
const NumTextField({Key? key, this.onChanged, this.maxNumber = 999})
: super(key: key);
const NumTextField({super.key, this.onChanged, this.maxNumber = 999});
final ValueChanged<int?>? onChanged;
final int maxNumber;
@override
... ... @@ -318,8 +317,8 @@ class NumericalRangeFormatter extends TextInputFormatter {
class SectionTitle extends StatelessWidget {
const SectionTitle(
this.text, {
Key? key,
}) : super(key: key);
super.key,
});
final String text;
... ...
... ... @@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:sheet/route.dart';
class ModalInsideModal extends StatelessWidget {
const ModalInsideModal({Key? key, this.reverse = false}) : super(key: key);
const ModalInsideModal({super.key, this.reverse = false});
final bool reverse;
@override
... ...
... ... @@ -2,8 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SimpleModal extends StatelessWidget {
const SimpleModal({Key? key, required this.scrollController})
: super(key: key);
const SimpleModal({super.key, required this.scrollController});
final ScrollController scrollController;
@override
... ...
... ... @@ -24,8 +24,7 @@ class AvatarSheetRoute<T> extends SheetRoute<T> {
}
class _AvatarSheet extends StatelessWidget {
const _AvatarSheet({Key? key, required this.child, required this.animation})
: super(key: key);
const _AvatarSheet({required this.child, required this.animation});
final Widget child;
final Animation<double> animation;
... ...
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sheet/route.dart';
import 'package:sheet/sheet.dart';
const Radius _default_bar_top_radius = Radius.circular(15);
const Radius _defaultBarTopRadius = Radius.circular(15);
class BarBottomSheet extends StatelessWidget {
const BarBottomSheet(
{Key? key,
{super.key,
required this.child,
this.control,
this.clipBehavior,
this.shape,
this.elevation})
: super(key: key);
this.elevation});
final Widget child;
final Widget? control;
final Clip? clipBehavior;
... ... @@ -49,8 +47,8 @@ class BarBottomSheet extends StatelessWidget {
const RoundedRectangleBorder(
side: BorderSide(),
borderRadius: BorderRadius.only(
topLeft: _default_bar_top_radius,
topRight: _default_bar_top_radius),
topLeft: _defaultBarTopRadius,
topRight: _defaultBarTopRadius),
),
clipBehavior: clipBehavior ?? Clip.hardEdge,
elevation: elevation ?? 2,
... ... @@ -72,13 +70,13 @@ class BarSheetRoute<T> extends SheetRoute<T> {
double? elevation,
ShapeBorder? shape,
Clip? clipBehavior,
Color barrierColor = Colors.black87,
SheetFit fit = SheetFit.expand,
Curve? animationCurve,
Color super.barrierColor = Colors.black87,
super.fit,
super.animationCurve,
bool isDismissible = true,
bool enableDrag = true,
Widget? topControl,
Duration? duration,
super.duration,
}) : super(
builder: (BuildContext context) {
return BarBottomSheet(
... ... @@ -89,11 +87,7 @@ class BarSheetRoute<T> extends SheetRoute<T> {
elevation: elevation,
);
},
fit: fit,
barrierDismissible: isDismissible,
barrierColor: barrierColor,
draggable: enableDrag,
animationCurve: animationCurve,
duration: duration,
);
}
... ...
... ... @@ -3,8 +3,7 @@ import 'package:sheet/route.dart';
import 'package:sheet/sheet.dart';
class DialogSheet extends StatelessWidget {
const DialogSheet({Key? key, required this.child, this.backgroundColor})
: super(key: key);
const DialogSheet({super.key, required this.child, this.backgroundColor});
final Widget child;
final Color? backgroundColor;
... ...
... ... @@ -3,8 +3,7 @@ import 'package:sheet/route.dart';
import 'package:sheet/sheet.dart';
class FloatingModal extends StatelessWidget {
const FloatingModal({Key? key, required this.child, this.backgroundColor})
: super(key: key);
const FloatingModal({super.key, required this.child, this.backgroundColor});
final Widget child;
final Color? backgroundColor;
... ...
import 'package:flutter/material.dart';
import 'package:sheet/route.dart';
import 'package:sheet/sheet.dart';
class MaterialSheetRoute<T> extends SheetRoute<T> {
MaterialSheetRoute({
... ... @@ -9,14 +8,14 @@ class MaterialSheetRoute<T> extends SheetRoute<T> {
double? elevation,
ShapeBorder? shape,
Clip? clipBehavior,
Color barrierColor = Colors.black87,
SheetFit fit = SheetFit.expand,
Curve? animationCurve,
bool barrierDismissible = true,
Color super.barrierColor = Colors.black87,
super.fit,
super.animationCurve,
super.barrierDismissible,
bool enableDrag = true,
List<double>? stops,
super.stops,
double initialStop = 1,
Duration? duration,
super.duration,
}) : super(
builder: (BuildContext context) => Material(
child: Builder(
... ... @@ -27,13 +26,7 @@ class MaterialSheetRoute<T> extends SheetRoute<T> {
shape: shape,
elevation: elevation ?? 1,
),
stops: stops,
initialExtent: initialStop,
fit: fit,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
draggable: enableDrag,
animationCurve: animationCurve,
duration: duration,
);
}
... ...
import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:sheet/route.dart';
class Book {
const Book(this.id, this.title, this.author);
final String id;
final String title;
final String author;
}
class AdvancedGoRouterBooksApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => _GoRouterBooksAppState();
}
class _GoRouterBooksAppState extends State<AdvancedGoRouterBooksApp> {
List<Book> books = <Book>[
const Book('1', 'Stranger in a Strange Land', 'Robert A. Heinlein'),
const Book('2', 'Foundation', 'Isaac Asimov'),
const Book('3', 'Fahrenheit 451', 'Ray Bradbury'),
];
@override
void initState() {
super.initState();
}
Brightness brightness = Brightness.light;
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationProvider: _router.routeInformationProvider,
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
debugShowCheckedModeBanner: false,
theme:
brightness == Brightness.light ? ThemeData.light() : ThemeData.dark(),
title: 'Books App',
builder: (BuildContext context, Widget? child) {
return CupertinoTheme(
data: CupertinoThemeData(brightness: brightness),
child: child!,
);
},
);
}
final rootNavigatorKey = GlobalKey<NavigatorState>();
final nestedNavigationKey = GlobalKey<NavigatorState>();
late final GoRouter _router = GoRouter(
debugLogDiagnostics: true,
navigatorKey: rootNavigatorKey,
routes: <GoRoute>[
GoRoute(
path: '/',
pageBuilder: (BuildContext context, GoRouterState state) {
return MaterialExtendedPage<void>(
key: state.pageKey,
child: BooksListScreen(
books: books,
onBrigthnessChanged: (Brightness brightness) {
setState(() {
this.brightness = brightness;
});
},
),
);
},
routes: <RouteBase>[
ShellRoute(
navigatorKey: nestedNavigationKey,
parentNavigatorKey: rootNavigatorKey,
pageBuilder: (context, state, child) {
return CupertinoSheetPage<void>(child: child);
},
routes: [
GoRoute(
name: 'book',
path: 'book/:bid',
parentNavigatorKey: nestedNavigationKey,
pageBuilder: (BuildContext context, GoRouterState state) {
final String id = state.pathParameters['bid']!;
final Book? book =
books.firstWhereOrNull((Book b) => b.id == id);
return MaterialPage<void>(
key: state.pageKey,
child: BookDetailsScreen(
book: book!,
),
);
},
redirect: (context, state) {
final String id = state.pathParameters['bid']!;
final Book? book =
books.firstWhereOrNull((Book b) => b.id == id);
if (book == null) {
return '/404';
}
// no need to redirect at all
return null;
},
routes: [
GoRoute(
name: 'Reviews',
path: 'reviews',
parentNavigatorKey: nestedNavigationKey,
pageBuilder: (context, state) {
return MaterialPage<void>(
key: state.pageKey,
child: Scaffold(
appBar: AppBar(
title: const Text('Reviews'),
),
),
);
},
),
]),
]),
GoRoute(
name: 'new',
path: 'new',
parentNavigatorKey: rootNavigatorKey,
pageBuilder: (BuildContext context, GoRouterState state) {
return CupertinoSheetPage<void>(
key: state.pageKey,
child: Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
title: const Text('New'),
),
),
);
},
),
]),
],
);
}
class BooksListScreen extends StatelessWidget {
const BooksListScreen({
required this.books,
required this.onBrigthnessChanged,
});
final List<Book> books;
final void Function(Brightness) onBrigthnessChanged;
@override
Widget build(BuildContext context) {
final Brightness brightness = Theme.of(context).brightness;
return Scaffold(
appBar: CupertinoNavigationBar(
leading: BackButton(onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
}),
middle: const Text('Book'),
trailing: IconButton(
icon: Icon(brightness == Brightness.light
? Icons.nightlight_round
: Icons.wb_sunny),
onPressed: () {
onBrigthnessChanged(
brightness == Brightness.light
? Brightness.dark
: Brightness.light,
);
},
),
),
body: SafeArea(
child: ListView(
children: <Widget>[
for (Book book in books)
ListTile(
title: Text(book.title),
subtitle: Text(book.author),
onTap: () {
context.go('/book/${book.id}');
},
trailing: TextButton(
onPressed: () {
context.go('/book/${book.id}');
context.go('/book/${book.id}/reviews');
},
child: const Text('Reviews'),
))
],
),
),
);
}
}
class BookDetailsScreen extends StatelessWidget {
const BookDetailsScreen({
required this.book,
});
final Book book;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const CupertinoNavigationBar(
middle: Text('Book'),
),
body: Padding(
padding: const EdgeInsets.all(8.0) + const EdgeInsets.only(top: 52.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(book.title, style: Theme.of(context).textTheme.titleLarge),
Text(book.author, style: Theme.of(context).textTheme.titleMedium),
TextButton(
onPressed: () {
context.go('/book/${book.id}/reviews');
},
child: Text('Reviews'),
)
],
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
context.push('/new');
},
),
);
}
}
... ...
... ... @@ -20,7 +20,7 @@ class BounceTopSheet extends StatelessWidget {
class BouncingSheetPage extends StatefulWidget {
@override
_BouncingSheetPageState createState() => _BouncingSheetPageState();
State<BouncingSheetPage> createState() => _BouncingSheetPageState();
}
class _BouncingSheetPageState extends State<BouncingSheetPage> {
... ...
... ... @@ -6,7 +6,7 @@ import 'package:sheet/sheet.dart';
class ClampedSheet extends StatefulWidget {
@override
_ClampedSheetState createState() => _ClampedSheetState();
State<ClampedSheet> createState() => _ClampedSheetState();
}
class _ClampedSheetState extends State<ClampedSheet> {
... ...
... ... @@ -8,7 +8,7 @@ import 'package:sheet/sheet.dart';
class AdvancedSnapSheetPage extends StatefulWidget {
@override
_AdvancedSnapSheetPageState createState() => _AdvancedSnapSheetPageState();
State<AdvancedSnapSheetPage> createState() => _AdvancedSnapSheetPageState();
}
class _AdvancedSnapSheetPageState extends State<AdvancedSnapSheetPage>
... ... @@ -71,7 +71,7 @@ class _AdvancedSnapSheetPageState extends State<AdvancedSnapSheetPage>
}
class MapAppBar extends StatefulWidget implements PreferredSizeWidget {
const MapAppBar({Key? key, required this.controller}) : super(key: key);
const MapAppBar({super.key, required this.controller});
final SheetController controller;
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
... ... @@ -174,7 +174,7 @@ class _MapAppBarState extends State<MapAppBar> {
}
class FloatingButtons extends StatelessWidget {
const FloatingButtons({Key? key, required this.controller}) : super(key: key);
const FloatingButtons({super.key, required this.controller});
final SheetController controller;
@override
Widget build(BuildContext context) {
... ... @@ -225,7 +225,7 @@ class FloatingButtons extends StatelessWidget {
}
class MapSheet extends StatelessWidget {
const MapSheet({Key? key, required this.controller}) : super(key: key);
const MapSheet({super.key, required this.controller});
final SheetController controller;
@override
Widget build(BuildContext context) {
... ...
... ... @@ -5,7 +5,7 @@ import 'package:sheet/sheet.dart';
class FitResizableSheet extends StatefulWidget {
@override
_FitSheetState createState() => _FitSheetState();
State<FitResizableSheet> createState() => _FitSheetState();
}
class _FitSheetState extends State<FitResizableSheet> {
... ...
... ... @@ -6,7 +6,7 @@ import 'package:sheet/sheet.dart';
class FitSnapSheet extends StatefulWidget {
@override
_FitSheetState createState() => _FitSheetState();
State<FitSnapSheet> createState() => _FitSheetState();
}
class _FitSheetState extends State<FitSnapSheet> {
... ...
... ... @@ -3,7 +3,7 @@ import 'package:sheet/sheet.dart';
class FloatingSheet extends StatefulWidget {
@override
_FitSheetState createState() => _FitSheetState();
State<FloatingSheet> createState() => _FitSheetState();
}
class _FitSheetState extends State<FloatingSheet> {
... ...
... ... @@ -3,7 +3,7 @@ import 'package:sheet/sheet.dart';
class FoldableScreenFloatingSheet extends StatefulWidget {
@override
_FitSheetState createState() => _FitSheetState();
State<FoldableScreenFloatingSheet> createState() => _FitSheetState();
}
class _FitSheetState extends State<FoldableScreenFloatingSheet> {
... ...
... ... @@ -6,7 +6,7 @@ import 'package:sheet/sheet.dart';
class NoMomentumSheet extends StatefulWidget {
@override
_NoMomentumSheetState createState() => _NoMomentumSheetState();
State<NoMomentumSheet> createState() => _NoMomentumSheetState();
}
class _NoMomentumSheetState extends State<NoMomentumSheet> {
... ...
... ... @@ -6,7 +6,7 @@ import 'package:sheet/sheet.dart';
class SnapSheet extends StatefulWidget {
@override
_SnapSheetState createState() => _SnapSheetState();
State<SnapSheet> createState() => _SnapSheetState();
}
class _SnapSheetState extends State<SnapSheet> {
... ...
... ... @@ -4,7 +4,7 @@ import 'package:sheet/sheet.dart';
class TextFieldSheet extends StatefulWidget {
@override
_TextFieldSheetState createState() => _TextFieldSheetState();
State<TextFieldSheet> createState() => _TextFieldSheetState();
}
class _TextFieldSheetState extends State<TextFieldSheet>
... ...
import 'package:example/route_example_page.dart';
import 'package:example/sheet_example_page.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:sheet/route.dart';
void main() => runApp(MyApp());
final goRouter = GoRouter(routes: [
GoRoute(
path: '/',
pageBuilder: (context, state) =>
MaterialExtendedPage<void>(child: const BottomNavigationScaffold()),
),
]);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
return MaterialApp.router(
theme: ThemeData(platform: TargetPlatform.iOS),
debugShowCheckedModeBanner: false,
title: 'BottomSheet Modals',
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/') {
return MaterialExtendedPageRoute<void>(
builder: (BuildContext context) {
return const BottomNavigationScaffold();
},
);
}
return null;
},
routerConfig: goRouter,
);
}
}
... ...
... ... @@ -19,6 +19,7 @@ import 'package:sheet/sheet.dart';
import 'examples/route/examples/modal_with_nested_scroll.dart';
import 'examples/route/navigation/go_router.dart';
import 'examples/route/navigation/go_router_advanced.dart';
class RouteExamplePage extends StatelessWidget {
const RouteExamplePage({super.key});
... ... @@ -84,6 +85,16 @@ class RouteExamplePage extends StatelessWidget {
),
),
),
ListTile(
title: const Text('Go router - ShellRoutes'),
onTap: () => Navigator.of(context).push(
MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (BuildContext context) =>
AdvancedGoRouterBooksApp(),
),
),
),
const SectionTitle('STYLES'),
ListTile(
title: const Text('Material fit'),
... ... @@ -313,11 +324,10 @@ class RouteExamplePage extends StatelessWidget {
class SectionTitle extends StatelessWidget {
final String title;
// ignore: sort_constructors_first
const SectionTitle(
this.title, {
Key? key,
}) : super(key: key);
super.key,
});
@override
Widget build(BuildContext context) {
return Padding(
... ...
... ... @@ -75,11 +75,10 @@ class SheetExamplesPage extends StatelessWidget {
class SectionTitle extends StatelessWidget {
final String title;
// ignore: sort_constructors_first
const SectionTitle(
this.title, {
Key? key,
}) : super(key: key);
super.key,
});
@override
Widget build(BuildContext context) {
return Padding(
... ...
... ... @@ -34,7 +34,7 @@ packages:
source: hosted
version: "1.1.1"
collection:
dependency: transitive
dependency: "direct main"
description:
name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
... ... @@ -84,10 +84,18 @@ packages:
dependency: "direct main"
description:
name: go_router
sha256: "2cb236ba3f923043fdbe14a6a3a796b8c250e85658e28caee3e86c0c275847e5"
sha256: "5098760d7478aabfe682a462bf121d61bc5dbe5df5aac8dad733564a0aee33bc"
url: "https://pub.dev"
source: hosted
version: "12.1.0"
lints:
dependency: "direct dev"
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "8.2.0"
version: "3.0.0"
logging:
dependency: transitive
description:
... ... @@ -222,4 +230,4 @@ packages:
version: "0.1.4-beta"
sdks:
dart: ">=3.1.0-185.0.dev <4.0.0"
flutter: ">=3.3.0"
flutter: ">=3.7.0"
... ...
... ... @@ -10,15 +10,17 @@ dependencies:
flutter:
sdk: flutter
equatable: ^2.0.5
cupertino_icons: ^1.0.0
collection:
cupertino_icons: ^1.0.6
provider: ^6.0.5
sheet:
path: ../
go_router: ^8.0.5
go_router: ^12.1.0
dev_dependencies:
flutter_test:
sdk: flutter
lints: ^3.0.0
flutter:
uses-material-design: true
... ...
... ... @@ -414,6 +414,16 @@ class SnapSheetPhysics extends ScrollPhysics with SheetPhysics {
(old.relative != relative || !listEquals(old.stops, stops));
}
@override
double adjustPositionForNewDimensions(
{required ScrollMetrics oldPosition,
required ScrollMetrics newPosition,
required bool isScrolling,
required double velocity}) {
final Tolerance tolerance = toleranceFor(newPosition);
return _getTargetPixels(newPosition, tolerance, velocity);
}
double _getTargetPixels(
ScrollMetrics position, Tolerance tolerance, double velocity) {
int page = _getPage(position) ?? 0;
... ...
... ... @@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'package:sheet/route.dart';
import 'package:sheet/sheet.dart';
... ... @@ -219,7 +220,7 @@ class CupertinoSheetRoute<T> extends SheetRoute<T> {
}
/// Animation for previous route when a [CupertinoSheetRoute] enters/exits
@visibleForTesting
@internal
class CupertinoSheetBottomRouteTransition extends StatelessWidget {
const CupertinoSheetBottomRouteTransition({
super.key,
... ... @@ -349,7 +350,10 @@ class _PageBasedCupertinoSheetRoute<T> extends CupertinoSheetRoute<T> {
super.maintainState,
}) : super(
settings: page,
builder: (BuildContext context) => page.child,
builder: (BuildContext context) {
return (ModalRoute.of(context)!.settings as CupertinoSheetPage<T>)
.child;
},
);
CupertinoSheetPage<T> get _page => settings as CupertinoSheetPage<T>;
... ...
... ... @@ -343,6 +343,9 @@ class _PageBasedSheetRoute<T> extends SheetRoute<T> {
SheetPage<T> get _page => settings as SheetPage<T>;
@override
WidgetBuilder get builder => (context) => _page.child;
@override
bool get maintainState => _page.maintainState;
@override
... ...
... ... @@ -4,7 +4,7 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:sheet/src/widgets/resizable_sheet.dart';
import 'package:sheet/src/widgets/scroll_to_top_status_handler.dart';
import 'package:sheet/src/widgets/status_bar_gesture_detector.dart';
import '../sheet.dart';
... ... @@ -261,7 +261,7 @@ class Sheet extends StatelessWidget {
scrollBehavior: SheetBehavior(),
viewportBuilder: (BuildContext context, ViewportOffset offset) {
return _DefaultSheetScrollController(
child: ScrollToTopStatusBarHandler(
child: StatusBarGestureDetector.scrollToTop(
child: SheetViewport(
clipBehavior: Clip.antiAlias,
axisDirection: AxisDirection.down,
... ... @@ -894,8 +894,12 @@ class RenderSheetViewport extends RenderBox
}
@override
RevealedOffset getOffsetToReveal(RenderObject target, double alignment,
{Rect? rect, Axis? axis}) {
RevealedOffset getOffsetToReveal(
RenderObject target,
double alignment, {
Axis? axis,
Rect? rect,
}) {
rect ??= target.paintBounds;
if (target is! RenderBox) {
return RevealedOffset(offset: offset.pixels, rect: rect);
... ...
... ... @@ -14,7 +14,11 @@ typedef SheetControllerCallback = void Function(SheetController controller);
///
///
class DefaultSheetController extends StatefulWidget {
const DefaultSheetController({super.key, required this.child, this.onCreated});
const DefaultSheetController({
super.key,
required this.child,
this.onCreated,
});
final Widget child;
... ... @@ -43,7 +47,9 @@ class _DefaultSheetControllerState extends State<DefaultSheetController> {
@override
Widget build(BuildContext context) {
return _InheritedSheetController(
child: widget.child, controller: controller);
child: widget.child,
controller: controller,
);
}
@override
... ... @@ -54,8 +60,10 @@ class _DefaultSheetControllerState extends State<DefaultSheetController> {
}
class _InheritedSheetController extends InheritedWidget {
const _InheritedSheetController(
{required super.child, required this.controller});
const _InheritedSheetController({
required super.child,
required this.controller,
});
final SheetController controller;
... ...
import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart';
import 'package:meta/meta.dart';
/// A widget that add a min interaction zone where hitTestSelf is true
/// This is rarely used by its own
... ... @@ -8,12 +9,13 @@ import 'package:flutter/rendering.dart';
///
/// * [Sheet], that uses this widget that enables to drag closed/hidden
/// sheets
@internal
class MinInteractionZone extends SingleChildRenderObjectWidget {
const MinInteractionZone({
required this.direction,
required this.extent,
required Widget child,
}) : super(child: child);
super.child,
});
final AxisDirection direction;
... ... @@ -41,7 +43,6 @@ class MinInteractionPaddingRenderBox extends RenderProxyBox {
AxisDirection _direction;
AxisDirection get direction => _direction;
set direction(AxisDirection value) {
// ignore: always_put_control_body_on_new_line
if (value == _direction) return;
_direction = value;
}
... ... @@ -49,7 +50,6 @@ class MinInteractionPaddingRenderBox extends RenderProxyBox {
double _extent;
double get extent => _extent;
set extent(double value) {
// ignore: always_put_control_body_on_new_line
if (value == _extent) return;
_extent = value;
}
... ...
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
typedef StatusBarGestureDetectorCallback = void Function(BuildContext context);
/// Widget that that will make the [scrollController] to scroll the top
/// when tapped on the status bar
///
/// Extracted from [Scaffold] and used in [Sheet]
class ScrollToTopStatusBarHandler extends StatefulWidget {
const ScrollToTopStatusBarHandler({
class StatusBarGestureDetector extends StatefulWidget {
const StatusBarGestureDetector({
super.key,
required this.child,
required this.onTap,
});
const StatusBarGestureDetector.scrollToTop({
super.key,
required this.child,
}) : onTap = _scrollToTopBarTap;
static void _scrollToTopBarTap(BuildContext context) {
final controller = PrimaryScrollController.maybeOf(context);
if (controller != null && controller.hasClients) {
controller.animateTo(
0.0,
duration: const Duration(milliseconds: 1000),
curve: Curves.easeOutCirc,
);
}
}
final Widget child;
final StatusBarGestureDetectorCallback onTap;
@override
ScrollToTopStatusBarState createState() => ScrollToTopStatusBarState();
State<StatusBarGestureDetector> createState() =>
_StatusBarGestureDetectorState();
}
class ScrollToTopStatusBarState extends State<ScrollToTopStatusBarHandler> {
class _StatusBarGestureDetectorState extends State<StatusBarGestureDetector> {
final OverlayPortalController controller = OverlayPortalController();
@override
void initState() {
controller.show();
super.initState();
}
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
widget.child,
Positioned(
top: 0,
left: 0,
right: 0,
height: MediaQuery.maybeOf(context)?.padding.top ?? 0,
child: Builder(
builder: (BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _handleStatusBarTap(context),
// iOS accessibility automatically adds scroll-to-top to the clock in the status bar
excludeFromSemantics: true,
);
},
final view = View.of(context);
return OverlayPortal.targetsRootOverlay(
controller: controller,
overlayChildBuilder: (context) {
return Align(
alignment: Alignment.topCenter,
child: SizedBox(
height: view.padding.top / view.devicePixelRatio,
width: double.infinity,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => widget.onTap(context),
// iOS accessibility automatically adds scroll-to-top to the clock in the status bar
excludeFromSemantics: true,
),
),
),
],
);
},
child: widget.child,
);
}
void _handleStatusBarTap(BuildContext context) {
final ScrollController? controller =
PrimaryScrollController.maybeOf(context);
if (controller != null && controller.hasClients) {
controller.animateTo(
0.0,
duration: const Duration(milliseconds: 300),
curve: Curves.linear, // TODO(ianh): Use a more appropriate curve.
);
}
}
}
... ...
... ... @@ -84,7 +84,7 @@ packages:
source: hosted
version: "0.5.0"
meta:
dependency: transitive
dependency: "direct main"
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
... ...
... ... @@ -4,12 +4,13 @@ version: 1.0.0-pre
homepage: https://github.com/jamesblasco/modal_bottom_sheet
environment:
sdk: ">=2.17.0 <3.0.0"
sdk: ">=3.0.0 <4.0.0"
dependencies:
flutter:
sdk: flutter
meta: ^1.9.1
dev_dependencies:
flutter_test:
sdk: flutter
... ...
... ... @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sheet/sheet.dart';
... ... @@ -54,9 +54,8 @@ void main() {
// ScrollController's ScrollPosition to be rebuilt.
Widget buildFrame(SheetPhysics? physics) {
return Directionality(
textDirection: TextDirection.ltr,
child: Sheet(
return MaterialApp(
home: Sheet(
controller: controller,
physics: physics,
child: ScrollPositionListener(
... ...
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sheet/src/widgets/scroll_to_top_status_handler.dart';
import 'package:sheet/src/widgets/status_bar_gesture_detector.dart';
void main() {
testWidgets('Tap status bar scrolls primary scroll controller',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
await tester.pumpWidget(MaterialApp(
builder: (BuildContext context, Widget? child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
padding: const EdgeInsets.all(20),
),
child: child!,
);
},
home: PrimaryScrollController(
controller: controller,
child: ScrollToTopStatusBarHandler(
child: Material(
child: ListView.builder(
controller: controller,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Text $index'));
},
group('StatusBarGestureDetector', () {
testWidgets('Tap status bar calls onTap callback',
(WidgetTester tester) async {
tester.view.padding = FakeViewPadding(top: 20);
bool called = false;
await tester.pumpWidget(MaterialApp(
home: StatusBarGestureDetector(
onTap: (_) => called = true,
child: Container(),
),
));
await tester.tapAt(Offset.zero);
await tester.pumpAndSettle();
expect(called, isTrue);
});
testWidgets('Tap status bar scrolls primary scroll controller',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
tester.view.padding = FakeViewPadding(top: 20);
await tester.pumpWidget(MaterialApp(
home: PrimaryScrollController(
controller: controller,
child: StatusBarGestureDetector.scrollToTop(
child: Material(
child: ListView.builder(
controller: controller,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Text $index'));
},
),
),
),
),
),
));
controller.jumpTo(200);
await tester.pump();
await tester.tapAt(Offset.zero);
await tester.pumpAndSettle();
expect(controller.position.pixels, isZero);
));
controller.jumpTo(200);
await tester.pump();
await tester.tapAt(Offset.zero);
await tester.pumpAndSettle();
expect(controller.position.pixels, isZero);
});
});
}
... ...