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
c3a8df1d
Commit
c3a8df1d
authored
Jul 20, 2015
by
Hans Muller
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #122 from HansMuller/shrinking-card
Card Collection dismiss animation
parents
94a4b972
b954e020
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
328 additions
and
135 deletions
+328
-135
block_viewport.dart
packages/flutter/example/widgets/block_viewport.dart
+3
-1
card_collection.dart
packages/flutter/example/widgets/card_collection.dart
+111
-27
block_viewport.dart
packages/flutter/lib/widgets/block_viewport.dart
+185
-93
card.dart
packages/flutter/lib/widgets/card.dart
+3
-1
variable_height_scrollable.dart
packages/flutter/lib/widgets/variable_height_scrollable.dart
+26
-13
No files found.
packages/flutter/example/widgets/block_viewport.dart
View file @
c3a8df1d
...
@@ -15,6 +15,7 @@ import 'package:sky/widgets/widget.dart';
...
@@ -15,6 +15,7 @@ import 'package:sky/widgets/widget.dart';
class
BlockViewportApp
extends
App
{
class
BlockViewportApp
extends
App
{
BlockViewportLayoutState
layoutState
=
new
BlockViewportLayoutState
();
List
<
double
>
lengths
=
<
double
>[];
List
<
double
>
lengths
=
<
double
>[];
double
offset
=
0.0
;
double
offset
=
0.0
;
...
@@ -96,7 +97,8 @@ class BlockViewportApp extends App {
...
@@ -96,7 +97,8 @@ class BlockViewportApp extends App {
child:
new
BlockViewport
(
child:
new
BlockViewport
(
builder:
builder
,
builder:
builder
,
startOffset:
offset
,
startOffset:
offset
,
token:
lengths
.
length
token:
lengths
.
length
,
layoutState:
layoutState
)
)
)
)
),
),
...
...
packages/flutter/example/widgets/card_collection.dart
View file @
c3a8df1d
...
@@ -2,62 +2,145 @@
...
@@ -2,62 +2,145 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:sky/animation/animation_performance.dart'
;
import
'package:sky/animation/curves.dart'
;
import
'package:sky/base/lerp.dart'
;
import
'package:sky/base/lerp.dart'
;
import
'package:sky/painting/text_style.dart'
;
import
'package:sky/painting/text_style.dart'
;
import
'package:sky/theme/colors.dart'
;
import
'package:sky/theme/colors.dart'
;
import
'package:sky/widgets/animated_component.dart'
;
import
'package:sky/widgets/basic.dart'
;
import
'package:sky/widgets/basic.dart'
;
import
'package:sky/widgets/block_viewport.dart'
;
import
'package:sky/widgets/card.dart'
;
import
'package:sky/widgets/card.dart'
;
import
'package:sky/widgets/dismissable.dart'
;
import
'package:sky/widgets/dismissable.dart'
;
import
'package:sky/widgets/scaffold.dart'
;
import
'package:sky/widgets/variable_height_scrollable.dart'
;
import
'package:sky/widgets/variable_height_scrollable.dart'
;
import
'package:sky/widgets/scaffold.dart'
;
import
'package:sky/widgets/theme.dart'
;
import
'package:sky/widgets/theme.dart'
;
import
'package:sky/widgets/tool_bar.dart'
;
import
'package:sky/widgets/tool_bar.dart'
;
import
'package:sky/widgets/widget.dart'
;
import
'package:sky/widgets/widget.dart'
;
import
'package:sky/theme/colors.dart'
as
colors
;
import
'package:sky/widgets/task_description.dart'
;
import
'package:sky/widgets/task_description.dart'
;
class
CardModel
{
CardModel
(
this
.
value
,
this
.
height
,
this
.
color
);
int
value
;
double
height
;
Color
color
;
AnimationPerformance
performance
;
String
get
label
=>
"Item
$value
"
;
String
get
key
=>
value
.
toString
();
bool
operator
==(
other
)
=>
other
is
CardModel
&&
other
.
value
==
value
;
int
get
hashCode
=>
373
*
37
*
value
.
hashCode
;
}
class
ShrinkingCard
extends
AnimatedComponent
{
ShrinkingCard
({
String
key
,
CardModel
this
.
card
,
Function
this
.
onUpdated
,
Function
this
.
onCompleted
})
:
super
(
key:
key
);
CardModel
card
;
Function
onUpdated
;
Function
onCompleted
;
double
get
currentHeight
=>
card
.
performance
.
variable
.
value
;
void
initState
()
{
assert
(
card
.
performance
!=
null
);
card
.
performance
.
addListener
(
handleAnimationProgress
);
watch
(
card
.
performance
);
}
void
handleAnimationProgress
()
{
if
(
card
.
performance
.
isCompleted
)
{
if
(
onCompleted
!=
null
)
onCompleted
();
}
else
if
(
onUpdated
!=
null
)
{
onUpdated
();
}
}
void
syncFields
(
ShrinkingCard
source
)
{
card
=
source
.
card
;
onCompleted
=
source
.
onCompleted
;
onUpdated
=
source
.
onUpdated
;
super
.
syncFields
(
source
);
}
Widget
build
()
=>
new
Container
(
height:
currentHeight
);
}
class
CardCollectionApp
extends
App
{
class
CardCollectionApp
extends
App
{
final
TextStyle
cardLabelStyle
=
final
TextStyle
cardLabelStyle
=
new
TextStyle
(
color:
white
,
fontSize:
18.0
,
fontWeight:
bold
);
new
TextStyle
(
color:
white
,
fontSize:
18.0
,
fontWeight:
bold
);
final
List
<
double
>
cardHeights
=
[
BlockViewportLayoutState
layoutState
=
new
BlockViewportLayoutState
();
48.0
,
64.0
,
82.0
,
46.0
,
60.0
,
55.0
,
84.0
,
96.0
,
50.0
,
List
<
CardModel
>
cardModels
;
48.0
,
64.0
,
82.0
,
46.0
,
60.0
,
55.0
,
84.0
,
96.0
,
50.0
,
48.0
,
64.0
,
82.0
,
46.0
,
60.0
,
55.0
,
84.0
,
96.0
,
50.0
,
48.0
,
64.0
,
82.0
,
46.0
,
60.0
,
55.0
,
84.0
,
96.0
,
50.0
];
List
<
int
>
visibleCardIndices
;
void
initState
()
{
void
initState
()
{
visibleCardIndices
=
new
List
.
generate
(
cardHeights
.
length
,
(
i
)
=>
i
);
List
<
double
>
cardHeights
=
<
double
>[
48.0
,
63.0
,
82.0
,
146.0
,
60.0
,
55.0
,
84.0
,
96.0
,
50.0
,
48.0
,
63.0
,
82.0
,
146.0
,
60.0
,
55.0
,
84.0
,
96.0
,
50.0
,
48.0
,
63.0
,
82.0
,
146.0
,
60.0
,
55.0
,
84.0
,
96.0
,
50.0
];
cardModels
=
new
List
.
generate
(
cardHeights
.
length
,
(
i
)
{
Color
color
=
lerpColor
(
Red
[
300
],
Blue
[
900
],
i
/
cardHeights
.
length
);
return
new
CardModel
(
i
,
cardHeights
[
i
],
color
);
});
super
.
initState
();
super
.
initState
();
}
}
void
dismissCard
(
int
cardIndex
)
{
void
shrinkCard
(
CardModel
card
,
int
index
)
{
if
(
card
.
performance
!=
null
)
return
;
layoutState
.
invalidate
([
index
]);
setState
(()
{
setState
(()
{
visibleCardIndices
.
remove
(
cardIndex
);
assert
(
card
.
performance
==
null
);
card
.
performance
=
new
AnimationPerformance
()
..
duration
=
const
Duration
(
milliseconds:
300
)
..
variable
=
new
AnimatedType
<
double
>(
card
.
height
+
kCardMargins
.
top
+
kCardMargins
.
bottom
,
end:
0.0
,
curve:
ease
,
interval:
new
Interval
(
0.5
,
1.0
)
)
..
play
();
});
});
}
}
Widget
_builder
(
int
index
)
{
void
dismissCard
(
CardModel
card
)
{
if
(
index
>=
visibleCardIndices
.
length
)
if
(
cardModels
.
contains
(
card
))
{
setState
(()
{
cardModels
.
remove
(
card
);
});
}
}
Widget
builder
(
int
index
)
{
if
(
index
>=
cardModels
.
length
)
return
null
;
return
null
;
CardModel
card
=
cardModels
[
index
];
if
(
card
.
performance
!=
null
)
{
return
new
ShrinkingCard
(
key:
card
.
key
,
card:
card
,
onUpdated:
()
{
layoutState
.
invalidate
([
index
]);
},
onCompleted:
()
{
dismissCard
(
card
);
}
);
}
int
cardIndex
=
visibleCardIndices
[
index
];
Color
color
=
lerpColor
(
Red
[
500
],
Blue
[
500
],
cardIndex
/
cardHeights
.
length
);
Widget
label
=
new
Text
(
"Item
${cardIndex}
"
,
style:
cardLabelStyle
);
return
new
Dismissable
(
return
new
Dismissable
(
key:
card
Index
.
toString
()
,
key:
card
.
key
,
onDismissed:
()
{
dismissCard
(
cardI
ndex
);
},
onDismissed:
()
{
shrinkCard
(
card
,
i
ndex
);
},
child:
new
Card
(
child:
new
Card
(
color:
color
,
color:
c
ard
.
c
olor
,
child:
new
Container
(
child:
new
Container
(
height:
card
Heights
[
cardIndex
]
,
height:
card
.
height
,
padding:
const
EdgeDims
.
all
(
8.0
),
padding:
const
EdgeDims
.
all
(
8.0
),
child:
new
Center
(
child:
label
)
child:
new
Center
(
child:
new
Text
(
card
.
label
,
style:
cardLabelStyle
)
)
)
)
)
)
);
);
...
@@ -68,16 +151,17 @@ class CardCollectionApp extends App {
...
@@ -68,16 +151,17 @@ class CardCollectionApp extends App {
padding:
const
EdgeDims
.
symmetric
(
vertical:
12.0
,
horizontal:
8.0
),
padding:
const
EdgeDims
.
symmetric
(
vertical:
12.0
,
horizontal:
8.0
),
decoration:
new
BoxDecoration
(
backgroundColor:
Theme
.
of
(
this
).
primarySwatch
[
50
]),
decoration:
new
BoxDecoration
(
backgroundColor:
Theme
.
of
(
this
).
primarySwatch
[
50
]),
child:
new
VariableHeightScrollable
(
child:
new
VariableHeightScrollable
(
builder:
_builder
,
builder:
builder
,
token:
visibleCardIndices
.
length
token:
cardModels
.
length
,
layoutState:
layoutState
)
)
);
);
return
new
Theme
(
return
new
Theme
(
data:
new
ThemeData
(
data:
new
ThemeData
(
brightness:
ThemeBrightness
.
light
,
brightness:
ThemeBrightness
.
light
,
primarySwatch:
colors
.
Blue
,
primarySwatch:
Blue
,
accentColor:
colors
.
RedAccent
[
200
]
accentColor:
RedAccent
[
200
]
),
),
child:
new
TaskDescription
(
child:
new
TaskDescription
(
label:
'Cards'
,
label:
'Cards'
,
...
...
packages/flutter/lib/widgets/block_viewport.dart
View file @
c3a8df1d
...
@@ -12,13 +12,6 @@ import 'package:sky/widgets/widget.dart';
...
@@ -12,13 +12,6 @@ import 'package:sky/widgets/widget.dart';
// return null if index is greater than index of last entry
// return null if index is greater than index of last entry
typedef
Widget
IndexedBuilder
(
int
index
);
typedef
Widget
IndexedBuilder
(
int
index
);
typedef
void
LayoutChangedCallback
(
int
firstVisibleChildIndex
,
int
visibleChildCount
,
UnmodifiableListView
<
double
>
childOffsets
,
bool
didReachLastChild
);
class
_Key
{
class
_Key
{
const
_Key
(
this
.
type
,
this
.
key
);
const
_Key
(
this
.
type
,
this
.
key
);
factory
_Key
.
fromWidget
(
Widget
widget
)
=>
new
_Key
(
widget
.
runtimeType
,
widget
.
key
);
factory
_Key
.
fromWidget
(
Widget
widget
)
=>
new
_Key
(
widget
.
runtimeType
,
widget
.
key
);
...
@@ -26,24 +19,80 @@ class _Key {
...
@@ -26,24 +19,80 @@ class _Key {
final
String
key
;
final
String
key
;
bool
operator
==(
other
)
=>
other
is
_Key
&&
other
.
type
==
type
&&
other
.
key
==
key
;
bool
operator
==(
other
)
=>
other
is
_Key
&&
other
.
type
==
type
&&
other
.
key
==
key
;
int
get
hashCode
=>
373
*
37
*
type
.
hashCode
+
key
.
hashCode
;
int
get
hashCode
=>
373
*
37
*
type
.
hashCode
+
key
.
hashCode
;
String
toString
()
=>
"_Key(type:
$type
, key:
$key
)"
;
}
typedef
void
LayoutChangedCallback
(
);
class
BlockViewportLayoutState
{
BlockViewportLayoutState
()
:
_childOffsets
=
<
double
>[
0.0
],
_firstVisibleChildIndex
=
0
,
_visibleChildCount
=
0
,
_didReachLastChild
=
false
{
_readOnlyChildOffsets
=
new
UnmodifiableListView
<
double
>(
_childOffsets
);
}
Map
<
_Key
,
Widget
>
_childrenByKey
=
new
Map
<
_Key
,
Widget
>();
bool
_dirty
=
true
;
int
_firstVisibleChildIndex
;
int
get
firstVisibleChildIndex
=>
_firstVisibleChildIndex
;
int
_visibleChildCount
;
int
get
visibleChildCount
=>
_visibleChildCount
;
// childOffsets contains the offsets of each child from the top of the
// list up to the last one we've ever created, and the offset of the
// end of the last one. If there are no children, then the only offset
// is 0.0.
List
<
double
>
_childOffsets
;
UnmodifiableListView
<
double
>
_readOnlyChildOffsets
;
UnmodifiableListView
<
double
>
get
childOffsets
=>
_readOnlyChildOffsets
;
double
get
contentsSize
=>
_childOffsets
.
last
;
bool
_didReachLastChild
;
bool
get
didReachLastChild
=>
_didReachLastChild
;
Set
<
int
>
_invalidIndices
=
new
Set
<
int
>();
bool
get
isValid
=>
_invalidIndices
.
length
==
0
;
// Notify the BlockViewport that the children at indices have either
// changed size and/or changed type.
void
invalidate
(
Iterable
<
int
>
indices
)
{
_invalidIndices
.
addAll
(
indices
);
}
final
List
<
Function
>
_listeners
=
new
List
<
Function
>();
void
addListener
(
Function
listener
)
{
_listeners
.
add
(
listener
);
}
void
removeListener
(
Function
listener
)
{
_listeners
.
remove
(
listener
);
}
void
_notifyListeners
()
{
List
<
Function
>
localListeners
=
new
List
<
Function
>.
from
(
_listeners
);
for
(
Function
listener
in
localListeners
)
listener
();
}
}
}
class
BlockViewport
extends
RenderObjectWrapper
{
class
BlockViewport
extends
RenderObjectWrapper
{
BlockViewport
({
this
.
builder
,
this
.
startOffset
,
this
.
token
,
this
.
onLayoutChanged
,
String
key
})
BlockViewport
({
this
.
builder
,
this
.
startOffset
,
this
.
token
,
this
.
layoutState
,
String
key
})
:
super
(
key:
key
);
:
super
(
key:
key
)
{
assert
(
this
.
layoutState
!=
null
);
}
IndexedBuilder
builder
;
IndexedBuilder
builder
;
double
startOffset
;
double
startOffset
;
Object
token
;
Object
token
;
LayoutChangedCallback
onLayoutChanged
;
BlockViewportLayoutState
layoutState
;
RenderBlockViewport
get
root
=>
super
.
root
;
RenderBlockViewport
get
root
=>
super
.
root
;
RenderBlockViewport
createNode
()
=>
new
RenderBlockViewport
();
RenderBlockViewport
createNode
()
=>
new
RenderBlockViewport
();
Map
<
_Key
,
Widget
>
_childrenByKey
=
new
Map
<
_Key
,
Widget
>();
void
walkChildren
(
WidgetTreeWalker
walker
)
{
void
walkChildren
(
WidgetTreeWalker
walker
)
{
for
(
Widget
child
in
_childrenByKey
.
values
)
for
(
Widget
child
in
layoutState
.
_childrenByKey
.
values
)
walker
(
child
);
walker
(
child
);
}
}
...
@@ -69,7 +118,7 @@ class BlockViewport extends RenderObjectWrapper {
...
@@ -69,7 +118,7 @@ class BlockViewport extends RenderObjectWrapper {
}
}
void
remove
()
{
void
remove
()
{
for
(
Widget
child
in
_childrenByKey
.
values
)
{
for
(
Widget
child
in
layoutState
.
_childrenByKey
.
values
)
{
assert
(
child
!=
null
);
assert
(
child
!=
null
);
removeChild
(
child
);
removeChild
(
child
);
}
}
...
@@ -86,23 +135,15 @@ class BlockViewport extends RenderObjectWrapper {
...
@@ -86,23 +135,15 @@ class BlockViewport extends RenderObjectWrapper {
super
.
didUnmount
();
super
.
didUnmount
();
}
}
// _offsets contains the offsets of each child from the top of the
// list up to the last one we've ever created, and the offset of the
// end of the last one. If there's no children, then the only offset
// is 0.0.
List
<
double
>
_offsets
=
<
double
>[
0.0
];
int
_currentStartIndex
=
0
;
int
_currentChildCount
=
0
;
bool
_didReachLastChild
=
false
;
int
_findIndexForOffsetBeforeOrAt
(
double
offset
)
{
int
_findIndexForOffsetBeforeOrAt
(
double
offset
)
{
final
List
<
double
>
offsets
=
layoutState
.
_childOffsets
;
int
left
=
0
;
int
left
=
0
;
int
right
=
_
offsets
.
length
-
1
;
int
right
=
offsets
.
length
-
1
;
while
(
right
>=
left
)
{
while
(
right
>=
left
)
{
int
middle
=
left
+
((
right
-
left
)
~/
2
);
int
middle
=
left
+
((
right
-
left
)
~/
2
);
if
(
_
offsets
[
middle
]
<
offset
)
{
if
(
offsets
[
middle
]
<
offset
)
{
left
=
middle
+
1
;
left
=
middle
+
1
;
}
else
if
(
_
offsets
[
middle
]
>
offset
)
{
}
else
if
(
offsets
[
middle
]
>
offset
)
{
right
=
middle
-
1
;
right
=
middle
-
1
;
}
else
{
}
else
{
return
middle
;
return
middle
;
...
@@ -111,94 +152,154 @@ class BlockViewport extends RenderObjectWrapper {
...
@@ -111,94 +152,154 @@ class BlockViewport extends RenderObjectWrapper {
return
right
;
return
right
;
}
}
bool
_dirty
=
true
;
bool
retainStatefulNodeIfPossible
(
BlockViewport
newNode
)
{
bool
retainStatefulNodeIfPossible
(
BlockViewport
newNode
)
{
assert
(
layoutState
==
newNode
.
layoutState
);
retainStatefulRenderObjectWrapper
(
newNode
);
retainStatefulRenderObjectWrapper
(
newNode
);
if
(
startOffset
!=
newNode
.
startOffset
)
{
if
(
startOffset
!=
newNode
.
startOffset
)
{
_dirty
=
true
;
layoutState
.
_dirty
=
true
;
startOffset
=
newNode
.
startOffset
;
startOffset
=
newNode
.
startOffset
;
}
}
if
(
token
!=
newNode
.
token
||
builder
!=
newNode
.
builder
)
{
if
(
token
!=
newNode
.
token
||
builder
!=
newNode
.
builder
)
{
_dirty
=
true
;
layoutState
.
_dirty
=
true
;
builder
=
newNode
.
builder
;
builder
=
newNode
.
builder
;
token
=
newNode
.
token
;
token
=
newNode
.
token
;
_offsets
=
<
double
>[
0.0
];
layoutState
.
_didReachLastChild
=
false
;
_didReachLastChild
=
false
;
layoutState
.
_childOffsets
=
<
double
>[
0.0
];
layoutState
.
_invalidIndices
=
new
Set
<
int
>();
}
}
return
true
;
return
true
;
}
}
void
syncRenderObject
(
BlockViewport
old
)
{
void
syncRenderObject
(
BlockViewport
old
)
{
super
.
syncRenderObject
(
old
);
super
.
syncRenderObject
(
old
);
if
(
_dirty
)
{
if
(
layoutState
.
_dirty
||
!
layoutState
.
isValid
)
{
root
.
markNeedsLayout
();
root
.
markNeedsLayout
();
}
else
{
}
else
{
if
(
_current
ChildCount
>
0
)
{
if
(
layoutState
.
_visible
ChildCount
>
0
)
{
assert
(
_currentStart
Index
>=
0
);
assert
(
layoutState
.
firstVisibleChild
Index
>=
0
);
assert
(
builder
!=
null
);
assert
(
builder
!=
null
);
assert
(
root
!=
null
);
assert
(
root
!=
null
);
int
lastIndex
=
_currentStartIndex
+
_currentChildCount
-
1
;
final
int
startIndex
=
layoutState
.
_firstVisibleChildIndex
;
for
(
int
index
=
_currentStartIndex
;
index
<=
lastIndex
;
index
+=
1
)
{
int
lastIndex
=
startIndex
+
layoutState
.
_visibleChildCount
-
1
;
for
(
int
index
=
startIndex
;
index
<=
lastIndex
;
index
+=
1
)
{
Widget
widget
=
builder
(
index
);
Widget
widget
=
builder
(
index
);
assert
(
widget
!=
null
);
assert
(
widget
!=
null
);
assert
(
widget
.
key
!=
null
);
assert
(
widget
.
key
!=
null
);
_Key
key
=
new
_Key
.
fromWidget
(
widget
);
_Key
key
=
new
_Key
.
fromWidget
(
widget
);
Widget
oldWidget
=
_childrenByKey
[
key
];
Widget
oldWidget
=
layoutState
.
_childrenByKey
[
key
];
assert
(
oldWidget
!=
null
);
assert
(
oldWidget
!=
null
);
assert
(
oldWidget
.
root
.
parent
==
root
);
assert
(
oldWidget
.
root
.
parent
==
root
);
widget
=
syncChild
(
widget
,
oldWidget
,
root
.
childAfter
(
oldWidget
.
root
));
widget
=
syncChild
(
widget
,
oldWidget
,
root
.
childAfter
(
oldWidget
.
root
));
assert
(
widget
!=
null
);
assert
(
widget
!=
null
);
_childrenByKey
[
key
]
=
widget
;
layoutState
.
_childrenByKey
[
key
]
=
widget
;
}
}
}
}
}
}
}
}
// Build the widget at index, and use its maxIntrinsicHeight to fix up
// the offsets from index+1 to endIndex. Return the newWidget.
Widget
_getWidgetAndRecomputeOffsets
(
int
index
,
int
endIndex
,
BoxConstraints
innerConstraints
)
{
final
List
<
double
>
offsets
=
layoutState
.
_childOffsets
;
// Create the newWidget at index.
assert
(
index
>=
0
);
assert
(
endIndex
>
index
);
assert
(
endIndex
<
offsets
.
length
);
assert
(
builder
!=
null
);
Widget
newWidget
=
builder
(
index
);
assert
(
newWidget
!=
null
);
assert
(
newWidget
.
key
!=
null
);
final
_Key
key
=
new
_Key
.
fromWidget
(
newWidget
);
Widget
oldWidget
=
layoutState
.
_childrenByKey
[
key
];
newWidget
=
syncChild
(
newWidget
,
oldWidget
,
_omit
);
assert
(
newWidget
!=
null
);
// Update the offsets based on the newWidget's height.
RenderBox
widgetRoot
=
newWidget
.
root
;
assert
(
widgetRoot
is
RenderBox
);
double
newHeight
=
widgetRoot
.
getMaxIntrinsicHeight
(
innerConstraints
);
double
oldHeight
=
offsets
[
index
+
1
]
-
offsets
[
index
];
double
heightDelta
=
newHeight
-
oldHeight
;
for
(
int
i
=
index
+
1
;
i
<=
endIndex
;
i
++)
offsets
[
i
]
+=
heightDelta
;
return
newWidget
;
}
Widget
_getWidget
(
int
index
,
BoxConstraints
innerConstraints
)
{
Widget
_getWidget
(
int
index
,
BoxConstraints
innerConstraints
)
{
final
List
<
double
>
offsets
=
layoutState
.
_childOffsets
;
assert
(
index
>=
0
);
Widget
widget
=
builder
==
null
?
null
:
builder
(
index
);
if
(
widget
==
null
)
return
null
;
assert
(
widget
.
key
!=
null
);
// items in lists must have keys
final
_Key
key
=
new
_Key
.
fromWidget
(
widget
);
Widget
oldWidget
=
layoutState
.
_childrenByKey
[
key
];
widget
=
syncChild
(
widget
,
oldWidget
,
_omit
);
if
(
index
>=
offsets
.
length
-
1
)
{
assert
(
index
==
offsets
.
length
-
1
);
final
double
widgetStartOffset
=
offsets
[
index
];
RenderBox
widgetRoot
=
widget
.
root
;
assert
(
widgetRoot
is
RenderBox
);
final
double
widgetEndOffset
=
widgetStartOffset
+
widgetRoot
.
getMaxIntrinsicHeight
(
innerConstraints
);
offsets
.
add
(
widgetEndOffset
);
}
return
widget
;
}
void
layout
(
BoxConstraints
constraints
)
{
if
(!
layoutState
.
_dirty
&&
layoutState
.
isValid
)
return
;
layoutState
.
_dirty
=
false
;
LayoutCallbackBuilderHandle
handle
=
enterLayoutCallbackBuilder
();
LayoutCallbackBuilderHandle
handle
=
enterLayoutCallbackBuilder
();
try
{
try
{
assert
(
index
>=
0
);
_doLayout
(
constraints
);
Widget
widget
=
builder
==
null
?
null
:
builder
(
index
);
if
(
widget
==
null
)
return
null
;
assert
(
widget
.
key
!=
null
);
// items in lists must have keys
final
_Key
key
=
new
_Key
.
fromWidget
(
widget
);
Widget
oldWidget
=
_childrenByKey
[
key
];
widget
=
syncChild
(
widget
,
oldWidget
,
_omit
);
if
(
oldWidget
!=
null
)
_childrenByKey
[
key
]
=
widget
;
if
(
index
>=
_offsets
.
length
-
1
)
{
assert
(
index
==
_offsets
.
length
-
1
);
final
double
widgetStartOffset
=
_offsets
[
index
];
RenderBox
widgetRoot
=
widget
.
root
;
assert
(
widgetRoot
is
RenderBox
);
final
double
widgetEndOffset
=
widgetStartOffset
+
widgetRoot
.
getMaxIntrinsicHeight
(
innerConstraints
);
_offsets
.
add
(
widgetEndOffset
);
}
return
widget
;
}
finally
{
}
finally
{
exitLayoutCallbackBuilder
(
handle
);
exitLayoutCallbackBuilder
(
handle
);
}
}
}
void
layout
(
BoxConstraints
constraints
)
{
layoutState
.
_notifyListeners
();
if
(!
_dirty
)
}
return
;
_dirty
=
false
;
void
_doLayout
(
BoxConstraints
constraints
)
{
Map
<
_Key
,
Widget
>
newChildren
=
new
Map
<
_Key
,
Widget
>();
Map
<
_Key
,
Widget
>
newChildren
=
new
Map
<
_Key
,
Widget
>();
Map
<
int
,
Widget
>
builtChildren
=
new
Map
<
int
,
Widget
>();
Map
<
int
,
Widget
>
builtChildren
=
new
Map
<
int
,
Widget
>();
final
List
<
double
>
offsets
=
layoutState
.
_childOffsets
;
final
Map
<
_Key
,
Widget
>
childrenByKey
=
layoutState
.
_childrenByKey
;
final
double
height
=
root
.
size
.
height
;
final
double
height
=
root
.
size
.
height
;
final
double
endOffset
=
startOffset
+
height
;
final
double
endOffset
=
startOffset
+
height
;
BoxConstraints
innerConstraints
=
new
BoxConstraints
.
tightFor
(
width:
constraints
.
constrainWidth
());
BoxConstraints
innerConstraints
=
new
BoxConstraints
.
tightFor
(
width:
constraints
.
constrainWidth
());
// Before doing the actual layout, fix the offsets for the widgets
// whose size or type has changed.
if
(!
layoutState
.
isValid
&&
offsets
.
length
>
0
)
{
List
<
int
>
invalidIndices
=
layoutState
.
_invalidIndices
.
toList
();
invalidIndices
.
sort
();
// Ensure all of the offsets after invalidIndices[0] are updated.
if
(
invalidIndices
.
last
<
offsets
.
length
-
1
)
invalidIndices
.
add
(
offsets
.
length
-
1
);
for
(
int
i
=
0
;
i
<
invalidIndices
.
length
-
1
;
i
+=
1
)
{
int
index
=
invalidIndices
[
i
];
int
endIndex
=
invalidIndices
[
i
+
1
];
Widget
widget
=
_getWidgetAndRecomputeOffsets
(
index
,
endIndex
,
innerConstraints
);
_Key
widgetKey
=
new
_Key
.
fromWidget
(
widget
);
bool
isVisible
=
offsets
[
index
]
<
endOffset
&&
offsets
[
index
+
1
]
>=
startOffset
;
if
(
isVisible
)
{
newChildren
[
widgetKey
]
=
widget
;
builtChildren
[
index
]
=
widget
;
}
else
{
childrenByKey
.
remove
(
widgetKey
);
syncChild
(
null
,
widget
,
null
);
}
}
}
layoutState
.
_invalidIndices
.
clear
();
int
startIndex
;
int
startIndex
;
bool
haveChildren
;
bool
haveChildren
;
if
(
startOffset
<=
0.0
)
{
if
(
startOffset
<=
0.0
)
{
startIndex
=
0
;
startIndex
=
0
;
if
(
_
offsets
.
length
>
1
)
{
if
(
offsets
.
length
>
1
)
{
haveChildren
=
true
;
haveChildren
=
true
;
}
else
{
}
else
{
Widget
widget
=
_getWidget
(
startIndex
,
innerConstraints
);
Widget
widget
=
_getWidget
(
startIndex
,
innerConstraints
);
...
@@ -208,41 +309,41 @@ class BlockViewport extends RenderObjectWrapper {
...
@@ -208,41 +309,41 @@ class BlockViewport extends RenderObjectWrapper {
haveChildren
=
true
;
haveChildren
=
true
;
}
else
{
}
else
{
haveChildren
=
false
;
haveChildren
=
false
;
_didReachLastChild
=
true
;
layoutState
.
_didReachLastChild
=
true
;
}
}
}
}
}
else
{
}
else
{
startIndex
=
_findIndexForOffsetBeforeOrAt
(
startOffset
);
startIndex
=
_findIndexForOffsetBeforeOrAt
(
startOffset
);
if
(
startIndex
==
_
offsets
.
length
-
1
)
{
if
(
startIndex
==
offsets
.
length
-
1
)
{
// We don't have an offset on the list that is beyond the start offset.
// We don't have an offset on the list that is beyond the start offset.
assert
(
_
offsets
.
last
<=
startOffset
);
assert
(
offsets
.
last
<=
startOffset
);
// Fill the list until this isn't true or until we know that the
// Fill the list until this isn't true or until we know that the
// list is complete (and thus we are overscrolled).
// list is complete (and thus we are overscrolled).
while
(
true
)
{
while
(
true
)
{
Widget
widget
=
_getWidget
(
startIndex
,
innerConstraints
);
Widget
widget
=
_getWidget
(
startIndex
,
innerConstraints
);
if
(
widget
==
null
)
{
if
(
widget
==
null
)
{
_didReachLastChild
=
true
;
layoutState
.
_didReachLastChild
=
true
;
break
;
break
;
}
}
_Key
widgetKey
=
new
_Key
.
fromWidget
(
widget
);
_Key
widgetKey
=
new
_Key
.
fromWidget
(
widget
);
if
(
_
offsets
.
last
>
startOffset
)
{
if
(
offsets
.
last
>
startOffset
)
{
newChildren
[
widgetKey
]
=
widget
;
newChildren
[
widgetKey
]
=
widget
;
builtChildren
[
startIndex
]
=
widget
;
builtChildren
[
startIndex
]
=
widget
;
break
;
break
;
}
}
if
(!
_
childrenByKey
.
containsKey
(
widgetKey
))
{
if
(!
childrenByKey
.
containsKey
(
widgetKey
))
{
// we don't actually need this one, release it
// we don't actually need this one, release it
syncChild
(
null
,
widget
,
null
);
syncChild
(
null
,
widget
,
null
);
}
// else we'll get rid of it later, when we remove old children
}
// else we'll get rid of it later, when we remove old children
startIndex
+=
1
;
startIndex
+=
1
;
assert
(
startIndex
==
_
offsets
.
length
-
1
);
assert
(
startIndex
==
offsets
.
length
-
1
);
}
}
if
(
_
offsets
.
last
>
startOffset
)
{
if
(
offsets
.
last
>
startOffset
)
{
// If we're here, we have at least one child, so our list has
// If we're here, we have at least one child, so our list has
// at least two offsets, the top of the child and the bottom
// at least two offsets, the top of the child and the bottom
// of the child.
// of the child.
assert
(
_
offsets
.
length
>=
2
);
assert
(
offsets
.
length
>=
2
);
assert
(
startIndex
==
_
offsets
.
length
-
2
);
assert
(
startIndex
==
offsets
.
length
-
2
);
haveChildren
=
true
;
haveChildren
=
true
;
}
else
{
}
else
{
// If we're here, there are no children to show.
// If we're here, there are no children to show.
...
@@ -253,20 +354,20 @@ class BlockViewport extends RenderObjectWrapper {
...
@@ -253,20 +354,20 @@ class BlockViewport extends RenderObjectWrapper {
}
}
}
}
assert
(
haveChildren
!=
null
);
assert
(
haveChildren
!=
null
);
assert
(
haveChildren
||
_didReachLastChild
);
assert
(
haveChildren
||
layoutState
.
_didReachLastChild
);
assert
(
startIndex
>=
0
);
assert
(
startIndex
>=
0
);
assert
(
startIndex
<
_
offsets
.
length
);
assert
(
startIndex
<
offsets
.
length
);
int
index
=
startIndex
;
int
index
=
startIndex
;
if
(
haveChildren
)
{
if
(
haveChildren
)
{
// Build all the widgets we need.
// Build all the widgets we need.
root
.
startOffset
=
_
offsets
[
index
]
-
startOffset
;
root
.
startOffset
=
offsets
[
index
]
-
startOffset
;
while
(
_
offsets
[
index
]
<
endOffset
)
{
while
(
offsets
[
index
]
<
endOffset
)
{
if
(!
builtChildren
.
containsKey
(
index
))
{
if
(!
builtChildren
.
containsKey
(
index
))
{
Widget
widget
=
_getWidget
(
index
,
innerConstraints
);
Widget
widget
=
_getWidget
(
index
,
innerConstraints
);
if
(
widget
==
null
)
{
if
(
widget
==
null
)
{
_didReachLastChild
=
true
;
layoutState
.
_didReachLastChild
=
true
;
break
;
break
;
}
}
newChildren
[
new
_Key
.
fromWidget
(
widget
)]
=
widget
;
newChildren
[
new
_Key
.
fromWidget
(
widget
)]
=
widget
;
...
@@ -278,9 +379,9 @@ class BlockViewport extends RenderObjectWrapper {
...
@@ -278,9 +379,9 @@ class BlockViewport extends RenderObjectWrapper {
}
}
// Remove any old children.
// Remove any old children.
for
(
_Key
oldChildKey
in
_
childrenByKey
.
keys
)
{
for
(
_Key
oldChildKey
in
childrenByKey
.
keys
)
{
if
(!
newChildren
.
containsKey
(
oldChildKey
))
if
(!
newChildren
.
containsKey
(
oldChildKey
))
syncChild
(
null
,
_
childrenByKey
[
oldChildKey
],
null
);
// calls detachChildRoot()
syncChild
(
null
,
childrenByKey
[
oldChildKey
],
null
);
// calls detachChildRoot()
}
}
if
(
haveChildren
)
{
if
(
haveChildren
)
{
...
@@ -302,18 +403,9 @@ class BlockViewport extends RenderObjectWrapper {
...
@@ -302,18 +403,9 @@ class BlockViewport extends RenderObjectWrapper {
}
}
}
}
_childrenByKey
=
newChildren
;
layoutState
.
_childrenByKey
=
newChildren
;
_currentStartIndex
=
startIndex
;
layoutState
.
_firstVisibleChildIndex
=
startIndex
;
_currentChildCount
=
_childrenByKey
.
length
;
layoutState
.
_visibleChildCount
=
newChildren
.
length
;
if
(
onLayoutChanged
!=
null
)
{
onLayoutChanged
(
_currentStartIndex
,
_currentChildCount
,
new
UnmodifiableListView
<
double
>(
_offsets
),
_didReachLastChild
);
}
}
}
}
}
packages/flutter/lib/widgets/card.dart
View file @
c3a8df1d
...
@@ -5,6 +5,8 @@
...
@@ -5,6 +5,8 @@
import
'package:sky/widgets/basic.dart'
;
import
'package:sky/widgets/basic.dart'
;
import
'package:sky/widgets/material.dart'
;
import
'package:sky/widgets/material.dart'
;
const
EdgeDims
kCardMargins
=
const
EdgeDims
.
all
(
4.0
);
/// A material design card
/// A material design card
///
///
/// <https://www.google.com/design/spec/components/cards.html>
/// <https://www.google.com/design/spec/components/cards.html>
...
@@ -16,7 +18,7 @@ class Card extends Component {
...
@@ -16,7 +18,7 @@ class Card extends Component {
Widget
build
()
{
Widget
build
()
{
return
new
Container
(
return
new
Container
(
margin:
const
EdgeDims
.
all
(
4.0
)
,
margin:
kCardMargins
,
child:
new
Material
(
child:
new
Material
(
color:
color
,
color:
color
,
type:
MaterialType
.
card
,
type:
MaterialType
.
card
,
...
...
packages/flutter/lib/widgets/variable_height_scrollable.dart
View file @
c3a8df1d
...
@@ -2,8 +2,6 @@
...
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:collection'
;
import
'package:sky/animation/scroll_behavior.dart'
;
import
'package:sky/animation/scroll_behavior.dart'
;
import
'package:sky/widgets/basic.dart'
;
import
'package:sky/widgets/basic.dart'
;
import
'package:sky/widgets/block_viewport.dart'
;
import
'package:sky/widgets/block_viewport.dart'
;
...
@@ -14,18 +12,39 @@ class VariableHeightScrollable extends Scrollable {
...
@@ -14,18 +12,39 @@ class VariableHeightScrollable extends Scrollable {
VariableHeightScrollable
({
VariableHeightScrollable
({
String
key
,
String
key
,
this
.
builder
,
this
.
builder
,
this
.
token
this
.
token
,
this
.
layoutState
})
:
super
(
key:
key
);
})
:
super
(
key:
key
);
IndexedBuilder
builder
;
IndexedBuilder
builder
;
Object
token
;
Object
token
;
BlockViewportLayoutState
layoutState
;
// When the token changes the scrollable's contents may have
// changed. Remember as much so that after the new contents
// have been laid out we can adjust the scrollOffset so that
// the last page of content is still visible.
bool
_contentsChanged
=
true
;
bool
_contentsChanged
=
true
;
void
initState
()
{
assert
(
layoutState
!=
null
);
layoutState
.
removeListener
(
_handleLayoutChanged
);
layoutState
.
addListener
(
_handleLayoutChanged
);
super
.
initState
();
}
void
syncFields
(
VariableHeightScrollable
source
)
{
void
syncFields
(
VariableHeightScrollable
source
)
{
builder
=
source
.
builder
;
builder
=
source
.
builder
;
if
(
token
!=
source
.
token
)
if
(
token
!=
source
.
token
)
_contentsChanged
=
true
;
_contentsChanged
=
true
;
token
=
source
.
token
;
token
=
source
.
token
;
if
(
layoutState
!=
source
.
layoutState
)
{
// Warning: this is unlikely to be what you intended.
assert
(
source
.
layoutState
!=
null
);
layoutState
==
source
.
layoutState
;
layoutState
.
removeListener
(
_handleLayoutChanged
);
layoutState
.
addListener
(
_handleLayoutChanged
);
}
super
.
syncFields
(
source
);
super
.
syncFields
(
source
);
}
}
...
@@ -36,15 +55,9 @@ class VariableHeightScrollable extends Scrollable {
...
@@ -36,15 +55,9 @@ class VariableHeightScrollable extends Scrollable {
scrollBehavior
.
containerSize
=
newSize
.
height
;
scrollBehavior
.
containerSize
=
newSize
.
height
;
}
}
void
_handleLayoutChanged
(
void
_handleLayoutChanged
()
{
int
firstVisibleChildIndex
,
if
(
layoutState
.
didReachLastChild
)
{
int
visibleChildCount
,
scrollBehavior
.
contentsSize
=
layoutState
.
contentsSize
;
UnmodifiableListView
<
double
>
childOffsets
,
bool
didReachLastChild
)
{
assert
(
childOffsets
.
length
>
0
);
if
(
didReachLastChild
)
{
scrollBehavior
.
contentsSize
=
childOffsets
.
last
;
if
(
_contentsChanged
&&
scrollOffset
>
scrollBehavior
.
maxScrollOffset
)
{
if
(
_contentsChanged
&&
scrollOffset
>
scrollBehavior
.
maxScrollOffset
)
{
_contentsChanged
=
false
;
_contentsChanged
=
false
;
settleScrollOffset
();
settleScrollOffset
();
...
@@ -59,7 +72,7 @@ class VariableHeightScrollable extends Scrollable {
...
@@ -59,7 +72,7 @@ class VariableHeightScrollable extends Scrollable {
callback:
_handleSizeChanged
,
callback:
_handleSizeChanged
,
child:
new
BlockViewport
(
child:
new
BlockViewport
(
builder:
builder
,
builder:
builder
,
onLayoutChanged:
_handleLayoutChanged
,
layoutState:
layoutState
,
startOffset:
scrollOffset
,
startOffset:
scrollOffset
,
token:
token
token:
token
)
)
...
...
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