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
25de9419
Unverified
Commit
25de9419
authored
Aug 11, 2020
by
Michael Goderbauer
Committed by
GitHub
Aug 11, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make Scrollables restorable (#63131)
parent
a84e3902
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
840 additions
and
6 deletions
+840
-6
restoration.dart
packages/flutter/lib/src/services/restoration.dart
+33
-4
list_wheel_scroll_view.dart
packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart
+8
-0
nested_scroll_view.dart
packages/flutter/lib/src/widgets/nested_scroll_view.dart
+7
-0
page_view.dart
packages/flutter/lib/src/widgets/page_view.dart
+23
-0
scroll_context.dart
packages/flutter/lib/src/widgets/scroll_context.dart
+9
-0
scroll_position.dart
packages/flutter/lib/src/widgets/scroll_position.dart
+40
-0
scroll_view.dart
packages/flutter/lib/src/widgets/scroll_view.dart
+29
-0
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+72
-2
single_child_scroll_view.dart
...ges/flutter/lib/src/widgets/single_child_scroll_view.dart
+5
-0
restoration_test.dart
packages/flutter/test/services/restoration_test.dart
+37
-0
scrollable_restoration_test.dart
...ges/flutter/test/widgets/scrollable_restoration_test.dart
+577
-0
No files found.
packages/flutter/lib/src/services/restoration.dart
View file @
25de9419
...
@@ -250,7 +250,7 @@ class RestorationManager extends ChangeNotifier {
...
@@ -250,7 +250,7 @@ class RestorationManager extends ChangeNotifier {
}
}
bool
_debugDoingUpdate
=
false
;
bool
_debugDoingUpdate
=
false
;
bool
_
postFrame
Scheduled
=
false
;
bool
_
serialization
Scheduled
=
false
;
final
Set
<
RestorationBucket
>
_bucketsNeedingSerialization
=
<
RestorationBucket
>{};
final
Set
<
RestorationBucket
>
_bucketsNeedingSerialization
=
<
RestorationBucket
>{};
...
@@ -270,8 +270,8 @@ class RestorationManager extends ChangeNotifier {
...
@@ -270,8 +270,8 @@ class RestorationManager extends ChangeNotifier {
assert
(
bucket
.
_manager
==
this
);
assert
(
bucket
.
_manager
==
this
);
assert
(!
_debugDoingUpdate
);
assert
(!
_debugDoingUpdate
);
_bucketsNeedingSerialization
.
add
(
bucket
);
_bucketsNeedingSerialization
.
add
(
bucket
);
if
(!
_
postFrame
Scheduled
)
{
if
(!
_
serialization
Scheduled
)
{
_
postFrame
Scheduled
=
true
;
_
serialization
Scheduled
=
true
;
SchedulerBinding
.
instance
!.
addPostFrameCallback
((
Duration
_
)
=>
_doSerialization
());
SchedulerBinding
.
instance
!.
addPostFrameCallback
((
Duration
_
)
=>
_doSerialization
());
}
}
}
}
...
@@ -295,11 +295,14 @@ class RestorationManager extends ChangeNotifier {
...
@@ -295,11 +295,14 @@ class RestorationManager extends ChangeNotifier {
}
}
void
_doSerialization
()
{
void
_doSerialization
()
{
if
(!
_serializationScheduled
)
{
return
;
}
assert
(()
{
assert
(()
{
_debugDoingUpdate
=
true
;
_debugDoingUpdate
=
true
;
return
true
;
return
true
;
}());
}());
_
postFrame
Scheduled
=
false
;
_
serialization
Scheduled
=
false
;
for
(
final
RestorationBucket
bucket
in
_bucketsNeedingSerialization
)
{
for
(
final
RestorationBucket
bucket
in
_bucketsNeedingSerialization
)
{
bucket
.
finalize
();
bucket
.
finalize
();
...
@@ -312,6 +315,32 @@ class RestorationManager extends ChangeNotifier {
...
@@ -312,6 +315,32 @@ class RestorationManager extends ChangeNotifier {
return
true
;
return
true
;
}());
}());
}
}
/// Called to manually flush the restoration data to the engine.
///
/// A change in restoration data is usually accompanied by scheduling a frame
/// (because the restoration data is modified inside a [State.setState] call,
/// because it is usually something that affects the interface). Restoration
/// data is automatically flushed to the engine at the end of a frame. As a
/// result, it is uncommon to need to call this method directly. However, if
/// restoration data is changed without triggering a frame, this method must
/// be called to ensure that the updated restoration data is sent to the
/// engine in a timely manner. An example of such a use case is the
/// [Scrollable], where the final scroll offset after a scroll activity
/// finishes is determined between frames without scheduling a new frame.
///
/// Calling this method is a no-op if a frame is already scheduled. In that
/// case, the restoration data will be flushed to the engine at the end of
/// that frame. If this method is called and no frame is scheduled, the
/// current restoration data is directly sent to the engine.
void
flushData
()
{
assert
(!
_debugDoingUpdate
);
if
(
SchedulerBinding
.
instance
!.
hasScheduledFrame
)
{
return
;
}
_doSerialization
();
assert
(!
_serializationScheduled
);
}
}
}
/// A [RestorationBucket] holds pieces of the restoration data that a part of
/// A [RestorationBucket] holds pieces of the restoration data that a part of
...
...
packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart
View file @
25de9419
...
@@ -434,12 +434,14 @@ class _FixedExtentScrollable extends Scrollable {
...
@@ -434,12 +434,14 @@ class _FixedExtentScrollable extends Scrollable {
ScrollPhysics
physics
,
ScrollPhysics
physics
,
@required
this
.
itemExtent
,
@required
this
.
itemExtent
,
@required
ViewportBuilder
viewportBuilder
,
@required
ViewportBuilder
viewportBuilder
,
String
restorationId
,
})
:
super
(
})
:
super
(
key:
key
,
key:
key
,
axisDirection:
axisDirection
,
axisDirection:
axisDirection
,
controller:
controller
,
controller:
controller
,
physics:
physics
,
physics:
physics
,
viewportBuilder:
viewportBuilder
,
viewportBuilder:
viewportBuilder
,
restorationId:
restorationId
,
);
);
final
double
itemExtent
;
final
double
itemExtent
;
...
@@ -585,6 +587,7 @@ class ListWheelScrollView extends StatefulWidget {
...
@@ -585,6 +587,7 @@ class ListWheelScrollView extends StatefulWidget {
this
.
onSelectedItemChanged
,
this
.
onSelectedItemChanged
,
this
.
renderChildrenOutsideViewport
=
false
,
this
.
renderChildrenOutsideViewport
=
false
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
restorationId
,
@required
List
<
Widget
>
children
,
@required
List
<
Widget
>
children
,
})
:
assert
(
children
!=
null
),
})
:
assert
(
children
!=
null
),
assert
(
diameterRatio
!=
null
),
assert
(
diameterRatio
!=
null
),
...
@@ -625,6 +628,7 @@ class ListWheelScrollView extends StatefulWidget {
...
@@ -625,6 +628,7 @@ class ListWheelScrollView extends StatefulWidget {
this
.
onSelectedItemChanged
,
this
.
onSelectedItemChanged
,
this
.
renderChildrenOutsideViewport
=
false
,
this
.
renderChildrenOutsideViewport
=
false
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
restorationId
,
@required
this
.
childDelegate
,
@required
this
.
childDelegate
,
})
:
assert
(
childDelegate
!=
null
),
})
:
assert
(
childDelegate
!=
null
),
assert
(
diameterRatio
!=
null
),
assert
(
diameterRatio
!=
null
),
...
@@ -713,6 +717,9 @@ class ListWheelScrollView extends StatefulWidget {
...
@@ -713,6 +717,9 @@ class ListWheelScrollView extends StatefulWidget {
/// Defaults to [Clip.hardEdge].
/// Defaults to [Clip.hardEdge].
final
Clip
clipBehavior
;
final
Clip
clipBehavior
;
/// {@macro flutter.widgets.scrollable.restorationId}
final
String
restorationId
;
@override
@override
_ListWheelScrollViewState
createState
()
=>
_ListWheelScrollViewState
();
_ListWheelScrollViewState
createState
()
=>
_ListWheelScrollViewState
();
}
}
...
@@ -765,6 +772,7 @@ class _ListWheelScrollViewState extends State<ListWheelScrollView> {
...
@@ -765,6 +772,7 @@ class _ListWheelScrollViewState extends State<ListWheelScrollView> {
controller:
scrollController
,
controller:
scrollController
,
physics:
widget
.
physics
,
physics:
widget
.
physics
,
itemExtent:
widget
.
itemExtent
,
itemExtent:
widget
.
itemExtent
,
restorationId:
widget
.
restorationId
,
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
offset
)
{
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
offset
)
{
return
ListWheelViewport
(
return
ListWheelViewport
(
diameterRatio:
widget
.
diameterRatio
,
diameterRatio:
widget
.
diameterRatio
,
...
...
packages/flutter/lib/src/widgets/nested_scroll_view.dart
View file @
25de9419
...
@@ -376,6 +376,7 @@ class NestedScrollView extends StatefulWidget {
...
@@ -376,6 +376,7 @@ class NestedScrollView extends StatefulWidget {
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
floatHeaderSlivers
=
false
,
this
.
floatHeaderSlivers
=
false
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
restorationId
,
})
:
assert
(
scrollDirection
!=
null
),
})
:
assert
(
scrollDirection
!=
null
),
assert
(
reverse
!=
null
),
assert
(
reverse
!=
null
),
assert
(
headerSliverBuilder
!=
null
),
assert
(
headerSliverBuilder
!=
null
),
...
@@ -457,6 +458,9 @@ class NestedScrollView extends StatefulWidget {
...
@@ -457,6 +458,9 @@ class NestedScrollView extends StatefulWidget {
/// Defaults to [Clip.hardEdge].
/// Defaults to [Clip.hardEdge].
final
Clip
clipBehavior
;
final
Clip
clipBehavior
;
/// {@macro flutter.widgets.scrollable.restorationId}
final
String
restorationId
;
/// Returns the [SliverOverlapAbsorberHandle] of the nearest ancestor
/// Returns the [SliverOverlapAbsorberHandle] of the nearest ancestor
/// [NestedScrollView].
/// [NestedScrollView].
///
///
...
@@ -636,6 +640,7 @@ class NestedScrollViewState extends State<NestedScrollView> {
...
@@ -636,6 +640,7 @@ class NestedScrollViewState extends State<NestedScrollView> {
),
),
handle:
_absorberHandle
,
handle:
_absorberHandle
,
clipBehavior:
widget
.
clipBehavior
,
clipBehavior:
widget
.
clipBehavior
,
restorationId:
widget
.
restorationId
,
);
);
},
},
),
),
...
@@ -653,6 +658,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
...
@@ -653,6 +658,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
@required
this
.
handle
,
@required
this
.
handle
,
@required
this
.
clipBehavior
,
@required
this
.
clipBehavior
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
String
restorationId
,
})
:
super
(
})
:
super
(
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
reverse:
reverse
,
reverse:
reverse
,
...
@@ -660,6 +666,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
...
@@ -660,6 +666,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
controller:
controller
,
controller:
controller
,
slivers:
slivers
,
slivers:
slivers
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
restorationId:
restorationId
,
);
);
final
SliverOverlapAbsorberHandle
handle
;
final
SliverOverlapAbsorberHandle
handle
;
...
...
packages/flutter/lib/src/widgets/page_view.dart
View file @
25de9419
...
@@ -390,6 +390,22 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri
...
@@ -390,6 +390,22 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri
}
}
}
}
@override
void
saveOffset
()
{
context
.
saveOffset
(
getPageFromPixels
(
pixels
,
viewportDimension
));
}
@override
void
restoreOffset
(
double
offset
,
{
bool
initialRestore
=
false
})
{
assert
(
initialRestore
!=
null
);
assert
(
offset
!=
null
);
if
(
initialRestore
)
{
_pageToUseOnStartup
=
offset
;
}
else
{
jumpTo
(
getPixelsFromPage
(
offset
));
}
}
@override
@override
bool
applyViewportDimension
(
double
viewportDimension
)
{
bool
applyViewportDimension
(
double
viewportDimension
)
{
final
double
oldViewportDimensions
=
this
.
viewportDimension
;
final
double
oldViewportDimensions
=
this
.
viewportDimension
;
...
@@ -570,6 +586,7 @@ class PageView extends StatefulWidget {
...
@@ -570,6 +586,7 @@ class PageView extends StatefulWidget {
List
<
Widget
>
children
=
const
<
Widget
>[],
List
<
Widget
>
children
=
const
<
Widget
>[],
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
allowImplicitScrolling
=
false
,
this
.
allowImplicitScrolling
=
false
,
this
.
restorationId
,
})
:
assert
(
allowImplicitScrolling
!=
null
),
})
:
assert
(
allowImplicitScrolling
!=
null
),
controller
=
controller
??
_defaultPageController
,
controller
=
controller
??
_defaultPageController
,
childrenDelegate
=
SliverChildListDelegate
(
children
),
childrenDelegate
=
SliverChildListDelegate
(
children
),
...
@@ -605,6 +622,7 @@ class PageView extends StatefulWidget {
...
@@ -605,6 +622,7 @@ class PageView extends StatefulWidget {
int
itemCount
,
int
itemCount
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
allowImplicitScrolling
=
false
,
this
.
allowImplicitScrolling
=
false
,
this
.
restorationId
,
})
:
assert
(
allowImplicitScrolling
!=
null
),
})
:
assert
(
allowImplicitScrolling
!=
null
),
controller
=
controller
??
_defaultPageController
,
controller
=
controller
??
_defaultPageController
,
childrenDelegate
=
SliverChildBuilderDelegate
(
itemBuilder
,
childCount:
itemCount
),
childrenDelegate
=
SliverChildBuilderDelegate
(
itemBuilder
,
childCount:
itemCount
),
...
@@ -703,6 +721,7 @@ class PageView extends StatefulWidget {
...
@@ -703,6 +721,7 @@ class PageView extends StatefulWidget {
@required
this
.
childrenDelegate
,
@required
this
.
childrenDelegate
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
allowImplicitScrolling
=
false
,
this
.
allowImplicitScrolling
=
false
,
this
.
restorationId
,
})
:
assert
(
childrenDelegate
!=
null
),
})
:
assert
(
childrenDelegate
!=
null
),
assert
(
allowImplicitScrolling
!=
null
),
assert
(
allowImplicitScrolling
!=
null
),
controller
=
controller
??
_defaultPageController
,
controller
=
controller
??
_defaultPageController
,
...
@@ -721,6 +740,9 @@ class PageView extends StatefulWidget {
...
@@ -721,6 +740,9 @@ class PageView extends StatefulWidget {
/// will traverse to the next page in the page view.
/// will traverse to the next page in the page view.
final
bool
allowImplicitScrolling
;
final
bool
allowImplicitScrolling
;
/// {@macro flutter.widgets.scrollable.restorationId}
final
String
restorationId
;
/// The axis along which the page view scrolls.
/// The axis along which the page view scrolls.
///
///
/// Defaults to [Axis.horizontal].
/// Defaults to [Axis.horizontal].
...
@@ -824,6 +846,7 @@ class _PageViewState extends State<PageView> {
...
@@ -824,6 +846,7 @@ class _PageViewState extends State<PageView> {
axisDirection:
axisDirection
,
axisDirection:
axisDirection
,
controller:
widget
.
controller
,
controller:
widget
.
controller
,
physics:
physics
,
physics:
physics
,
restorationId:
widget
.
restorationId
,
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
position
)
{
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
position
)
{
return
Viewport
(
return
Viewport
(
// TODO(dnfield): we should provide a way to set cacheExtent
// TODO(dnfield): we should provide a way to set cacheExtent
...
...
packages/flutter/lib/src/widgets/scroll_context.dart
View file @
25de9419
...
@@ -35,6 +35,7 @@ abstract class ScrollContext {
...
@@ -35,6 +35,7 @@ abstract class ScrollContext {
/// particular, it should involve any [GlobalKey]s that are dynamically
/// particular, it should involve any [GlobalKey]s that are dynamically
/// created as part of creating the scrolling widget, since those would be
/// created as part of creating the scrolling widget, since those would be
/// different each time the widget is created.
/// different each time the widget is created.
// TODO(goderbauer): Deprecate this when state restoration supports all features of PageStorage.
BuildContext
get
storageContext
;
BuildContext
get
storageContext
;
/// A [TickerProvider] to use when animating the scroll position.
/// A [TickerProvider] to use when animating the scroll position.
...
@@ -61,4 +62,12 @@ abstract class ScrollContext {
...
@@ -61,4 +62,12 @@ abstract class ScrollContext {
/// Set the [SemanticsAction]s that should be expose to the semantics tree.
/// Set the [SemanticsAction]s that should be expose to the semantics tree.
void
setSemanticsActions
(
Set
<
SemanticsAction
>
actions
);
void
setSemanticsActions
(
Set
<
SemanticsAction
>
actions
);
/// Called by the [ScrollPosition] whenever scrolling ends to persist the
/// provided scroll `offset` for state restoration purposes.
///
/// The [ScrollContext] may pass the value back to a [ScrollPosition] by
/// calling [ScrollPosition.restoreOffset] at a later point in time or after
/// the application has restarted to restore the scroll offset.
void
saveOffset
(
double
offset
);
}
}
packages/flutter/lib/src/widgets/scroll_position.dart
View file @
25de9419
...
@@ -128,6 +128,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
...
@@ -128,6 +128,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
///
///
/// * [ScrollController.keepScrollOffset] and [PageController.keepPage], which
/// * [ScrollController.keepScrollOffset] and [PageController.keepPage], which
/// create scroll positions and initialize this property.
/// create scroll positions and initialize this property.
// TODO(goderbauer): Deprecate this when state restoration supports all features of PageStorage.
final
bool
keepScrollOffset
;
final
bool
keepScrollOffset
;
/// A label that is used in the [toString] output.
/// A label that is used in the [toString] output.
...
@@ -358,6 +359,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
...
@@ -358,6 +359,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
/// The default implementation writes the [pixels] using the nearest
/// The default implementation writes the [pixels] using the nearest
/// [PageStorage] found from the [context]'s [ScrollContext.storageContext]
/// [PageStorage] found from the [context]'s [ScrollContext.storageContext]
/// property.
/// property.
// TODO(goderbauer): Deprecate this when state restoration supports all features of PageStorage.
@protected
@protected
void
saveScrollOffset
()
{
void
saveScrollOffset
()
{
PageStorage
.
of
(
context
.
storageContext
)?.
writeState
(
context
.
storageContext
,
pixels
);
PageStorage
.
of
(
context
.
storageContext
)?.
writeState
(
context
.
storageContext
,
pixels
);
...
@@ -378,6 +380,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
...
@@ -378,6 +380,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
///
///
/// This method is called from the constructor, so layout has not yet
/// This method is called from the constructor, so layout has not yet
/// occurred, and the viewport dimensions aren't yet known when it is called.
/// occurred, and the viewport dimensions aren't yet known when it is called.
// TODO(goderbauer): Deprecate this when state restoration supports all features of PageStorage.
@protected
@protected
void
restoreScrollOffset
()
{
void
restoreScrollOffset
()
{
if
(
pixels
==
null
)
{
if
(
pixels
==
null
)
{
...
@@ -387,6 +390,42 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
...
@@ -387,6 +390,42 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
}
}
}
}
/// Called by [context] to restore the scroll offset to the provided value.
///
/// The provided value has previously been provided to the [context] by
/// calling [ScrollContext.saveOffset], e.g. from [saveOffset].
///
/// This method may be called right after the scroll position is created
/// before layout has occurred. In that case, `initialRestore` is set to true
/// and the viewport dimensions will not be known yet. If the [context]
/// doesn't have any information to restore the scroll offset this method is
/// not called.
///
/// The method may be called multiple times in the lifecycle of a
/// [ScrollPosition] to restore it to different scroll offsets.
void
restoreOffset
(
double
offset
,
{
bool
initialRestore
=
false
})
{
assert
(
initialRestore
!=
null
);
assert
(
offset
!=
null
);
if
(
initialRestore
)
{
correctPixels
(
offset
);
}
else
{
jumpTo
(
offset
);
}
}
/// Called whenever scrolling ends, to persist the current scroll offset for
/// state restoration purposes.
///
/// The default implementation stores the current value of [pixels] on the
/// [context] by calling [ScrollContext.saveOffset]. At a later point in time
/// or after the application restarts, the [context] may restore the scroll
/// position to the persisted offset by calling [restoreOffset].
@protected
void
saveOffset
()
{
assert
(
pixels
!=
null
);
context
.
saveOffset
(
pixels
);
}
/// Returns the overscroll by applying the boundary conditions.
/// Returns the overscroll by applying the boundary conditions.
///
///
/// If the given value is in bounds, returns 0.0. Otherwise, returns the
/// If the given value is in bounds, returns 0.0. Otherwise, returns the
...
@@ -761,6 +800,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
...
@@ -761,6 +800,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
/// This also saves the scroll offset using [saveScrollOffset].
/// This also saves the scroll offset using [saveScrollOffset].
void
didEndScroll
()
{
void
didEndScroll
()
{
activity
.
dispatchScrollEndNotification
(
copyWith
(),
context
.
notificationContext
);
activity
.
dispatchScrollEndNotification
(
copyWith
(),
context
.
notificationContext
);
saveOffset
();
if
(
keepScrollOffset
)
if
(
keepScrollOffset
)
saveScrollOffset
();
saveScrollOffset
();
}
}
...
...
packages/flutter/lib/src/widgets/scroll_view.dart
View file @
25de9419
...
@@ -91,6 +91,7 @@ abstract class ScrollView extends StatelessWidget {
...
@@ -91,6 +91,7 @@ abstract class ScrollView extends StatelessWidget {
this
.
semanticChildCount
,
this
.
semanticChildCount
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
this
.
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
this
.
restorationId
,
})
:
assert
(
scrollDirection
!=
null
),
})
:
assert
(
scrollDirection
!=
null
),
assert
(
reverse
!=
null
),
assert
(
reverse
!=
null
),
assert
(
shrinkWrap
!=
null
),
assert
(
shrinkWrap
!=
null
),
...
@@ -260,6 +261,9 @@ abstract class ScrollView extends StatelessWidget {
...
@@ -260,6 +261,9 @@ abstract class ScrollView extends StatelessWidget {
/// dismiss the keyboard automatically.
/// dismiss the keyboard automatically.
final
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
;
final
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
;
/// {@macro flutter.widgets.scrollable.restorationId}
final
String
restorationId
;
/// Returns the [AxisDirection] in which the scroll view scrolls.
/// Returns the [AxisDirection] in which the scroll view scrolls.
///
///
/// Combines the [scrollDirection] with the [reverse] boolean to obtain the
/// Combines the [scrollDirection] with the [reverse] boolean to obtain the
...
@@ -333,6 +337,7 @@ abstract class ScrollView extends StatelessWidget {
...
@@ -333,6 +337,7 @@ abstract class ScrollView extends StatelessWidget {
controller:
scrollController
,
controller:
scrollController
,
physics:
physics
,
physics:
physics
,
semanticChildCount:
semanticChildCount
,
semanticChildCount:
semanticChildCount
,
restorationId:
restorationId
,
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
offset
)
{
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
offset
)
{
return
buildViewport
(
context
,
offset
,
axisDirection
,
slivers
);
return
buildViewport
(
context
,
offset
,
axisDirection
,
slivers
);
},
},
...
@@ -568,6 +573,7 @@ class CustomScrollView extends ScrollView {
...
@@ -568,6 +573,7 @@ class CustomScrollView extends ScrollView {
this
.
slivers
=
const
<
Widget
>[],
this
.
slivers
=
const
<
Widget
>[],
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
String
restorationId
,
})
:
super
(
})
:
super
(
key:
key
,
key:
key
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
...
@@ -581,6 +587,7 @@ class CustomScrollView extends ScrollView {
...
@@ -581,6 +587,7 @@ class CustomScrollView extends ScrollView {
cacheExtent:
cacheExtent
,
cacheExtent:
cacheExtent
,
semanticChildCount:
semanticChildCount
,
semanticChildCount:
semanticChildCount
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
restorationId:
restorationId
,
);
);
/// The slivers to place inside the viewport.
/// The slivers to place inside the viewport.
...
@@ -615,6 +622,7 @@ abstract class BoxScrollView extends ScrollView {
...
@@ -615,6 +622,7 @@ abstract class BoxScrollView extends ScrollView {
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
super
(
})
:
super
(
key:
key
,
key:
key
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
...
@@ -627,6 +635,7 @@ abstract class BoxScrollView extends ScrollView {
...
@@ -627,6 +635,7 @@ abstract class BoxScrollView extends ScrollView {
semanticChildCount:
semanticChildCount
,
semanticChildCount:
semanticChildCount
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// The amount of space by which to inset the children.
/// The amount of space by which to inset the children.
...
@@ -1027,6 +1036,7 @@ class ListView extends BoxScrollView {
...
@@ -1027,6 +1036,7 @@ class ListView extends BoxScrollView {
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
childrenDelegate
=
SliverChildListDelegate
(
})
:
childrenDelegate
=
SliverChildListDelegate
(
children
,
children
,
addAutomaticKeepAlives:
addAutomaticKeepAlives
,
addAutomaticKeepAlives:
addAutomaticKeepAlives
,
...
@@ -1046,6 +1056,7 @@ class ListView extends BoxScrollView {
...
@@ -1046,6 +1056,7 @@ class ListView extends BoxScrollView {
semanticChildCount:
semanticChildCount
??
children
.
length
,
semanticChildCount:
semanticChildCount
??
children
.
length
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// Creates a scrollable, linear array of widgets that are created on demand.
/// Creates a scrollable, linear array of widgets that are created on demand.
...
@@ -1098,6 +1109,7 @@ class ListView extends BoxScrollView {
...
@@ -1098,6 +1109,7 @@ class ListView extends BoxScrollView {
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
assert
(
itemCount
==
null
||
itemCount
>=
0
),
})
:
assert
(
itemCount
==
null
||
itemCount
>=
0
),
assert
(
semanticChildCount
==
null
||
semanticChildCount
<=
itemCount
),
assert
(
semanticChildCount
==
null
||
semanticChildCount
<=
itemCount
),
childrenDelegate
=
SliverChildBuilderDelegate
(
childrenDelegate
=
SliverChildBuilderDelegate
(
...
@@ -1120,6 +1132,7 @@ class ListView extends BoxScrollView {
...
@@ -1120,6 +1132,7 @@ class ListView extends BoxScrollView {
semanticChildCount:
semanticChildCount
??
itemCount
,
semanticChildCount:
semanticChildCount
??
itemCount
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// Creates a fixed-length scrollable linear array of list "items" separated
/// Creates a fixed-length scrollable linear array of list "items" separated
...
@@ -1187,6 +1200,7 @@ class ListView extends BoxScrollView {
...
@@ -1187,6 +1200,7 @@ class ListView extends BoxScrollView {
double
cacheExtent
,
double
cacheExtent
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
assert
(
itemBuilder
!=
null
),
})
:
assert
(
itemBuilder
!=
null
),
assert
(
separatorBuilder
!=
null
),
assert
(
separatorBuilder
!=
null
),
assert
(
itemCount
!=
null
&&
itemCount
>=
0
),
assert
(
itemCount
!=
null
&&
itemCount
>=
0
),
...
@@ -1229,6 +1243,7 @@ class ListView extends BoxScrollView {
...
@@ -1229,6 +1243,7 @@ class ListView extends BoxScrollView {
semanticChildCount:
itemCount
,
semanticChildCount:
itemCount
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// Creates a scrollable, linear array of widgets with a custom child model.
/// Creates a scrollable, linear array of widgets with a custom child model.
...
@@ -1328,6 +1343,7 @@ class ListView extends BoxScrollView {
...
@@ -1328,6 +1343,7 @@ class ListView extends BoxScrollView {
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
assert
(
childrenDelegate
!=
null
),
})
:
assert
(
childrenDelegate
!=
null
),
super
(
super
(
key:
key
,
key:
key
,
...
@@ -1342,6 +1358,7 @@ class ListView extends BoxScrollView {
...
@@ -1342,6 +1358,7 @@ class ListView extends BoxScrollView {
semanticChildCount:
semanticChildCount
,
semanticChildCount:
semanticChildCount
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// If non-null, forces the children to have the given extent in the scroll
/// If non-null, forces the children to have the given extent in the scroll
...
@@ -1621,6 +1638,7 @@ class GridView extends BoxScrollView {
...
@@ -1621,6 +1638,7 @@ class GridView extends BoxScrollView {
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
assert
(
gridDelegate
!=
null
),
})
:
assert
(
gridDelegate
!=
null
),
childrenDelegate
=
SliverChildListDelegate
(
childrenDelegate
=
SliverChildListDelegate
(
children
,
children
,
...
@@ -1641,6 +1659,7 @@ class GridView extends BoxScrollView {
...
@@ -1641,6 +1659,7 @@ class GridView extends BoxScrollView {
semanticChildCount:
semanticChildCount
??
children
.
length
,
semanticChildCount:
semanticChildCount
??
children
.
length
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// Creates a scrollable, 2D array of widgets that are created on demand.
/// Creates a scrollable, 2D array of widgets that are created on demand.
...
@@ -1681,6 +1700,7 @@ class GridView extends BoxScrollView {
...
@@ -1681,6 +1700,7 @@ class GridView extends BoxScrollView {
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
assert
(
gridDelegate
!=
null
),
})
:
assert
(
gridDelegate
!=
null
),
childrenDelegate
=
SliverChildBuilderDelegate
(
childrenDelegate
=
SliverChildBuilderDelegate
(
itemBuilder
,
itemBuilder
,
...
@@ -1702,6 +1722,7 @@ class GridView extends BoxScrollView {
...
@@ -1702,6 +1722,7 @@ class GridView extends BoxScrollView {
semanticChildCount:
semanticChildCount
??
itemCount
,
semanticChildCount:
semanticChildCount
??
itemCount
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// Creates a scrollable, 2D array of widgets with both a custom
/// Creates a scrollable, 2D array of widgets with both a custom
...
@@ -1726,6 +1747,7 @@ class GridView extends BoxScrollView {
...
@@ -1726,6 +1747,7 @@ class GridView extends BoxScrollView {
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
assert
(
gridDelegate
!=
null
),
})
:
assert
(
gridDelegate
!=
null
),
assert
(
childrenDelegate
!=
null
),
assert
(
childrenDelegate
!=
null
),
super
(
super
(
...
@@ -1741,6 +1763,7 @@ class GridView extends BoxScrollView {
...
@@ -1741,6 +1763,7 @@ class GridView extends BoxScrollView {
semanticChildCount:
semanticChildCount
,
semanticChildCount:
semanticChildCount
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// Creates a scrollable, 2D array of widgets with a fixed number of tiles in
/// Creates a scrollable, 2D array of widgets with a fixed number of tiles in
...
@@ -1778,6 +1801,7 @@ class GridView extends BoxScrollView {
...
@@ -1778,6 +1801,7 @@ class GridView extends BoxScrollView {
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
gridDelegate
=
SliverGridDelegateWithFixedCrossAxisCount
(
})
:
gridDelegate
=
SliverGridDelegateWithFixedCrossAxisCount
(
crossAxisCount:
crossAxisCount
,
crossAxisCount:
crossAxisCount
,
mainAxisSpacing:
mainAxisSpacing
,
mainAxisSpacing:
mainAxisSpacing
,
...
@@ -1803,6 +1827,7 @@ class GridView extends BoxScrollView {
...
@@ -1803,6 +1827,7 @@ class GridView extends BoxScrollView {
semanticChildCount:
semanticChildCount
??
children
.
length
,
semanticChildCount:
semanticChildCount
??
children
.
length
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// Creates a scrollable, 2D array of widgets with tiles that each have a
/// Creates a scrollable, 2D array of widgets with tiles that each have a
...
@@ -1835,10 +1860,12 @@ class GridView extends BoxScrollView {
...
@@ -1835,10 +1860,12 @@ class GridView extends BoxScrollView {
bool
addAutomaticKeepAlives
=
true
,
bool
addAutomaticKeepAlives
=
true
,
bool
addRepaintBoundaries
=
true
,
bool
addRepaintBoundaries
=
true
,
bool
addSemanticIndexes
=
true
,
bool
addSemanticIndexes
=
true
,
double
cacheExtent
,
List
<
Widget
>
children
=
const
<
Widget
>[],
List
<
Widget
>
children
=
const
<
Widget
>[],
int
semanticChildCount
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
restorationId
,
})
:
gridDelegate
=
SliverGridDelegateWithMaxCrossAxisExtent
(
})
:
gridDelegate
=
SliverGridDelegateWithMaxCrossAxisExtent
(
maxCrossAxisExtent:
maxCrossAxisExtent
,
maxCrossAxisExtent:
maxCrossAxisExtent
,
mainAxisSpacing:
mainAxisSpacing
,
mainAxisSpacing:
mainAxisSpacing
,
...
@@ -1860,9 +1887,11 @@ class GridView extends BoxScrollView {
...
@@ -1860,9 +1887,11 @@ class GridView extends BoxScrollView {
physics:
physics
,
physics:
physics
,
shrinkWrap:
shrinkWrap
,
shrinkWrap:
shrinkWrap
,
padding:
padding
,
padding:
padding
,
cacheExtent:
cacheExtent
,
semanticChildCount:
semanticChildCount
??
children
.
length
,
semanticChildCount:
semanticChildCount
??
children
.
length
,
dragStartBehavior:
dragStartBehavior
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
restorationId:
restorationId
,
);
);
/// A delegate that controls the layout of the children within the [GridView].
/// A delegate that controls the layout of the children within the [GridView].
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
25de9419
...
@@ -12,6 +12,7 @@ import 'package:flutter/gestures.dart';
...
@@ -12,6 +12,7 @@ import 'package:flutter/gestures.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/services.dart'
;
import
'actions.dart'
;
import
'actions.dart'
;
import
'basic.dart'
;
import
'basic.dart'
;
...
@@ -19,6 +20,8 @@ import 'focus_manager.dart';
...
@@ -19,6 +20,8 @@ import 'focus_manager.dart';
import
'framework.dart'
;
import
'framework.dart'
;
import
'gesture_detector.dart'
;
import
'gesture_detector.dart'
;
import
'notification_listener.dart'
;
import
'notification_listener.dart'
;
import
'restoration.dart'
;
import
'restoration_properties.dart'
;
import
'scroll_configuration.dart'
;
import
'scroll_configuration.dart'
;
import
'scroll_context.dart'
;
import
'scroll_context.dart'
;
import
'scroll_controller.dart'
;
import
'scroll_controller.dart'
;
...
@@ -90,6 +93,7 @@ class Scrollable extends StatefulWidget {
...
@@ -90,6 +93,7 @@ class Scrollable extends StatefulWidget {
this
.
excludeFromSemantics
=
false
,
this
.
excludeFromSemantics
=
false
,
this
.
semanticChildCount
,
this
.
semanticChildCount
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
restorationId
,
})
:
assert
(
axisDirection
!=
null
),
})
:
assert
(
axisDirection
!=
null
),
assert
(
dragStartBehavior
!=
null
),
assert
(
dragStartBehavior
!=
null
),
assert
(
viewportBuilder
!=
null
),
assert
(
viewportBuilder
!=
null
),
...
@@ -226,6 +230,22 @@ class Scrollable extends StatefulWidget {
...
@@ -226,6 +230,22 @@ class Scrollable extends StatefulWidget {
/// {@endtemplate}
/// {@endtemplate}
final
DragStartBehavior
dragStartBehavior
;
final
DragStartBehavior
dragStartBehavior
;
/// {@template flutter.widgets.scrollable.restorationId}
/// Restoration ID to save and restore the scroll offset of the scrollable.
///
/// If a restoration id is provided, the scrollable will persist its current
/// scroll offset and restore it during state restoration.
///
/// The scroll offset is persisted in a [RestorationBucket] claimed from
/// the surrounding [RestorationScope] using the provided restoration ID.
///
/// See also:
///
/// * [RestorationManager], which explains how state restoration works in
/// Flutter.
/// {@endtemplate}
final
String
restorationId
;
/// The axis along which the scroll view scrolls.
/// The axis along which the scroll view scrolls.
///
///
/// Determined by the [axisDirection].
/// Determined by the [axisDirection].
...
@@ -239,6 +259,7 @@ class Scrollable extends StatefulWidget {
...
@@ -239,6 +259,7 @@ class Scrollable extends StatefulWidget {
super
.
debugFillProperties
(
properties
);
super
.
debugFillProperties
(
properties
);
properties
.
add
(
EnumProperty
<
AxisDirection
>(
'axisDirection'
,
axisDirection
));
properties
.
add
(
EnumProperty
<
AxisDirection
>(
'axisDirection'
,
axisDirection
));
properties
.
add
(
DiagnosticsProperty
<
ScrollPhysics
>(
'physics'
,
physics
));
properties
.
add
(
DiagnosticsProperty
<
ScrollPhysics
>(
'physics'
,
physics
));
properties
.
add
(
StringProperty
(
'restorationId'
,
restorationId
));
}
}
/// The state from the closest instance of this class that encloses the given context.
/// The state from the closest instance of this class that encloses the given context.
...
@@ -340,7 +361,7 @@ class _ScrollableScope extends InheritedWidget {
...
@@ -340,7 +361,7 @@ class _ScrollableScope extends InheritedWidget {
///
///
/// This class is not intended to be subclassed. To specialize the behavior of a
/// This class is not intended to be subclassed. To specialize the behavior of a
/// [Scrollable], provide it with a [ScrollPhysics].
/// [Scrollable], provide it with a [ScrollPhysics].
class
ScrollableState
extends
State
<
Scrollable
>
with
TickerProviderStateMixin
class
ScrollableState
extends
State
<
Scrollable
>
with
TickerProviderStateMixin
,
RestorationMixin
implements
ScrollContext
{
implements
ScrollContext
{
/// The manager for this [Scrollable] widget's viewport position.
/// The manager for this [Scrollable] widget's viewport position.
///
///
...
@@ -350,6 +371,8 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
...
@@ -350,6 +371,8 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
ScrollPosition
get
position
=>
_position
;
ScrollPosition
get
position
=>
_position
;
ScrollPosition
_position
;
ScrollPosition
_position
;
final
_RestorableScrollOffset
_persistedScrollOffset
=
_RestorableScrollOffset
();
@override
@override
AxisDirection
get
axisDirection
=>
widget
.
axisDirection
;
AxisDirection
get
axisDirection
=>
widget
.
axisDirection
;
...
@@ -378,10 +401,28 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
...
@@ -378,10 +401,28 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
controller
?.
attach
(
position
);
controller
?.
attach
(
position
);
}
}
@override
void
restoreState
(
RestorationBucket
oldBucket
)
{
registerForRestoration
(
_persistedScrollOffset
,
'offset'
);
assert
(
position
!=
null
);
if
(
_persistedScrollOffset
.
value
!=
null
)
{
position
.
restoreOffset
(
_persistedScrollOffset
.
value
,
initialRestore:
oldBucket
==
null
);
}
}
@override
void
saveOffset
(
double
offset
)
{
assert
(
debugIsSerializableForRestoration
(
offset
));
_persistedScrollOffset
.
value
=
offset
;
// [saveOffset] is called after a scrolling ends and it is usually not
// followed by a frame. Therefore, manually flush restoration data.
ServicesBinding
.
instance
.
restorationManager
.
flushData
();
}
@override
@override
void
didChangeDependencies
()
{
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
_updatePosition
();
_updatePosition
();
super
.
didChangeDependencies
();
}
}
bool
_shouldUpdatePosition
(
Scrollable
oldWidget
)
{
bool
_shouldUpdatePosition
(
Scrollable
oldWidget
)
{
...
@@ -414,6 +455,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
...
@@ -414,6 +455,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
void
dispose
()
{
void
dispose
()
{
widget
.
controller
?.
detach
(
position
);
widget
.
controller
?.
detach
(
position
);
position
.
dispose
();
position
.
dispose
();
_persistedScrollOffset
.
dispose
();
super
.
dispose
();
super
.
dispose
();
}
}
...
@@ -663,6 +705,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
...
@@ -663,6 +705,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
super
.
debugFillProperties
(
properties
);
super
.
debugFillProperties
(
properties
);
properties
.
add
(
DiagnosticsProperty
<
ScrollPosition
>(
'position'
,
position
));
properties
.
add
(
DiagnosticsProperty
<
ScrollPosition
>(
'position'
,
position
));
}
}
@override
String
get
restorationId
=>
widget
.
restorationId
;
}
}
/// With [_ScrollSemantics] certain child [SemanticsNode]s can be
/// With [_ScrollSemantics] certain child [SemanticsNode]s can be
...
@@ -1029,3 +1074,28 @@ class ScrollAction extends Action<ScrollIntent> {
...
@@ -1029,3 +1074,28 @@ class ScrollAction extends Action<ScrollIntent> {
);
);
}
}
}
}
// Not using a RestorableDouble because we want to allow null values and override
// [enabled].
class
_RestorableScrollOffset
extends
RestorableValue
<
double
>
{
@override
double
createDefaultValue
()
=>
null
;
@override
void
didUpdateValue
(
double
oldValue
)
{
notifyListeners
();
}
@override
double
fromPrimitives
(
Object
data
)
{
return
data
as
double
;
}
@override
Object
toPrimitives
()
{
return
value
;
}
@override
bool
get
enabled
=>
value
!=
null
;
}
packages/flutter/lib/src/widgets/single_child_scroll_view.dart
View file @
25de9419
...
@@ -222,6 +222,7 @@ class SingleChildScrollView extends StatelessWidget {
...
@@ -222,6 +222,7 @@ class SingleChildScrollView extends StatelessWidget {
this
.
child
,
this
.
child
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
restorationId
,
})
:
assert
(
scrollDirection
!=
null
),
})
:
assert
(
scrollDirection
!=
null
),
assert
(
dragStartBehavior
!=
null
),
assert
(
dragStartBehavior
!=
null
),
assert
(
clipBehavior
!=
null
),
assert
(
clipBehavior
!=
null
),
...
@@ -299,6 +300,9 @@ class SingleChildScrollView extends StatelessWidget {
...
@@ -299,6 +300,9 @@ class SingleChildScrollView extends StatelessWidget {
/// Defaults to [Clip.hardEdge].
/// Defaults to [Clip.hardEdge].
final
Clip
clipBehavior
;
final
Clip
clipBehavior
;
/// {@macro flutter.widgets.scrollable.restorationId}
final
String
restorationId
;
AxisDirection
_getDirection
(
BuildContext
context
)
{
AxisDirection
_getDirection
(
BuildContext
context
)
{
return
getAxisDirectionFromAxisReverseAndDirectionality
(
context
,
scrollDirection
,
reverse
);
return
getAxisDirectionFromAxisReverseAndDirectionality
(
context
,
scrollDirection
,
reverse
);
}
}
...
@@ -317,6 +321,7 @@ class SingleChildScrollView extends StatelessWidget {
...
@@ -317,6 +321,7 @@ class SingleChildScrollView extends StatelessWidget {
axisDirection:
axisDirection
,
axisDirection:
axisDirection
,
controller:
scrollController
,
controller:
scrollController
,
physics:
physics
,
physics:
physics
,
restorationId:
restorationId
,
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
offset
)
{
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
offset
)
{
return
_SingleChildViewport
(
return
_SingleChildViewport
(
axisDirection:
axisDirection
,
axisDirection:
axisDirection
,
...
...
packages/flutter/test/services/restoration_test.dart
View file @
25de9419
...
@@ -7,6 +7,7 @@
...
@@ -7,6 +7,7 @@
import
'dart:async'
;
import
'dart:async'
;
import
'dart:typed_data'
;
import
'dart:typed_data'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
...
@@ -197,6 +198,42 @@ void main() {
...
@@ -197,6 +198,42 @@ void main() {
});
});
expect
(
rootBucket
,
isNull
);
expect
(
rootBucket
,
isNull
);
});
});
testWidgets
(
'flushData'
,
(
WidgetTester
tester
)
async
{
final
List
<
MethodCall
>
callsToEngine
=
<
MethodCall
>[];
final
Completer
<
Map
<
dynamic
,
dynamic
>>
result
=
Completer
<
Map
<
dynamic
,
dynamic
>>();
SystemChannels
.
restoration
.
setMockMethodCallHandler
((
MethodCall
call
)
{
callsToEngine
.
add
(
call
);
return
result
.
future
;
});
final
RestorationManager
manager
=
RestorationManager
();
final
Future
<
RestorationBucket
>
rootBucketFuture
=
manager
.
rootBucket
;
RestorationBucket
rootBucket
;
rootBucketFuture
.
then
((
RestorationBucket
bucket
)
{
rootBucket
=
bucket
;
});
result
.
complete
(
_createEncodedRestorationData1
());
await
tester
.
pump
();
expect
(
rootBucket
,
isNotNull
);
callsToEngine
.
clear
();
// Schedule a frame.
SchedulerBinding
.
instance
.
ensureVisualUpdate
();
rootBucket
.
write
(
'foo'
,
1
);
// flushData is no-op because frame is scheduled.
manager
.
flushData
();
expect
(
callsToEngine
,
isEmpty
);
// Data is flushed at the end of the frame.
await
tester
.
pump
();
expect
(
callsToEngine
,
hasLength
(
1
));
callsToEngine
.
clear
();
// flushData without frame sends data directly.
rootBucket
.
write
(
'foo'
,
2
);
manager
.
flushData
();
expect
(
callsToEngine
,
hasLength
(
1
));
});
});
});
test
(
'debugIsSerializableForRestoration'
,
()
{
test
(
'debugIsSerializableForRestoration'
,
()
{
...
...
packages/flutter/test/widgets/scrollable_restoration_test.dart
0 → 100644
View file @
25de9419
This diff is collapsed.
Click to expand it.
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