Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
1426ef99
Commit
1426ef99
authored
Jan 25, 2017
by
Ian Hickson
Committed by
GitHub
Jan 25, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Appbar slivers. (#7631)
parent
eba5fb1a
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1144 additions
and
0 deletions
+1144
-0
rendering.dart
packages/flutter/lib/rendering.dart
+1
-0
sliver_app_bar.dart
packages/flutter/lib/src/rendering/sliver_app_bar.dart
+308
-0
app_bar.dart
packages/flutter/lib/src/widgets/app_bar.dart
+228
-0
widgets.dart
packages/flutter/lib/widgets.dart
+1
-0
slivers_appbar_floating_test.dart
...es/flutter/test/widgets/slivers_appbar_floating_test.dart
+251
-0
slivers_appbar_pinned_test.dart
...ages/flutter/test/widgets/slivers_appbar_pinned_test.dart
+243
-0
slivers_appbar_scrolling_test.dart
...s/flutter/test/widgets/slivers_appbar_scrolling_test.dart
+112
-0
No files found.
packages/flutter/lib/rendering.dart
View file @
1426ef99
...
...
@@ -45,6 +45,7 @@ export 'src/rendering/rotated_box.dart';
export
'src/rendering/semantics.dart'
;
export
'src/rendering/shifted_box.dart'
;
export
'src/rendering/sliver.dart'
;
export
'src/rendering/sliver_app_bar.dart'
;
export
'src/rendering/sliver_block.dart'
;
export
'src/rendering/stack.dart'
;
export
'src/rendering/table.dart'
;
...
...
packages/flutter/lib/src/rendering/sliver_app_bar.dart
0 → 100644
View file @
1426ef99
// Copyright 2016 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
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:meta/meta.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'box.dart'
;
import
'binding.dart'
;
import
'object.dart'
;
import
'sliver.dart'
;
abstract
class
RenderSliverAppBar
extends
RenderSliver
with
RenderObjectWithChildMixin
<
RenderBox
>,
RenderSliverHelpers
{
RenderSliverAppBar
({
RenderBox
child
})
{
this
.
child
=
child
;
}
double
get
maxExtent
;
/// The intrinsic size of the child as of the last time the sliver was laid out.
///
/// If the render object is dirty (i.e. if [markNeedsLayout] has been called,
/// or if the object was newly created), then the returned value will be stale
/// until [layoutChild] has been called.
@protected
double
get
minExtent
=>
_minExtent
;
double
_minExtent
;
@protected
double
get
childExtent
{
if
(
child
==
null
)
return
0.0
;
assert
(
child
.
hasSize
);
assert
(
constraints
.
axis
!=
null
);
switch
(
constraints
.
axis
)
{
case
Axis
.
vertical
:
return
child
.
size
.
height
;
case
Axis
.
horizontal
:
return
child
.
size
.
width
;
}
return
null
;
}
@protected
double
_getChildIntrinsicExtent
()
{
if
(
child
==
null
)
return
0.0
;
assert
(
child
!=
null
);
assert
(
constraints
.
axis
!=
null
);
switch
(
constraints
.
axis
)
{
case
Axis
.
vertical
:
return
child
.
getMinIntrinsicHeight
(
constraints
.
crossAxisExtent
);
case
Axis
.
horizontal
:
return
child
.
getMinIntrinsicWidth
(
constraints
.
crossAxisExtent
);
}
return
null
;
}
/// The last value that we passed to updateChild().
double
_lastShrinkOffset
;
/// Called during layout if the shrink offset has changed.
///
/// During this callback, the [child] can be set, mutated, or replaced.
@protected
void
updateChild
(
double
shrinkOffset
)
{
}
/// Flag the current child as stale and needing updating even if the shrink
/// offset has not changed.
///
/// Call this whenever [updateChild] would change or mutate the child even if
/// given the same `shrinkOffset` as the last time it was called.
///
/// This must be implemented by [RenderSliverAppBar] subclasses such that the
/// next layout after this call will result in [updateChild] being called.
@protected
void
markNeedsUpdate
()
{
markNeedsLayout
();
_lastShrinkOffset
=
null
;
}
void
layoutChild
(
double
scrollOffset
,
double
maxExtent
)
{
assert
(
maxExtent
!=
null
);
final
double
shrinkOffset
=
math
.
min
(
scrollOffset
,
maxExtent
);
if
(
shrinkOffset
!=
_lastShrinkOffset
)
{
invokeLayoutCallback
<
SliverConstraints
>((
SliverConstraints
constraints
)
{
assert
(
constraints
==
this
.
constraints
);
updateChild
(
shrinkOffset
);
_minExtent
=
_getChildIntrinsicExtent
();
});
_lastShrinkOffset
=
shrinkOffset
;
}
assert
(
_minExtent
!=
null
);
assert
(()
{
if
(
_minExtent
<=
maxExtent
)
return
true
;
throw
new
FlutterError
(
'The maxExtent for this
$runtimeType
is less than the child
\'
s intrinsic extent.
\n
'
'The specified maxExtent was:
${maxExtent.toStringAsFixed(1)}
\n
'
'The child was updated with shrink offset:
${shrinkOffset.toStringAsFixed(1)}
\n
'
'The actual measured intrinsic extent of the child was:
${_minExtent.toStringAsFixed(1)}
\n
'
);
});
child
?.
layout
(
constraints
.
asBoxConstraints
(
maxExtent:
math
.
max
(
_minExtent
,
maxExtent
-
shrinkOffset
)),
parentUsesSize:
true
,
);
}
/// Returns the distance from the leading _visible_ edge of the sliver to the
/// side of the child closest to that edge, in the scroll axis direction.
///
/// For example, if the [constraints] describe this sliver as having an axis
/// direction of [AxisDirection.down], then this is the distance from the top
/// of the visible portion of the sliver to the top of the child. If the child
/// is scrolled partially off the top of the viewport, then this will be
/// negative. On the other hand, if the [constraints] describe this sliver as
/// having an axis direction of [AxisDirection.up], then this is the distance
/// from the bottom of the visible portion of the sliver to the bottom of the
/// child. In both cases, this is the direction of increasing
/// [SliverConstraints.scrollOffset].
///
/// Calling this when the child is not visible is not valid.
///
/// The argument must be the value of the [child] property.
///
/// This must be implemented by [RenderSliverAppBar] subclasses.
///
/// If there is no child, this should return 0.0.
@override
double
childPosition
(
@checked
RenderObject
child
)
=>
super
.
childPosition
(
child
);
@override
bool
hitTestChildren
(
HitTestResult
result
,
{
@required
double
mainAxisPosition
,
@required
double
crossAxisPosition
})
{
assert
(
geometry
.
hitTestExtent
>
0.0
);
if
(
child
!=
null
)
return
hitTestBoxChild
(
result
,
child
,
mainAxisPosition:
mainAxisPosition
,
crossAxisPosition:
crossAxisPosition
);
return
false
;
}
@override
void
applyPaintTransform
(
RenderObject
child
,
Matrix4
transform
)
{
assert
(
child
!=
null
);
assert
(
child
==
this
.
child
);
applyPaintTransformForBoxChild
(
child
,
transform
);
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
&&
geometry
.
visible
)
{
assert
(
constraints
.
axisDirection
!=
null
);
switch
(
applyGrowthDirectionToAxisDirection
(
constraints
.
axisDirection
,
constraints
.
growthDirection
))
{
case
AxisDirection
.
up
:
offset
+=
new
Offset
(
0.0
,
geometry
.
paintExtent
-
childPosition
(
child
)
-
childExtent
);
break
;
case
AxisDirection
.
down
:
offset
+=
new
Offset
(
0.0
,
childPosition
(
child
));
break
;
case
AxisDirection
.
left
:
offset
+=
new
Offset
(
geometry
.
paintExtent
-
childPosition
(
child
)
-
childExtent
,
0.0
);
break
;
case
AxisDirection
.
right
:
offset
+=
new
Offset
(
childPosition
(
child
),
0.0
);
break
;
}
context
.
paintChild
(
child
,
offset
);
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
try
{
description
.
add
(
'maxExtent:
${maxExtent.toStringAsFixed(1)}
'
);
}
catch
(
e
)
{
description
.
add
(
'maxExtent: EXCEPTION (
${e.runtimeType}
) WHILE COMPUTING MAX EXTENT'
);
}
try
{
description
.
add
(
'child position:
${childPosition(child).toStringAsFixed(1)}
'
);
}
catch
(
e
)
{
description
.
add
(
'child position: EXCEPTION (
${e.runtimeType}
) WHILE COMPUTING CHILD POSITION'
);
}
}
}
/// A sliver with a [RenderBox] child which scrolls normally, except that when
/// it hits the leading edge (typically the top) of the viewport, it shrinks to
/// a minimum size before continuing to scroll.
///
/// This sliver makes no effort to avoid overlapping other content.
abstract
class
RenderSliverScrollingAppBar
extends
RenderSliverAppBar
{
RenderSliverScrollingAppBar
({
RenderBox
child
,
})
:
super
(
child:
child
);
// Distance from our leading edge to the child's leading edge, in the axis
// direction. Negative if we're scrolled off the top.
double
_childPosition
;
@override
void
performLayout
()
{
final
double
maxExtent
=
this
.
maxExtent
;
layoutChild
(
constraints
.
scrollOffset
,
maxExtent
);
final
double
paintExtent
=
maxExtent
-
constraints
.
scrollOffset
;
geometry
=
new
SliverGeometry
(
scrollExtent:
maxExtent
,
paintExtent:
paintExtent
.
clamp
(
0.0
,
constraints
.
remainingPaintExtent
),
maxPaintExtent:
maxExtent
,
);
_childPosition
=
math
.
min
(
0.0
,
paintExtent
-
childExtent
);
}
@override
double
childPosition
(
RenderBox
child
)
{
assert
(
child
==
this
.
child
);
return
_childPosition
;
}
}
/// A sliver with a [RenderBox] child which never scrolls off the viewport in
/// the positive scroll direction, and which first scrolls on at a full size but
/// then shrinks as the viewport continues to scroll.
///
/// This sliver avoids overlapping other earlier slivers where possible.
abstract
class
RenderSliverPinnedAppBar
extends
RenderSliverAppBar
{
RenderSliverPinnedAppBar
({
RenderBox
child
,
})
:
super
(
child:
child
);
@override
void
performLayout
()
{
final
double
maxExtent
=
this
.
maxExtent
;
layoutChild
(
constraints
.
scrollOffset
+
constraints
.
overlap
,
maxExtent
);
geometry
=
new
SliverGeometry
(
scrollExtent:
maxExtent
,
paintExtent:
math
.
min
(
constraints
.
overlap
+
childExtent
,
constraints
.
remainingPaintExtent
),
layoutExtent:
(
maxExtent
-
constraints
.
scrollOffset
).
clamp
(
0.0
,
constraints
.
remainingPaintExtent
),
maxPaintExtent:
constraints
.
overlap
+
maxExtent
,
);
}
@override
double
childPosition
(
RenderBox
child
)
{
assert
(
child
==
this
.
child
);
return
constraints
?.
overlap
;
}
}
abstract
class
RenderSliverFloatingAppBar
extends
RenderSliverAppBar
{
RenderSliverFloatingAppBar
({
RenderBox
child
,
})
:
super
(
child:
child
);
double
_lastActualScrollOffset
;
double
_effectiveScrollOffset
;
// Distance from our leading edge to the child's leading edge, in the axis
// direction. Negative if we're scrolled off the top.
double
_childPosition
;
@override
void
performLayout
()
{
final
double
maxExtent
=
this
.
maxExtent
;
if
(
_lastActualScrollOffset
!=
null
&&
// We've laid out at least once to get an initial position, and either
((
constraints
.
scrollOffset
<
_lastActualScrollOffset
)
||
// we are scrolling back, so should reveal, or
(
_effectiveScrollOffset
<
maxExtent
)))
{
// some part of it is visible, so should shrink or reveal as appropriate.
double
delta
=
_lastActualScrollOffset
-
constraints
.
scrollOffset
;
final
bool
allowFloatingExpansion
=
constraints
.
userScrollDirection
==
ScrollDirection
.
forward
;
if
(
allowFloatingExpansion
)
{
if
(
_effectiveScrollOffset
>
maxExtent
)
// We're scrolled off-screen, but should reveal, so
_effectiveScrollOffset
=
maxExtent
;
// pretend we're just at the limit.
}
else
{
if
(
delta
>
0.0
)
// If we are trying to expand when allowFloatingExpansion is false,
delta
=
0.0
;
// disallow the expansion. (But allow shrinking, i.e. delta < 0.0 is fine.)
}
_effectiveScrollOffset
=
(
_effectiveScrollOffset
-
delta
).
clamp
(
0.0
,
constraints
.
scrollOffset
);
}
else
{
_effectiveScrollOffset
=
constraints
.
scrollOffset
;
}
layoutChild
(
_effectiveScrollOffset
,
maxExtent
);
final
double
paintExtent
=
maxExtent
-
_effectiveScrollOffset
;
final
double
layoutExtent
=
(
maxExtent
-
constraints
.
scrollOffset
).
clamp
(
0.0
,
constraints
.
remainingPaintExtent
);
geometry
=
new
SliverGeometry
(
scrollExtent:
maxExtent
,
paintExtent:
paintExtent
.
clamp
(
0.0
,
constraints
.
remainingPaintExtent
),
layoutExtent:
layoutExtent
,
maxPaintExtent:
maxExtent
,
);
_childPosition
=
math
.
min
(
0.0
,
paintExtent
-
childExtent
);
_lastActualScrollOffset
=
constraints
.
scrollOffset
;
}
@override
double
childPosition
(
RenderBox
child
)
{
assert
(
child
==
this
.
child
);
return
_childPosition
;
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'effective scroll offset:
${_effectiveScrollOffset?.toStringAsFixed(1)}
'
);
}
}
packages/flutter/lib/src/widgets/app_bar.dart
0 → 100644
View file @
1426ef99
// 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'
;
abstract
class
SliverAppBarDelegate
{
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const
SliverAppBarDelegate
();
Widget
build
(
BuildContext
context
,
double
shrinkOffset
);
double
get
maxExtent
;
bool
shouldRebuild
(
@checked
SliverAppBarDelegate
oldDelegate
);
}
class
SliverAppBar
extends
StatelessWidget
{
SliverAppBar
({
Key
key
,
@required
this
.
delegate
,
this
.
pinned
:
false
,
this
.
floating
:
false
,
})
:
super
(
key:
key
)
{
assert
(
delegate
!=
null
);
assert
(
pinned
!=
null
);
assert
(
floating
!=
null
);
assert
(!
pinned
||
!
floating
);
}
final
SliverAppBarDelegate
delegate
;
final
bool
pinned
;
final
bool
floating
;
@override
Widget
build
(
BuildContext
context
)
{
if
(
pinned
)
return
new
_SliverPinnedAppBar
(
delegate:
delegate
);
if
(
floating
)
return
new
_SliverFloatingAppBar
(
delegate:
delegate
);
return
new
_SliverScrollingAppBar
(
delegate:
delegate
);
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'delegate:
$delegate
'
);
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
_SliverAppBarElement
extends
RenderObjectElement
{
_SliverAppBarElement
(
_SliverAppBarRenderObjectWidget
widget
)
:
super
(
widget
);
@override
_SliverAppBarRenderObjectWidget
get
widget
=>
super
.
widget
;
@override
_RenderSliverAppBarForWidgetsMixin
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
(
_SliverAppBarRenderObjectWidget
newWidget
)
{
final
_SliverAppBarRenderObjectWidget
oldWidget
=
widget
;
super
.
update
(
newWidget
);
final
SliverAppBarDelegate
newDelegate
=
newWidget
.
delegate
;
final
SliverAppBarDelegate
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
)
{
owner
.
buildScope
(
this
,
()
{
child
=
updateChild
(
child
,
widget
.
delegate
.
build
(
this
,
shrinkOffset
),
null
);
});
}
@override
void
forgetChild
(
Element
child
)
{
assert
(
child
==
this
.
child
);
this
.
child
=
null
;
}
@override
void
insertChildRenderObject
(
@checked
RenderObject
child
,
Null
slot
)
{
renderObject
.
child
=
child
;
}
@override
void
moveChildRenderObject
(
@checked
RenderObject
child
,
Null
slot
)
{
assert
(
false
);
}
@override
void
removeChildRenderObject
(
@checked
RenderObject
child
)
{
renderObject
.
child
=
null
;
}
@override
void
visitChildren
(
ElementVisitor
visitor
)
{
visitor
(
child
);
}
}
abstract
class
_SliverAppBarRenderObjectWidget
extends
RenderObjectWidget
{
_SliverAppBarRenderObjectWidget
({
Key
key
,
@required
this
.
delegate
,
})
:
super
(
key:
key
)
{
assert
(
delegate
!=
null
);
}
final
SliverAppBarDelegate
delegate
;
@override
_SliverAppBarElement
createElement
()
=>
new
_SliverAppBarElement
(
this
);
@override
_RenderSliverAppBarForWidgetsMixin
createRenderObject
(
BuildContext
context
);
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'delegate:
$delegate
'
);
}
}
abstract
class
_RenderSliverAppBarForWidgetsMixin
implements
RenderSliverAppBar
{
_SliverAppBarElement
_element
;
@override
double
get
maxExtent
=>
_element
.
widget
.
delegate
.
maxExtent
;
@override
void
updateChild
(
double
shrinkOffset
)
{
assert
(
_element
!=
null
);
_element
.
_build
(
shrinkOffset
);
}
@protected
void
triggerRebuild
()
{
markNeedsUpdate
();
}
}
class
_SliverScrollingAppBar
extends
_SliverAppBarRenderObjectWidget
{
_SliverScrollingAppBar
({
Key
key
,
@required
SliverAppBarDelegate
delegate
,
})
:
super
(
key:
key
,
delegate:
delegate
);
@override
_RenderSliverAppBarForWidgetsMixin
createRenderObject
(
BuildContext
context
)
{
return
new
_RenderSliverScrollingAppBarForWidgets
();
}
}
// This class exists to work around https://github.com/dart-lang/sdk/issues/15101
abstract
class
_RenderSliverScrollingAppBar
extends
RenderSliverScrollingAppBar
{
}
class
_RenderSliverScrollingAppBarForWidgets
extends
_RenderSliverScrollingAppBar
with
_RenderSliverAppBarForWidgetsMixin
{
}
class
_SliverPinnedAppBar
extends
_SliverAppBarRenderObjectWidget
{
_SliverPinnedAppBar
({
Key
key
,
@required
SliverAppBarDelegate
delegate
,
})
:
super
(
key:
key
,
delegate:
delegate
);
@override
_RenderSliverAppBarForWidgetsMixin
createRenderObject
(
BuildContext
context
)
{
return
new
_RenderSliverPinnedAppBarForWidgets
();
}
}
// This class exists to work around https://github.com/dart-lang/sdk/issues/15101
abstract
class
_RenderSliverPinnedAppBar
extends
RenderSliverPinnedAppBar
{
}
class
_RenderSliverPinnedAppBarForWidgets
extends
_RenderSliverPinnedAppBar
with
_RenderSliverAppBarForWidgetsMixin
{
}
class
_SliverFloatingAppBar
extends
_SliverAppBarRenderObjectWidget
{
_SliverFloatingAppBar
({
Key
key
,
@required
SliverAppBarDelegate
delegate
,
})
:
super
(
key:
key
,
delegate:
delegate
);
@override
_RenderSliverAppBarForWidgetsMixin
createRenderObject
(
BuildContext
context
)
{
return
new
_RenderSliverFloatingAppBarForWidgets
();
}
}
// This class exists to work around https://github.com/dart-lang/sdk/issues/15101
abstract
class
_RenderSliverFloatingAppBar
extends
RenderSliverFloatingAppBar
{
}
class
_RenderSliverFloatingAppBarForWidgets
extends
_RenderSliverFloatingAppBar
with
_RenderSliverAppBarForWidgetsMixin
{
}
packages/flutter/lib/widgets.dart
View file @
1426ef99
...
...
@@ -10,6 +10,7 @@ library widgets;
export
'src/widgets/animated_cross_fade.dart'
;
export
'src/widgets/animated_size.dart'
;
export
'src/widgets/app.dart'
;
export
'src/widgets/app_bar.dart'
;
export
'src/widgets/banner.dart'
;
export
'src/widgets/basic.dart'
;
export
'src/widgets/binding.dart'
;
...
...
packages/flutter/test/widgets/slivers_appbar_floating_test.dart
0 → 100644
View file @
1426ef99
// Copyright 2016 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_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
void
verifyPaintPosition
(
GlobalKey
key
,
Offset
ideal
,
bool
visible
)
{
RenderSliver
target
=
key
.
currentContext
.
findRenderObject
();
expect
(
target
.
parent
,
new
isInstanceOf
<
RenderViewport2
>());
SliverPhysicalParentData
parentData
=
target
.
parentData
;
Offset
actual
=
parentData
.
paintOffset
;
expect
(
actual
,
ideal
);
SliverGeometry
geometry
=
target
.
geometry
;
expect
(
geometry
.
visible
,
visible
);
}
void
verifyActualBoxPosition
(
WidgetTester
tester
,
Finder
finder
,
int
index
,
Rect
ideal
)
{
RenderBox
box
=
tester
.
renderObjectList
<
RenderBox
>(
finder
).
elementAt
(
index
);
Rect
rect
=
new
Rect
.
fromPoints
(
box
.
localToGlobal
(
Point
.
origin
),
box
.
localToGlobal
(
box
.
size
.
bottomRight
(
Point
.
origin
)));
expect
(
rect
,
equals
(
ideal
));
}
void
main
(
)
{
testWidgets
(
'Sliver appbars - floating - scroll offset doesn
\'
t change'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
const
double
bigHeight
=
1000.0
;
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(
height:
bigHeight
),
new
SliverAppBar
(
delegate:
new
TestDelegate
(),
floating:
true
),
new
BigSliver
(
height:
bigHeight
),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
final
double
max
=
bigHeight
*
2.0
+
new
TestDelegate
().
maxExtent
-
600.0
;
// 600 is the height of the test viewport
assert
(
max
<
10000.0
);
expect
(
max
,
1600.0
);
expect
(
position
.
pixels
,
0.0
);
expect
(
position
.
minScrollExtent
,
0.0
);
expect
(
position
.
maxScrollExtent
,
max
);
position
.
animate
(
to:
10000.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
10
));
expect
(
position
.
pixels
,
max
);
expect
(
position
.
minScrollExtent
,
0.0
);
expect
(
position
.
maxScrollExtent
,
max
);
});
testWidgets
(
'Sliver appbars - floating - normal behavior works'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
final
TestDelegate
delegate
=
new
TestDelegate
();
const
double
bigHeight
=
1000.0
;
GlobalKey
key1
,
key2
,
key3
;
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(
key:
key1
=
new
GlobalKey
(),
height:
bigHeight
),
new
SliverAppBar
(
key:
key2
=
new
GlobalKey
(),
delegate:
delegate
,
floating:
true
),
new
BigSliver
(
key:
key3
=
new
GlobalKey
(),
height:
bigHeight
),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
600.0
),
false
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
bigHeight
-
600.0
+
delegate
.
maxExtent
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
600.0
-
delegate
.
maxExtent
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
0
,
new
Rect
.
fromLTWH
(
0.0
,
600.0
-
delegate
.
maxExtent
,
800.0
,
delegate
.
maxExtent
));
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
600.0
),
false
);
assert
(
delegate
.
maxExtent
*
2.0
<
600.0
);
// make sure this fits on the test screen...
position
.
animate
(
to:
bigHeight
-
600.0
+
delegate
.
maxExtent
*
2.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
600.0
-
delegate
.
maxExtent
*
2.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
0
,
new
Rect
.
fromLTWH
(
0.0
,
600.0
-
delegate
.
maxExtent
*
2.0
,
800.0
,
delegate
.
maxExtent
));
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
600.0
-
delegate
.
maxExtent
),
true
);
position
.
animate
(
to:
bigHeight
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
0
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
800.0
,
delegate
.
maxExtent
));
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
delegate
.
maxExtent
),
true
);
position
.
animate
(
to:
bigHeight
+
delegate
.
maxExtent
*
0.1
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
0
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
800.0
,
delegate
.
maxExtent
*
0.9
));
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
delegate
.
maxExtent
*
0.9
),
true
);
position
.
animate
(
to:
bigHeight
+
delegate
.
maxExtent
*
0.5
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
0
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
800.0
,
delegate
.
maxExtent
*
0.5
));
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
delegate
.
maxExtent
*
0.5
),
true
);
position
.
animate
(
to:
bigHeight
+
delegate
.
maxExtent
*
0.9
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
0
,
new
Rect
.
fromLTWH
(
0.0
,
-
delegate
.
maxExtent
*
0.4
,
800.0
,
delegate
.
maxExtent
*
0.5
));
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
delegate
.
maxExtent
*
0.1
),
true
);
position
.
animate
(
to:
bigHeight
+
delegate
.
maxExtent
*
2.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
});
testWidgets
(
'Sliver appbars - floating - no floating behavior when animating'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
final
TestDelegate
delegate
=
new
TestDelegate
();
const
double
bigHeight
=
1000.0
;
GlobalKey
key1
,
key2
,
key3
;
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(
key:
key1
=
new
GlobalKey
(),
height:
bigHeight
),
new
SliverAppBar
(
key:
key2
=
new
GlobalKey
(),
delegate:
delegate
,
floating:
true
),
new
BigSliver
(
key:
key3
=
new
GlobalKey
(),
height:
bigHeight
),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
600.0
),
false
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
bigHeight
+
delegate
.
maxExtent
*
2.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
position
.
animate
(
to:
bigHeight
+
delegate
.
maxExtent
*
1.9
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
});
testWidgets
(
'Sliver appbars - floating - floating behavior when dragging down'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
final
TestDelegate
delegate
=
new
TestDelegate
();
const
double
bigHeight
=
1000.0
;
GlobalKey
key1
,
key2
,
key3
;
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(
key:
key1
=
new
GlobalKey
(),
height:
bigHeight
),
new
SliverAppBar
(
key:
key2
=
new
GlobalKey
(),
delegate:
delegate
,
floating:
true
),
new
BigSliver
(
key:
key3
=
new
GlobalKey
(),
height:
bigHeight
),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
600.0
),
false
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
bigHeight
+
delegate
.
maxExtent
*
2.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
position
.
animate
(
to:
bigHeight
+
delegate
.
maxExtent
*
1.9
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
position
.
updateUserScrollDirection
(
ScrollDirection
.
forward
);
// ignore: INVALID_USE_OF_PROTECTED_MEMBER, since this is using a protected method for testing purposes
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
0
,
new
Rect
.
fromLTWH
(
0.0
,
-
delegate
.
maxExtent
*
0.4
,
800.0
,
delegate
.
maxExtent
*
0.5
));
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
});
}
class
TestDelegate
extends
SliverAppBarDelegate
{
@override
double
get
maxExtent
=>
200.0
;
@override
Widget
build
(
BuildContext
context
,
double
shrinkOffset
)
{
return
new
Container
(
constraints:
new
BoxConstraints
(
minHeight:
maxExtent
/
2.0
,
maxHeight:
maxExtent
));
}
@override
bool
shouldRebuild
(
TestDelegate
oldDelegate
)
=>
false
;
}
class
RenderBigSliver
extends
RenderSliver
{
RenderBigSliver
(
double
height
)
:
_height
=
height
;
double
get
height
=>
_height
;
double
_height
;
set
height
(
double
value
)
{
if
(
value
==
_height
)
return
;
_height
=
value
;
markNeedsLayout
();
}
double
get
paintExtent
=>
(
height
-
constraints
.
scrollOffset
).
clamp
(
0.0
,
constraints
.
remainingPaintExtent
);
@override
void
performLayout
()
{
geometry
=
new
SliverGeometry
(
scrollExtent:
height
,
paintExtent:
paintExtent
,
maxPaintExtent:
height
,
);
}
}
class
BigSliver
extends
LeafRenderObjectWidget
{
BigSliver
({
Key
key
,
this
.
height
})
:
super
(
key:
key
);
final
double
height
;
@override
RenderBigSliver
createRenderObject
(
BuildContext
context
)
{
return
new
RenderBigSliver
(
height
);
}
@override
void
updateRenderObject
(
BuildContext
context
,
RenderBigSliver
renderObject
)
{
renderObject
.
height
=
height
;
}
}
packages/flutter/test/widgets/slivers_appbar_pinned_test.dart
0 → 100644
View file @
1426ef99
// Copyright 2016 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_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
void
verifyPaintPosition
(
GlobalKey
key
,
Offset
ideal
,
bool
visible
)
{
RenderSliver
target
=
key
.
currentContext
.
findRenderObject
();
expect
(
target
.
parent
,
new
isInstanceOf
<
RenderViewport2
>());
SliverPhysicalParentData
parentData
=
target
.
parentData
;
Offset
actual
=
parentData
.
paintOffset
;
expect
(
actual
,
ideal
);
SliverGeometry
geometry
=
target
.
geometry
;
expect
(
geometry
.
visible
,
visible
);
}
void
verifyActualBoxPosition
(
WidgetTester
tester
,
Finder
finder
,
int
index
,
Rect
ideal
)
{
RenderBox
box
=
tester
.
renderObjectList
/*<RenderBox>*/
(
finder
).
elementAt
(
index
);
Rect
rect
=
new
Rect
.
fromPoints
(
box
.
localToGlobal
(
Point
.
origin
),
box
.
localToGlobal
(
box
.
size
.
bottomRight
(
Point
.
origin
)));
expect
(
rect
,
equals
(
ideal
));
}
void
main
(
)
{
testWidgets
(
'Sliver appbars - pinned'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
const
double
bigHeight
=
550.0
;
GlobalKey
key1
,
key2
,
key3
,
key4
,
key5
;
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(
key:
key1
=
new
GlobalKey
(),
height:
bigHeight
),
new
SliverAppBar
(
key:
key2
=
new
GlobalKey
(),
delegate:
new
TestDelegate
(),
pinned:
true
),
new
SliverAppBar
(
key:
key3
=
new
GlobalKey
(),
delegate:
new
TestDelegate
(),
pinned:
true
),
new
BigSliver
(
key:
key4
=
new
GlobalKey
(),
height:
bigHeight
),
new
BigSliver
(
key:
key5
=
new
GlobalKey
(),
height:
bigHeight
),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
final
double
max
=
bigHeight
*
3.0
+
new
TestDelegate
().
maxExtent
*
2.0
-
600.0
;
// 600 is the height of the test viewport
assert
(
max
<
10000.0
);
expect
(
max
,
1450.0
);
expect
(
position
.
pixels
,
0.0
);
expect
(
position
.
minScrollExtent
,
0.0
);
expect
(
position
.
maxScrollExtent
,
max
);
position
.
animate
(
to:
10000.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
10
));
expect
(
position
.
pixels
,
max
);
expect
(
position
.
minScrollExtent
,
0.0
);
expect
(
position
.
maxScrollExtent
,
max
);
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
50.0
),
true
);
});
testWidgets
(
'Sliver appbars - pinned with slow scroll'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
const
double
bigHeight
=
550.0
;
GlobalKey
key1
,
key2
,
key3
,
key4
,
key5
;
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(
key:
key1
=
new
GlobalKey
(),
height:
bigHeight
),
new
SliverAppBar
(
key:
key2
=
new
GlobalKey
(),
delegate:
new
TestDelegate
(),
pinned:
true
),
new
SliverAppBar
(
key:
key3
=
new
GlobalKey
(),
delegate:
new
TestDelegate
(),
pinned:
true
),
new
BigSliver
(
key:
key4
=
new
GlobalKey
(),
height:
bigHeight
),
new
BigSliver
(
key:
key5
=
new
GlobalKey
(),
height:
bigHeight
),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
550.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
600.0
),
false
);
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
600.0
),
false
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
550.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
100
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
200.0
),
true
);
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
400.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
600.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
200
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
150.0
),
true
);
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
350.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
650.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
300
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
100.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
1
,
new
Rect
.
fromLTWH
(
0.0
,
100.0
,
800.0
,
200.0
));
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
300.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
700.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
400
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
50.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
1
,
new
Rect
.
fromLTWH
(
0.0
,
100.0
,
800.0
,
150.0
));
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
250.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
750.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
500
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
1
,
new
Rect
.
fromLTWH
(
0.0
,
100.0
,
800.0
,
100.0
));
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
200.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
800.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
60
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
150.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
850.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
70
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
100.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
900.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
80
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
50.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
600.0
),
false
);
position
.
animate
(
to:
950.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
90
));
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyActualBoxPosition
(
tester
,
find
.
byType
(
Container
),
1
,
new
Rect
.
fromLTWH
(
0.0
,
100.0
,
800.0
,
100.0
));
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
550.0
),
true
);
});
testWidgets
(
'Sliver appbars - pinned with less overlap'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
const
double
bigHeight
=
650.0
;
GlobalKey
key1
,
key2
,
key3
,
key4
,
key5
;
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(
key:
key1
=
new
GlobalKey
(),
height:
bigHeight
),
new
SliverAppBar
(
key:
key2
=
new
GlobalKey
(),
delegate:
new
TestDelegate
(),
pinned:
true
),
new
SliverAppBar
(
key:
key3
=
new
GlobalKey
(),
delegate:
new
TestDelegate
(),
pinned:
true
),
new
BigSliver
(
key:
key4
=
new
GlobalKey
(),
height:
bigHeight
),
new
BigSliver
(
key:
key5
=
new
GlobalKey
(),
height:
bigHeight
),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
final
double
max
=
bigHeight
*
3.0
+
new
TestDelegate
().
maxExtent
*
2.0
-
600.0
;
// 600 is the height of the test viewport
assert
(
max
<
10000.0
);
expect
(
max
,
1750.0
);
expect
(
position
.
pixels
,
0.0
);
expect
(
position
.
minScrollExtent
,
0.0
);
expect
(
position
.
maxScrollExtent
,
max
);
position
.
animate
(
to:
10000.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
10
));
expect
(
position
.
pixels
,
max
);
expect
(
position
.
minScrollExtent
,
0.0
);
expect
(
position
.
maxScrollExtent
,
max
);
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
),
true
);
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
0.0
),
false
);
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
0.0
),
true
);
});
}
class
TestDelegate
extends
SliverAppBarDelegate
{
@override
double
get
maxExtent
=>
200.0
;
@override
Widget
build
(
BuildContext
context
,
double
shrinkOffset
)
{
return
new
Container
(
constraints:
new
BoxConstraints
(
minHeight:
maxExtent
/
2.0
,
maxHeight:
maxExtent
));
}
@override
bool
shouldRebuild
(
TestDelegate
oldDelegate
)
=>
false
;
}
class
RenderBigSliver
extends
RenderSliver
{
RenderBigSliver
(
double
height
)
:
_height
=
height
;
double
get
height
=>
_height
;
double
_height
;
set
height
(
double
value
)
{
if
(
value
==
_height
)
return
;
_height
=
value
;
markNeedsLayout
();
}
double
get
paintExtent
=>
(
height
-
constraints
.
scrollOffset
).
clamp
(
0.0
,
constraints
.
remainingPaintExtent
);
@override
void
performLayout
()
{
geometry
=
new
SliverGeometry
(
scrollExtent:
height
,
paintExtent:
paintExtent
,
maxPaintExtent:
height
,
);
}
}
class
BigSliver
extends
LeafRenderObjectWidget
{
BigSliver
({
Key
key
,
this
.
height
})
:
super
(
key:
key
);
final
double
height
;
@override
RenderBigSliver
createRenderObject
(
BuildContext
context
)
{
return
new
RenderBigSliver
(
height
);
}
@override
void
updateRenderObject
(
BuildContext
context
,
RenderBigSliver
renderObject
)
{
renderObject
.
height
=
height
;
}
}
packages/flutter/test/widgets/slivers_appbar_scrolling_test.dart
0 → 100644
View file @
1426ef99
// Copyright 2016 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_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
void
verifyPaintPosition
(
GlobalKey
key
,
Offset
ideal
)
{
RenderObject
target
=
key
.
currentContext
.
findRenderObject
();
expect
(
target
.
parent
,
new
isInstanceOf
<
RenderViewport2
>());
SliverPhysicalParentData
parentData
=
target
.
parentData
;
Offset
actual
=
parentData
.
paintOffset
;
expect
(
actual
,
ideal
);
}
void
main
(
)
{
testWidgets
(
'Sliver appbars - scrolling'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
GlobalKey
key1
,
key2
,
key3
,
key4
,
key5
;
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(
key:
key1
=
new
GlobalKey
()),
new
SliverAppBar
(
key:
key2
=
new
GlobalKey
(),
delegate:
new
TestDelegate
()),
new
SliverAppBar
(
key:
key3
=
new
GlobalKey
(),
delegate:
new
TestDelegate
()),
new
BigSliver
(
key:
key4
=
new
GlobalKey
()),
new
BigSliver
(
key:
key5
=
new
GlobalKey
()),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
final
double
max
=
RenderBigSliver
.
height
*
3.0
+
new
TestDelegate
().
maxExtent
*
2.0
-
600.0
;
// 600 is the height of the test viewport
assert
(
max
<
10000.0
);
expect
(
max
,
1450.0
);
expect
(
position
.
pixels
,
0.0
);
expect
(
position
.
minScrollExtent
,
0.0
);
expect
(
position
.
maxScrollExtent
,
max
);
position
.
animate
(
to:
10000.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
10
));
expect
(
position
.
pixels
,
max
);
expect
(
position
.
minScrollExtent
,
0.0
);
expect
(
position
.
maxScrollExtent
,
max
);
verifyPaintPosition
(
key1
,
new
Offset
(
0.0
,
0.0
));
verifyPaintPosition
(
key2
,
new
Offset
(
0.0
,
0.0
));
verifyPaintPosition
(
key3
,
new
Offset
(
0.0
,
0.0
));
verifyPaintPosition
(
key4
,
new
Offset
(
0.0
,
0.0
));
verifyPaintPosition
(
key5
,
new
Offset
(
0.0
,
50.0
));
});
testWidgets
(
'Sliver appbars - scrolling off screen'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
Scrollable2State
>
scrollableKey
=
new
GlobalKey
<
Scrollable2State
>();
GlobalKey
key
=
new
GlobalKey
();
TestDelegate
delegate
=
new
TestDelegate
();
await
tester
.
pumpWidget
(
new
Scrollable2
(
key:
scrollableKey
,
axisDirection:
AxisDirection
.
down
,
children:
<
Widget
>[
new
BigSliver
(),
new
SliverAppBar
(
key:
key
,
delegate:
delegate
),
new
BigSliver
(),
new
BigSliver
(),
],
),
);
AbsoluteScrollPosition
position
=
scrollableKey
.
currentState
.
position
;
position
.
animate
(
to:
RenderBigSliver
.
height
+
delegate
.
maxExtent
-
5.0
,
curve:
Curves
.
linear
,
duration:
const
Duration
(
minutes:
1
));
await
tester
.
pumpUntilNoTransientCallbacks
(
const
Duration
(
milliseconds:
1000
));
RenderBox
box
=
tester
.
renderObject
/*<RenderBox>*/
(
find
.
byType
(
Container
));
Rect
rect
=
new
Rect
.
fromPoints
(
box
.
localToGlobal
(
Point
.
origin
),
box
.
localToGlobal
(
box
.
size
.
bottomRight
(
Point
.
origin
)));
expect
(
rect
,
equals
(
new
Rect
.
fromLTWH
(
0.0
,
-
195.0
,
800.0
,
200.0
)));
});
}
class
TestDelegate
extends
SliverAppBarDelegate
{
@override
double
get
maxExtent
=>
200.0
;
@override
Widget
build
(
BuildContext
context
,
double
shrinkOffset
)
{
return
new
Container
(
height:
maxExtent
);
}
@override
bool
shouldRebuild
(
TestDelegate
oldDelegate
)
=>
false
;
}
class
RenderBigSliver
extends
RenderSliver
{
static
const
double
height
=
550.0
;
double
get
paintExtent
=>
(
height
-
constraints
.
scrollOffset
).
clamp
(
0.0
,
constraints
.
remainingPaintExtent
);
@override
void
performLayout
()
{
geometry
=
new
SliverGeometry
(
scrollExtent:
height
,
paintExtent:
paintExtent
,
maxPaintExtent:
height
,
);
}
}
class
BigSliver
extends
LeafRenderObjectWidget
{
BigSliver
({
Key
key
})
:
super
(
key:
key
);
@override
RenderBigSliver
createRenderObject
(
BuildContext
context
)
{
return
new
RenderBigSliver
();
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment