Exposing ScrollBehaviors for app-wide settings (#76739)
This commit is contained in:
parent
aee756ff1e
commit
38fd5af5f1
@ -168,6 +168,7 @@ class CupertinoApp extends StatefulWidget {
|
|||||||
this.shortcuts,
|
this.shortcuts,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.restorationScopeId,
|
this.restorationScopeId,
|
||||||
|
this.scrollBehavior,
|
||||||
}) : assert(routes != null),
|
}) : assert(routes != null),
|
||||||
assert(navigatorObservers != null),
|
assert(navigatorObservers != null),
|
||||||
assert(title != null),
|
assert(title != null),
|
||||||
@ -207,6 +208,7 @@ class CupertinoApp extends StatefulWidget {
|
|||||||
this.shortcuts,
|
this.shortcuts,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.restorationScopeId,
|
this.restorationScopeId,
|
||||||
|
this.scrollBehavior,
|
||||||
}) : assert(title != null),
|
}) : assert(title != null),
|
||||||
assert(showPerformanceOverlay != null),
|
assert(showPerformanceOverlay != null),
|
||||||
assert(checkerboardRasterCacheImages != null),
|
assert(checkerboardRasterCacheImages != null),
|
||||||
@ -393,6 +395,16 @@ class CupertinoApp extends StatefulWidget {
|
|||||||
/// {@macro flutter.widgets.widgetsApp.restorationScopeId}
|
/// {@macro flutter.widgets.widgetsApp.restorationScopeId}
|
||||||
final String? restorationScopeId;
|
final String? restorationScopeId;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.materialApp.scrollBehavior}
|
||||||
|
///
|
||||||
|
/// When null, defaults to [CupertinoScrollBehavior].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollConfiguration], which controls how [Scrollable] widgets behave
|
||||||
|
/// in a subtree.
|
||||||
|
final ScrollBehavior? scrollBehavior;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_CupertinoAppState createState() => _CupertinoAppState();
|
_CupertinoAppState createState() => _CupertinoAppState();
|
||||||
|
|
||||||
@ -403,7 +415,18 @@ class CupertinoApp extends StatefulWidget {
|
|||||||
HeroController(); // Linear tweening.
|
HeroController(); // Linear tweening.
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AlwaysCupertinoScrollBehavior extends ScrollBehavior {
|
/// Describes how [Scrollable] widgets behave for [CupertinoApp]s.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.scrollBehavior}
|
||||||
|
///
|
||||||
|
/// Setting a [CupertinoScrollBehavior] will result in descendant [Scrollable] widgets
|
||||||
|
/// using [BouncingScrollPhysics] by default. No [GlowingOverscrollIndicator] is
|
||||||
|
/// applied when using a [CupertinoScrollBehavior] either, regardless of platform.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollBehavior], the default scrolling behavior extended by this class.
|
||||||
|
class CupertinoScrollBehavior extends ScrollBehavior {
|
||||||
@override
|
@override
|
||||||
Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
|
Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
|
||||||
// Never build any overscroll glow indicators.
|
// Never build any overscroll glow indicators.
|
||||||
@ -521,7 +544,7 @@ class _CupertinoAppState extends State<CupertinoApp> {
|
|||||||
final CupertinoThemeData effectiveThemeData = widget.theme ?? const CupertinoThemeData();
|
final CupertinoThemeData effectiveThemeData = widget.theme ?? const CupertinoThemeData();
|
||||||
|
|
||||||
return ScrollConfiguration(
|
return ScrollConfiguration(
|
||||||
behavior: _AlwaysCupertinoScrollBehavior(),
|
behavior: widget.scrollBehavior ?? CupertinoScrollBehavior(),
|
||||||
child: CupertinoUserInterfaceLevel(
|
child: CupertinoUserInterfaceLevel(
|
||||||
data: CupertinoUserInterfaceLevelData.base,
|
data: CupertinoUserInterfaceLevelData.base,
|
||||||
child: CupertinoTheme(
|
child: CupertinoTheme(
|
||||||
|
@ -196,6 +196,7 @@ class MaterialApp extends StatefulWidget {
|
|||||||
this.shortcuts,
|
this.shortcuts,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.restorationScopeId,
|
this.restorationScopeId,
|
||||||
|
this.scrollBehavior,
|
||||||
}) : assert(routes != null),
|
}) : assert(routes != null),
|
||||||
assert(navigatorObservers != null),
|
assert(navigatorObservers != null),
|
||||||
assert(title != null),
|
assert(title != null),
|
||||||
@ -242,6 +243,7 @@ class MaterialApp extends StatefulWidget {
|
|||||||
this.shortcuts,
|
this.shortcuts,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.restorationScopeId,
|
this.restorationScopeId,
|
||||||
|
this.scrollBehavior,
|
||||||
}) : assert(routeInformationParser != null),
|
}) : assert(routeInformationParser != null),
|
||||||
assert(routerDelegate != null),
|
assert(routerDelegate != null),
|
||||||
assert(title != null),
|
assert(title != null),
|
||||||
@ -632,6 +634,23 @@ class MaterialApp extends StatefulWidget {
|
|||||||
/// {@macro flutter.widgets.widgetsApp.restorationScopeId}
|
/// {@macro flutter.widgets.widgetsApp.restorationScopeId}
|
||||||
final String? restorationScopeId;
|
final String? restorationScopeId;
|
||||||
|
|
||||||
|
/// {@template flutter.material.materialApp.scrollBehavior}
|
||||||
|
/// The default [ScrollBehavior] for the application.
|
||||||
|
///
|
||||||
|
/// [ScrollBehavior]s describe how [Scrollable] widgets behave. Providing
|
||||||
|
/// a [ScrollBehavior] can set the default [ScrollPhysics] across
|
||||||
|
/// an application, and manage [Scrollable] decorations like [Scrollbar]s and
|
||||||
|
/// [GlowingOverscrollIndicator]s.
|
||||||
|
/// {@endtemplate}
|
||||||
|
///
|
||||||
|
/// When null, defaults to [MaterialScrollBehavior].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollConfiguration], which controls how [Scrollable] widgets behave
|
||||||
|
/// in a subtree.
|
||||||
|
final ScrollBehavior? scrollBehavior;
|
||||||
|
|
||||||
/// Turns on a [GridPaper] overlay that paints a baseline grid
|
/// Turns on a [GridPaper] overlay that paints a baseline grid
|
||||||
/// Material apps.
|
/// Material apps.
|
||||||
///
|
///
|
||||||
@ -657,7 +676,18 @@ class MaterialApp extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MaterialScrollBehavior extends ScrollBehavior {
|
/// Describes how [Scrollable] widgets behave for [MaterialApp]s.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.scrollBehavior}
|
||||||
|
///
|
||||||
|
/// Setting a [MaterialScrollBehavior] will apply a
|
||||||
|
/// [GlowingOverscrollIndicator] to [Scrollable] descendants when executing on
|
||||||
|
/// [TargetPlatform.android] and [TargetPlatform.fuchsia].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollBehavior], the default scrolling behavior extended by this class.
|
||||||
|
class MaterialScrollBehavior extends ScrollBehavior {
|
||||||
@override
|
@override
|
||||||
TargetPlatform getPlatform(BuildContext context) {
|
TargetPlatform getPlatform(BuildContext context) {
|
||||||
return Theme.of(context).platform;
|
return Theme.of(context).platform;
|
||||||
@ -850,7 +880,7 @@ class _MaterialAppState extends State<MaterialApp> {
|
|||||||
}());
|
}());
|
||||||
|
|
||||||
return ScrollConfiguration(
|
return ScrollConfiguration(
|
||||||
behavior: _MaterialScrollBehavior(),
|
behavior: widget.scrollBehavior ?? MaterialScrollBehavior(),
|
||||||
child: HeroControllerScope(
|
child: HeroControllerScope(
|
||||||
controller: _heroController,
|
controller: _heroController,
|
||||||
child: result,
|
child: result,
|
||||||
|
@ -14,8 +14,21 @@ const Color _kDefaultGlowColor = Color(0xFFFFFFFF);
|
|||||||
|
|
||||||
/// Describes how [Scrollable] widgets should behave.
|
/// Describes how [Scrollable] widgets should behave.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.scrollBehavior}
|
||||||
/// Used by [ScrollConfiguration] to configure the [Scrollable] widgets in a
|
/// Used by [ScrollConfiguration] to configure the [Scrollable] widgets in a
|
||||||
/// subtree.
|
/// subtree.
|
||||||
|
///
|
||||||
|
/// This class can be extended to further customize a [ScrollBehavior] for a
|
||||||
|
/// subtree. For example, overriding [ScrollBehavior.getScrollPhysics] sets the
|
||||||
|
/// default [ScrollPhysics] for [Scrollable]s that inherit this [ScrollConfiguration].
|
||||||
|
/// Overriding [ScrollBehavior.buildViewportChrome] can be used to add or change
|
||||||
|
/// default decorations like [GlowingOverscrollIndicator]s.
|
||||||
|
/// {@endtemplate}
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollConfiguration], the inherited widget that controls how
|
||||||
|
/// [Scrollable] widgets behave in a subtree.
|
||||||
@immutable
|
@immutable
|
||||||
class ScrollBehavior {
|
class ScrollBehavior {
|
||||||
/// Creates a description of how [Scrollable] widgets should behave.
|
/// Creates a description of how [Scrollable] widgets should behave.
|
||||||
@ -33,7 +46,7 @@ class ScrollBehavior {
|
|||||||
/// overscrolls.
|
/// overscrolls.
|
||||||
Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
|
Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
|
||||||
// When modifying this function, consider modifying the implementation in
|
// When modifying this function, consider modifying the implementation in
|
||||||
// _MaterialScrollBehavior as well.
|
// MaterialScrollBehavior as well.
|
||||||
switch (getPlatform(context)) {
|
switch (getPlatform(context)) {
|
||||||
case TargetPlatform.iOS:
|
case TargetPlatform.iOS:
|
||||||
case TargetPlatform.linux:
|
case TargetPlatform.linux:
|
||||||
|
@ -178,6 +178,44 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text('popped'), findsOneWidget);
|
expect(find.text('popped'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('CupertinoApp has correct default ScrollBehavior', (WidgetTester tester) async {
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
CupertinoApp(
|
||||||
|
home: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
capturedContext = context;
|
||||||
|
return const Placeholder();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(ScrollConfiguration.of(capturedContext).runtimeType, CupertinoScrollBehavior);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('A ScrollBehavior can be set for CupertinoApp', (WidgetTester tester) async {
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
CupertinoApp(
|
||||||
|
scrollBehavior: MockScrollBehavior(),
|
||||||
|
home: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
capturedContext = context;
|
||||||
|
return const Placeholder();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final ScrollBehavior scrollBehavior = ScrollConfiguration.of(capturedContext);
|
||||||
|
expect(scrollBehavior.runtimeType, MockScrollBehavior);
|
||||||
|
expect(scrollBehavior.getScrollPhysics(capturedContext).runtimeType, NeverScrollableScrollPhysics);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockScrollBehavior extends ScrollBehavior {
|
||||||
|
@override
|
||||||
|
ScrollPhysics getScrollPhysics(BuildContext context) => const NeverScrollableScrollPhysics();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef SimpleRouterDelegateBuilder = Widget Function(BuildContext, RouteInformation);
|
typedef SimpleRouterDelegateBuilder = Widget Function(BuildContext, RouteInformation);
|
||||||
|
@ -1026,6 +1026,44 @@ void main() {
|
|||||||
));
|
));
|
||||||
expect(builderChild, isNull);
|
expect(builderChild, isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('MaterialApp has correct default ScrollBehavior', (WidgetTester tester) async {
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
capturedContext = context;
|
||||||
|
return const Placeholder();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(ScrollConfiguration.of(capturedContext).runtimeType, MaterialScrollBehavior);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('A ScrollBehavior can be set for MaterialApp', (WidgetTester tester) async {
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
scrollBehavior: MockScrollBehavior(),
|
||||||
|
home: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
capturedContext = context;
|
||||||
|
return const Placeholder();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final ScrollBehavior scrollBehavior = ScrollConfiguration.of(capturedContext);
|
||||||
|
expect(scrollBehavior.runtimeType, MockScrollBehavior);
|
||||||
|
expect(scrollBehavior.getScrollPhysics(capturedContext).runtimeType, NeverScrollableScrollPhysics);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockScrollBehavior extends ScrollBehavior {
|
||||||
|
@override
|
||||||
|
ScrollPhysics getScrollPhysics(BuildContext context) => const NeverScrollableScrollPhysics();
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockAccessibilityFeature implements AccessibilityFeatures {
|
class MockAccessibilityFeature implements AccessibilityFeatures {
|
||||||
|
@ -308,6 +308,25 @@ void main() {
|
|||||||
));
|
));
|
||||||
expect(find.text('/'), findsOneWidget);
|
expect(find.text('/'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('WidgetsApp has correct default ScrollBehavior', (WidgetTester tester) async {
|
||||||
|
late BuildContext capturedContext;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
WidgetsApp(
|
||||||
|
builder: (BuildContext context, Widget? child) {
|
||||||
|
capturedContext = context;
|
||||||
|
return const Placeholder();
|
||||||
|
},
|
||||||
|
color: const Color(0xFF123456),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(ScrollConfiguration.of(capturedContext).runtimeType, ScrollBehavior);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockScrollBehavior extends ScrollBehavior {
|
||||||
|
@override
|
||||||
|
ScrollPhysics getScrollPhysics(BuildContext context) => const NeverScrollableScrollPhysics();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef SimpleRouterDelegateBuilder = Widget Function(BuildContext, RouteInformation);
|
typedef SimpleRouterDelegateBuilder = Widget Function(BuildContext, RouteInformation);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user