This reverts commit 17870d5d7eb81bc2021454093e9115c2c0d3c850.
This commit is contained in:
parent
c0c231be3b
commit
989a2f24d2
@ -19,7 +19,6 @@ import 'icon_button.dart';
|
|||||||
import 'icons.dart';
|
import 'icons.dart';
|
||||||
import 'material.dart';
|
import 'material.dart';
|
||||||
import 'material_localizations.dart';
|
import 'material_localizations.dart';
|
||||||
import 'material_state.dart';
|
|
||||||
import 'scaffold.dart';
|
import 'scaffold.dart';
|
||||||
import 'tabs.dart';
|
import 'tabs.dart';
|
||||||
import 'text_theme.dart';
|
import 'text_theme.dart';
|
||||||
@ -703,28 +702,6 @@ class _AppBarState extends State<AppBar> {
|
|||||||
static const double _defaultElevation = 4.0;
|
static const double _defaultElevation = 4.0;
|
||||||
static const Color _defaultShadowColor = Color(0xFF000000);
|
static const Color _defaultShadowColor = Color(0xFF000000);
|
||||||
|
|
||||||
ScrollNotificationObserverState? _scrollNotificationObserver;
|
|
||||||
bool _scrolledUnder = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
if (_scrollNotificationObserver != null)
|
|
||||||
_scrollNotificationObserver!.removeListener(_handleScrollNotification);
|
|
||||||
_scrollNotificationObserver = ScrollNotificationObserver.of(context);
|
|
||||||
if (_scrollNotificationObserver != null)
|
|
||||||
_scrollNotificationObserver!.addListener(_handleScrollNotification);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
if (_scrollNotificationObserver != null) {
|
|
||||||
_scrollNotificationObserver!.removeListener(_handleScrollNotification);
|
|
||||||
_scrollNotificationObserver = null;
|
|
||||||
}
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleDrawerButton() {
|
void _handleDrawerButton() {
|
||||||
Scaffold.of(context).openDrawer();
|
Scaffold.of(context).openDrawer();
|
||||||
}
|
}
|
||||||
@ -733,22 +710,6 @@ class _AppBarState extends State<AppBar> {
|
|||||||
Scaffold.of(context).openEndDrawer();
|
Scaffold.of(context).openEndDrawer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleScrollNotification(ScrollNotification notification) {
|
|
||||||
final bool oldScrolledUnder = _scrolledUnder;
|
|
||||||
_scrolledUnder = notification.depth == 0 && notification.metrics.extentBefore > 0;
|
|
||||||
if (_scrolledUnder != oldScrolledUnder) {
|
|
||||||
setState(() {
|
|
||||||
// React to a change in MaterialState.scrolledUnder
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Color _resolveColor(Set<MaterialState> states, Color? widgetColor, Color? themeColor, Color defaultColor) {
|
|
||||||
return MaterialStateProperty.resolveAs<Color?>(widgetColor, states)
|
|
||||||
?? MaterialStateProperty.resolveAs<Color?>(themeColor, states)
|
|
||||||
?? MaterialStateProperty.resolveAs<Color>(defaultColor, states);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert(!widget.primary || debugCheckHasMediaQuery(context));
|
assert(!widget.primary || debugCheckHasMediaQuery(context));
|
||||||
@ -759,11 +720,6 @@ class _AppBarState extends State<AppBar> {
|
|||||||
final ScaffoldState? scaffold = Scaffold.maybeOf(context);
|
final ScaffoldState? scaffold = Scaffold.maybeOf(context);
|
||||||
final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context);
|
final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context);
|
||||||
|
|
||||||
final FlexibleSpaceBarSettings? settings = context.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();
|
|
||||||
final Set<MaterialState> states = <MaterialState>{
|
|
||||||
if (settings?.isScrolledUnder ?? _scrolledUnder) MaterialState.scrolledUnder,
|
|
||||||
};
|
|
||||||
|
|
||||||
final bool hasDrawer = scaffold?.hasDrawer ?? false;
|
final bool hasDrawer = scaffold?.hasDrawer ?? false;
|
||||||
final bool hasEndDrawer = scaffold?.hasEndDrawer ?? false;
|
final bool hasEndDrawer = scaffold?.hasEndDrawer ?? false;
|
||||||
final bool canPop = parentRoute?.canPop ?? false;
|
final bool canPop = parentRoute?.canPop ?? false;
|
||||||
@ -776,11 +732,9 @@ class _AppBarState extends State<AppBar> {
|
|||||||
? widget.backgroundColor
|
? widget.backgroundColor
|
||||||
?? appBarTheme.backgroundColor
|
?? appBarTheme.backgroundColor
|
||||||
?? theme.primaryColor
|
?? theme.primaryColor
|
||||||
: _resolveColor(
|
: widget.backgroundColor
|
||||||
states,
|
?? appBarTheme.backgroundColor
|
||||||
widget.backgroundColor,
|
?? (colorScheme.brightness == Brightness.dark ? colorScheme.surface : colorScheme.primary);
|
||||||
appBarTheme.backgroundColor,
|
|
||||||
colorScheme.brightness == Brightness.dark ? colorScheme.surface : colorScheme.primary);
|
|
||||||
|
|
||||||
final Color foregroundColor = widget.foregroundColor
|
final Color foregroundColor = widget.foregroundColor
|
||||||
?? appBarTheme.foregroundColor
|
?? appBarTheme.foregroundColor
|
||||||
@ -1182,7 +1136,6 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
|||||||
final double extraToolbarHeight = math.max(minExtent - _bottomHeight - topPadding - (toolbarHeight ?? kToolbarHeight), 0.0);
|
final double extraToolbarHeight = math.max(minExtent - _bottomHeight - topPadding - (toolbarHeight ?? kToolbarHeight), 0.0);
|
||||||
final double visibleToolbarHeight = visibleMainHeight - _bottomHeight - extraToolbarHeight;
|
final double visibleToolbarHeight = visibleMainHeight - _bottomHeight - extraToolbarHeight;
|
||||||
|
|
||||||
final bool isScrolledUnder = overlapsContent || (pinned && shrinkOffset > maxExtent - minExtent);
|
|
||||||
final bool isPinnedWithOpacityFade = pinned && floating && bottom != null && extraToolbarHeight == 0.0;
|
final bool isPinnedWithOpacityFade = pinned && floating && bottom != null && extraToolbarHeight == 0.0;
|
||||||
final double toolbarOpacity = !pinned || isPinnedWithOpacityFade
|
final double toolbarOpacity = !pinned || isPinnedWithOpacityFade
|
||||||
? (visibleToolbarHeight / (toolbarHeight ?? kToolbarHeight)).clamp(0.0, 1.0)
|
? (visibleToolbarHeight / (toolbarHeight ?? kToolbarHeight)).clamp(0.0, 1.0)
|
||||||
@ -1193,7 +1146,6 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
|||||||
maxExtent: maxExtent,
|
maxExtent: maxExtent,
|
||||||
currentExtent: math.max(minExtent, maxExtent - shrinkOffset),
|
currentExtent: math.max(minExtent, maxExtent - shrinkOffset),
|
||||||
toolbarOpacity: toolbarOpacity,
|
toolbarOpacity: toolbarOpacity,
|
||||||
isScrolledUnder: isScrolledUnder,
|
|
||||||
child: AppBar(
|
child: AppBar(
|
||||||
leading: leading,
|
leading: leading,
|
||||||
automaticallyImplyLeading: automaticallyImplyLeading,
|
automaticallyImplyLeading: automaticallyImplyLeading,
|
||||||
@ -1203,7 +1155,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
|||||||
? Semantics(child: flexibleSpace, header: true)
|
? Semantics(child: flexibleSpace, header: true)
|
||||||
: flexibleSpace,
|
: flexibleSpace,
|
||||||
bottom: bottom,
|
bottom: bottom,
|
||||||
elevation: forceElevated || isScrolledUnder ? elevation : 0.0,
|
elevation: forceElevated || overlapsContent || (pinned && shrinkOffset > maxExtent - minExtent) ? elevation : 0.0,
|
||||||
shadowColor: shadowColor,
|
shadowColor: shadowColor,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
foregroundColor: foregroundColor,
|
foregroundColor: foregroundColor,
|
||||||
@ -1562,10 +1514,6 @@ class SliverAppBar extends StatefulWidget {
|
|||||||
/// {@macro flutter.material.appbar.backgroundColor}
|
/// {@macro flutter.material.appbar.backgroundColor}
|
||||||
///
|
///
|
||||||
/// This property is used to configure an [AppBar].
|
/// This property is used to configure an [AppBar].
|
||||||
///
|
|
||||||
/// If this color is a [MaterialStateColor] it will be resolved against
|
|
||||||
/// [MaterialState.scrolledUnder] when the content of the app's
|
|
||||||
/// primary scrollable overlaps the app bar.
|
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
||||||
/// {@macro flutter.material.appbar.foregroundColor}
|
/// {@macro flutter.material.appbar.foregroundColor}
|
||||||
|
@ -210,9 +210,8 @@ class FlexibleSpaceBar extends StatefulWidget {
|
|||||||
/// height of the resulting [FlexibleSpaceBar] when fully expanded.
|
/// height of the resulting [FlexibleSpaceBar] when fully expanded.
|
||||||
/// `currentExtent` sets the scale of the [FlexibleSpaceBar.background] and
|
/// `currentExtent` sets the scale of the [FlexibleSpaceBar.background] and
|
||||||
/// [FlexibleSpaceBar.title] widgets of [FlexibleSpaceBar] upon
|
/// [FlexibleSpaceBar.title] widgets of [FlexibleSpaceBar] upon
|
||||||
/// initialization. `scrolledUnder` is true if the the [FlexibleSpaceBar]
|
/// initialization.
|
||||||
/// overlaps the app's primary scrollable, false if it does not, and null
|
///
|
||||||
/// if the caller has not determined as much.
|
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [FlexibleSpaceBarSettings] which creates a settings object that can be
|
/// * [FlexibleSpaceBarSettings] which creates a settings object that can be
|
||||||
@ -221,7 +220,6 @@ class FlexibleSpaceBar extends StatefulWidget {
|
|||||||
double? toolbarOpacity,
|
double? toolbarOpacity,
|
||||||
double? minExtent,
|
double? minExtent,
|
||||||
double? maxExtent,
|
double? maxExtent,
|
||||||
bool? isScrolledUnder,
|
|
||||||
required double currentExtent,
|
required double currentExtent,
|
||||||
required Widget child,
|
required Widget child,
|
||||||
}) {
|
}) {
|
||||||
@ -230,7 +228,6 @@ class FlexibleSpaceBar extends StatefulWidget {
|
|||||||
toolbarOpacity: toolbarOpacity ?? 1.0,
|
toolbarOpacity: toolbarOpacity ?? 1.0,
|
||||||
minExtent: minExtent ?? currentExtent,
|
minExtent: minExtent ?? currentExtent,
|
||||||
maxExtent: maxExtent ?? currentExtent,
|
maxExtent: maxExtent ?? currentExtent,
|
||||||
isScrolledUnder: isScrolledUnder,
|
|
||||||
currentExtent: currentExtent,
|
currentExtent: currentExtent,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
@ -444,7 +441,6 @@ class FlexibleSpaceBarSettings extends InheritedWidget {
|
|||||||
required this.maxExtent,
|
required this.maxExtent,
|
||||||
required this.currentExtent,
|
required this.currentExtent,
|
||||||
required Widget child,
|
required Widget child,
|
||||||
this.isScrolledUnder,
|
|
||||||
}) : assert(toolbarOpacity != null),
|
}) : assert(toolbarOpacity != null),
|
||||||
assert(minExtent != null && minExtent >= 0),
|
assert(minExtent != null && minExtent >= 0),
|
||||||
assert(maxExtent != null && maxExtent >= 0),
|
assert(maxExtent != null && maxExtent >= 0),
|
||||||
@ -469,23 +465,11 @@ class FlexibleSpaceBarSettings extends InheritedWidget {
|
|||||||
/// these elements upon initialization.
|
/// these elements upon initialization.
|
||||||
final double currentExtent;
|
final double currentExtent;
|
||||||
|
|
||||||
/// True if the FlexibleSpaceBar overlaps the primary scrollable's contents.
|
|
||||||
///
|
|
||||||
/// This value is used by the [AppBar] to resolve
|
|
||||||
/// [AppBar.backgroundColor] against [MaterialState.scrolledUnder],
|
|
||||||
/// i.e. to enable apps to specify different colors when content
|
|
||||||
/// has been scrolled up and behind the app bar.
|
|
||||||
///
|
|
||||||
/// Null if the caller hasn't determined if the FlexibleSpaceBar
|
|
||||||
/// overlaps the primary scrollable's contents.
|
|
||||||
final bool? isScrolledUnder;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(FlexibleSpaceBarSettings oldWidget) {
|
bool updateShouldNotify(FlexibleSpaceBarSettings oldWidget) {
|
||||||
return toolbarOpacity != oldWidget.toolbarOpacity
|
return toolbarOpacity != oldWidget.toolbarOpacity
|
||||||
|| minExtent != oldWidget.minExtent
|
|| minExtent != oldWidget.minExtent
|
||||||
|| maxExtent != oldWidget.maxExtent
|
|| maxExtent != oldWidget.maxExtent
|
||||||
|| currentExtent != oldWidget.currentExtent
|
|| currentExtent != oldWidget.currentExtent;
|
||||||
|| isScrolledUnder != oldWidget.isScrolledUnder;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,13 +63,7 @@ enum MaterialState {
|
|||||||
/// See: https://material.io/design/interaction/states.html#selected.
|
/// See: https://material.io/design/interaction/states.html#selected.
|
||||||
selected,
|
selected,
|
||||||
|
|
||||||
/// The state when this widget overlaps the content of a scrollable below.
|
/// The state when this widget disabled and can not be interacted with.
|
||||||
///
|
|
||||||
/// Used by [AppBar] to indicate that the primary scrollable's
|
|
||||||
/// content has scrolled up and behind the app bar.
|
|
||||||
scrolledUnder,
|
|
||||||
|
|
||||||
/// The state when this widget is disabled and cannot be interacted with.
|
|
||||||
///
|
///
|
||||||
/// Disabled widgets should not respond to hover, focus, press, or drag
|
/// Disabled widgets should not respond to hover, focus, press, or drag
|
||||||
/// interactions.
|
/// interactions.
|
||||||
|
@ -3238,7 +3238,6 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
|||||||
return _ScaffoldScope(
|
return _ScaffoldScope(
|
||||||
hasDrawer: hasDrawer,
|
hasDrawer: hasDrawer,
|
||||||
geometryNotifier: _geometryNotifier,
|
geometryNotifier: _geometryNotifier,
|
||||||
child: ScrollNotificationObserver(
|
|
||||||
child: Material(
|
child: Material(
|
||||||
color: widget.backgroundColor ?? themeData.scaffoldBackgroundColor,
|
color: widget.backgroundColor ?? themeData.scaffoldBackgroundColor,
|
||||||
child: AnimatedBuilder(animation: _floatingActionButtonMoveController, builder: (BuildContext context, Widget? child) {
|
child: AnimatedBuilder(animation: _floatingActionButtonMoveController, builder: (BuildContext context, Widget? child) {
|
||||||
@ -3261,7 +3260,6 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,174 +0,0 @@
|
|||||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:collection';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
|
|
||||||
import 'framework.dart';
|
|
||||||
import 'notification_listener.dart';
|
|
||||||
import 'scroll_notification.dart';
|
|
||||||
|
|
||||||
/// A [ScrollNotification] listener for [ScrollNotificationObserver].
|
|
||||||
///
|
|
||||||
/// [ScrollNotificationObserver] is similar to
|
|
||||||
/// [NotificationListener]. It supports a listener list instead of
|
|
||||||
/// just a single listener and its listeners run unconditionally, they
|
|
||||||
/// do not require a gating boolean return value.
|
|
||||||
typedef ScrollNotificationCallback = void Function(ScrollNotification notification);
|
|
||||||
|
|
||||||
class _ScrollNotificationObserverScope extends InheritedWidget {
|
|
||||||
const _ScrollNotificationObserverScope({
|
|
||||||
Key? key,
|
|
||||||
required Widget child,
|
|
||||||
required ScrollNotificationObserverState scrollNotificationObserverState,
|
|
||||||
}) : _scrollNotificationObserverState = scrollNotificationObserverState,
|
|
||||||
super(key: key, child: child);
|
|
||||||
|
|
||||||
final ScrollNotificationObserverState _scrollNotificationObserverState;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotify(_ScrollNotificationObserverScope old) => _scrollNotificationObserverState != old._scrollNotificationObserverState;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ListenerEntry extends LinkedListEntry<_ListenerEntry> {
|
|
||||||
_ListenerEntry(this.listener);
|
|
||||||
final ScrollNotificationCallback listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notifies its listeners when a descendant scrolls.
|
|
||||||
///
|
|
||||||
/// To add a listener to a [ScrollNotificationObserver] ancestor:
|
|
||||||
/// ```dart
|
|
||||||
/// void listener(ScrollNotification notification) {
|
|
||||||
/// // Do something, maybe setState()
|
|
||||||
/// }
|
|
||||||
/// ScrollNotificationObserver.of(context).addListener(listener)
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// To remove the listener from a [ScrollNotificationObserver] ancestor:
|
|
||||||
/// ```dart
|
|
||||||
/// ScrollNotificationObserver.of(context).removeListener(listener);
|
|
||||||
///```
|
|
||||||
///
|
|
||||||
/// Stateful widgets that share an ancestor [ScrollNotificationObserver] typically
|
|
||||||
/// add a listener in [State.didChangeDependencies] (removing the old one
|
|
||||||
/// if necessary) and remove the listener in their [State.dispose] method.
|
|
||||||
///
|
|
||||||
/// This widget is similar to [NotificationListener]. It supports
|
|
||||||
/// a listener list instead of just a single listener and its listeners
|
|
||||||
/// run unconditionally, they do not require a gating boolean return value.
|
|
||||||
class ScrollNotificationObserver extends StatefulWidget {
|
|
||||||
/// Create a [ScrollNotificationObserver].
|
|
||||||
///
|
|
||||||
/// The [child] parameter must not be null.
|
|
||||||
const ScrollNotificationObserver({
|
|
||||||
Key? key,
|
|
||||||
required this.child,
|
|
||||||
}) : assert(child != null), super(key: key);
|
|
||||||
|
|
||||||
/// The subtree below this widget.
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
/// The closest instance of this class that encloses the given context.
|
|
||||||
///
|
|
||||||
/// If there is no enclosing [ScrollNotificationObserver] widget, then null is returned.
|
|
||||||
static ScrollNotificationObserverState? of(BuildContext context) {
|
|
||||||
return context.dependOnInheritedWidgetOfExactType<_ScrollNotificationObserverScope>()?._scrollNotificationObserverState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
ScrollNotificationObserverState createState() => ScrollNotificationObserverState();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The listener list state for a [ScrollNotificationObserver] returned by
|
|
||||||
/// [ScrollNotificationObserver.of].
|
|
||||||
///
|
|
||||||
/// [ScrollNotificationObserver] is similar to
|
|
||||||
/// [NotificationListener]. It supports a listener list instead of
|
|
||||||
/// just a single listener and its listeners run unconditionally, they
|
|
||||||
/// do not require a gating boolean return value.
|
|
||||||
class ScrollNotificationObserverState extends State<ScrollNotificationObserver> {
|
|
||||||
LinkedList<_ListenerEntry>? _listeners = LinkedList<_ListenerEntry>();
|
|
||||||
|
|
||||||
bool _debugAssertNotDisposed() {
|
|
||||||
assert(() {
|
|
||||||
if (_listeners == null) {
|
|
||||||
throw FlutterError(
|
|
||||||
'A $runtimeType was used after being disposed.\n'
|
|
||||||
'Once you have called dispose() on a $runtimeType, it can no longer be used.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a [ScrollNotificationCallback] that will be called each time
|
|
||||||
/// a descendant scrolls.
|
|
||||||
void addListener(ScrollNotificationCallback listener) {
|
|
||||||
assert(_debugAssertNotDisposed());
|
|
||||||
_listeners!.add(_ListenerEntry(listener));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove the specified [ScrollNotificationCallback].
|
|
||||||
void removeListener(ScrollNotificationCallback listener) {
|
|
||||||
assert(_debugAssertNotDisposed());
|
|
||||||
for (final _ListenerEntry entry in _listeners!) {
|
|
||||||
if (entry.listener == listener) {
|
|
||||||
entry.unlink();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _notifyListeners(ScrollNotification notification) {
|
|
||||||
assert(_debugAssertNotDisposed());
|
|
||||||
if (_listeners!.isEmpty)
|
|
||||||
return;
|
|
||||||
|
|
||||||
final List<_ListenerEntry> localListeners = List<_ListenerEntry>.from(_listeners!);
|
|
||||||
for (final _ListenerEntry entry in localListeners) {
|
|
||||||
try {
|
|
||||||
if (entry.list != null)
|
|
||||||
entry.listener(notification);
|
|
||||||
} catch (exception, stack) {
|
|
||||||
FlutterError.reportError(FlutterErrorDetails(
|
|
||||||
exception: exception,
|
|
||||||
stack: stack,
|
|
||||||
library: 'widget library',
|
|
||||||
context: ErrorDescription('while dispatching notifications for $runtimeType'),
|
|
||||||
informationCollector: () sync* {
|
|
||||||
yield DiagnosticsProperty<ScrollNotificationObserverState>(
|
|
||||||
'The $runtimeType sending notification was',
|
|
||||||
this,
|
|
||||||
style: DiagnosticsTreeStyle.errorProperty,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return NotificationListener<ScrollNotification>(
|
|
||||||
onNotification: (ScrollNotification notification) {
|
|
||||||
_notifyListeners(notification);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
child: _ScrollNotificationObserverScope(
|
|
||||||
scrollNotificationObserverState: this,
|
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
assert(_debugAssertNotDisposed());
|
|
||||||
_listeners = null;
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
@ -98,7 +98,6 @@ export 'src/widgets/scroll_context.dart';
|
|||||||
export 'src/widgets/scroll_controller.dart';
|
export 'src/widgets/scroll_controller.dart';
|
||||||
export 'src/widgets/scroll_metrics.dart';
|
export 'src/widgets/scroll_metrics.dart';
|
||||||
export 'src/widgets/scroll_notification.dart';
|
export 'src/widgets/scroll_notification.dart';
|
||||||
export 'src/widgets/scroll_notification_observer.dart';
|
|
||||||
export 'src/widgets/scroll_physics.dart';
|
export 'src/widgets/scroll_physics.dart';
|
||||||
export 'src/widgets/scroll_position.dart';
|
export 'src/widgets/scroll_position.dart';
|
||||||
export 'src/widgets/scroll_position_with_single_context.dart';
|
export 'src/widgets/scroll_position_with_single_context.dart';
|
||||||
|
@ -2518,239 +2518,4 @@ void main() {
|
|||||||
expect(actionIconTheme.color, foregroundColor);
|
expect(actionIconTheme.color, foregroundColor);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('SliverAppBar.backgroundColor MaterialStateColor scrolledUnder', (WidgetTester tester) async {
|
|
||||||
const double collapsedHeight = kToolbarHeight;
|
|
||||||
const double expandedHeight = 200.0;
|
|
||||||
const Color scrolledColor = Color(0xff00ff00);
|
|
||||||
const Color defaultColor = Color(0xff0000ff);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: CustomScrollView(
|
|
||||||
slivers: <Widget>[
|
|
||||||
SliverAppBar(
|
|
||||||
backwardsCompatibility: false,
|
|
||||||
elevation: 0,
|
|
||||||
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
||||||
return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
|
|
||||||
}),
|
|
||||||
expandedHeight: expandedHeight,
|
|
||||||
pinned: true,
|
|
||||||
),
|
|
||||||
SliverList(
|
|
||||||
delegate: SliverChildListDelegate(
|
|
||||||
<Widget>[
|
|
||||||
Container(height: 1200.0, color: Colors.teal),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Finder findAppBarMaterial() {
|
|
||||||
return find.descendant(of: find.byType(AppBar), matching: find.byType(Material));
|
|
||||||
}
|
|
||||||
|
|
||||||
Color? getAppBarBackgroundColor() {
|
|
||||||
return tester.widget<Material>(findAppBarMaterial()).color;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), defaultColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
|
|
||||||
|
|
||||||
TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
|
|
||||||
await gesture.moveBy(const Offset(0.0, -expandedHeight));
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), scrolledColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
|
|
||||||
|
|
||||||
gesture = await tester.startGesture(const Offset(50.0, 300.0));
|
|
||||||
await gesture.moveBy(const Offset(0.0, expandedHeight));
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), defaultColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('SliverAppBar.backgroundColor with FlexibleSpace MaterialStateColor scrolledUnder', (WidgetTester tester) async {
|
|
||||||
const double collapsedHeight = kToolbarHeight;
|
|
||||||
const double expandedHeight = 200.0;
|
|
||||||
const Color scrolledColor = Color(0xff00ff00);
|
|
||||||
const Color defaultColor = Color(0xff0000ff);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: CustomScrollView(
|
|
||||||
slivers: <Widget>[
|
|
||||||
SliverAppBar(
|
|
||||||
backwardsCompatibility: false,
|
|
||||||
elevation: 0,
|
|
||||||
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
||||||
return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
|
|
||||||
}),
|
|
||||||
expandedHeight: expandedHeight,
|
|
||||||
pinned: true,
|
|
||||||
flexibleSpace: const FlexibleSpaceBar(
|
|
||||||
title: Text('SliverAppBar'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SliverList(
|
|
||||||
delegate: SliverChildListDelegate(
|
|
||||||
<Widget>[
|
|
||||||
Container(height: 1200.0, color: Colors.teal),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Finder findAppBarMaterial() {
|
|
||||||
// There are 2 Material widgets below AppBar. The second is only added if
|
|
||||||
// flexibleSpace is non-null.
|
|
||||||
return find.descendant(of: find.byType(AppBar), matching: find.byType(Material)).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color? getAppBarBackgroundColor() {
|
|
||||||
return tester.widget<Material>(findAppBarMaterial()).color;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), defaultColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
|
|
||||||
|
|
||||||
TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
|
|
||||||
await gesture.moveBy(const Offset(0.0, -expandedHeight));
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), scrolledColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, collapsedHeight);
|
|
||||||
|
|
||||||
gesture = await tester.startGesture(const Offset(50.0, 300.0));
|
|
||||||
await gesture.moveBy(const Offset(0.0, expandedHeight));
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), defaultColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, expandedHeight);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('AppBar.backgroundColor MaterialStateColor scrolledUnder', (WidgetTester tester) async {
|
|
||||||
const Color scrolledColor = Color(0xff00ff00);
|
|
||||||
const Color defaultColor = Color(0xff0000ff);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
backwardsCompatibility: false,
|
|
||||||
elevation: 0,
|
|
||||||
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
||||||
return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
|
|
||||||
}),
|
|
||||||
title: const Text('AppBar'),
|
|
||||||
),
|
|
||||||
body: ListView(
|
|
||||||
children: <Widget>[
|
|
||||||
Container(height: 1200.0, color: Colors.teal),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Finder findAppBarMaterial() {
|
|
||||||
return find.descendant(of: find.byType(AppBar), matching: find.byType(Material));
|
|
||||||
}
|
|
||||||
|
|
||||||
Color? getAppBarBackgroundColor() {
|
|
||||||
return tester.widget<Material>(findAppBarMaterial()).color;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), defaultColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
|
|
||||||
|
|
||||||
TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
|
|
||||||
await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), scrolledColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
|
|
||||||
|
|
||||||
gesture = await tester.startGesture(const Offset(50.0, 300.0));
|
|
||||||
await gesture.moveBy(const Offset(0.0, kToolbarHeight));
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), defaultColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('AppBar.backgroundColor with FlexibleSpace MaterialStateColor scrolledUnder', (WidgetTester tester) async {
|
|
||||||
const Color scrolledColor = Color(0xff00ff00);
|
|
||||||
const Color defaultColor = Color(0xff0000ff);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
backwardsCompatibility: false,
|
|
||||||
elevation: 0,
|
|
||||||
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
|
||||||
return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
|
|
||||||
}),
|
|
||||||
title: const Text('AppBar'),
|
|
||||||
flexibleSpace: const FlexibleSpaceBar(
|
|
||||||
title: Text('FlexibleSpace'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: ListView(
|
|
||||||
children: <Widget>[
|
|
||||||
Container(height: 1200.0, color: Colors.teal),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Finder findAppBarMaterial() {
|
|
||||||
// There are 2 Material widgets below AppBar. The second is only added if
|
|
||||||
// flexibleSpace is non-null.
|
|
||||||
return find.descendant(of: find.byType(AppBar), matching: find.byType(Material)).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color? getAppBarBackgroundColor() {
|
|
||||||
return tester.widget<Material>(findAppBarMaterial()).color;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), defaultColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
|
|
||||||
|
|
||||||
TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
|
|
||||||
await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), scrolledColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
|
|
||||||
|
|
||||||
gesture = await tester.startGesture(const Offset(50.0, 300.0));
|
|
||||||
await gesture.moveBy(const Offset(0.0, kToolbarHeight));
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(getAppBarBackgroundColor(), defaultColor);
|
|
||||||
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -2190,9 +2190,6 @@ void main() {
|
|||||||
' PhysicalModel\n'
|
' PhysicalModel\n'
|
||||||
' AnimatedPhysicalModel\n'
|
' AnimatedPhysicalModel\n'
|
||||||
' Material\n'
|
' Material\n'
|
||||||
' _ScrollNotificationObserverScope\n'
|
|
||||||
' NotificationListener<ScrollNotification>\n'
|
|
||||||
' ScrollNotificationObserver\n'
|
|
||||||
' _ScaffoldScope\n'
|
' _ScaffoldScope\n'
|
||||||
' Scaffold\n'
|
' Scaffold\n'
|
||||||
' MediaQuery\n'
|
' MediaQuery\n'
|
||||||
|
@ -150,69 +150,4 @@ void main() {
|
|||||||
expect(notificationTypes, equals(types));
|
expect(notificationTypes, equals(types));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ScrollNotificationObserver', (WidgetTester tester) async {
|
|
||||||
late ScrollNotificationObserverState observer;
|
|
||||||
ScrollNotification? notification;
|
|
||||||
|
|
||||||
void handleNotification(ScrollNotification value) {
|
|
||||||
if (value is ScrollStartNotification || value is ScrollUpdateNotification || value is ScrollEndNotification)
|
|
||||||
notification = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
ScrollNotificationObserver(
|
|
||||||
child: Builder(
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
observer = ScrollNotificationObserver.of(context)!;
|
|
||||||
return const SingleChildScrollView(
|
|
||||||
child: SizedBox(height: 1200.0),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
observer.addListener(handleNotification);
|
|
||||||
|
|
||||||
TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(notification, isA<ScrollStartNotification>());
|
|
||||||
expect(notification!.depth, equals(0));
|
|
||||||
|
|
||||||
final ScrollStartNotification start = notification! as ScrollStartNotification;
|
|
||||||
expect(start.dragDetails, isNotNull);
|
|
||||||
expect(start.dragDetails!.globalPosition, equals(const Offset(100.0, 100.0)));
|
|
||||||
|
|
||||||
await gesture.moveBy(const Offset(-10.0, -10.0));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(notification, isA<ScrollUpdateNotification>());
|
|
||||||
expect(notification!.depth, equals(0));
|
|
||||||
final ScrollUpdateNotification update = notification! as ScrollUpdateNotification;
|
|
||||||
expect(update.dragDetails, isNotNull);
|
|
||||||
expect(update.dragDetails!.globalPosition, equals(const Offset(90.0, 90.0)));
|
|
||||||
expect(update.dragDetails!.delta, equals(const Offset(0.0, -10.0)));
|
|
||||||
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(notification, isA<ScrollEndNotification>());
|
|
||||||
expect(notification!.depth, equals(0));
|
|
||||||
final ScrollEndNotification end = notification! as ScrollEndNotification;
|
|
||||||
expect(end.dragDetails, isNotNull);
|
|
||||||
expect(end.dragDetails!.velocity, equals(Velocity.zero));
|
|
||||||
|
|
||||||
observer.removeListener(handleNotification);
|
|
||||||
notification = null;
|
|
||||||
|
|
||||||
gesture = await tester.startGesture(const Offset(100.0, 100.0));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(notification, isNull);
|
|
||||||
|
|
||||||
await gesture.moveBy(const Offset(-10.0, -10.0));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(notification, isNull);
|
|
||||||
|
|
||||||
await gesture.up();
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(notification, isNull);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user