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
90574b04
Commit
90574b04
authored
Feb 17, 2017
by
Adam Barth
Committed by
GitHub
Feb 17, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove Scrollable1 (#8225)
All the clients have migrated to Scrollable2.
parent
9ec5330f
Changes
25
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
94 additions
and
2396 deletions
+94
-2396
card_collection.dart
dev/manual_tests/card_collection.dart
+1
-1
rendering.dart
packages/flutter/lib/rendering.dart
+0
-1
app.dart
packages/flutter/lib/src/material/app.dart
+0
-43
page.dart
packages/flutter/lib/src/material/page.dart
+1
-0
viewport.dart
packages/flutter/lib/src/rendering/viewport.dart
+0
-338
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+2
-70
clamp_overscrolls.dart
packages/flutter/lib/src/widgets/clamp_overscrolls.dart
+0
-122
focus.dart
packages/flutter/lib/src/widgets/focus.dart
+0
-2
scroll_behavior.dart
packages/flutter/lib/src/widgets/scroll_behavior.dart
+0
-318
scroll_configuration.dart
packages/flutter/lib/src/widgets/scroll_configuration.dart
+0
-99
scroll_physics.dart
packages/flutter/lib/src/widgets/scroll_physics.dart
+1
-1
scroll_simulation.dart
packages/flutter/lib/src/widgets/scroll_simulation.dart
+1
-137
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+0
-1136
widgets.dart
packages/flutter/lib/widgets.dart
+0
-2
app_bar_test.dart
packages/flutter/test/material/app_bar_test.dart
+1
-0
scaffold_test.dart
packages/flutter/test/material/scaffold_test.dart
+1
-2
stepper_test.dart
packages/flutter/test/material/stepper_test.dart
+1
-1
two_level_list_test.dart
packages/flutter/test/material/two_level_list_test.dart
+2
-2
newton_test.dart
packages/flutter/test/physics/newton_test.dart
+11
-16
independent_layout_test.dart
packages/flutter/test/rendering/independent_layout_test.dart
+12
-10
offstage_test.dart
packages/flutter/test/rendering/offstage_test.dart
+14
-12
reattach_test.dart
packages/flutter/test/rendering/reattach_test.dart
+44
-40
viewport_test.dart
packages/flutter/test/rendering/viewport_test.dart
+0
-40
aspect_ratio_test.dart
packages/flutter/test/widgets/aspect_ratio_test.dart
+2
-2
extension.dart
packages/flutter_driver/lib/src/extension.dart
+0
-1
No files found.
dev/manual_tests/card_collection.dart
View file @
90574b04
...
...
@@ -321,7 +321,7 @@ class CardCollectionState extends State<CardCollection> {
Widget
background
=
new
Positioned
.
fill
(
child:
new
Container
(
margin:
const
EdgeInsets
.
all
(
4.0
),
child:
new
Viewport
(
child:
new
SingleChildScrollView
(
child:
new
Container
(
height:
cardModel
.
height
,
decoration:
new
BoxDecoration
(
backgroundColor:
theme
.
primaryColor
),
...
...
packages/flutter/lib/rendering.dart
View file @
90574b04
...
...
@@ -53,7 +53,6 @@ export 'src/rendering/stack.dart';
export
'src/rendering/table.dart'
;
export
'src/rendering/tweens.dart'
;
export
'src/rendering/view.dart'
;
export
'src/rendering/viewport.dart'
;
export
'src/rendering/viewport_offset.dart'
;
export
'package:flutter/foundation.dart'
show
...
...
packages/flutter/lib/src/material/app.dart
View file @
90574b04
...
...
@@ -167,32 +167,6 @@ class MaterialApp extends StatefulWidget {
_MaterialAppState
createState
()
=>
new
_MaterialAppState
();
}
class
_ScrollLikeCupertinoDelegate
extends
ScrollConfigurationDelegate
{
const
_ScrollLikeCupertinoDelegate
();
@override
TargetPlatform
get
platform
=>
TargetPlatform
.
iOS
;
@override
ExtentScrollBehavior
createScrollBehavior
()
=>
new
OverscrollWhenScrollableBehavior
(
platform:
TargetPlatform
.
iOS
);
@override
bool
updateShouldNotify
(
ScrollConfigurationDelegate
old
)
=>
false
;
}
class
_ScrollLikeMountainViewDelegate
extends
ScrollConfigurationDelegate
{
const
_ScrollLikeMountainViewDelegate
(
this
.
platform
);
@override
final
TargetPlatform
platform
;
@override
ExtentScrollBehavior
createScrollBehavior
()
=>
new
OverscrollWhenScrollableBehavior
(
platform:
TargetPlatform
.
android
);
@override
bool
updateShouldNotify
(
ScrollConfigurationDelegate
old
)
=>
false
;
}
class
_MaterialScrollBehavior
extends
ScrollBehavior2
{
@override
TargetPlatform
getPlatform
(
BuildContext
context
)
{
...
...
@@ -233,18 +207,6 @@ class _MaterialAppState extends State<MaterialApp> {
return
null
;
}
ScrollConfigurationDelegate
_getScrollDelegate
(
TargetPlatform
platform
)
{
switch
(
platform
)
{
case
TargetPlatform
.
android
:
return
const
_ScrollLikeMountainViewDelegate
(
TargetPlatform
.
android
);
case
TargetPlatform
.
fuchsia
:
return
const
_ScrollLikeMountainViewDelegate
(
TargetPlatform
.
fuchsia
);
case
TargetPlatform
.
iOS
:
return
const
_ScrollLikeCupertinoDelegate
();
}
return
null
;
}
@override
Widget
build
(
BuildContext
context
)
{
ThemeData
theme
=
config
.
theme
??
new
ThemeData
.
fallback
();
...
...
@@ -283,11 +245,6 @@ class _MaterialAppState extends State<MaterialApp> {
return
true
;
});
result
=
new
ScrollConfiguration
(
delegate:
_getScrollDelegate
(
theme
.
platform
),
child:
result
);
return
new
ScrollConfiguration2
(
behavior:
new
_MaterialScrollBehavior
(),
child:
result
...
...
packages/flutter/lib/src/material/page.dart
View file @
90574b04
...
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:meta/meta.dart'
;
...
...
packages/flutter/lib/src/rendering/viewport.dart
deleted
100644 → 0
View file @
9ec5330f
// 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
'dart:ui'
as
ui
show
window
;
import
'package:vector_math/vector_math_64.dart'
;
import
'box.dart'
;
import
'object.dart'
;
/// The end of the viewport from which the paint offset is computed.
enum
ViewportAnchor
{
/// The start (e.g., top or left, depending on the axis) of the first item
/// should be aligned with the start (e.g., top or left, depending on the
/// axis) of the viewport.
start
,
/// The end (e.g., bottom or right, depending on the axis) of the last item
/// should be aligned with the end (e.g., bottom or right, depending on the
/// axis) of the viewport.
end
,
}
/// The interior and exterior dimensions of a viewport.
class
ViewportDimensions
{
/// Creates dimensions for a viewport.
///
/// By default, the content and container sizes are zero.
const
ViewportDimensions
({
this
.
contentSize
:
Size
.
zero
,
this
.
containerSize
:
Size
.
zero
});
/// A viewport that has zero size, both inside and outside.
static
const
ViewportDimensions
zero
=
const
ViewportDimensions
();
/// The size of the content inside the viewport.
final
Size
contentSize
;
/// The size of the outside of the viewport.
final
Size
containerSize
;
bool
get
_debugHasAtLeastOneCommonDimension
{
return
contentSize
.
width
==
containerSize
.
width
||
contentSize
.
height
==
containerSize
.
height
;
}
/// Returns the offset at which to paint the content, accounting for the given
/// anchor and the dimensions of the viewport.
Offset
getAbsolutePaintOffset
({
Offset
paintOffset
,
ViewportAnchor
anchor
})
{
assert
(
_debugHasAtLeastOneCommonDimension
);
switch
(
anchor
)
{
case
ViewportAnchor
.
start
:
return
paintOffset
;
case
ViewportAnchor
.
end
:
return
paintOffset
+
(
containerSize
-
contentSize
);
}
assert
(
anchor
!=
null
);
return
null
;
}
@override
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
ViewportDimensions
)
return
false
;
final
ViewportDimensions
typedOther
=
other
;
return
contentSize
==
typedOther
.
contentSize
&&
containerSize
==
typedOther
.
containerSize
;
}
@override
int
get
hashCode
=>
hashValues
(
contentSize
,
containerSize
);
@override
String
toString
()
=>
'ViewportDimensions(container:
$containerSize
, content:
$contentSize
)'
;
}
/// A base class for render objects that are bigger on the inside.
///
/// This class holds the common fields for viewport render objects but does not
/// have a child model. See [RenderViewport] for a viewport with a single child
/// and [RenderVirtualViewport] for a viewport with multiple children.
class
RenderViewportBase
extends
RenderBox
{
/// Initializes fields for subclasses.
///
/// The [paintOffset] and [mainAxis] arguments must not be null.
///
/// This constructor uses positional arguments rather than named arguments to
/// work around limitations of mixins.
RenderViewportBase
(
Offset
paintOffset
,
Axis
mainAxis
,
ViewportAnchor
anchor
)
:
_paintOffset
=
paintOffset
,
_mainAxis
=
mainAxis
,
_anchor
=
anchor
{
assert
(
paintOffset
!=
null
);
assert
(
mainAxis
!=
null
);
assert
(
_offsetIsSane
(
_paintOffset
,
mainAxis
));
}
bool
_offsetIsSane
(
Offset
offset
,
Axis
direction
)
{
switch
(
direction
)
{
case
Axis
.
horizontal
:
return
offset
.
dy
==
0.0
;
case
Axis
.
vertical
:
return
offset
.
dx
==
0.0
;
}
assert
(
direction
!=
null
);
return
null
;
}
/// The offset at which to paint the child.
///
/// The offset can be non-zero only in the [mainAxis].
Offset
get
paintOffset
=>
_paintOffset
;
Offset
_paintOffset
;
set
paintOffset
(
Offset
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_paintOffset
)
return
;
assert
(
_offsetIsSane
(
value
,
mainAxis
));
_paintOffset
=
value
;
markNeedsPaint
();
markNeedsSemanticsUpdate
();
}
/// The direction in which the child is permitted to be larger than the viewport.
///
/// The child is given layout constraints that are fully unconstrained along
/// the main axis (e.g., the child can be as tall as it wants if the main axis
/// is vertical).
Axis
get
mainAxis
=>
_mainAxis
;
Axis
_mainAxis
;
set
mainAxis
(
Axis
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_mainAxis
)
return
;
assert
(
_offsetIsSane
(
_paintOffset
,
value
));
_mainAxis
=
value
;
markNeedsLayout
();
}
/// The end of the viewport from which the paint offset is computed.
///
/// See [ViewportAnchor] for more detail.
ViewportAnchor
get
anchor
=>
_anchor
;
ViewportAnchor
_anchor
;
set
anchor
(
ViewportAnchor
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_anchor
)
return
;
_anchor
=
value
;
markNeedsPaint
();
markNeedsSemanticsUpdate
();
}
/// The interior and exterior extent of the viewport.
ViewportDimensions
get
dimensions
=>
_dimensions
;
ViewportDimensions
_dimensions
=
ViewportDimensions
.
zero
;
set
dimensions
(
ViewportDimensions
value
)
{
assert
(
debugDoingThisLayout
);
_dimensions
=
value
;
}
Offset
get
_effectivePaintOffset
{
final
double
devicePixelRatio
=
ui
.
window
.
devicePixelRatio
;
int
dxInDevicePixels
=
(
_paintOffset
.
dx
*
devicePixelRatio
).
round
();
int
dyInDevicePixels
=
(
_paintOffset
.
dy
*
devicePixelRatio
).
round
();
return
_dimensions
.
getAbsolutePaintOffset
(
paintOffset:
new
Offset
(
dxInDevicePixels
/
devicePixelRatio
,
dyInDevicePixels
/
devicePixelRatio
),
anchor:
_anchor
);
}
@override
void
applyPaintTransform
(
RenderBox
child
,
Matrix4
transform
)
{
final
Offset
effectivePaintOffset
=
_effectivePaintOffset
;
super
.
applyPaintTransform
(
child
,
transform
..
translate
(
effectivePaintOffset
.
dx
,
effectivePaintOffset
.
dy
));
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'paintOffset:
$paintOffset
'
);
description
.
add
(
'mainAxis:
$mainAxis
'
);
description
.
add
(
'anchor:
$anchor
'
);
}
}
/// Signature for notifications about [RenderViewport] dimensions changing.
///
/// Used by [RenderViewport.onPaintOffsetUpdateNeeded].
typedef
Offset
ViewportDimensionsChangeCallback
(
ViewportDimensions
dimensions
);
/// A render object that's bigger on the inside.
///
/// The child of a viewport can layout to a larger size along the viewport's
/// [mainAxis] than the viewport itself. If that happens, only a portion of the
/// child will be visible through the viewport. The portion of the child that is
/// visible can be controlled with the [paintOffset].
///
/// See also:
///
/// * [RenderVirtualViewport] (which works with more than one child)
class
RenderViewport
extends
RenderViewportBase
with
RenderObjectWithChildMixin
<
RenderBox
>
{
/// Creates a render object that's bigger on the inside.
///
/// The [paintOffset] and [mainAxis] arguments must not be null.
RenderViewport
({
RenderBox
child
,
Offset
paintOffset:
Offset
.
zero
,
Axis
mainAxis:
Axis
.
vertical
,
ViewportAnchor
anchor:
ViewportAnchor
.
start
,
this
.
onPaintOffsetUpdateNeeded
})
:
super
(
paintOffset
,
mainAxis
,
anchor
)
{
this
.
child
=
child
;
}
/// Called during [layout] to report the dimensions of the viewport
/// and its child.
///
/// The return value of this function is used as the new [paintOffset] and
/// must not be null.
ViewportDimensionsChangeCallback
onPaintOffsetUpdateNeeded
;
BoxConstraints
_getInnerConstraints
(
BoxConstraints
constraints
)
{
BoxConstraints
innerConstraints
;
switch
(
mainAxis
)
{
case
Axis
.
horizontal
:
innerConstraints
=
constraints
.
heightConstraints
();
break
;
case
Axis
.
vertical
:
innerConstraints
=
constraints
.
widthConstraints
();
break
;
}
return
innerConstraints
;
}
@override
double
computeMinIntrinsicWidth
(
double
height
)
{
if
(
child
!=
null
)
return
child
.
getMinIntrinsicWidth
(
height
);
return
0.0
;
}
@override
double
computeMaxIntrinsicWidth
(
double
height
)
{
if
(
child
!=
null
)
return
child
.
getMaxIntrinsicWidth
(
height
);
return
0.0
;
}
@override
double
computeMinIntrinsicHeight
(
double
width
)
{
if
(
child
!=
null
)
return
child
.
getMinIntrinsicHeight
(
width
);
return
0.0
;
}
@override
double
computeMaxIntrinsicHeight
(
double
width
)
{
if
(
child
!=
null
)
return
child
.
getMaxIntrinsicHeight
(
width
);
return
0.0
;
}
// We don't override computeDistanceToActualBaseline(), because we
// want the default behavior (returning null). Otherwise, as you
// scroll the RenderViewport, it would shift in its parent if the
// parent was baseline-aligned, which makes no sense.
@override
void
performLayout
()
{
final
ViewportDimensions
oldDimensions
=
dimensions
;
if
(
child
!=
null
)
{
child
.
layout
(
_getInnerConstraints
(
constraints
),
parentUsesSize:
true
);
size
=
constraints
.
constrain
(
child
.
size
);
final
BoxParentData
childParentData
=
child
.
parentData
;
childParentData
.
offset
=
Offset
.
zero
;
dimensions
=
new
ViewportDimensions
(
containerSize:
size
,
contentSize:
child
.
size
);
}
else
{
performResize
();
dimensions
=
new
ViewportDimensions
(
containerSize:
size
);
}
if
(
onPaintOffsetUpdateNeeded
!=
null
&&
dimensions
!=
oldDimensions
)
paintOffset
=
onPaintOffsetUpdateNeeded
(
dimensions
);
assert
(
paintOffset
!=
null
);
}
bool
_shouldClipAtPaintOffset
(
Offset
paintOffset
)
{
assert
(
child
!=
null
);
return
paintOffset
<
Offset
.
zero
||
!(
Offset
.
zero
&
size
).
contains
((
paintOffset
&
child
.
size
).
bottomRight
);
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
)
{
final
Offset
effectivePaintOffset
=
_effectivePaintOffset
;
void
paintContents
(
PaintingContext
context
,
Offset
offset
)
{
context
.
paintChild
(
child
,
offset
+
effectivePaintOffset
);
}
if
(
_shouldClipAtPaintOffset
(
effectivePaintOffset
))
{
context
.
pushClipRect
(
needsCompositing
,
offset
,
Point
.
origin
&
size
,
paintContents
);
}
else
{
paintContents
(
context
,
offset
);
}
}
}
@override
Rect
describeApproximatePaintClip
(
RenderObject
child
)
{
if
(
child
!=
null
&&
_shouldClipAtPaintOffset
(
_effectivePaintOffset
))
return
Point
.
origin
&
size
;
return
null
;
}
// Workaround for https://github.com/dart-lang/sdk/issues/25232
@override
void
applyPaintTransform
(
RenderBox
child
,
Matrix4
transform
)
{
super
.
applyPaintTransform
(
child
,
transform
);
}
@override
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
if
(
child
!=
null
)
{
assert
(
child
.
parentData
is
BoxParentData
);
Point
transformed
=
position
+
-
_effectivePaintOffset
;
return
child
.
hitTest
(
result
,
position:
transformed
);
}
return
false
;
}
}
packages/flutter/lib/src/widgets/basic.dart
View file @
90574b04
...
...
@@ -11,6 +11,7 @@ import 'package:flutter/services.dart';
import
'debug.dart'
;
import
'framework.dart'
;
export
'package:flutter/foundation.dart'
show
TargetPlatform
;
export
'package:flutter/animation.dart'
;
export
'package:flutter/painting.dart'
;
export
'package:flutter/rendering.dart'
show
...
...
@@ -43,10 +44,7 @@ export 'package:flutter/rendering.dart' show
SingleChildLayoutDelegate
,
TextOverflow
,
ValueChanged
,
ValueGetter
,
ViewportAnchor
,
ViewportDimensions
,
ViewportDimensionsChangeCallback
;
ValueGetter
;
// PAINTING NODES
...
...
@@ -1375,72 +1373,6 @@ class Baseline extends SingleChildRenderObjectWidget {
}
}
/// A widget that's bigger on the inside.
///
/// The child of a viewport can layout to a larger size along the viewport's
/// [mainAxis] than the viewport itself. If that happens, only a portion of the
/// child will be visible through the viewport. The portion of the child that is
/// visible is controlled by the scroll offset.
///
/// Viewport is the core scrolling primitive in the system, but it can be used
/// in other situations.
class
Viewport
extends
SingleChildRenderObjectWidget
{
/// Creates a widget that's bigger on the inside.
///
/// The [mainAxis] and [paintOffset] arguments must not be null.
Viewport
({
Key
key
,
this
.
paintOffset
:
Offset
.
zero
,
this
.
mainAxis
:
Axis
.
vertical
,
this
.
anchor
:
ViewportAnchor
.
start
,
this
.
onPaintOffsetUpdateNeeded
,
Widget
child
})
:
super
(
key:
key
,
child:
child
)
{
assert
(
mainAxis
!=
null
);
assert
(
paintOffset
!=
null
);
}
/// The offset at which to paint the child.
///
/// The offset can be non-zero only in the [mainAxis].
final
Offset
paintOffset
;
/// The direction in which the child is permitted to be larger than the viewport.
///
/// The child is given layout constraints that are fully unconstrained along
/// the main axis (e.g., the child can be as tall as it wants if the main axis
/// is vertical).
final
Axis
mainAxis
;
/// The end of the viewport from which the paint offset is computed.
///
/// See [ViewportAnchor] for more detail.
final
ViewportAnchor
anchor
;
/// Called when the interior or exterior dimensions of the viewport change.
final
ViewportDimensionsChangeCallback
onPaintOffsetUpdateNeeded
;
@override
RenderViewport
createRenderObject
(
BuildContext
context
)
{
return
new
RenderViewport
(
paintOffset:
paintOffset
,
mainAxis:
mainAxis
,
anchor:
anchor
,
onPaintOffsetUpdateNeeded:
onPaintOffsetUpdateNeeded
);
}
@override
void
updateRenderObject
(
BuildContext
context
,
RenderViewport
renderObject
)
{
// Order dependency: RenderViewport validates scrollOffset based on mainAxis.
renderObject
..
mainAxis
=
mainAxis
..
anchor
=
anchor
..
paintOffset
=
paintOffset
..
onPaintOffsetUpdateNeeded
=
onPaintOffsetUpdateNeeded
;
}
}
// SLIVERS
...
...
packages/flutter/lib/src/widgets/clamp_overscrolls.dart
deleted
100644 → 0
View file @
9ec5330f
// 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.
////////////////////////////////////////////////////////////////////////////////
// DELETE THIS FILE WHEN REMOVING LEGACY SCROLLING CODE
////////////////////////////////////////////////////////////////////////////////
import
'package:flutter/foundation.dart'
;
import
'framework.dart'
;
import
'scrollable.dart'
;
/// A widget that controls whether viewport descendants will overscroll their contents.
/// Overscrolling is clamped at the beginning or end or both according to the
/// [edge] parameter.
///
/// Scroll offset limits are defined by the enclosing Scrollable's [ScrollBehavior].
class
ClampOverscrolls
extends
InheritedWidget
{
/// Creates a widget that controls whether viewport descendants will overscroll
/// their contents.
///
/// The [edge] and [child] arguments must not be null.
ClampOverscrolls
({
Key
key
,
this
.
edge
:
ScrollableEdge
.
none
,
@required
Widget
child
,
})
:
super
(
key:
key
,
child:
child
)
{
assert
(
edge
!=
null
);
assert
(
child
!=
null
);
}
/// Creates a widget that controls whether viewport descendants will overscroll
/// based on the given [edge] and the inherited ClampOverscrolls widget for
/// the given [context]. For example if edge is ScrollableEdge.leading
/// and a ClampOverscrolls ancestor exists that specified ScrollableEdge.trailing,
/// then this widget would clamp both scrollable edges.
///
/// The [context], [edge] and [child] arguments must not be null.
factory
ClampOverscrolls
.
inherit
({
Key
key
,
@required
BuildContext
context
,
@required
ScrollableEdge
edge:
ScrollableEdge
.
none
,
@required
Widget
child
})
{
assert
(
context
!=
null
);
assert
(
edge
!=
null
);
assert
(
child
!=
null
);
// The child's clamped edge is the union of the given edge and the
// parent's clamped edge.
ScrollableEdge
parentEdge
=
ClampOverscrolls
.
of
(
context
)?.
edge
??
ScrollableEdge
.
none
;
ScrollableEdge
childEdge
=
edge
;
switch
(
parentEdge
)
{
case
ScrollableEdge
.
leading
:
if
(
edge
==
ScrollableEdge
.
trailing
||
edge
==
ScrollableEdge
.
both
)
childEdge
=
ScrollableEdge
.
both
;
break
;
case
ScrollableEdge
.
trailing
:
if
(
edge
==
ScrollableEdge
.
leading
||
edge
==
ScrollableEdge
.
both
)
childEdge
=
ScrollableEdge
.
both
;
break
;
case
ScrollableEdge
.
both
:
childEdge
=
ScrollableEdge
.
both
;
break
;
case
ScrollableEdge
.
none
:
break
;
}
return
new
ClampOverscrolls
(
key:
key
,
edge:
childEdge
,
child:
child
);
}
/// Defines when viewport scrollOffsets are clamped in terms of the scrollDirection.
/// If edge is `leading` the viewport's scrollOffset will be clamped at its minimum
/// value (often 0.0). If edge is `trailing` then the scrollOffset will be clamped
/// to its maximum value. If edge is `both` then both the leading and trailing
/// constraints are applied.
final
ScrollableEdge
edge
;
/// Return the [newScrollOffset] clamped according to [edge] and [scrollable]'s
/// scroll behavior. The value of [newScrollOffset] defaults to `scrollable.scrollOffset`.
double
clampScrollOffset
(
ScrollableState
scrollable
,
[
double
newScrollOffset
])
{
final
double
scrollOffset
=
newScrollOffset
??
scrollable
.
scrollOffset
;
final
double
minScrollOffset
=
scrollable
.
scrollBehavior
.
minScrollOffset
;
final
double
maxScrollOffset
=
scrollable
.
scrollBehavior
.
maxScrollOffset
;
switch
(
edge
)
{
case
ScrollableEdge
.
both
:
return
scrollOffset
.
clamp
(
minScrollOffset
,
maxScrollOffset
);
case
ScrollableEdge
.
leading
:
return
scrollOffset
.
clamp
(
minScrollOffset
,
double
.
INFINITY
);
case
ScrollableEdge
.
trailing
:
return
scrollOffset
.
clamp
(
double
.
NEGATIVE_INFINITY
,
maxScrollOffset
);
case
ScrollableEdge
.
none
:
return
scrollOffset
;
}
return
scrollOffset
;
}
/// The closest instance of this class that encloses the given context.
///
/// Typical usage is as follows:
///
/// ```dart
/// ScrollableEdge edge = ClampOverscrolls.of(context).edge;
/// ```
static
ClampOverscrolls
of
(
BuildContext
context
)
{
return
context
.
inheritFromWidgetOfExactType
(
ClampOverscrolls
);
}
@override
bool
updateShouldNotify
(
ClampOverscrolls
old
)
=>
edge
!=
old
.
edge
;
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'edge:
$edge
'
);
}
}
packages/flutter/lib/src/widgets/focus.dart
View file @
90574b04
...
...
@@ -192,7 +192,6 @@ class Focus extends StatefulWidget {
_FocusScope
focusScope
=
key
.
currentContext
.
ancestorWidgetOfExactType
(
_FocusScope
);
if
(
focusScope
!=
null
)
{
focusScope
.
focusState
.
_setFocusedWidget
(
key
);
Scrollable
.
ensureVisible
(
focusedContext
);
// ignore: DEPRECATED_MEMBER_USE
Scrollable2
.
ensureVisible
(
focusedContext
);
}
}
...
...
@@ -360,7 +359,6 @@ class _FocusState extends State<Focus> {
BuildContext
focusedContext
=
_focusedWidget
?.
currentContext
;
if
(
focusedContext
==
null
)
return
;
Scrollable
.
ensureVisible
(
focusedContext
);
// ignore: DEPRECATED_MEMBER_USE
Scrollable2
.
ensureVisible
(
focusedContext
);
}
...
...
packages/flutter/lib/src/widgets/scroll_behavior.dart
deleted
100644 → 0
View file @
9ec5330f
// 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.
////////////////////////////////////////////////////////////////////////////////
// DELETE THIS FILE WHEN REMOVING LEGACY SCROLLING CODE
////////////////////////////////////////////////////////////////////////////////
import
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/physics.dart'
;
import
'scroll_simulation.dart'
;
export
'package:flutter/foundation.dart'
show
TargetPlatform
;
Simulation
_createSnapScrollSimulation
(
double
startOffset
,
double
endOffset
,
double
startVelocity
,
double
endVelocity
)
{
return
new
FrictionSimulation
.
through
(
startOffset
,
endOffset
,
startVelocity
,
endVelocity
);
}
// TODO(hansmuller): Simplify these classes. We're no longer using the ScrollBehavior<T, U>
// base class directly. Only LazyBlock uses BoundedBehavior's updateExtents minScrollOffset
// parameter; simpler to move that into ExtentScrollBehavior. All of the classes should
// be called FooScrollBehavior. See https://github.com/flutter/flutter/issues/5281
/// An interface for controlling the behavior of scrollable widgets.
///
/// The type argument T is the type that describes the scroll offset.
/// The type argument U is the type that describes the scroll velocity.
abstract
class
ScrollBehavior
<
T
,
U
>
{
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
///
/// The [platform] must not be null.
const
ScrollBehavior
({
@required
this
.
platform
});
/// The platform for which physics constants should be approximated.
///
/// This is what makes flings go further on iOS than Android.
///
/// Must not be null.
final
TargetPlatform
platform
;
/// Returns a simulation that propels the scrollOffset.
///
/// This function is called when a drag gesture ends.
///
/// Returns `null` if the behavior is to do nothing.
Simulation
createScrollSimulation
(
T
position
,
U
velocity
)
=>
null
;
/// Returns an animation that ends at the snap offset.
///
/// This function is called when a drag gesture ends and a
/// [SnapOffsetCallback] is specified for the scrollable.
///
/// Returns `null` if the behavior is to do nothing.
Simulation
createSnapScrollSimulation
(
T
startOffset
,
T
endOffset
,
U
startVelocity
,
U
endVelocity
)
=>
null
;
/// Returns the scroll offset to use when the user attempts to scroll
/// from the given offset by the given delta.
T
applyCurve
(
T
scrollOffset
,
T
scrollDelta
)
=>
scrollOffset
;
/// Whether this scroll behavior currently permits scrolling.
bool
get
isScrollable
=>
true
;
@override
String
toString
()
{
List
<
String
>
description
=
<
String
>[];
debugFillDescription
(
description
);
return
'
$runtimeType
(
${description.join("; ")}
)'
;
}
/// Accumulates a list of strings describing the current node's fields, one
/// field per string. Subclasses should override this to have their
/// information included in [toString].
@protected
@mustCallSuper
void
debugFillDescription
(
List
<
String
>
description
)
{
description
.
add
(
isScrollable
?
'scrollable'
:
'not scrollable'
);
}
}
/// A scroll behavior for a scrollable widget with linear extent (i.e.
/// that only scrolls along one axis).
abstract
class
ExtentScrollBehavior
extends
ScrollBehavior
<
double
,
double
>
{
/// Creates a scroll behavior for a scrollable widget with linear extent.
/// We start with an INFINITE contentExtent so that we don't accidentally
/// clamp a scrollOffset until we receive an accurate value in updateExtents.
///
/// The extents and the [platform] must not be null.
ExtentScrollBehavior
({
double
contentExtent:
double
.
INFINITY
,
double
containerExtent:
0.0
,
@required
TargetPlatform
platform
})
:
_contentExtent
=
contentExtent
,
_containerExtent
=
containerExtent
,
super
(
platform:
platform
);
/// The linear extent of the content inside the scrollable widget.
double
get
contentExtent
=>
_contentExtent
;
double
_contentExtent
;
/// The linear extent of the exterior of the scrollable widget.
double
get
containerExtent
=>
_containerExtent
;
double
_containerExtent
;
/// Updates either content or container extent (or both)
///
/// Returns the new scroll offset of the widget after the change in extent.
///
/// The [scrollOffset] parameter is the scroll offset of the widget before the
/// change in extent.
double
updateExtents
({
double
contentExtent
,
double
containerExtent
,
double
scrollOffset:
0.0
})
{
assert
(
minScrollOffset
<=
maxScrollOffset
);
if
(
contentExtent
!=
null
)
_contentExtent
=
contentExtent
;
if
(
containerExtent
!=
null
)
_containerExtent
=
containerExtent
;
return
scrollOffset
.
clamp
(
minScrollOffset
,
maxScrollOffset
);
}
/// The minimum value the scroll offset can obtain.
double
get
minScrollOffset
;
/// The maximum value the scroll offset can obtain.
double
get
maxScrollOffset
;
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'content:
${contentExtent.toStringAsFixed(1)}
'
);
description
.
add
(
'container:
${containerExtent.toStringAsFixed(1)}
'
);
description
.
add
(
'range:
${minScrollOffset?.toStringAsFixed(1)}
..
${maxScrollOffset?.toStringAsFixed(1)}
'
);
}
}
/// A scroll behavior that prevents the user from exceeding scroll bounds.
class
BoundedBehavior
extends
ExtentScrollBehavior
{
/// Creates a scroll behavior that does not overscroll.
BoundedBehavior
({
double
contentExtent:
double
.
INFINITY
,
double
containerExtent:
0.0
,
double
minScrollOffset:
0.0
,
@required
TargetPlatform
platform
})
:
_minScrollOffset
=
minScrollOffset
,
super
(
contentExtent:
contentExtent
,
containerExtent:
containerExtent
,
platform:
platform
);
double
_minScrollOffset
;
@override
double
updateExtents
({
double
contentExtent
,
double
containerExtent
,
double
minScrollOffset
,
double
scrollOffset:
0.0
})
{
if
(
minScrollOffset
!=
null
)
{
_minScrollOffset
=
minScrollOffset
;
assert
(
minScrollOffset
<=
maxScrollOffset
);
}
return
super
.
updateExtents
(
contentExtent:
contentExtent
,
containerExtent:
containerExtent
,
scrollOffset:
scrollOffset
);
}
@override
double
get
minScrollOffset
=>
_minScrollOffset
;
@override
double
get
maxScrollOffset
=>
math
.
max
(
minScrollOffset
,
minScrollOffset
+
_contentExtent
-
_containerExtent
);
@override
double
applyCurve
(
double
scrollOffset
,
double
scrollDelta
)
{
return
(
scrollOffset
+
scrollDelta
).
clamp
(
minScrollOffset
,
maxScrollOffset
);
}
}
/// A scroll behavior that does not prevent the user from exceeding scroll bounds.
class
UnboundedBehavior
extends
ExtentScrollBehavior
{
/// Creates a scroll behavior with no scrolling limits.
UnboundedBehavior
({
double
contentExtent:
double
.
INFINITY
,
double
containerExtent:
0.0
,
@required
TargetPlatform
platform
})
:
super
(
contentExtent:
contentExtent
,
containerExtent:
containerExtent
,
platform:
platform
);
@override
Simulation
createScrollSimulation
(
double
position
,
double
velocity
)
{
return
new
ScrollSimulation
(
position:
position
,
velocity:
velocity
,
leadingExtent:
double
.
NEGATIVE_INFINITY
,
trailingExtent:
double
.
INFINITY
,
platform:
platform
,
);
}
@override
Simulation
createSnapScrollSimulation
(
double
startOffset
,
double
endOffset
,
double
startVelocity
,
double
endVelocity
)
{
return
_createSnapScrollSimulation
(
startOffset
,
endOffset
,
startVelocity
,
endVelocity
);
}
@override
double
get
minScrollOffset
=>
double
.
NEGATIVE_INFINITY
;
@override
double
get
maxScrollOffset
=>
double
.
INFINITY
;
@override
double
applyCurve
(
double
scrollOffset
,
double
scrollDelta
)
{
return
scrollOffset
+
scrollDelta
;
}
}
/// A scroll behavior that lets the user scroll beyond the scroll bounds with some resistance.
class
OverscrollBehavior
extends
BoundedBehavior
{
/// Creates a scroll behavior that resists, but does not prevent, scrolling beyond its limits.
OverscrollBehavior
({
double
contentExtent:
double
.
INFINITY
,
double
containerExtent:
0.0
,
double
minScrollOffset:
0.0
,
@required
TargetPlatform
platform
})
:
super
(
contentExtent:
contentExtent
,
containerExtent:
containerExtent
,
minScrollOffset:
minScrollOffset
,
platform:
platform
);
@override
Simulation
createScrollSimulation
(
double
position
,
double
velocity
)
{
return
new
ScrollSimulation
(
position:
position
,
velocity:
velocity
,
leadingExtent:
minScrollOffset
,
trailingExtent:
maxScrollOffset
,
platform:
platform
,
);
}
@override
Simulation
createSnapScrollSimulation
(
double
startOffset
,
double
endOffset
,
double
startVelocity
,
double
endVelocity
)
{
return
_createSnapScrollSimulation
(
startOffset
,
endOffset
,
startVelocity
,
endVelocity
);
}
@override
double
applyCurve
(
double
scrollOffset
,
double
scrollDelta
)
{
double
newScrollOffset
=
scrollOffset
+
scrollDelta
;
// If we're overscrolling, we want move the scroll offset 2x
// slower than we would otherwise. Therefore, we "rewind" the
// newScrollOffset by half the amount that we moved it above.
// Notice that we clamp the "old" value to 0.0 so that we only
// reduce the portion of scrollDelta that's applied beyond 0.0. We
// do similar things for overscroll in the other direction.
if
(
newScrollOffset
<
minScrollOffset
)
{
newScrollOffset
-=
(
newScrollOffset
-
math
.
min
(
minScrollOffset
,
scrollOffset
))
/
2.0
;
}
else
if
(
newScrollOffset
>
maxScrollOffset
)
{
newScrollOffset
-=
(
newScrollOffset
-
math
.
max
(
maxScrollOffset
,
scrollOffset
))
/
2.0
;
}
return
newScrollOffset
;
}
}
/// A scroll behavior that lets the user scroll beyond the scroll bounds only when the bounds are disjoint.
class
OverscrollWhenScrollableBehavior
extends
OverscrollBehavior
{
/// Creates a scroll behavior that allows overscrolling only when some amount of scrolling is already possible.
OverscrollWhenScrollableBehavior
({
double
contentExtent:
double
.
INFINITY
,
double
containerExtent:
0.0
,
double
minScrollOffset:
0.0
,
@required
TargetPlatform
platform
})
:
super
(
contentExtent:
contentExtent
,
containerExtent:
containerExtent
,
minScrollOffset:
minScrollOffset
,
platform:
platform
);
@override
bool
get
isScrollable
=>
contentExtent
>
containerExtent
;
@override
Simulation
createScrollSimulation
(
double
position
,
double
velocity
)
{
if
((
isScrollable
&&
velocity
.
abs
()
>
0
)
||
position
<
minScrollOffset
||
position
>
maxScrollOffset
)
{
// If the triggering gesture starts at or beyond the contentExtent's limits
// then the simulation only serves to settle the scrollOffset back to its
// minimum or maximum value.
if
(
position
<
minScrollOffset
||
position
>
maxScrollOffset
)
velocity
=
0.0
;
return
super
.
createScrollSimulation
(
position
,
velocity
);
}
return
null
;
}
@override
double
applyCurve
(
double
scrollOffset
,
double
scrollDelta
)
{
if
(
isScrollable
)
return
super
.
applyCurve
(
scrollOffset
,
scrollDelta
);
return
minScrollOffset
;
}
}
packages/flutter/lib/src/widgets/scroll_configuration.dart
View file @
90574b04
...
...
@@ -6,7 +6,6 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/rendering.dart'
;
import
'framework.dart'
;
import
'scroll_behavior.dart'
;
import
'scroll_physics.dart'
;
import
'overscroll_indicator.dart'
;
...
...
@@ -78,101 +77,3 @@ class ScrollConfiguration2 extends InheritedWidget {
||
behavior
.
shouldNotify
(
old
.
behavior
);
}
}
////////////////////////////////////////////////////////////////////////////////
// DELETE EVERYTHING BELOW THIS LINE WHEN REMOVING LEGACY SCROLLING CODE
////////////////////////////////////////////////////////////////////////////////
/// Controls how [Scrollable] widgets in a subtree behave.
///
/// Used by [ScrollConfiguration].
abstract
class
ScrollConfigurationDelegate
{
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const
ScrollConfigurationDelegate
();
/// Returns the platform whose scroll physics should be approximated. See
/// [ScrollBehavior.platform].
TargetPlatform
get
platform
;
/// Returns the ScrollBehavior to be used by generic scrolling containers like
/// [Block].
ExtentScrollBehavior
createScrollBehavior
();
/// Generic scrolling containers like [Block] will apply this function to the
/// Scrollable they create. It can be used to add widgets that wrap the
/// Scrollable, like scrollbars or overscroll indicators. By default the
/// [scrollWidget] parameter is returned unchanged.
Widget
wrapScrollWidget
(
BuildContext
context
,
Widget
scrollWidget
)
=>
scrollWidget
;
/// Overrides should return true if this ScrollConfigurationDelegate differs
/// from the provided old delegate in a way that requires rebuilding its
/// scrolling container descendants.
bool
updateShouldNotify
(
@checked
ScrollConfigurationDelegate
old
);
}
class
_DefaultScrollConfigurationDelegate
extends
ScrollConfigurationDelegate
{
const
_DefaultScrollConfigurationDelegate
();
@override
TargetPlatform
get
platform
=>
defaultTargetPlatform
;
@override
ExtentScrollBehavior
createScrollBehavior
()
=>
new
OverscrollWhenScrollableBehavior
(
platform:
platform
);
@override
bool
updateShouldNotify
(
ScrollConfigurationDelegate
old
)
=>
false
;
}
/// A widget that controls descendant [Scrollable] widgets.
///
/// Classes that create Scrollables are not required to depend on this
/// Widget. The following general purpose scrolling widgets do depend
/// on [ScrollConfiguration]: [Block], [LazyBlock], [ScrollableViewport],
/// [ScrollableList], [ScrollableLazyList]. The [Scrollable] base class uses
/// [ScrollConfiguration] to create its [ScrollBehavior].
class
ScrollConfiguration
extends
InheritedWidget
{
/// Creates a widget that controls descendant [Scrollable] widgets.
///
/// If the [delegate] argument is null, the scroll configuration for this
/// subtree is controlled by the default implementation of
/// [ScrollConfigurationDelegate].
ScrollConfiguration
({
Key
key
,
this
.
delegate
,
@required
Widget
child
})
:
super
(
key:
key
,
child:
child
);
static
const
ScrollConfigurationDelegate
_defaultDelegate
=
const
_DefaultScrollConfigurationDelegate
();
/// Defines the ScrollBehavior and scrollable wrapper for descendants.
final
ScrollConfigurationDelegate
delegate
;
/// The delegate property of the closest instance of this class that encloses
/// the given context.
///
/// If no such instance exists, returns a default
/// [ScrollConfigurationDelegate] that approximates the scrolling physics of
/// the current platform (see [defaultTargetPlatform]) using a
/// [OverscrollWhenScrollableBehavior] behavior model.
///
/// Typical usage is as follows:
///
/// ```dart
/// ScrollConfigurationDelegate scrollConfiguration = ScrollConfiguration.of(context);
/// ```
static
ScrollConfigurationDelegate
of
(
BuildContext
context
)
{
ScrollConfiguration
configuration
=
context
.
inheritFromWidgetOfExactType
(
ScrollConfiguration
);
return
configuration
?.
delegate
??
_defaultDelegate
;
}
/// A utility function that calls [ScrollConfigurationDelegate.wrapScrollWidget].
static
Widget
wrap
(
BuildContext
context
,
Widget
scrollWidget
)
{
return
ScrollConfiguration
.
of
(
context
).
wrapScrollWidget
(
context
,
scrollWidget
);
}
@override
bool
updateShouldNotify
(
ScrollConfiguration
old
)
{
return
delegate
?.
updateShouldNotify
(
old
.
delegate
)
??
false
;
}
}
packages/flutter/lib/src/widgets/scroll_physics.dart
View file @
90574b04
...
...
@@ -73,7 +73,7 @@ class BouncingScrollPhysics extends ScrollPhysics {
return
new
BouncingScrollSimulation
(
spring:
spring
,
position:
position
.
pixels
,
velocity:
velocity
,
velocity:
velocity
*
0.91
,
// TODO(abarth): We should move this constant closer to the drag end.
leadingExtent:
position
.
minScrollExtent
,
trailingExtent:
position
.
maxScrollExtent
,
)..
tolerance
=
tolerance
;
...
...
packages/flutter/lib/src/widgets/scroll_simulation.dart
View file @
90574b04
...
...
@@ -81,7 +81,7 @@ class BouncingScrollSimulation extends SimulationGroup {
_currentSimulation
=
new
ScrollSpringSimulation
(
_spring
,
position
,
_leadingExtent
,
velocity
);
return
true
;
}
else
if
(
_currentSimulation
==
null
)
{
_currentSimulation
=
new
FrictionSimulation
(
0.135
,
position
,
velocity
*
0.91
);
_currentSimulation
=
new
FrictionSimulation
(
0.135
,
position
,
velocity
);
return
true
;
}
}
...
...
@@ -196,139 +196,3 @@ class ClampingScrollSimulation extends Simulation {
return
time
>=
_duration
;
}
}
////////////////////////////////////////////////////////////////////////////////
// DELETE EVERYTHING BELOW THIS LINE WHEN REMOVING LEGACY SCROLLING CODE
////////////////////////////////////////////////////////////////////////////////
final
SpringDescription
_kScrollSpring
=
new
SpringDescription
.
withDampingRatio
(
mass:
0.5
,
springConstant:
100.0
,
ratio:
1.1
);
final
double
_kDrag
=
0.025
;
class
_CupertinoSimulation
extends
FrictionSimulation
{
static
const
double
drag
=
0.135
;
_CupertinoSimulation
({
double
position
,
double
velocity
})
:
super
(
drag
,
position
,
velocity
*
0.91
);
}
class
_MountainViewSimulation
extends
ClampingScrollSimulation
{
_MountainViewSimulation
({
double
position
,
double
velocity
,
double
friction:
0.015
,
})
:
super
(
position:
position
,
velocity:
velocity
,
friction:
friction
,
);
}
/// Composite simulation for scrollable interfaces.
///
/// Simulates kinetic scrolling behavior between a leading and trailing
/// boundary. Friction is applied within the extents and a spring action is
/// applied at the boundaries. This simulation can only step forward.
class
ScrollSimulation
extends
SimulationGroup
{
/// Creates a [ScrollSimulation] with the given parameters.
///
/// The position and velocity arguments must use the same units as will be
/// expected from the [x] and [dx] methods respectively.
///
/// The leading and trailing extents must use the unit of length, the same
/// unit as used for the position argument and as expected from the [x]
/// method.
///
/// The units used with the provided [SpringDescription] must similarly be
/// consistent with the other arguments.
///
/// The final argument is the coefficient of friction, which is unitless.
ScrollSimulation
({
@required
double
position
,
@required
double
velocity
,
@required
double
leadingExtent
,
@required
double
trailingExtent
,
SpringDescription
spring
,
double
drag
,
TargetPlatform
platform
,
})
:
_leadingExtent
=
leadingExtent
,
_trailingExtent
=
trailingExtent
,
_spring
=
spring
??
_kScrollSpring
,
_drag
=
drag
??
_kDrag
,
_platform
=
platform
{
assert
(
position
!=
null
);
assert
(
velocity
!=
null
);
assert
(
_leadingExtent
!=
null
);
assert
(
_trailingExtent
!=
null
);
assert
(
_spring
!=
null
);
_chooseSimulation
(
position
,
velocity
,
0.0
);
}
final
double
_leadingExtent
;
final
double
_trailingExtent
;
final
SpringDescription
_spring
;
final
double
_drag
;
final
TargetPlatform
_platform
;
bool
_isSpringing
=
false
;
Simulation
_currentSimulation
;
double
_offset
=
0.0
;
@override
bool
step
(
double
time
)
=>
_chooseSimulation
(
_currentSimulation
.
x
(
time
-
_offset
),
_currentSimulation
.
dx
(
time
-
_offset
),
time
);
@override
Simulation
get
currentSimulation
=>
_currentSimulation
;
@override
double
get
currentIntervalOffset
=>
_offset
;
bool
_chooseSimulation
(
double
position
,
double
velocity
,
double
intervalOffset
)
{
if
(
_spring
==
null
&&
(
position
>
_trailingExtent
||
position
<
_leadingExtent
))
return
false
;
// This simulation can only step forward.
if
(!
_isSpringing
)
{
if
(
position
>
_trailingExtent
)
{
_isSpringing
=
true
;
_offset
=
intervalOffset
;
_currentSimulation
=
new
ScrollSpringSimulation
(
_spring
,
position
,
_trailingExtent
,
velocity
);
return
true
;
}
else
if
(
position
<
_leadingExtent
)
{
_isSpringing
=
true
;
_offset
=
intervalOffset
;
_currentSimulation
=
new
ScrollSpringSimulation
(
_spring
,
position
,
_leadingExtent
,
velocity
);
return
true
;
}
}
if
(
_currentSimulation
==
null
)
{
switch
(
_platform
)
{
case
TargetPlatform
.
android
:
case
TargetPlatform
.
fuchsia
:
_currentSimulation
=
new
_MountainViewSimulation
(
position:
position
,
velocity:
velocity
,
);
break
;
case
TargetPlatform
.
iOS
:
_currentSimulation
=
new
_CupertinoSimulation
(
position:
position
,
velocity:
velocity
,
);
break
;
}
// No platform specified
_currentSimulation
??=
new
FrictionSimulation
(
_drag
,
position
,
velocity
);
return
true
;
}
return
false
;
}
@override
String
toString
()
{
return
'ScrollSimulation(leadingExtent:
$_leadingExtent
, trailingExtent:
$_trailingExtent
)'
;
}
}
packages/flutter/lib/src/widgets/scrollable.dart
View file @
90574b04
...
...
@@ -3,24 +3,16 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:math'
as
math
;
import
'dart:ui'
as
ui
show
window
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/physics.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'banner.dart'
;
import
'basic.dart'
;
import
'clamp_overscrolls.dart'
;
import
'debug.dart'
;
import
'framework.dart'
;
import
'gesture_detector.dart'
;
import
'notification_listener.dart'
;
import
'page_storage.dart'
;
import
'scroll_behavior.dart'
;
import
'scroll_configuration.dart'
;
import
'scroll_controller.dart'
;
import
'scroll_notification.dart'
;
...
...
@@ -325,1131 +317,3 @@ class Scrollable2State extends State<Scrollable2> with TickerProviderStateMixin
description
.
add
(
'position:
$position
'
);
}
}
////////////////////////////////////////////////////////////////////////////////
// DELETE EVERYTHING BELOW THIS LINE WHEN REMOVING LEGACY SCROLLING CODE
////////////////////////////////////////////////////////////////////////////////
/// Identifies one or both limits of a [Scrollable] in terms of its scrollDirection.
enum
ScrollableEdge
{
/// The top and bottom of the scrollable if its scrollDirection is vertical
/// or the left and right if its scrollDirection is horizontal.
both
,
/// Only the top of the scrollable if its scrollDirection is vertical,
/// or only the left if its scrollDirection is horizontal.
leading
,
/// Only the bottom of the scrollable if its scroll-direction is vertical,
/// or only the right if its scrollDirection is horizontal.
trailing
,
/// The overscroll indicator should not appear at all.
none
,
}
/// The accuracy to which scrolling is computed.
final
Tolerance
kPixelScrollTolerance
=
new
Tolerance
(
// TODO(ianh): Handle the case of the device pixel ratio changing.
velocity:
1.0
/
(
0.050
*
ui
.
window
.
devicePixelRatio
),
// logical pixels per second
distance:
1.0
/
ui
.
window
.
devicePixelRatio
// logical pixels
);
/// Signature for building a widget based on [ScrollableState].
///
/// Used by [Scrollable.builder].
typedef
Widget
ScrollBuilder
(
BuildContext
context
,
ScrollableState
state
);
/// Signature for callbacks that receive a scroll offset.
///
/// Used by [Scrollable.onScrollStart], [Scrollable.onScroll], and [Scrollable.onScrollEnd].
typedef
void
ScrollListener
(
double
scrollOffset
);
/// Signature for determining the offset at which scrolling should snap.
///
/// Used by [Scrollable.snapOffsetCallback].
typedef
double
SnapOffsetCallback
(
double
scrollOffset
,
Size
containerSize
);
/// Will be removed soon.
class
Scrollable
extends
StatefulWidget
{
/// Initializes fields for subclasses.
///
/// The [scrollDirection] and [scrollAnchor] arguments must not be null.
Scrollable
({
Key
key
,
this
.
initialScrollOffset
,
this
.
scrollDirection
:
Axis
.
vertical
,
this
.
scrollAnchor
:
ViewportAnchor
.
start
,
this
.
onScrollStart
,
this
.
onScroll
,
this
.
onScrollEnd
,
this
.
snapOffsetCallback
,
this
.
builder
})
:
super
(
key:
key
)
{
assert
(
scrollDirection
==
Axis
.
vertical
||
scrollDirection
==
Axis
.
horizontal
);
assert
(
scrollAnchor
==
ViewportAnchor
.
start
||
scrollAnchor
==
ViewportAnchor
.
end
);
}
// Warning: keep the dartdoc comments that follow in sync with the copies in
// ScrollableViewport and LazyBlock.
// And see: https://github.com/dart-lang/dartdoc/issues/1161.
/// The scroll offset this widget should use when first created.
final
double
initialScrollOffset
;
/// The axis along which this widget should scroll.
final
Axis
scrollDirection
;
/// Whether to place first child at the start of the container or
/// the last child at the end of the container, when the scrollable
/// has not been scrolled and has no initial scroll offset.
///
/// For example, if the [scrollDirection] is [Axis.vertical] and
/// there are enough items to overflow the container, then
/// [ViewportAnchor.start] means that the top of the first item
/// should be aligned with the top of the scrollable with the last
/// item below the bottom, and [ViewportAnchor.end] means the bottom
/// of the last item should be aligned with the bottom of the
/// scrollable, with the first item above the top.
///
/// This also affects whether, when an item is added or removed, the
/// displacement will be towards the first item or the last item.
/// Continuing the earlier example, if a new item is inserted in the
/// middle of the list, in the [ViewportAnchor.start] case the items
/// after it (with greater indices, down to the item with the
/// highest index) will be pushed down, while in the
/// [ViewportAnchor.end] case the items before it (with lower
/// indices, up to the item with the index 0) will be pushed up.
///
/// Subclasses may ignore this value if, for instance, they do not
/// have a concept of an anchor, or have more complicated behavior
/// (e.g. they would by default put the middle item in the middle of
/// the container).
final
ViewportAnchor
scrollAnchor
;
/// Called whenever this widget starts to scroll.
final
ScrollListener
onScrollStart
;
/// Called whenever this widget's scroll offset changes.
final
ScrollListener
onScroll
;
/// Called whenever this widget stops scrolling.
final
ScrollListener
onScrollEnd
;
/// Called to determine the offset to which scrolling should snap,
/// when handling a fling.
///
/// This callback, if set, will be called with the offset that the
/// Scrollable would have scrolled to in the absence of this
/// callback, and a Size describing the size of the Scrollable
/// itself.
///
/// The callback's return value is used as the new scroll offset to
/// aim for.
///
/// If the callback simply returns its first argument (the offset),
/// then it is as if the callback was null.
final
SnapOffsetCallback
snapOffsetCallback
;
/// Using to build the content of this widget.
///
/// See [buildContent] for details.
final
ScrollBuilder
builder
;
/// The state from the closest instance of this class that encloses the given context.
///
/// Typical usage is as follows:
///
/// ```dart
/// ScrollableState scrollable = Scrollable.of(context);
/// ```
static
ScrollableState
of
(
BuildContext
context
)
{
return
context
.
ancestorStateOfType
(
const
TypeMatcher
<
ScrollableState
>());
}
/// Scrolls the closest enclosing scrollable to make the given context visible.
static
Future
<
Null
>
ensureVisible
(
BuildContext
context
,
{
Duration
duration
,
Curve
curve:
Curves
.
ease
})
{
assert
(
context
.
findRenderObject
()
is
RenderBox
);
// TODO(abarth): This function doesn't handle nested scrollable widgets.
ScrollableState
scrollable
=
Scrollable
.
of
(
context
);
if
(
scrollable
==
null
)
return
new
Future
<
Null
>.
value
();
RenderBox
targetBox
=
context
.
findRenderObject
();
assert
(
targetBox
.
attached
);
Size
targetSize
=
targetBox
.
size
;
RenderBox
scrollableBox
=
scrollable
.
context
.
findRenderObject
();
assert
(
scrollableBox
.
attached
);
Size
scrollableSize
=
scrollableBox
.
size
;
double
targetMin
;
double
targetMax
;
double
scrollableMin
;
double
scrollableMax
;
switch
(
scrollable
.
config
.
scrollDirection
)
{
case
Axis
.
vertical
:
targetMin
=
targetBox
.
localToGlobal
(
Point
.
origin
).
y
;
targetMax
=
targetBox
.
localToGlobal
(
new
Point
(
0.0
,
targetSize
.
height
)).
y
;
scrollableMin
=
scrollableBox
.
localToGlobal
(
Point
.
origin
).
y
;
scrollableMax
=
scrollableBox
.
localToGlobal
(
new
Point
(
0.0
,
scrollableSize
.
height
)).
y
;
break
;
case
Axis
.
horizontal
:
targetMin
=
targetBox
.
localToGlobal
(
Point
.
origin
).
x
;
targetMax
=
targetBox
.
localToGlobal
(
new
Point
(
targetSize
.
width
,
0.0
)).
x
;
scrollableMin
=
scrollableBox
.
localToGlobal
(
Point
.
origin
).
x
;
scrollableMax
=
scrollableBox
.
localToGlobal
(
new
Point
(
scrollableSize
.
width
,
0.0
)).
x
;
break
;
}
double
scrollOffsetDelta
;
if
(
targetMin
<
scrollableMin
)
{
if
(
targetMax
>
scrollableMax
)
{
// The target is too big to fit inside the scrollable. The best we can do
// is to center the target.
double
targetCenter
=
(
targetMin
+
targetMax
)
/
2.0
;
double
scrollableCenter
=
(
scrollableMin
+
scrollableMax
)
/
2.0
;
scrollOffsetDelta
=
targetCenter
-
scrollableCenter
;
}
else
{
scrollOffsetDelta
=
targetMin
-
scrollableMin
;
}
}
else
if
(
targetMax
>
scrollableMax
)
{
scrollOffsetDelta
=
targetMax
-
scrollableMax
;
}
else
{
return
new
Future
<
Null
>.
value
();
}
ExtentScrollBehavior
scrollBehavior
=
scrollable
.
scrollBehavior
;
double
scrollOffset
=
(
scrollable
.
scrollOffset
+
scrollOffsetDelta
)
.
clamp
(
scrollBehavior
.
minScrollOffset
,
scrollBehavior
.
maxScrollOffset
);
if
(
scrollOffset
!=
scrollable
.
scrollOffset
)
return
scrollable
.
scrollTo
(
scrollOffset
,
duration:
duration
,
curve:
curve
);
return
new
Future
<
Null
>.
value
();
}
@override
ScrollableState
createState
()
=>
new
ScrollableState
<
Scrollable
>();
}
/// Contains the state for common scrolling widgets that scroll only
/// along one axis.
///
/// Widgets that subclass [Scrollable] typically use state objects
/// that subclass [ScrollableState].
///
/// The main state of a ScrollableState is the "scroll offset", which
/// is the the logical description of the current scroll position and
/// is stored in [scrollOffset] as a double. The units of the scroll
/// offset are defined by the specific subclass. By default, the units
/// are logical pixels.
///
/// A "pixel offset" is a distance in logical pixels (or a velocity in
/// logical pixels per second). The pixel offset corresponding to the
/// current scroll position is typically used as the paint offset
/// argument to the underlying [Viewport] class (or equivalent); see
/// the [buildContent] method.
///
/// A "pixel delta" is an [Offset] that describes a two-dimensional
/// distance as reported by input events. If the scrolling convention
/// is axis-aligned (as in a vertical scrolling list or a horizontal
/// scrolling list), then the pixel delta will consist of a pixel
/// offset in the scroll axis, and a value in the other axis that is
/// either ignored (when converting to a scroll offset) or set to zero
/// (when converting a scroll offset to a pixel delta).
///
/// If the units of the scroll offset are not logical pixels, then a
/// mapping must be made from logical pixels (as used by incoming
/// input events) and the scroll offset (as stored internally). To
/// provide this mapping, override the [pixelOffsetToScrollOffset] and
/// [scrollOffsetToPixelOffset] methods.
///
/// If the scrollable is not providing axis-aligned scrolling, then,
/// to convert pixel deltas to scroll offsets and vice versa, override
/// the [pixelDeltaToScrollOffset] and [scrollOffsetToPixelOffset]
/// methods. By default, these assume an axis-aligned scroll behavior
/// along the [config.scrollDirection] axis and are implemented in
/// terms of the [pixelOffsetToScrollOffset] and
/// [scrollOffsetToPixelOffset] methods.
@optionalTypeArgs
class
ScrollableState
<
T
extends
Scrollable
>
extends
State
<
T
>
with
SingleTickerProviderStateMixin
{
@override
void
initState
()
{
super
.
initState
();
_controller
=
new
AnimationController
.
unbounded
(
vsync:
this
)
..
addListener
(
_handleAnimationChanged
)
..
addStatusListener
(
_handleAnimationStatusChanged
);
_scrollOffset
=
PageStorage
.
of
(
context
)?.
readState
(
context
)
??
config
.
initialScrollOffset
??
0.0
;
_virtualScrollOffset
=
_scrollOffset
;
}
Simulation
_simulation
;
// if we're flinging, then this is the animation with which we're doing it
AnimationController
_controller
;
double
_contentExtent
;
double
_containerExtent
;
bool
_scrollUnderway
=
false
;
@override
void
dispose
()
{
_controller
.
dispose
();
_simulation
=
null
;
super
.
dispose
();
}
@override
void
dependenciesChanged
()
{
_scrollBehavior
=
createScrollBehavior
();
didUpdateScrollBehavior
(
_scrollBehavior
.
updateExtents
(
contentExtent:
_contentExtent
,
containerExtent:
_containerExtent
,
scrollOffset:
scrollOffset
));
super
.
dependenciesChanged
();
}
/// The current scroll offset.
///
/// The scroll offset is applied to the child widget along the scroll
/// direction before painting. A positive scroll offset indicates that
/// more content in the preferred reading direction is visible.
///
/// The scroll offset's value may be above or below the limits defined
/// by the [scrollBehavior]. This is called "overscrolling" and it can be
/// prevented with the [ClampOverscrolls] widget.
///
/// See also:
///
/// * [virtualScrollOffset]
/// * [initialScrollOffset]
/// * [onScrollStart]
/// * [onScroll]
/// * [onScrollEnd]
/// * [ScrollNotification]
double
get
scrollOffset
=>
_scrollOffset
;
double
_scrollOffset
;
/// The current scroll offset, irrespective of the constraints defined
/// by any [ClampOverscrolls] widget ancestors.
///
/// See also:
///
/// * [scrollOffset]
double
get
virtualScrollOffset
=>
_virtualScrollOffset
;
double
_virtualScrollOffset
;
/// Convert a position or velocity measured in terms of pixels to a scrollOffset.
/// Scrollable gesture handlers convert their incoming values with this method.
/// Subclasses that define scrollOffset in units other than pixels must
/// override this method.
///
/// This function should be the inverse of [scrollOffsetToPixelOffset].
double
pixelOffsetToScrollOffset
(
double
pixelOffset
)
{
switch
(
config
.
scrollAnchor
)
{
case
ViewportAnchor
.
start
:
// We negate the delta here because a positive scroll offset moves the
// the content up (or to the left) rather than down (or the right).
return
-
pixelOffset
;
case
ViewportAnchor
.
end
:
return
pixelOffset
;
}
assert
(
config
.
scrollAnchor
!=
null
);
return
null
;
}
/// Convert a scrollOffset value to the number of pixels to which it corresponds.
///
/// This function should be the inverse of [pixelOffsetToScrollOffset].
double
scrollOffsetToPixelOffset
(
double
scrollOffset
)
{
switch
(
config
.
scrollAnchor
)
{
case
ViewportAnchor
.
start
:
return
-
scrollOffset
;
case
ViewportAnchor
.
end
:
return
scrollOffset
;
}
assert
(
config
.
scrollAnchor
!=
null
);
return
null
;
}
/// Returns the scroll offset component of the given pixel delta, accounting
/// for the scroll direction and scroll anchor.
///
/// A pixel delta is an [Offset] in pixels. Typically this function
/// is implemented in terms of [pixelOffsetToScrollOffset].
double
pixelDeltaToScrollOffset
(
Offset
pixelDelta
)
{
switch
(
config
.
scrollDirection
)
{
case
Axis
.
horizontal
:
return
pixelOffsetToScrollOffset
(
pixelDelta
.
dx
);
case
Axis
.
vertical
:
return
pixelOffsetToScrollOffset
(
pixelDelta
.
dy
);
}
assert
(
config
.
scrollDirection
!=
null
);
return
null
;
}
/// Returns a two-dimensional representation of the scroll offset, accounting
/// for the scroll direction and scroll anchor.
///
/// See the definition of [ScrollableState] for more details.
Offset
scrollOffsetToPixelDelta
(
double
scrollOffset
)
{
switch
(
config
.
scrollDirection
)
{
case
Axis
.
horizontal
:
return
new
Offset
(
scrollOffsetToPixelOffset
(
scrollOffset
),
0.0
);
case
Axis
.
vertical
:
return
new
Offset
(
0.0
,
scrollOffsetToPixelOffset
(
scrollOffset
));
}
assert
(
config
.
scrollDirection
!=
null
);
return
null
;
}
/// The current scroll behavior of this widget.
///
/// Scroll behaviors control where the boundaries of the scrollable are placed
/// and how the scrolling physics should behave near those boundaries and
/// after the user stops directly manipulating the scrollable.
ExtentScrollBehavior
get
scrollBehavior
=>
_scrollBehavior
;
ExtentScrollBehavior
_scrollBehavior
;
/// Use the value returned by [ScrollConfiguration.createScrollBehavior].
/// If this widget doesn't have a ScrollConfiguration ancestor,
/// or its createScrollBehavior callback is null, then return a new instance
/// of [OverscrollWhenScrollableBehavior].
@protected
ExtentScrollBehavior
createScrollBehavior
()
{
return
ScrollConfiguration
.
of
(
context
)?.
createScrollBehavior
();
}
bool
_scrollOffsetIsInBounds
(
double
scrollOffset
)
{
if
(
scrollBehavior
is
!
ExtentScrollBehavior
)
return
false
;
final
ExtentScrollBehavior
behavior
=
scrollBehavior
;
return
scrollOffset
>=
behavior
.
minScrollOffset
&&
scrollOffset
<
behavior
.
maxScrollOffset
;
}
void
_handleAnimationChanged
()
{
_setScrollOffset
(
_controller
.
value
);
}
void
_handleAnimationStatusChanged
(
AnimationStatus
status
)
{
// this is not called when stop() is called on the controller
setState
(()
{
if
(!
_controller
.
isAnimating
)
{
_simulation
=
null
;
_scrollUnderway
=
false
;
}
});
}
void
_setScrollOffset
(
double
newScrollOffset
,
{
DragUpdateDetails
details
})
{
if
(
_scrollOffset
==
newScrollOffset
)
return
;
final
ClampOverscrolls
clampOverscrolls
=
ClampOverscrolls
.
of
(
context
);
final
double
clampedScrollOffset
=
clampOverscrolls
?.
clampScrollOffset
(
this
,
newScrollOffset
)
??
newScrollOffset
;
_setStateMaybeDuringBuild
(()
{
_virtualScrollOffset
=
newScrollOffset
;
_scrollUnderway
=
_scrollOffset
!=
clampedScrollOffset
;
_scrollOffset
=
clampedScrollOffset
;
});
PageStorage
.
of
(
context
)?.
writeState
(
context
,
_scrollOffset
);
_startScroll
();
dispatchOnScroll
();
new
ScrollNotification
(
scrollable:
this
,
kind:
ScrollNotificationKind
.
updated
,
details:
details
).
dispatch
(
context
);
_endScroll
();
}
/// Scroll this widget by the given scroll delta.
///
/// If a non-null [duration] is provided, the widget will animate to the new
/// scroll offset over the given duration with the given curve.
Future
<
Null
>
scrollBy
(
double
scrollDelta
,
{
Duration
duration
,
Curve
curve:
Curves
.
ease
,
DragUpdateDetails
details
})
{
double
newScrollOffset
=
scrollBehavior
.
applyCurve
(
virtualScrollOffset
,
scrollDelta
);
return
scrollTo
(
newScrollOffset
,
duration:
duration
,
curve:
curve
,
details:
details
);
}
/// Scroll this widget to the given scroll offset.
///
/// If a non-null [duration] is provided, the widget will animate to the new
/// scroll offset over the given duration with the given curve.
///
/// This function does not accept a zero duration. To jump-scroll to
/// the new offset, do not provide a duration, rather than providing
/// a zero duration.
///
/// The returned [Future] completes when the scrolling animation is complete.
Future
<
Null
>
scrollTo
(
double
newScrollOffset
,
{
Duration
duration
,
Curve
curve:
Curves
.
ease
,
DragUpdateDetails
details
})
{
if
(
newScrollOffset
==
_scrollOffset
)
return
new
Future
<
Null
>.
value
();
if
(
duration
==
null
)
{
_stop
();
_setScrollOffset
(
newScrollOffset
,
details:
details
);
return
new
Future
<
Null
>.
value
();
}
assert
(
duration
>
Duration
.
ZERO
);
return
_animateTo
(
newScrollOffset
,
duration
,
curve
);
}
Future
<
Null
>
_animateTo
(
double
newScrollOffset
,
Duration
duration
,
Curve
curve
)
{
_stop
();
_controller
.
value
=
virtualScrollOffset
;
_startScroll
();
return
_controller
.
animateTo
(
newScrollOffset
,
duration:
duration
,
curve:
curve
).
then
((
Null
_
)
{
_endScroll
();
});
}
/// Update any in-progress scrolling physics to account for new scroll behavior.
///
/// The scrolling physics depends on the scroll behavior. When changing the
/// scrolling behavior, call this function to update any in-progress scrolling
/// physics to account for the new scroll behavior. This function preserves
/// the current velocity when updating the physics.
///
/// If there are no in-progress scrolling physics, this function scrolls to
/// the given offset instead.
void
didUpdateScrollBehavior
(
double
newScrollOffset
)
{
_setStateMaybeDuringBuild
(()
{
_contentExtent
=
scrollBehavior
.
contentExtent
;
_containerExtent
=
scrollBehavior
.
containerExtent
;
});
// This does not call setState, because if anything below actually
// changes our build, it will itself independently trigger a frame.
assert
(
_controller
.
isAnimating
||
_simulation
==
null
);
if
(
_numberOfInProgressScrolls
>
0
)
{
if
(
_simulation
!=
null
)
{
double
dx
=
_simulation
.
dx
(
_controller
.
lastElapsedDuration
.
inMicroseconds
/
Duration
.
MICROSECONDS_PER_SECOND
);
_startToEndAnimation
(
dx
);
// dx - logical pixels / second
}
return
;
}
scrollTo
(
newScrollOffset
);
}
/// Updates the scroll behavior for the new content and container extent.
///
/// For convenience, this function combines three common operations:
///
/// 1. Updating the scroll behavior extents with
/// [ExtentScrollBehavior.updateExtents].
/// 2. Notifying this object that the scroll behavior was updated with
/// [didUpdateScrollBehavior].
/// 3. Updating this object's gesture detector with [updateGestureDetector].
void
handleExtentsChanged
(
double
contentExtent
,
double
containerExtent
)
{
didUpdateScrollBehavior
(
scrollBehavior
.
updateExtents
(
contentExtent:
contentExtent
,
containerExtent:
containerExtent
,
scrollOffset:
scrollOffset
));
updateGestureDetector
();
}
/// If [scrollVelocity] is greater than [PixelScrollTolerance.velocity] then
/// fling the scroll offset with the given velocity in logical pixels/second.
/// Otherwise, if this scrollable is overscrolled or a [snapOffsetCallback]
/// was given, animate the scroll offset to its final value with [settleScrollOffset].
///
/// Calling this function starts a physics-based animation of the scroll
/// offset with the given value as the initial velocity. The physics
/// simulation is determined by the scroll behavior.
///
/// The returned [Future] completes when the scrolling animation is complete.
Future
<
Null
>
fling
(
double
scrollVelocity
)
{
if
(
scrollVelocity
.
abs
()
>
kPixelScrollTolerance
.
velocity
)
return
_startToEndAnimation
(
scrollVelocity
);
// If a scroll animation isn't underway already and we're overscrolled or we're
// going to have to snap the scroll offset, then animate the scroll offset to its
// final value.
if
(!
_controller
.
isAnimating
&&
(
shouldSnapScrollOffset
||
!
_scrollOffsetIsInBounds
(
scrollOffset
)))
return
settleScrollOffset
();
return
new
Future
<
Null
>.
value
();
}
/// Animate the scroll offset to a value with a local minima of energy.
///
/// Calling this function starts a physics-based animation of the scroll
/// offset either to a snap point or to within the scrolling bounds. The
/// physics simulation used is determined by the scroll behavior.
Future
<
Null
>
settleScrollOffset
()
{
return
_startToEndAnimation
(
0.0
);
}
Future
<
Null
>
_startToEndAnimation
(
double
scrollVelocity
)
{
_stop
();
_simulation
=
_createSnapSimulation
(
scrollVelocity
)
??
_createFlingSimulation
(
scrollVelocity
);
if
(
_simulation
==
null
)
return
new
Future
<
Null
>.
value
();
_startScroll
();
return
_controller
.
animateWith
(
_simulation
).
then
((
Null
_
)
{
_endScroll
();
});
}
/// Whether this scrollable should attempt to snap scroll offsets.
bool
get
shouldSnapScrollOffset
=>
config
.
snapOffsetCallback
!=
null
;
/// Returns the snapped offset closest to the given scroll offset.
double
snapScrollOffset
(
double
scrollOffset
)
{
return
config
.
snapOffsetCallback
==
null
?
scrollOffset
:
config
.
snapOffsetCallback
(
scrollOffset
,
context
.
size
);
}
Simulation
_createSnapSimulation
(
double
scrollVelocity
)
{
if
(!
shouldSnapScrollOffset
||
scrollVelocity
==
0.0
||
!
_scrollOffsetIsInBounds
(
scrollOffset
))
return
null
;
Simulation
simulation
=
_createFlingSimulation
(
scrollVelocity
);
if
(
simulation
==
null
)
return
null
;
final
double
endScrollOffset
=
simulation
.
x
(
double
.
INFINITY
);
if
(
endScrollOffset
.
isNaN
)
return
null
;
final
double
snappedScrollOffset
=
snapScrollOffset
(
endScrollOffset
);
if
(!
_scrollOffsetIsInBounds
(
snappedScrollOffset
))
return
null
;
final
double
snapVelocity
=
scrollVelocity
.
abs
()
*
(
snappedScrollOffset
-
scrollOffset
).
sign
;
final
double
endVelocity
=
pixelOffsetToScrollOffset
(
kPixelScrollTolerance
.
velocity
).
abs
()
*
(
scrollVelocity
<
0.0
?
-
1.0
:
1.0
);
Simulation
toSnapSimulation
=
scrollBehavior
.
createSnapScrollSimulation
(
virtualScrollOffset
,
snappedScrollOffset
,
snapVelocity
,
endVelocity
);
if
(
toSnapSimulation
==
null
)
return
null
;
final
double
scrollOffsetMin
=
math
.
min
(
scrollOffset
,
snappedScrollOffset
);
final
double
scrollOffsetMax
=
math
.
max
(
scrollOffset
,
snappedScrollOffset
);
return
new
ClampedSimulation
(
toSnapSimulation
,
xMin:
scrollOffsetMin
,
xMax:
scrollOffsetMax
);
}
Simulation
_createFlingSimulation
(
double
scrollVelocity
)
{
final
Simulation
simulation
=
scrollBehavior
.
createScrollSimulation
(
virtualScrollOffset
,
scrollVelocity
);
if
(
simulation
!=
null
)
{
final
double
endVelocity
=
pixelOffsetToScrollOffset
(
kPixelScrollTolerance
.
velocity
).
abs
();
final
double
endDistance
=
pixelOffsetToScrollOffset
(
kPixelScrollTolerance
.
distance
).
abs
();
simulation
.
tolerance
=
new
Tolerance
(
velocity:
endVelocity
,
distance:
endDistance
);
}
return
simulation
;
}
// When we start an scroll animation, we stop any previous scroll animation.
// However, the code that would deliver the onScrollEnd callback is watching
// for animations to end using a Future that resolves at the end of the
// microtask. That causes animations to "overlap" between the time we start a
// new animation and the end of the microtask. By the time the microtask is
// over and we check whether to deliver an onScrollEnd callback, we will have
// started the new animation (having skipped the onScrollStart) and therefore
// we won't deliver the onScrollEnd until the second animation is finished.
int
_numberOfInProgressScrolls
=
0
;
/// Calls the onScroll callback.
///
/// Subclasses can override this method to hook the scroll callback.
void
dispatchOnScroll
()
{
assert
(
_numberOfInProgressScrolls
>
0
);
if
(
config
.
onScroll
!=
null
)
config
.
onScroll
(
_scrollOffset
);
}
void
_handleDragDown
(
DragDownDetails
details
)
{
setState
(()
{
_stop
();
});
}
void
_stop
()
{
assert
(
mounted
);
assert
(
_controller
.
isAnimating
||
_simulation
==
null
);
_controller
.
stop
();
// this does not trigger a status notification
_simulation
=
null
;
}
void
_handleDragStart
(
DragStartDetails
details
)
{
_startScroll
(
details:
details
);
}
void
_startScroll
({
DragStartDetails
details
})
{
_numberOfInProgressScrolls
+=
1
;
if
(
_numberOfInProgressScrolls
==
1
)
{
dispatchOnScrollStart
();
new
ScrollNotification
(
scrollable:
this
,
kind:
ScrollNotificationKind
.
started
,
details:
details
).
dispatch
(
context
);
}
}
/// Calls the onScrollStart callback.
///
/// Subclasses can override this method to hook the scroll start callback.
void
dispatchOnScrollStart
()
{
assert
(
_numberOfInProgressScrolls
==
1
);
if
(
config
.
onScrollStart
!=
null
)
config
.
onScrollStart
(
_scrollOffset
);
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
scrollBy
(
pixelOffsetToScrollOffset
(
details
.
primaryDelta
),
details:
details
);
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
final
double
scrollVelocity
=
pixelDeltaToScrollOffset
(
details
.
velocity
.
pixelsPerSecond
);
fling
(
scrollVelocity
).
then
<
Null
>((
Null
value
)
{
_endScroll
(
details:
details
);
});
}
// Used for state changes that sometimes occur during a build phase. If so,
// we skip calling setState, as the changes will apply to the next build.
// TODO(ianh): This is ugly and hopefully temporary. Ideally this won't be
// needed after Scrollable is rewritten.
void
_setStateMaybeDuringBuild
(
VoidCallback
fn
)
{
if
(
SchedulerBinding
.
instance
.
schedulerPhase
==
SchedulerPhase
.
persistentCallbacks
)
{
fn
();
}
else
{
setState
(
fn
);
}
}
void
_endScroll
({
DragEndDetails
details
})
{
_numberOfInProgressScrolls
-=
1
;
if
(
_numberOfInProgressScrolls
==
0
)
{
_simulation
=
null
;
if
(
_scrollUnderway
&&
mounted
)
{
// If the scroll hasn't already stopped because we've hit a clamped
// edge or the controller stopped animating, then rebuild the Scrollable
// with the IgnorePointer widget turned off.
_setStateMaybeDuringBuild
(()
{
_scrollUnderway
=
false
;
});
}
dispatchOnScrollEnd
();
if
(
mounted
)
{
new
ScrollNotification
(
scrollable:
this
,
kind:
ScrollNotificationKind
.
ended
,
details:
details
).
dispatch
(
context
);
}
}
}
/// Calls the dispatchOnScrollEnd callback.
///
/// Subclasses can override this method to hook the scroll end callback.
void
dispatchOnScrollEnd
()
{
assert
(
_numberOfInProgressScrolls
==
0
);
if
(
config
.
onScrollEnd
!=
null
)
config
.
onScrollEnd
(
_scrollOffset
);
}
final
GlobalKey
<
RawGestureDetectorState
>
_gestureDetectorKey
=
new
GlobalKey
<
RawGestureDetectorState
>();
@override
Widget
build
(
BuildContext
context
)
{
Widget
result
=
new
RawGestureDetector
(
key:
_gestureDetectorKey
,
gestures:
buildGestureDetectors
(),
behavior:
HitTestBehavior
.
opaque
,
child:
new
IgnorePointer
(
ignoring:
_scrollUnderway
,
child:
buildContent
(
context
),
),
);
if
(
debugHighlightDeprecatedWidgets
)
{
result
=
new
Banner
(
message:
'OLD'
,
color:
const
Color
(
0xFF009000
),
location:
BannerLocation
.
bottomRight
,
child:
result
,
);
}
return
result
;
}
/// Fixes up the gesture detector to listen to the appropriate
/// gestures based on the current information about the layout.
///
/// This method should be called from the
/// [onPaintOffsetUpdateNeeded] or [onExtentsChanged] handler given
/// to the [Viewport] or equivalent used by the subclass's
/// [buildContent] method. See the [buildContent] method's
/// description for details.
void
updateGestureDetector
()
{
_gestureDetectorKey
.
currentState
.
replaceGestureRecognizers
(
buildGestureDetectors
());
}
/// Return the gesture detectors, in the form expected by
/// [RawGestureDetector.gestures] and
/// [RawGestureDetectorState.replaceGestureRecognizers], that are
/// applicable to this [Scrollable] in its current state.
///
/// This is called by [build] and [updateGestureDetector].
Map
<
Type
,
GestureRecognizerFactory
>
buildGestureDetectors
()
{
if
(
scrollBehavior
.
isScrollable
)
{
switch
(
config
.
scrollDirection
)
{
case
Axis
.
vertical
:
return
<
Type
,
GestureRecognizerFactory
>{
VerticalDragGestureRecognizer:
(
VerticalDragGestureRecognizer
recognizer
)
{
// ignore: map_value_type_not_assignable, https://github.com/flutter/flutter/issues/5771
return
(
recognizer
??=
new
VerticalDragGestureRecognizer
())
..
onDown
=
_handleDragDown
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
;
}
};
case
Axis
.
horizontal
:
return
<
Type
,
GestureRecognizerFactory
>{
HorizontalDragGestureRecognizer:
(
HorizontalDragGestureRecognizer
recognizer
)
{
// ignore: map_value_type_not_assignable, https://github.com/flutter/flutter/issues/5771
return
(
recognizer
??=
new
HorizontalDragGestureRecognizer
())
..
onDown
=
_handleDragDown
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
;
}
};
}
}
return
const
<
Type
,
GestureRecognizerFactory
>{};
}
/// Calls the widget's [builder] by default.
///
/// Subclasses can override this method to build the interior of their
/// scrollable widget. Scrollable wraps the returned widget in a
/// [GestureDetector] to observe the user's interaction with this widget and
/// to adjust the scroll offset accordingly.
///
/// The widgets used by this method should be widgets that provide a
/// layout-time callback that reports the sizes that are relevant to
/// the scroll offset (typically the size of the scrollable
/// container and the scrolled contents). [Viewport] provides an
/// [onPaintOffsetUpdateNeeded] callback for this purpose; [GridViewport],
/// [ListViewport], [LazyListViewport], and [LazyBlockViewport] provide an
/// [onExtentsChanged] callback for this purpose.
///
/// This callback should be used to update the scroll behavior, if
/// necessary, and then to call [updateGestureDetector] to update
/// the gesture detectors accordingly.
Widget
buildContent
(
BuildContext
context
)
{
assert
(
config
.
builder
!=
null
);
return
config
.
builder
(
context
,
this
);
}
}
/// Indicates if a [ScrollNotification] indicates the start, end or the
/// middle of a scroll.
enum
ScrollNotificationKind
{
/// The [ScrollNotification] indicates that the scrollOffset has been changed
/// and no existing scroll is underway.
started
,
/// The [ScrollNotification] indicates that the scrollOffset has been changed.
updated
,
/// The [ScrollNotification] indicates that the scrollOffset has stopped changing.
/// This may be because the fling animation that follows a drag gesture has
/// completed or simply because the scrollOffset was reset.
ended
}
/// Indicates that a scrollable descendant is scrolling.
///
/// See also:
///
/// * [NotificationListener].
class
ScrollNotification
extends
Notification
{
/// Creates a notification about scrolling.
ScrollNotification
({
this
.
scrollable
,
this
.
kind
,
dynamic
details
})
:
_details
=
details
{
assert
(
scrollable
!=
null
);
assert
(
kind
!=
null
);
assert
(
details
==
null
||
(
kind
==
ScrollNotificationKind
.
started
&&
details
is
DragStartDetails
)
||
(
kind
==
ScrollNotificationKind
.
updated
&&
details
is
DragUpdateDetails
)
||
(
kind
==
ScrollNotificationKind
.
ended
&&
details
is
DragEndDetails
));
}
/// Indicates if we're at the start, middle, or end of a scroll.
final
ScrollNotificationKind
kind
;
/// The scrollable that scrolled.
final
ScrollableState
scrollable
;
/// The details from the underlying [DragGestureRecognizer] gesture, if the
/// notification ultimately came from a [DragGestureRecognizer.onStart]
/// handler; otherwise null.
DragStartDetails
get
dragStartDetails
=>
kind
==
ScrollNotificationKind
.
started
?
_details
:
null
;
/// The details from the underlying [DragGestureRecognizer] gesture, if the
/// notification ultimately came from a [DragGestureRecognizer.onUpdate]
/// handler; otherwise null.
DragUpdateDetails
get
dragUpdateDetails
=>
kind
==
ScrollNotificationKind
.
updated
?
_details
:
null
;
/// The details from the underlying [DragGestureRecognizer] gesture, if the
/// notification ultimately came from a [DragGestureRecognizer.onEnd]
/// handler; otherwise null.
DragEndDetails
get
dragEndDetails
=>
kind
==
ScrollNotificationKind
.
ended
?
_details
:
null
;
final
dynamic
_details
;
/// The number of scrollable widgets that have already received this
/// notification. Typically listeners only respond to notifications
/// with depth = 0.
int
get
depth
=>
_depth
;
int
_depth
=
0
;
@override
bool
visitAncestor
(
Element
element
)
{
if
(
element
is
StatefulElement
&&
element
.
state
is
ScrollableState
)
_depth
+=
1
;
return
super
.
visitAncestor
(
element
);
}
}
/// A simple scrolling widget that has a single child.
///
/// Use this widget if you are not worried about offscreen widgets consuming
/// resources.
///
/// See also:
///
/// * [Block], if your single child is a [Column].
/// * [ScrollableList], if you have many identically-sized children.
/// * [GridView], if your children are in a grid pattern.
/// * [LazyBlock], if you have many children of varying sizes.
@Deprecated
(
'use SingleChildScrollView'
)
class
ScrollableViewport
extends
StatelessWidget
{
/// Creates a simple scrolling widget that has a single child.
///
/// The [scrollDirection] and [scrollAnchor] arguments must not be null.
ScrollableViewport
({
Key
key
,
this
.
initialScrollOffset
,
this
.
scrollDirection
:
Axis
.
vertical
,
this
.
scrollAnchor
:
ViewportAnchor
.
start
,
this
.
onScrollStart
,
this
.
onScroll
,
this
.
onScrollEnd
,
this
.
snapOffsetCallback
,
this
.
scrollableKey
,
this
.
child
})
:
super
(
key:
key
)
{
assert
(
scrollDirection
!=
null
);
assert
(
scrollAnchor
!=
null
);
}
// Warning: keep the dartdoc comments that follow in sync with the copies in
// Scrollable, LazyBlock, ScrollableLazyList, and ScrollableList.
// And see: https://github.com/dart-lang/dartdoc/issues/1161.
/// The scroll offset this widget should use when first created.
final
double
initialScrollOffset
;
/// The axis along which this widget should scroll.
final
Axis
scrollDirection
;
/// Whether to place first child at the start of the container or
/// the last child at the end of the container, when the scrollable
/// has not been scrolled and has no initial scroll offset.
///
/// For example, if the [scrollDirection] is [Axis.vertical] and
/// there are enough items to overflow the container, then
/// [ViewportAnchor.start] means that the top of the first item
/// should be aligned with the top of the scrollable with the last
/// item below the bottom, and [ViewportAnchor.end] means the bottom
/// of the last item should be aligned with the bottom of the
/// scrollable, with the first item above the top.
///
/// This also affects whether, when an item is added or removed, the
/// displacement will be towards the first item or the last item.
/// Continuing the earlier example, if a new item is inserted in the
/// middle of the list, in the [ViewportAnchor.start] case the items
/// after it (with greater indices, down to the item with the
/// highest index) will be pushed down, while in the
/// [ViewportAnchor.end] case the items before it (with lower
/// indices, up to the item with the index 0) will be pushed up.
final
ViewportAnchor
scrollAnchor
;
/// Called whenever this widget starts to scroll.
final
ScrollListener
onScrollStart
;
/// Called whenever this widget's scroll offset changes.
final
ScrollListener
onScroll
;
/// Called whenever this widget stops scrolling.
final
ScrollListener
onScrollEnd
;
/// Called to determine the offset to which scrolling should snap,
/// when handling a fling.
///
/// This callback, if set, will be called with the offset that the
/// Scrollable would have scrolled to in the absence of this
/// callback, and a Size describing the size of the Scrollable
/// itself.
///
/// The callback's return value is used as the new scroll offset to
/// aim for.
///
/// If the callback simply returns its first argument (the offset),
/// then it is as if the callback was null.
final
SnapOffsetCallback
snapOffsetCallback
;
/// The key for the Scrollable created by this widget.
final
Key
scrollableKey
;
/// The widget that will be scrolled. It will become the child of a Scrollable.
final
Widget
child
;
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
)
{
return
new
Viewport
(
paintOffset:
state
.
scrollOffsetToPixelDelta
(
state
.
scrollOffset
),
mainAxis:
scrollDirection
,
anchor:
scrollAnchor
,
onPaintOffsetUpdateNeeded:
(
ViewportDimensions
dimensions
)
{
final
double
contentExtent
=
scrollDirection
==
Axis
.
vertical
?
dimensions
.
contentSize
.
height
:
dimensions
.
contentSize
.
width
;
final
double
containerExtent
=
scrollDirection
==
Axis
.
vertical
?
dimensions
.
containerSize
.
height
:
dimensions
.
containerSize
.
width
;
state
.
handleExtentsChanged
(
contentExtent
,
containerExtent
);
return
state
.
scrollOffsetToPixelDelta
(
state
.
scrollOffset
);
},
child:
child
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
result
=
new
Scrollable
(
key:
scrollableKey
,
initialScrollOffset:
initialScrollOffset
,
scrollDirection:
scrollDirection
,
scrollAnchor:
scrollAnchor
,
onScrollStart:
onScrollStart
,
onScroll:
onScroll
,
onScrollEnd:
onScrollEnd
,
snapOffsetCallback:
snapOffsetCallback
,
builder:
_buildViewport
);
return
ScrollConfiguration
.
wrap
(
context
,
result
);
}
}
/// A scrolling list of variably-sized children.
///
/// Useful when you have a small, fixed number of children that you wish to
/// arrange in a block layout and that might exceed the height of its container
/// (and therefore need to scroll).
///
/// If you have a large number of children, or if you always expect this to need
/// to scroll, consider using [LazyBlock] (if the children have variable height)
/// or [ScrollableList] (if the children all have the same fixed height), as
/// they avoid doing work for children that are not visible.
///
/// This widget is implemented using [ScrollableViewport] and [BlockBody]. If
/// you have a single child, consider using [ScrollableViewport] directly.
///
/// See also:
///
/// * [LazyBlock], if you have many children with varying heights.
/// * [ScrollableList], if all your children are the same height.
/// * [ScrollableViewport], if you only have one child.
@Deprecated
(
'use ListView instead'
)
class
Block
extends
StatelessWidget
{
/// Creates a scrollable array of children.
Block
({
Key
key
,
this
.
children
:
const
<
Widget
>[],
this
.
padding
,
this
.
initialScrollOffset
,
this
.
scrollDirection
:
Axis
.
vertical
,
this
.
scrollAnchor
:
ViewportAnchor
.
start
,
this
.
onScrollStart
,
this
.
onScroll
,
this
.
onScrollEnd
,
this
.
scrollableKey
})
:
super
(
key:
key
)
{
assert
(
children
!=
null
);
assert
(!
children
.
any
((
Widget
child
)
=>
child
==
null
));
}
/// The children, all of which are materialized.
final
List
<
Widget
>
children
;
/// The amount of space by which to inset the children inside the viewport.
final
EdgeInsets
padding
;
/// The scroll offset this widget should use when first created.
final
double
initialScrollOffset
;
/// The axis along which this widget should scroll.
final
Axis
scrollDirection
;
/// Whether to place first child at the start of the container or
/// the last child at the end of the container, when the scrollable
/// has not been scrolled and has no initial scroll offset.
///
/// For example, if the [scrollDirection] is [Axis.vertical] and
/// there are enough items to overflow the container, then
/// [ViewportAnchor.start] means that the top of the first item
/// should be aligned with the top of the scrollable with the last
/// item below the bottom, and [ViewportAnchor.end] means the bottom
/// of the last item should be aligned with the bottom of the
/// scrollable, with the first item above the top.
///
/// This also affects whether, when an item is added or removed, the
/// displacement will be towards the first item or the last item.
/// Continuing the earlier example, if a new item is inserted in the
/// middle of the list, in the [ViewportAnchor.start] case the items
/// after it (with greater indices, down to the item with the
/// highest index) will be pushed down, while in the
/// [ViewportAnchor.end] case the items before it (with lower
/// indices, up to the item with the index 0) will be pushed up.
final
ViewportAnchor
scrollAnchor
;
/// Called whenever this widget starts to scroll.
final
ScrollListener
onScrollStart
;
/// Called whenever this widget's scroll offset changes.
final
ScrollListener
onScroll
;
/// Called whenever this widget stops scrolling.
final
ScrollListener
onScrollEnd
;
/// The key to use for the underlying scrollable widget.
final
Key
scrollableKey
;
@override
Widget
build
(
BuildContext
context
)
{
Widget
contents
=
new
BlockBody
(
children:
children
,
mainAxis:
scrollDirection
);
if
(
padding
!=
null
)
contents
=
new
Padding
(
padding:
padding
,
child:
contents
);
return
new
ScrollableViewport
(
scrollableKey:
scrollableKey
,
initialScrollOffset:
initialScrollOffset
,
scrollDirection:
scrollDirection
,
scrollAnchor:
scrollAnchor
,
onScrollStart:
onScrollStart
,
onScroll:
onScroll
,
onScrollEnd:
onScrollEnd
,
child:
contents
);
}
}
packages/flutter/lib/widgets.dart
View file @
90574b04
...
...
@@ -14,7 +14,6 @@ export 'src/widgets/async.dart';
export
'src/widgets/banner.dart'
;
export
'src/widgets/basic.dart'
;
export
'src/widgets/binding.dart'
;
export
'src/widgets/clamp_overscrolls.dart'
;
export
'src/widgets/container.dart'
;
export
'src/widgets/debug.dart'
;
export
'src/widgets/dismissable.dart'
;
...
...
@@ -45,7 +44,6 @@ export 'src/widgets/placeholder.dart';
export
'src/widgets/primary_scroll_controller.dart'
;
export
'src/widgets/raw_keyboard_listener.dart'
;
export
'src/widgets/routes.dart'
;
export
'src/widgets/scroll_behavior.dart'
;
export
'src/widgets/scroll_configuration.dart'
;
export
'src/widgets/scroll_controller.dart'
;
export
'src/widgets/scroll_notification.dart'
;
...
...
packages/flutter/test/material/app_bar_test.dart
View file @
90574b04
...
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
...
...
packages/flutter/test/material/scaffold_test.dart
View file @
90574b04
...
...
@@ -78,8 +78,7 @@ void main() {
});
testWidgets
(
'Drawer scrolling'
,
(
WidgetTester
tester
)
async
{
GlobalKey
<
ScrollableState
<
Scrollable
>>
drawerKey
=
new
GlobalKey
<
ScrollableState
<
Scrollable
>>(
debugLabel:
'drawer'
);
Key
drawerKey
=
new
UniqueKey
();
const
double
appBarHeight
=
256.0
;
ScrollController
scrollOffset
=
new
ScrollController
();
...
...
packages/flutter/test/material/stepper_test.dart
View file @
90574b04
...
...
@@ -313,7 +313,7 @@ void main() {
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
scrollableState
.
position
.
pixels
,
greaterThan
(
0.0
));
}
,
skip:
Scrollable
!=
Scrollable2
);
// TODO(abarth): re-enable when ensureVisible is implemented
}
);
testWidgets
(
'Stepper index test'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
...
...
packages/flutter/test/material/two_level_list_test.dart
View file @
90574b04
...
...
@@ -33,7 +33,7 @@ void main() {
final
Map
<
String
,
WidgetBuilder
>
routes
=
<
String
,
WidgetBuilder
>{
'/'
:
(
_
)
{
return
new
Material
(
child:
new
Viewport
(
child:
new
SingleChildScrollView
(
child:
new
TwoLevelList
(
children:
<
Widget
>[
new
TwoLevelListItem
(
title:
new
Text
(
'Top'
),
key:
topKey
),
...
...
@@ -91,7 +91,7 @@ void main() {
final
Map
<
String
,
WidgetBuilder
>
routes
=
<
String
,
WidgetBuilder
>{
'/'
:
(
_
)
{
return
new
Material
(
child:
new
Viewport
(
child:
new
SingleChildScrollView
(
child:
new
TwoLevelList
(
children:
<
Widget
>[
new
TwoLevelSublist
(
...
...
packages/flutter/test/physics/newton_test.dart
View file @
90574b04
...
...
@@ -206,26 +206,24 @@ void main() {
SpringDescription
spring
=
new
SpringDescription
.
withDampingRatio
(
mass:
1.0
,
springConstant:
50.0
,
ratio:
0.5
);
ScrollSimulation
scroll
=
new
ScrollSimulation
(
BouncingScrollSimulation
scroll
=
new
Bouncing
ScrollSimulation
(
position:
100.0
,
velocity:
800.0
,
leadingExtent:
0.0
,
trailingExtent:
300.0
,
spring:
spring
,
drag:
0.3
);
scroll
.
tolerance
=
const
Tolerance
(
velocity:
0.5
,
distance:
0.1
);
expect
(
scroll
.
isDone
(
0.0
),
false
);
expect
(
scroll
.
isDone
(
0.5
),
false
);
// switch from friction to spring
expect
(
scroll
.
isDone
(
3.5
),
true
);
ScrollSimulation
scroll2
=
new
ScrollSimulation
(
BouncingScrollSimulation
scroll2
=
new
Bouncing
ScrollSimulation
(
position:
100.0
,
velocity:
-
800.0
,
leadingExtent:
0.0
,
trailingExtent:
300.0
,
spring:
spring
,
drag:
0.3
);
scroll2
.
tolerance
=
const
Tolerance
(
velocity:
0.5
,
distance:
0.1
);
expect
(
scroll2
.
isDone
(
0.0
),
false
);
...
...
@@ -237,13 +235,12 @@ void main() {
SpringDescription
spring
=
new
SpringDescription
.
withDampingRatio
(
mass:
1.0
,
springConstant:
50.0
,
ratio:
0.5
);
ScrollSimulation
scroll
=
new
ScrollSimulation
(
BouncingScrollSimulation
scroll
=
new
Bouncing
ScrollSimulation
(
position:
100.0
,
velocity:
400.0
,
leadingExtent:
0.0
,
trailingExtent:
double
.
INFINITY
,
spring:
spring
,
drag:
0.3
,
);
scroll
.
tolerance
=
const
Tolerance
(
velocity:
1.0
);
...
...
@@ -251,15 +248,14 @@ void main() {
expect
(
scroll
.
x
(
0.0
),
100
);
expect
(
scroll
.
dx
(
0.0
),
400.0
);
expect
(
scroll
.
x
(
1.0
)
>
330
&&
scroll
.
x
(
1.0
)
<
335
,
true
);
expect
(
scroll
.
x
(
1.0
)
,
closeTo
(
272.0
,
1.0
)
);
expect
(
scroll
.
dx
(
1.0
),
120.0
);
expect
(
scroll
.
dx
(
2.0
),
36.0
);
expect
(
scroll
.
dx
(
3.0
),
10.8
);
expect
(
scroll
.
dx
(
4.0
)
<
3.5
,
true
);
expect
(
scroll
.
dx
(
1.0
),
closeTo
(
54.0
,
1.0
));
expect
(
scroll
.
dx
(
2.0
),
closeTo
(
7.0
,
1.0
));
expect
(
scroll
.
dx
(
3.0
),
lessThan
(
1.0
));
expect
(
scroll
.
isDone
(
5.0
),
true
);
expect
(
scroll
.
x
(
5.0
)
>
431
&&
scroll
.
x
(
5.0
)
<
432
,
true
);
expect
(
scroll
.
x
(
5.0
)
,
closeTo
(
300.0
,
1.0
)
);
// We should never switch
expect
(
scroll
.
currentIntervalOffset
,
0.0
);
...
...
@@ -267,13 +263,12 @@ void main() {
test
(
'over/under scroll spring'
,
()
{
SpringDescription
spring
=
new
SpringDescription
.
withDampingRatio
(
mass:
1.0
,
springConstant:
170.0
,
ratio:
1.1
);
ScrollSimulation
scroll
=
new
ScrollSimulation
(
BouncingScrollSimulation
scroll
=
new
Bouncing
ScrollSimulation
(
position:
500.0
,
velocity:
-
7500.0
,
leadingExtent:
0.0
,
trailingExtent:
1000.0
,
spring:
spring
,
drag:
0.025
,
);
scroll
.
tolerance
=
const
Tolerance
(
velocity:
45.0
,
distance:
1.5
);
...
...
@@ -282,8 +277,8 @@ void main() {
expect
(
scroll
.
dx
(
0.0
),
closeTo
(-
7500.0
,
.
0001
));
expect
(
scroll
.
isDone
(
0.025
),
false
);
expect
(
scroll
.
x
(
0.025
),
closeTo
(
3
20
.0
,
1.0
));
expect
(
scroll
.
dx
(
0.25
),
closeTo
(-
2982
,
1.0
));
expect
(
scroll
.
x
(
0.025
),
closeTo
(
3
17
.0
,
1.0
));
expect
(
scroll
.
dx
(
0.25
),
closeTo
(-
4546
,
1.0
));
expect
(
scroll
.
isDone
(
2.0
),
true
);
expect
(
scroll
.
x
(
2.0
),
0.0
);
...
...
packages/flutter/test/rendering/independent_layout_test.dart
View file @
90574b04
...
...
@@ -10,17 +10,19 @@ import 'rendering_tester.dart';
class
TestLayout
{
TestLayout
()
{
// viewport incoming constraints are tight 800x600
// viewport is vertical by default
root
=
new
RenderViewport
(
child:
new
RenderCustomPaint
(
painter:
new
TestCallbackPainter
(
onPaint:
()
{
painted
=
true
;
}
// incoming constraints are tight 800x600
root
=
new
RenderPositionedBox
(
child:
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
width:
800.0
),
child:
new
RenderCustomPaint
(
painter:
new
TestCallbackPainter
(
onPaint:
()
{
painted
=
true
;
}
),
child:
child
=
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
10.0
,
width:
10.0
),
),
),
child:
child
=
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
10.0
,
width:
10.0
)
)
)
),
);
}
RenderBox
root
;
...
...
packages/flutter/test/rendering/offstage_test.dart
View file @
90574b04
...
...
@@ -12,19 +12,21 @@ void main() {
test
(
"offstage"
,
()
{
RenderBox
child
;
bool
painted
=
false
;
// viewport incoming constraints are tight 800x600
// viewport is vertical by default
RenderBox
root
=
new
RenderViewport
(
child:
new
RenderOffstage
(
child:
new
RenderCustomPaint
(
painter:
new
TestCallbackPainter
(
onPaint:
()
{
painted
=
true
;
}
// incoming constraints are tight 800x600
RenderBox
root
=
new
RenderPositionedBox
(
child:
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
width:
800.0
),
child:
new
RenderOffstage
(
child:
new
RenderCustomPaint
(
painter:
new
TestCallbackPainter
(
onPaint:
()
{
painted
=
true
;
},
),
child:
child
=
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
10.0
,
width:
10.0
),
),
),
child:
child
=
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
10.0
,
width:
10.0
)
)
)
)
),
),
);
expect
(
child
.
hasSize
,
isFalse
);
expect
(
painted
,
isFalse
);
...
...
packages/flutter/test/rendering/reattach_test.dart
View file @
90574b04
...
...
@@ -10,30 +10,32 @@ import 'rendering_tester.dart';
class
TestTree
{
TestTree
()
{
// viewport incoming constraints are tight 800x600
// viewport is vertical by default
root
=
new
RenderViewport
(
// Place the child to be evaluated within both a repaint boundary and a
// layout-root element (in this case a tightly constrained box). Otherwise
// the act of transplanting the root into a new container will cause the
// relayout/repaint of the new parent node to satisfy the test.
child:
new
RenderRepaintBoundary
(
child:
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
20.0
,
width:
20.0
),
child:
new
RenderRepaintBoundary
(
child:
new
RenderCustomPaint
(
painter:
new
TestCallbackPainter
(
onPaint:
()
{
painted
=
true
;
}
// incoming constraints are tight 800x600
root
=
new
RenderPositionedBox
(
child:
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
width:
800.0
),
// Place the child to be evaluated within both a repaint boundary and a
// layout-root element (in this case a tightly constrained box). Otherwise
// the act of transplanting the root into a new container will cause the
// relayout/repaint of the new parent node to satisfy the test.
child:
new
RenderRepaintBoundary
(
child:
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
20.0
,
width:
20.0
),
child:
new
RenderRepaintBoundary
(
child:
new
RenderCustomPaint
(
painter:
new
TestCallbackPainter
(
onPaint:
()
{
painted
=
true
;
},
),
child:
new
RenderPositionedBox
(
child:
child
=
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
20.0
,
width:
20.0
),
),
),
),
child:
new
RenderPositionedBox
(
child:
child
=
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
20.0
,
width:
20.0
)
)
)
)
)
)
)
),
),
),
),
);
}
RenderObject
root
;
...
...
@@ -50,24 +52,26 @@ class MutableCompositor extends RenderProxyBox {
class
TestCompositingBitsTree
{
TestCompositingBitsTree
()
{
// viewport incoming constraints are tight 800x600
// viewport is vertical by default
root
=
new
RenderViewport
(
// Place the child to be evaluated within a repaint boundary. Otherwise
// the act of transplanting the root into a new container will cause the
// repaint of the new parent node to satisfy the test.
child:
new
RenderRepaintBoundary
(
child:
compositor
=
new
MutableCompositor
(
child:
new
RenderCustomPaint
(
painter:
new
TestCallbackPainter
(
onPaint:
()
{
painted
=
true
;
}
// incoming constraints are tight 800x600
root
=
new
RenderPositionedBox
(
child:
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
width:
800.0
),
// Place the child to be evaluated within a repaint boundary. Otherwise
// the act of transplanting the root into a new container will cause the
// repaint of the new parent node to satisfy the test.
child:
new
RenderRepaintBoundary
(
child:
compositor
=
new
MutableCompositor
(
child:
new
RenderCustomPaint
(
painter:
new
TestCallbackPainter
(
onPaint:
()
{
painted
=
true
;
},
),
child:
child
=
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
20.0
,
width:
20.0
)
),
),
child:
child
=
new
RenderConstrainedBox
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
height:
20.0
,
width:
20.0
)
)
)
)
)
),
),
),
);
}
RenderObject
root
;
...
...
packages/flutter/test/rendering/viewport_test.dart
deleted
100644 → 0
View file @
9ec5330f
// 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/rendering.dart'
;
import
'package:test/test.dart'
;
import
'rendering_tester.dart'
;
void
main
(
)
{
test
(
'Should be able to hit with positive paint offset'
,
()
{
RenderBox
green
=
new
RenderDecoratedBox
(
decoration:
const
BoxDecoration
(
backgroundColor:
const
Color
(
0xFF00FF00
)
));
RenderBox
size
=
new
RenderConstrainedBox
(
additionalConstraints:
new
BoxConstraints
.
tight
(
const
Size
(
100.0
,
100.0
)),
child:
green
);
RenderBox
red
=
new
RenderDecoratedBox
(
decoration:
const
BoxDecoration
(
backgroundColor:
const
Color
(
0xFFFF0000
)
),
child:
size
);
RenderViewport
viewport
=
new
RenderViewport
(
child:
red
,
paintOffset:
const
Offset
(
0.0
,
10.0
));
layout
(
viewport
);
HitTestResult
result
;
result
=
new
HitTestResult
();
renderer
.
renderView
.
hitTest
(
result
,
position:
const
Point
(
15.0
,
0.0
));
expect
(
result
.
path
.
first
.
target
.
runtimeType
,
equals
(
RenderView
));
result
=
new
HitTestResult
();
renderer
.
renderView
.
hitTest
(
result
,
position:
const
Point
(
15.0
,
15.0
));
expect
(
result
.
path
.
first
.
target
,
equals
(
green
));
});
}
packages/flutter/test/widgets/aspect_ratio_test.dart
View file @
90574b04
...
...
@@ -35,8 +35,8 @@ void main() {
Key
childKey
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
Center
(
child:
new
Viewport
(
mainAxis
:
Axis
.
horizontal
,
child:
new
SingleChildScrollView
(
scrollDirection
:
Axis
.
horizontal
,
child:
new
AspectRatio
(
aspectRatio:
2.0
,
child:
new
Container
(
...
...
packages/flutter_driver/lib/src/extension.dart
View file @
90574b04
...
...
@@ -248,7 +248,6 @@ class _FlutterDriverExtension {
Future
<
ScrollResult
>
_scrollIntoView
(
Command
command
)
async
{
ScrollIntoView
scrollIntoViewCommand
=
command
;
Finder
target
=
await
_waitForElement
(
_createFinder
(
scrollIntoViewCommand
.
finder
));
await
Scrollable
.
ensureVisible
(
target
.
evaluate
().
single
,
duration:
const
Duration
(
milliseconds:
100
));
await
Scrollable2
.
ensureVisible
(
target
.
evaluate
().
single
,
duration:
const
Duration
(
milliseconds:
100
),
alignment:
scrollIntoViewCommand
.
alignment
??
0.0
);
return
new
ScrollResult
();
}
...
...
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