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
b954e020
Commit
b954e020
authored
Jul 17, 2015
by
Hans Muller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make it possible to identify BlockViewport children whose size or type has changed
parent
84da5238
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 @
b954e020
...
@@ -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 @
b954e020
...
@@ -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 @
b954e020
...
@@ -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 @
b954e020
...
@@ -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 @
b954e020
...
@@ -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