
Mainly, this adds documentation to members that were previously lacking documentation. It also adds a big block of documentation about improving performance of widgets. This also removes some references to package:collection and adds global setEquals and listEquals methods in foundation that we can use. (setEquals in particular should be much faster than the package:collection equivalent, though both should be faster as they avoid allocating new objects.) All remaining references now qualify the import so we know what our remaining dependencies are. Also lots of code reordering in Flutter driver to make the code consistent and apply the style guide more thoroughly.
362 lines
13 KiB
Dart
362 lines
13 KiB
Dart
// Copyright 2015 The Chromium 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 'package:flutter/foundation.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
import 'framework.dart';
|
|
|
|
/// Delegate for configuring a [SliverPersistentHeader].
|
|
abstract class SliverPersistentHeaderDelegate {
|
|
/// Abstract const constructor. This constructor enables subclasses to provide
|
|
/// const constructors so that they can be used in const expressions.
|
|
const SliverPersistentHeaderDelegate();
|
|
|
|
/// The widget to place inside the [SliverPersistentHeader].
|
|
///
|
|
/// The `context` is the [BuildContext] of the sliver.
|
|
///
|
|
/// The `shrinkOffset` is a distance from [maxExtent] towards [minExtent]
|
|
/// representing the current amount by which the sliver has been shrunk. When
|
|
/// the `shrinkOffset` is zero, the contents will be rendered with a dimension
|
|
/// of [maxExtent] in the main axis. When `shrinkOffset` equals the difference
|
|
/// between [maxExtent] and [minExtent] (a positive number), the contents will
|
|
/// be rendered with a dimension of [minExtent] in the main axis. The
|
|
/// `shrinkOffset` will always be a positive number in that range.
|
|
///
|
|
/// The `overlapsContent` argument is true if subsequent slivers (if any) will
|
|
/// be rendered beneath this one, and false if the sliver will not have any
|
|
/// contents below it. Typically this is used to decide whether to draw a
|
|
/// shadow to simulate the sliver being above the contents below it. Typically
|
|
/// this is true when `shrinkOffset` is at its greatest value and false
|
|
/// otherwise, but that is not guaranteed. See [NestedScrollView] for an
|
|
/// example of a case where `overlapsContent`'s value can be unrelated to
|
|
/// `shrinkOffset`.
|
|
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent);
|
|
|
|
/// The smallest size to allow the header to reach, when it shrinks at the
|
|
/// start of the viewport.
|
|
///
|
|
/// This must return a value equal to or less than [maxExtent].
|
|
///
|
|
/// This value should not change over the lifetime of the delegate. It should
|
|
/// be based entirely on the constructor arguments passed to the delegate. See
|
|
/// [shouldRebuild], which must return true if a new delegate would return a
|
|
/// different value.
|
|
double get minExtent;
|
|
|
|
/// The size of the header when it is not shrinking at the top of the
|
|
/// viewport.
|
|
///
|
|
/// This must return a value equal to or greater than [minExtent].
|
|
///
|
|
/// This value should not change over the lifetime of the delegate. It should
|
|
/// be based entirely on the constructor arguments passed to the delegate. See
|
|
/// [shouldRebuild], which must return true if a new delegate would return a
|
|
/// different value.
|
|
double get maxExtent;
|
|
|
|
/// Specifies how floating headers should animate in and out of view.
|
|
///
|
|
/// If the value of this property is null, then floating headers will
|
|
/// not animate into place.
|
|
///
|
|
/// This is only used for floating headers (those with
|
|
/// [SliverPersistentHeader.floating] set to true).
|
|
///
|
|
/// Defaults to null.
|
|
FloatingHeaderSnapConfiguration get snapConfiguration => null;
|
|
|
|
/// Whether this delegate is meaningfully different from the old delegate.
|
|
///
|
|
/// If this returns false, then the header might not be rebuilt, even though
|
|
/// the instance of the delegate changed.
|
|
///
|
|
/// This must return true if `oldDelegate` and this object would return
|
|
/// different values for [minExtent], [maxExtent], [snapConfiguration], or
|
|
/// would return a meaningfully different widget tree from [build] for the
|
|
/// same arguments.
|
|
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate);
|
|
}
|
|
|
|
/// A sliver whose size varies when the sliver is scrolled to the leading edge
|
|
/// of the viewport.
|
|
///
|
|
/// This is the layout primitive that [SliverAppBar] uses for its
|
|
/// shrinking/growing effect.
|
|
class SliverPersistentHeader extends StatelessWidget {
|
|
/// Creates a sliver that varies its size when it is scrolled to the start of
|
|
/// a viewport.
|
|
///
|
|
/// The [delegate], [pinned], and [floating] arguments must not be null.
|
|
const SliverPersistentHeader({
|
|
Key key,
|
|
@required this.delegate,
|
|
this.pinned: false,
|
|
this.floating: false,
|
|
}) : assert(delegate != null),
|
|
assert(pinned != null),
|
|
assert(floating != null),
|
|
super(key: key);
|
|
|
|
/// Configuration for the sliver's layout.
|
|
///
|
|
/// The delegate provides the following information:
|
|
///
|
|
/// * The minimum and maximum dimensions of the sliver.
|
|
///
|
|
/// * The builder for generating the widgets of the sliver.
|
|
///
|
|
/// * The instructions for snapping the scroll offset, if [floating] is true.
|
|
final SliverPersistentHeaderDelegate delegate;
|
|
|
|
/// Whether to stick the header to the start of the viewport once it has
|
|
/// reached its minimum size.
|
|
///
|
|
/// If this is false, the header will continue scrolling off the screen after
|
|
/// it has shrunk to its minimum extent.
|
|
final bool pinned;
|
|
|
|
/// Whether the header should immediately grow again if the user reverses
|
|
/// scroll direction.
|
|
///
|
|
/// If this is false, the header only grows again once the user reaches the
|
|
/// part of the viewport that contains the sliver.
|
|
///
|
|
/// The [delegate]'s [SliverPersistentHeaderDelegate.snapConfiguration] is
|
|
/// ignored unless [floating] is true.
|
|
final bool floating;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (floating && pinned)
|
|
return new _SliverFloatingPinnedPersistentHeader(delegate: delegate);
|
|
if (pinned)
|
|
return new _SliverPinnedPersistentHeader(delegate: delegate);
|
|
if (floating)
|
|
return new _SliverFloatingPersistentHeader(delegate: delegate);
|
|
return new _SliverScrollingPersistentHeader(delegate: delegate);
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('delegate: $delegate');
|
|
final List<String> flags = <String>[];
|
|
if (pinned)
|
|
flags.add('pinned');
|
|
if (floating)
|
|
flags.add('floating');
|
|
if (flags.isEmpty)
|
|
flags.add('normal');
|
|
description.add('mode: ${flags.join(", ")}');
|
|
}
|
|
}
|
|
|
|
class _SliverPersistentHeaderElement extends RenderObjectElement {
|
|
_SliverPersistentHeaderElement(_SliverPersistentHeaderRenderObjectWidget widget) : super(widget);
|
|
|
|
@override
|
|
_SliverPersistentHeaderRenderObjectWidget get widget => super.widget;
|
|
|
|
@override
|
|
_RenderSliverPersistentHeaderForWidgetsMixin get renderObject => super.renderObject;
|
|
|
|
@override
|
|
void mount(Element parent, dynamic newSlot) {
|
|
super.mount(parent, newSlot);
|
|
renderObject._element = this;
|
|
}
|
|
|
|
@override
|
|
void unmount() {
|
|
super.unmount();
|
|
renderObject._element = null;
|
|
}
|
|
|
|
@override
|
|
void update(_SliverPersistentHeaderRenderObjectWidget newWidget) {
|
|
final _SliverPersistentHeaderRenderObjectWidget oldWidget = widget;
|
|
super.update(newWidget);
|
|
final SliverPersistentHeaderDelegate newDelegate = newWidget.delegate;
|
|
final SliverPersistentHeaderDelegate oldDelegate = oldWidget.delegate;
|
|
if (newDelegate != oldDelegate &&
|
|
(newDelegate.runtimeType != oldDelegate.runtimeType || newDelegate.shouldRebuild(oldDelegate)))
|
|
renderObject.triggerRebuild();
|
|
}
|
|
|
|
@override
|
|
void performRebuild() {
|
|
renderObject.triggerRebuild();
|
|
}
|
|
|
|
Element child;
|
|
|
|
void _build(double shrinkOffset, bool overlapsContent) {
|
|
owner.buildScope(this, () {
|
|
child = updateChild(child, widget.delegate.build(this, shrinkOffset, overlapsContent), null);
|
|
});
|
|
}
|
|
|
|
@override
|
|
void forgetChild(Element child) {
|
|
assert(child == this.child);
|
|
this.child = null;
|
|
}
|
|
|
|
@override
|
|
void insertChildRenderObject(covariant RenderObject child, Null slot) {
|
|
assert(renderObject.debugValidateChild(child));
|
|
renderObject.child = child;
|
|
}
|
|
|
|
@override
|
|
void moveChildRenderObject(covariant RenderObject child, Null slot) {
|
|
assert(false);
|
|
}
|
|
|
|
@override
|
|
void removeChildRenderObject(covariant RenderObject child) {
|
|
renderObject.child = null;
|
|
}
|
|
|
|
@override
|
|
void visitChildren(ElementVisitor visitor) {
|
|
if (child != null)
|
|
visitor(child);
|
|
}
|
|
}
|
|
|
|
abstract class _SliverPersistentHeaderRenderObjectWidget extends RenderObjectWidget {
|
|
const _SliverPersistentHeaderRenderObjectWidget({
|
|
Key key,
|
|
@required this.delegate,
|
|
}) : assert(delegate != null),
|
|
super(key: key);
|
|
|
|
final SliverPersistentHeaderDelegate delegate;
|
|
|
|
@override
|
|
_SliverPersistentHeaderElement createElement() => new _SliverPersistentHeaderElement(this);
|
|
|
|
@override
|
|
_RenderSliverPersistentHeaderForWidgetsMixin createRenderObject(BuildContext context);
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('delegate: $delegate');
|
|
}
|
|
}
|
|
|
|
abstract class _RenderSliverPersistentHeaderForWidgetsMixin extends RenderSliverPersistentHeader {
|
|
// This class is intended to be used as a mixin, and should not be
|
|
// extended directly.
|
|
factory _RenderSliverPersistentHeaderForWidgetsMixin._() => null;
|
|
|
|
_SliverPersistentHeaderElement _element;
|
|
|
|
@override
|
|
double get minExtent => _element.widget.delegate.minExtent;
|
|
|
|
@override
|
|
double get maxExtent => _element.widget.delegate.maxExtent;
|
|
|
|
@override
|
|
void updateChild(double shrinkOffset, bool overlapsContent) {
|
|
assert(_element != null);
|
|
_element._build(shrinkOffset, overlapsContent);
|
|
}
|
|
|
|
@protected
|
|
void triggerRebuild() {
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
class _SliverScrollingPersistentHeader extends _SliverPersistentHeaderRenderObjectWidget {
|
|
const _SliverScrollingPersistentHeader({
|
|
Key key,
|
|
@required SliverPersistentHeaderDelegate delegate,
|
|
}) : super(key: key, delegate: delegate);
|
|
|
|
@override
|
|
_RenderSliverPersistentHeaderForWidgetsMixin createRenderObject(BuildContext context) {
|
|
return new _RenderSliverScrollingPersistentHeaderForWidgets();
|
|
}
|
|
}
|
|
|
|
// This class exists to work around https://github.com/dart-lang/sdk/issues/15101
|
|
abstract class _RenderSliverScrollingPersistentHeader extends RenderSliverScrollingPersistentHeader { }
|
|
|
|
class _RenderSliverScrollingPersistentHeaderForWidgets extends _RenderSliverScrollingPersistentHeader
|
|
with _RenderSliverPersistentHeaderForWidgetsMixin { }
|
|
|
|
class _SliverPinnedPersistentHeader extends _SliverPersistentHeaderRenderObjectWidget {
|
|
const _SliverPinnedPersistentHeader({
|
|
Key key,
|
|
@required SliverPersistentHeaderDelegate delegate,
|
|
}) : super(key: key, delegate: delegate);
|
|
|
|
@override
|
|
_RenderSliverPersistentHeaderForWidgetsMixin createRenderObject(BuildContext context) {
|
|
return new _RenderSliverPinnedPersistentHeaderForWidgets();
|
|
}
|
|
}
|
|
|
|
// This class exists to work around https://github.com/dart-lang/sdk/issues/15101
|
|
abstract class _RenderSliverPinnedPersistentHeader extends RenderSliverPinnedPersistentHeader { }
|
|
|
|
class _RenderSliverPinnedPersistentHeaderForWidgets extends _RenderSliverPinnedPersistentHeader with _RenderSliverPersistentHeaderForWidgetsMixin { }
|
|
|
|
class _SliverFloatingPersistentHeader extends _SliverPersistentHeaderRenderObjectWidget {
|
|
const _SliverFloatingPersistentHeader({
|
|
Key key,
|
|
@required SliverPersistentHeaderDelegate delegate,
|
|
}) : super(key: key, delegate: delegate);
|
|
|
|
@override
|
|
_RenderSliverPersistentHeaderForWidgetsMixin createRenderObject(BuildContext context) {
|
|
// Not passing this snapConfiguration as a constructor parameter to avoid the
|
|
// additional layers added due to https://github.com/dart-lang/sdk/issues/15101
|
|
return new _RenderSliverFloatingPersistentHeaderForWidgets()
|
|
..snapConfiguration = delegate.snapConfiguration;
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, _RenderSliverFloatingPersistentHeaderForWidgets renderObject) {
|
|
renderObject.snapConfiguration = delegate.snapConfiguration;
|
|
}
|
|
}
|
|
|
|
// This class exists to work around https://github.com/dart-lang/sdk/issues/15101
|
|
abstract class _RenderSliverFloatingPinnedPersistentHeader extends RenderSliverFloatingPinnedPersistentHeader { }
|
|
|
|
class _RenderSliverFloatingPinnedPersistentHeaderForWidgets extends _RenderSliverFloatingPinnedPersistentHeader with _RenderSliverPersistentHeaderForWidgetsMixin { }
|
|
|
|
class _SliverFloatingPinnedPersistentHeader extends _SliverPersistentHeaderRenderObjectWidget {
|
|
const _SliverFloatingPinnedPersistentHeader({
|
|
Key key,
|
|
@required SliverPersistentHeaderDelegate delegate,
|
|
}) : super(key: key, delegate: delegate);
|
|
|
|
@override
|
|
_RenderSliverPersistentHeaderForWidgetsMixin createRenderObject(BuildContext context) {
|
|
// Not passing this snapConfiguration as a constructor parameter to avoid the
|
|
// additional layers added due to https://github.com/dart-lang/sdk/issues/15101
|
|
return new _RenderSliverFloatingPinnedPersistentHeaderForWidgets()
|
|
..snapConfiguration = delegate.snapConfiguration;
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, _RenderSliverFloatingPinnedPersistentHeaderForWidgets renderObject) {
|
|
renderObject.snapConfiguration = delegate.snapConfiguration;
|
|
}
|
|
}
|
|
|
|
// This class exists to work around https://github.com/dart-lang/sdk/issues/15101
|
|
abstract class _RenderSliverFloatingPersistentHeader extends RenderSliverFloatingPersistentHeader { }
|
|
|
|
class _RenderSliverFloatingPersistentHeaderForWidgets extends _RenderSliverFloatingPersistentHeader with _RenderSliverPersistentHeaderForWidgetsMixin { }
|