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
0e70a97e
Unverified
Commit
0e70a97e
authored
Nov 02, 2022
by
Kate Lovett
Committed by
GitHub
Nov 02, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor Animated[List, Grid, SliverList, SliverGrid] to share common code (#113793)
parent
1cfdac4b
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
672 additions
and
983 deletions
+672
-983
sliver_grid.dart
packages/flutter/lib/src/rendering/sliver_grid.dart
+3
-3
animated_list.dart
packages/flutter/lib/src/widgets/animated_list.dart
+0
-700
animated_scroll_view.dart
packages/flutter/lib/src/widgets/animated_scroll_view.dart
+668
-278
widgets.dart
packages/flutter/lib/widgets.dart
+1
-2
No files found.
packages/flutter/lib/src/rendering/sliver_grid.dart
View file @
0e70a97e
...
@@ -462,9 +462,9 @@ class SliverGridDelegateWithMaxCrossAxisExtent extends SliverGridDelegate {
...
@@ -462,9 +462,9 @@ class SliverGridDelegateWithMaxCrossAxisExtent extends SliverGridDelegate {
SliverGridLayout
getLayout
(
SliverConstraints
constraints
)
{
SliverGridLayout
getLayout
(
SliverConstraints
constraints
)
{
assert
(
_debugAssertIsValid
(
constraints
.
crossAxisExtent
));
assert
(
_debugAssertIsValid
(
constraints
.
crossAxisExtent
));
int
crossAxisCount
=
(
constraints
.
crossAxisExtent
/
(
maxCrossAxisExtent
+
crossAxisSpacing
)).
ceil
();
int
crossAxisCount
=
(
constraints
.
crossAxisExtent
/
(
maxCrossAxisExtent
+
crossAxisSpacing
)).
ceil
();
//
TODO(gspencergoog): Figure out why we need this in release mode (and only
//
Ensure a minimum count of 1, can be zero and result in an infinite extent
//
in release mode). https://github.com/flutter/flutter/issues/113109
//
below when the window size is 0.
crossAxisCount
=
crossAxisCount
<
1
?
1
:
crossAxisCount
;
crossAxisCount
=
math
.
max
(
1
,
crossAxisCount
)
;
final
double
usableCrossAxisExtent
=
math
.
max
(
final
double
usableCrossAxisExtent
=
math
.
max
(
0.0
,
0.0
,
constraints
.
crossAxisExtent
-
crossAxisSpacing
*
(
crossAxisCount
-
1
),
constraints
.
crossAxisExtent
-
crossAxisSpacing
*
(
crossAxisCount
-
1
),
...
...
packages/flutter/lib/src/widgets/animated_list.dart
deleted
100644 → 0
View file @
1cfdac4b
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'animated_grid.dart'
;
import
'basic.dart'
;
import
'framework.dart'
;
import
'scroll_controller.dart'
;
import
'scroll_physics.dart'
;
import
'scroll_view.dart'
;
import
'sliver.dart'
;
import
'ticker_provider.dart'
;
/// Signature for the builder callback used by [AnimatedList].
///
/// This is deprecated, use the identical [AnimatedItemBuilder] instead.
@Deprecated
(
'Use AnimatedItemBuilder instead. '
'This feature was deprecated after v3.5.0-4.0.pre.'
,
)
typedef
AnimatedListItemBuilder
=
Widget
Function
(
BuildContext
context
,
int
index
,
Animation
<
double
>
animation
);
/// Signature for the builder callback used by [AnimatedListState.removeItem].
///
/// This is deprecated, use the identical [AnimatedRemovedItemBuilder]
/// instead.
@Deprecated
(
'Use AnimatedRemovedItemBuilder instead. '
'This feature was deprecated after v3.5.0-4.0.pre.'
,
)
typedef
AnimatedListRemovedItemBuilder
=
Widget
Function
(
BuildContext
context
,
Animation
<
double
>
animation
);
// The default insert/remove animation duration.
const
Duration
_kDuration
=
Duration
(
milliseconds:
300
);
// Incoming and outgoing AnimatedList items.
class
_ActiveItem
implements
Comparable
<
_ActiveItem
>
{
_ActiveItem
.
incoming
(
this
.
controller
,
this
.
itemIndex
)
:
removedItemBuilder
=
null
;
_ActiveItem
.
outgoing
(
this
.
controller
,
this
.
itemIndex
,
this
.
removedItemBuilder
);
_ActiveItem
.
index
(
this
.
itemIndex
)
:
controller
=
null
,
removedItemBuilder
=
null
;
final
AnimationController
?
controller
;
final
AnimatedRemovedItemBuilder
?
removedItemBuilder
;
int
itemIndex
;
@override
int
compareTo
(
_ActiveItem
other
)
=>
itemIndex
-
other
.
itemIndex
;
}
/// A scrolling container that animates items when they are inserted or removed.
///
/// This widget's [AnimatedListState] can be used to dynamically insert or
/// remove items. To refer to the [AnimatedListState] either provide a
/// [GlobalKey] or use the static [of] method from an item's input callback.
///
/// This widget is similar to one created by [ListView.builder].
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=ZtfItHwFlZ8}
///
/// {@tool dartpad}
/// This sample application uses an [AnimatedList] to create an effect when
/// items are removed or added to the list.
///
/// ** See code in examples/api/lib/widgets/animated_list/animated_list.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [SliverAnimatedList], a sliver that animates items when they are inserted
/// or removed from a list.
/// * [SliverAnimatedGrid], a sliver which animates items when they are
/// inserted or removed from a grid.
/// * [AnimatedGrid], a non-sliver scrolling container that animates items when
/// they are inserted or removed in a grid.
class
AnimatedList
extends
StatefulWidget
{
/// Creates a scrolling container that animates items when they are inserted
/// or removed.
const
AnimatedList
({
super
.
key
,
required
this
.
itemBuilder
,
this
.
initialItemCount
=
0
,
this
.
scrollDirection
=
Axis
.
vertical
,
this
.
reverse
=
false
,
this
.
controller
,
this
.
primary
,
this
.
physics
,
this
.
shrinkWrap
=
false
,
this
.
padding
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
/// Called, as needed, to build list item widgets.
///
/// List items are only built when they're scrolled into view.
///
/// The [AnimatedItemBuilder] index parameter indicates the item's
/// position in the list. The value of the index parameter will be between 0
/// and [initialItemCount] plus the total number of items that have been
/// inserted with [AnimatedListState.insertItem] and less the total number of
/// items that have been removed with [AnimatedListState.removeItem].
///
/// Implementations of this callback should assume that
/// [AnimatedListState.removeItem] removes an item immediately.
final
AnimatedItemBuilder
itemBuilder
;
/// {@template flutter.widgets.animatedList.initialItemCount}
/// The number of items the list will start with.
///
/// The appearance of the initial items is not animated. They
/// are created, as needed, by [itemBuilder] with an animation parameter
/// of [kAlwaysCompleteAnimation].
/// {@endtemplate}
final
int
initialItemCount
;
/// The axis along which the scroll view scrolls.
///
/// Defaults to [Axis.vertical].
final
Axis
scrollDirection
;
/// Whether the scroll view scrolls in the reading direction.
///
/// For example, if the reading direction is left-to-right and
/// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from
/// left to right when [reverse] is false and from right to left when
/// [reverse] is true.
///
/// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view
/// scrolls from top to bottom when [reverse] is false and from bottom to top
/// when [reverse] is true.
///
/// Defaults to false.
final
bool
reverse
;
/// An object that can be used to control the position to which this scroll
/// view is scrolled.
///
/// Must be null if [primary] is true.
///
/// A [ScrollController] serves several purposes. It can be used to control
/// the initial scroll position (see [ScrollController.initialScrollOffset]).
/// It can be used to control whether the scroll view should automatically
/// save and restore its scroll position in the [PageStorage] (see
/// [ScrollController.keepScrollOffset]). It can be used to read the current
/// scroll position (see [ScrollController.offset]), or change it (see
/// [ScrollController.animateTo]).
final
ScrollController
?
controller
;
/// Whether this is the primary scroll view associated with the parent
/// [PrimaryScrollController].
///
/// On iOS, this identifies the scroll view that will scroll to top in
/// response to a tap in the status bar.
///
/// Defaults to true when [scrollDirection] is [Axis.vertical] and
/// [controller] is null.
final
bool
?
primary
;
/// How the scroll view should respond to user input.
///
/// For example, determines how the scroll view continues to animate after the
/// user stops dragging the scroll view.
///
/// Defaults to matching platform conventions.
final
ScrollPhysics
?
physics
;
/// Whether the extent of the scroll view in the [scrollDirection] should be
/// determined by the contents being viewed.
///
/// If the scroll view does not shrink wrap, then the scroll view will expand
/// to the maximum allowed size in the [scrollDirection]. If the scroll view
/// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must
/// be true.
///
/// Shrink wrapping the content of the scroll view is significantly more
/// expensive than expanding to the maximum allowed size because the content
/// can expand and contract during scrolling, which means the size of the
/// scroll view needs to be recomputed whenever the scroll position changes.
///
/// Defaults to false.
final
bool
shrinkWrap
;
/// The amount of space by which to inset the children.
final
EdgeInsetsGeometry
?
padding
;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.hardEdge].
final
Clip
clipBehavior
;
/// The state from the closest instance of this class that encloses the given
/// context.
///
/// This method is typically used by [AnimatedList] item widgets that insert
/// or remove items in response to user input.
///
/// If no [AnimatedList] surrounds the context given, then this function will
/// assert in debug mode and throw an exception in release mode.
///
/// This method can be expensive (it walks the element tree).
///
/// See also:
///
/// * [maybeOf], a similar function that will return null if no
/// [AnimatedList] ancestor is found.
static
AnimatedListState
of
(
BuildContext
context
)
{
assert
(
context
!=
null
);
final
AnimatedListState
?
result
=
context
.
findAncestorStateOfType
<
AnimatedListState
>();
assert
(()
{
if
(
result
==
null
)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'AnimatedList.of() called with a context that does not contain an AnimatedList.'
),
ErrorDescription
(
'No AnimatedList ancestor could be found starting from the context that was passed to AnimatedList.of().'
,
),
ErrorHint
(
'This can happen when the context provided is from the same StatefulWidget that '
'built the AnimatedList. Please see the AnimatedList documentation for examples '
'of how to refer to an AnimatedListState object:
\n
'
' https://api.flutter.dev/flutter/widgets/AnimatedListState-class.html'
,
),
context
.
describeElement
(
'The context used was'
),
]);
}
return
true
;
}());
return
result
!;
}
/// The state from the closest instance of this class that encloses the given
/// context.
///
/// This method is typically used by [AnimatedList] item widgets that insert
/// or remove items in response to user input.
///
/// If no [AnimatedList] surrounds the context given, then this function will
/// return null.
///
/// This method can be expensive (it walks the element tree).
///
/// See also:
///
/// * [of], a similar function that will throw if no [AnimatedList] ancestor
/// is found.
static
AnimatedListState
?
maybeOf
(
BuildContext
context
)
{
assert
(
context
!=
null
);
return
context
.
findAncestorStateOfType
<
AnimatedListState
>();
}
@override
AnimatedListState
createState
()
=>
AnimatedListState
();
}
/// The state for a scrolling container that animates items when they are
/// inserted or removed.
///
/// When an item is inserted with [insertItem] an animation begins running. The
/// animation is passed to [AnimatedList.itemBuilder] whenever the item's widget
/// is needed.
///
/// When an item is removed with [removeItem] its animation is reversed.
/// The removed item's animation is passed to the [removeItem] builder
/// parameter.
///
/// An app that needs to insert or remove items in response to an event
/// can refer to the [AnimatedList]'s state with a global key:
///
/// ```dart
/// // (e.g. in a stateful widget)
/// GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
///
/// // ...
///
/// @override
/// Widget build(BuildContext context) {
/// return AnimatedList(
/// key: listKey,
/// itemBuilder: (BuildContext context, int index, Animation<double> animation) {
/// return const Placeholder();
/// },
/// );
/// }
///
/// // ...
///
/// void _updateList() {
/// // adds "123" to the AnimatedList
/// listKey.currentState!.insertItem(123);
/// }
/// ```
///
/// [AnimatedList] item input handlers can also refer to their [AnimatedListState]
/// with the static [AnimatedList.of] method.
class
AnimatedListState
extends
State
<
AnimatedList
>
with
TickerProviderStateMixin
<
AnimatedList
>
{
final
GlobalKey
<
SliverAnimatedListState
>
_sliverAnimatedListKey
=
GlobalKey
();
/// Insert an item at [index] and start an animation that will be passed
/// to [AnimatedList.itemBuilder] when the item is visible.
///
/// This method's semantics are the same as Dart's [List.insert] method:
/// it increases the length of the list by one and shifts all items at or
/// after [index] towards the end of the list.
void
insertItem
(
int
index
,
{
Duration
duration
=
_kDuration
})
{
_sliverAnimatedListKey
.
currentState
!.
insertItem
(
index
,
duration:
duration
);
}
/// Remove the item at [index] and start an animation that will be passed
/// to [builder] when the item is visible.
///
/// Items are removed immediately. After an item has been removed, its index
/// will no longer be passed to the [AnimatedList.itemBuilder]. However the
/// item will still appear in the list for [duration] and during that time
/// [builder] must construct its widget as needed.
///
/// This method's semantics are the same as Dart's [List.remove] method:
/// it decreases the length of the list by one and shifts all items at or
/// before [index] towards the beginning of the list.
void
removeItem
(
int
index
,
AnimatedRemovedItemBuilder
builder
,
{
Duration
duration
=
_kDuration
})
{
_sliverAnimatedListKey
.
currentState
!.
removeItem
(
index
,
builder
,
duration:
duration
);
}
@override
Widget
build
(
BuildContext
context
)
{
return
CustomScrollView
(
scrollDirection:
widget
.
scrollDirection
,
reverse:
widget
.
reverse
,
controller:
widget
.
controller
,
primary:
widget
.
primary
,
physics:
widget
.
physics
,
shrinkWrap:
widget
.
shrinkWrap
,
clipBehavior:
widget
.
clipBehavior
,
slivers:
<
Widget
>[
SliverPadding
(
padding:
widget
.
padding
??
EdgeInsets
.
zero
,
sliver:
SliverAnimatedList
(
key:
_sliverAnimatedListKey
,
itemBuilder:
widget
.
itemBuilder
,
initialItemCount:
widget
.
initialItemCount
,
),
),
],
);
}
}
/// A sliver that animates items when they are inserted or removed.
///
/// This widget's [SliverAnimatedListState] can be used to dynamically insert or
/// remove items. To refer to the [SliverAnimatedListState] either provide a
/// [GlobalKey] or use the static [SliverAnimatedList.of] method from an item's
/// input callback.
///
/// {@tool dartpad}
/// This sample application uses a [SliverAnimatedList] to create an animated
/// effect when items are removed or added to the list.
///
/// ** See code in examples/api/lib/widgets/animated_list/sliver_animated_list.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [SliverList], which does not animate items when they are inserted or
/// removed.
/// * [AnimatedList], a non-sliver scrolling container that animates items when
/// they are inserted or removed.
/// * [SliverAnimatedGrid], a sliver which animates items when they are
/// inserted into or removed from a grid.
/// * [AnimatedGrid], a non-sliver scrolling container that animates items when
/// they are inserted into or removed from a grid.
class
SliverAnimatedList
extends
StatefulWidget
{
/// Creates a sliver that animates items when they are inserted or removed.
const
SliverAnimatedList
({
super
.
key
,
required
this
.
itemBuilder
,
this
.
findChildIndexCallback
,
this
.
initialItemCount
=
0
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
/// Called, as needed, to build list item widgets.
///
/// List items are only built when they're scrolled into view.
///
/// The [AnimatedItemBuilder] index parameter indicates the item's
/// position in the list. The value of the index parameter will be between 0
/// and [initialItemCount] plus the total number of items that have been
/// inserted with [SliverAnimatedListState.insertItem] and less the total
/// number of items that have been removed with
/// [SliverAnimatedListState.removeItem].
///
/// Implementations of this callback should assume that
/// [SliverAnimatedListState.removeItem] removes an item immediately.
final
AnimatedItemBuilder
itemBuilder
;
/// {@macro flutter.widgets.SliverChildBuilderDelegate.findChildIndexCallback}
final
ChildIndexGetter
?
findChildIndexCallback
;
/// {@macro flutter.widgets.animatedList.initialItemCount}
final
int
initialItemCount
;
@override
SliverAnimatedListState
createState
()
=>
SliverAnimatedListState
();
/// The state from the closest instance of this class that encloses the given
/// context.
///
/// This method is typically used by [SliverAnimatedList] item widgets that
/// insert or remove items in response to user input.
///
/// If no [SliverAnimatedList] surrounds the context given, then this function
/// will assert in debug mode and throw an exception in release mode.
///
/// This method can be expensive (it walks the element tree).
///
/// See also:
///
/// * [maybeOf], a similar function that will return null if no
/// [SliverAnimatedList] ancestor is found.
static
SliverAnimatedListState
of
(
BuildContext
context
)
{
assert
(
context
!=
null
);
final
SliverAnimatedListState
?
result
=
context
.
findAncestorStateOfType
<
SliverAnimatedListState
>();
assert
(()
{
if
(
result
==
null
)
{
throw
FlutterError
(
'SliverAnimatedList.of() called with a context that does not contain a SliverAnimatedList.
\n
'
'No SliverAnimatedListState ancestor could be found starting from the '
'context that was passed to SliverAnimatedListState.of(). This can '
'happen when the context provided is from the same StatefulWidget that '
'built the AnimatedList. Please see the SliverAnimatedList documentation '
'for examples of how to refer to an AnimatedListState object: '
'https://api.flutter.dev/flutter/widgets/SliverAnimatedListState-class.html
\n
'
'The context used was:
\n
'
'
$context
'
,
);
}
return
true
;
}());
return
result
!;
}
/// The state from the closest instance of this class that encloses the given
/// context.
///
/// This method is typically used by [SliverAnimatedList] item widgets that
/// insert or remove items in response to user input.
///
/// If no [SliverAnimatedList] surrounds the context given, then this function
/// will return null.
///
/// This method can be expensive (it walks the element tree).
///
/// See also:
///
/// * [of], a similar function that will throw if no [SliverAnimatedList]
/// ancestor is found.
static
SliverAnimatedListState
?
maybeOf
(
BuildContext
context
)
{
assert
(
context
!=
null
);
return
context
.
findAncestorStateOfType
<
SliverAnimatedListState
>();
}
}
/// The state for a sliver that animates items when they are
/// inserted or removed.
///
/// When an item is inserted with [insertItem] an animation begins running. The
/// animation is passed to [SliverAnimatedList.itemBuilder] whenever the item's
/// widget is needed.
///
/// When an item is removed with [removeItem] its animation is reversed.
/// The removed item's animation is passed to the [removeItem] builder
/// parameter.
///
/// An app that needs to insert or remove items in response to an event
/// can refer to the [SliverAnimatedList]'s state with a global key:
///
/// ```dart
/// // (e.g. in a stateful widget)
/// GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
///
/// // ...
///
/// @override
/// Widget build(BuildContext context) {
/// return AnimatedList(
/// key: listKey,
/// itemBuilder: (BuildContext context, int index, Animation<double> animation) {
/// return const Placeholder();
/// },
/// );
/// }
///
/// // ...
///
/// void _updateList() {
/// // adds "123" to the AnimatedList
/// listKey.currentState!.insertItem(123);
/// }
/// ```
///
/// [SliverAnimatedList] item input handlers can also refer to their
/// [SliverAnimatedListState] with the static [SliverAnimatedList.of] method.
class
SliverAnimatedListState
extends
State
<
SliverAnimatedList
>
with
TickerProviderStateMixin
{
final
List
<
_ActiveItem
>
_incomingItems
=
<
_ActiveItem
>[];
final
List
<
_ActiveItem
>
_outgoingItems
=
<
_ActiveItem
>[];
int
_itemsCount
=
0
;
@override
void
initState
()
{
super
.
initState
();
_itemsCount
=
widget
.
initialItemCount
;
}
@override
void
dispose
()
{
for
(
final
_ActiveItem
item
in
_incomingItems
.
followedBy
(
_outgoingItems
))
{
item
.
controller
!.
dispose
();
}
super
.
dispose
();
}
_ActiveItem
?
_removeActiveItemAt
(
List
<
_ActiveItem
>
items
,
int
itemIndex
)
{
final
int
i
=
binarySearch
(
items
,
_ActiveItem
.
index
(
itemIndex
));
return
i
==
-
1
?
null
:
items
.
removeAt
(
i
);
}
_ActiveItem
?
_activeItemAt
(
List
<
_ActiveItem
>
items
,
int
itemIndex
)
{
final
int
i
=
binarySearch
(
items
,
_ActiveItem
.
index
(
itemIndex
));
return
i
==
-
1
?
null
:
items
[
i
];
}
// The insertItem() and removeItem() index parameters are defined as if the
// removeItem() operation removed the corresponding list entry immediately.
// The entry is only actually removed from the ListView when the remove animation
// finishes. The entry is added to _outgoingItems when removeItem is called
// and removed from _outgoingItems when the remove animation finishes.
int
_indexToItemIndex
(
int
index
)
{
int
itemIndex
=
index
;
for
(
final
_ActiveItem
item
in
_outgoingItems
)
{
if
(
item
.
itemIndex
<=
itemIndex
)
{
itemIndex
+=
1
;
}
else
{
break
;
}
}
return
itemIndex
;
}
int
_itemIndexToIndex
(
int
itemIndex
)
{
int
index
=
itemIndex
;
for
(
final
_ActiveItem
item
in
_outgoingItems
)
{
assert
(
item
.
itemIndex
!=
itemIndex
);
if
(
item
.
itemIndex
<
itemIndex
)
{
index
-=
1
;
}
else
{
break
;
}
}
return
index
;
}
SliverChildDelegate
_createDelegate
()
{
return
SliverChildBuilderDelegate
(
_itemBuilder
,
childCount:
_itemsCount
,
findChildIndexCallback:
widget
.
findChildIndexCallback
==
null
?
null
:
(
Key
key
)
{
final
int
?
index
=
widget
.
findChildIndexCallback
!(
key
);
return
index
!=
null
?
_indexToItemIndex
(
index
)
:
null
;
},
);
}
/// Insert an item at [index] and start an animation that will be passed to
/// [SliverAnimatedList.itemBuilder] when the item is visible.
///
/// This method's semantics are the same as Dart's [List.insert] method:
/// it increases the length of the list by one and shifts all items at or
/// after [index] towards the end of the list.
void
insertItem
(
int
index
,
{
Duration
duration
=
_kDuration
})
{
assert
(
index
!=
null
&&
index
>=
0
);
assert
(
duration
!=
null
);
final
int
itemIndex
=
_indexToItemIndex
(
index
);
assert
(
itemIndex
>=
0
&&
itemIndex
<=
_itemsCount
);
// Increment the incoming and outgoing item indices to account
// for the insertion.
for
(
final
_ActiveItem
item
in
_incomingItems
)
{
if
(
item
.
itemIndex
>=
itemIndex
)
{
item
.
itemIndex
+=
1
;
}
}
for
(
final
_ActiveItem
item
in
_outgoingItems
)
{
if
(
item
.
itemIndex
>=
itemIndex
)
{
item
.
itemIndex
+=
1
;
}
}
final
AnimationController
controller
=
AnimationController
(
duration:
duration
,
vsync:
this
,
);
final
_ActiveItem
incomingItem
=
_ActiveItem
.
incoming
(
controller
,
itemIndex
,
);
setState
(()
{
_incomingItems
..
add
(
incomingItem
)
..
sort
();
_itemsCount
+=
1
;
});
controller
.
forward
().
then
<
void
>((
_
)
{
_removeActiveItemAt
(
_incomingItems
,
incomingItem
.
itemIndex
)!.
controller
!.
dispose
();
});
}
/// Remove the item at [index] and start an animation that will be passed
/// to [builder] when the item is visible.
///
/// Items are removed immediately. After an item has been removed, its index
/// will no longer be passed to the [SliverAnimatedList.itemBuilder]. However
/// the item will still appear in the list for [duration] and during that time
/// [builder] must construct its widget as needed.
///
/// This method's semantics are the same as Dart's [List.remove] method:
/// it decreases the length of the list by one and shifts all items at or
/// before [index] towards the beginning of the list.
void
removeItem
(
int
index
,
AnimatedRemovedItemBuilder
builder
,
{
Duration
duration
=
_kDuration
})
{
assert
(
index
!=
null
&&
index
>=
0
);
assert
(
builder
!=
null
);
assert
(
duration
!=
null
);
final
int
itemIndex
=
_indexToItemIndex
(
index
);
assert
(
itemIndex
>=
0
&&
itemIndex
<
_itemsCount
);
assert
(
_activeItemAt
(
_outgoingItems
,
itemIndex
)
==
null
);
final
_ActiveItem
?
incomingItem
=
_removeActiveItemAt
(
_incomingItems
,
itemIndex
);
final
AnimationController
controller
=
incomingItem
?.
controller
??
AnimationController
(
duration:
duration
,
value:
1.0
,
vsync:
this
);
final
_ActiveItem
outgoingItem
=
_ActiveItem
.
outgoing
(
controller
,
itemIndex
,
builder
);
setState
(()
{
_outgoingItems
..
add
(
outgoingItem
)
..
sort
();
});
controller
.
reverse
().
then
<
void
>((
void
value
)
{
_removeActiveItemAt
(
_outgoingItems
,
outgoingItem
.
itemIndex
)!.
controller
!.
dispose
();
// Decrement the incoming and outgoing item indices to account
// for the removal.
for
(
final
_ActiveItem
item
in
_incomingItems
)
{
if
(
item
.
itemIndex
>
outgoingItem
.
itemIndex
)
{
item
.
itemIndex
-=
1
;
}
}
for
(
final
_ActiveItem
item
in
_outgoingItems
)
{
if
(
item
.
itemIndex
>
outgoingItem
.
itemIndex
)
{
item
.
itemIndex
-=
1
;
}
}
setState
(()
=>
_itemsCount
-=
1
);
});
}
Widget
_itemBuilder
(
BuildContext
context
,
int
itemIndex
)
{
final
_ActiveItem
?
outgoingItem
=
_activeItemAt
(
_outgoingItems
,
itemIndex
);
if
(
outgoingItem
!=
null
)
{
return
outgoingItem
.
removedItemBuilder
!(
context
,
outgoingItem
.
controller
!.
view
,
);
}
final
_ActiveItem
?
incomingItem
=
_activeItemAt
(
_incomingItems
,
itemIndex
);
final
Animation
<
double
>
animation
=
incomingItem
?.
controller
?.
view
??
kAlwaysCompleteAnimation
;
return
widget
.
itemBuilder
(
context
,
_itemIndexToIndex
(
itemIndex
),
animation
,
);
}
@override
Widget
build
(
BuildContext
context
)
{
return
SliverList
(
delegate:
_createDelegate
(),
);
}
}
packages/flutter/lib/src/widgets/animated_
grid
.dart
→
packages/flutter/lib/src/widgets/animated_
scroll_view
.dart
View file @
0e70a97e
...
@@ -12,54 +12,173 @@ import 'scroll_view.dart';
...
@@ -12,54 +12,173 @@ import 'scroll_view.dart';
import
'sliver.dart'
;
import
'sliver.dart'
;
import
'ticker_provider.dart'
;
import
'ticker_provider.dart'
;
/// Signature for the builder callback used by widgets like [AnimatedGrid] to
/// A scrolling container that animates items when they are inserted or removed.
/// build their animated children.
///
///
/// The `context` argument is the build context where the widget will be
/// This widget's [AnimatedListState] can be used to dynamically insert or
/// created, the `index` is the index of the item to be built, and the
/// remove items. To refer to the [AnimatedListState] either provide a
/// `animation` is an [Animation] that should be used to animate an entry
/// [GlobalKey] or use the static [of] method from an item's input callback.
/// transition for the widget that is built.
///
///
///
See also:
///
This widget is similar to one created by [ListView.builder].
///
///
/// * [AnimatedRemovedItemBuilder], a builder that is for removing items with
/// {@youtube 560 315 https://www.youtube.com/watch?v=ZtfItHwFlZ8}
/// animations instead of adding them.
typedef
AnimatedItemBuilder
=
Widget
Function
(
BuildContext
context
,
int
index
,
Animation
<
double
>
animation
);
/// Signature for the builder callback used by widgets like [AnimatedGrid] (in
/// [AnimatedGridState.removeItem]) to animated their children after they have
/// been removed.
///
///
/// The `context` argument is the build context where the widget will be
/// {@tool dartpad}
/// created, and the `animation` is an [Animation] that should be used to
/// This sample application uses an [AnimatedList] to create an effect when
/// animate an exit transition for the widget that is built.
/// items are removed or added to the list.
///
/// ** See code in examples/api/lib/widgets/animated_list/animated_list.0.dart **
/// {@end-tool}
///
///
/// See also:
/// See also:
///
///
/// * [AnimatedItemBuilder], a builder that is for adding items with animations
/// * [SliverAnimatedList], a sliver that animates items when they are inserted
/// instead of removing them.
/// or removed from a list.
typedef
AnimatedRemovedItemBuilder
=
Widget
Function
(
BuildContext
context
,
Animation
<
double
>
animation
);
/// * [SliverAnimatedGrid], a sliver which animates items when they are
/// inserted or removed from a grid.
/// * [AnimatedGrid], a non-sliver scrolling container that animates items when
/// they are inserted or removed in a grid.
class
AnimatedList
extends
_AnimatedScrollView
{
/// Creates a scrolling container that animates items when they are inserted
/// or removed.
const
AnimatedList
({
super
.
key
,
required
super
.
itemBuilder
,
super
.
initialItemCount
=
0
,
super
.
scrollDirection
=
Axis
.
vertical
,
super
.
reverse
=
false
,
super
.
controller
,
super
.
primary
,
super
.
physics
,
super
.
shrinkWrap
=
false
,
super
.
padding
,
super
.
clipBehavior
=
Clip
.
hardEdge
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
// The default insert/remove animation duration.
/// The state from the closest instance of this class that encloses the given
const
Duration
_kDuration
=
Duration
(
milliseconds:
300
);
/// context.
///
/// This method is typically used by [AnimatedList] item widgets that insert
/// or remove items in response to user input.
///
/// If no [AnimatedList] surrounds the context given, then this function will
/// assert in debug mode and throw an exception in release mode.
///
/// This method can be expensive (it walks the element tree).
///
/// This method does not create a dependency, and so will not cause rebuilding
/// when the state changes.
///
/// See also:
///
/// * [maybeOf], a similar function that will return null if no
/// [AnimatedList] ancestor is found.
static
AnimatedListState
of
(
BuildContext
context
)
{
assert
(
context
!=
null
);
final
AnimatedListState
?
result
=
AnimatedList
.
maybeOf
(
context
);
assert
(()
{
if
(
result
==
null
)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'AnimatedList.of() called with a context that does not contain an AnimatedList.'
),
ErrorDescription
(
'No AnimatedList ancestor could be found starting from the context that was passed to AnimatedList.of().'
,
),
ErrorHint
(
'This can happen when the context provided is from the same StatefulWidget that '
'built the AnimatedList. Please see the AnimatedList documentation for examples '
'of how to refer to an AnimatedListState object:
\n
'
' https://api.flutter.dev/flutter/widgets/AnimatedListState-class.html'
,
),
context
.
describeElement
(
'The context used was'
),
]);
}
return
true
;
}());
return
result
!;
}
// Incoming and outgoing AnimatedGrid items.
/// The [AnimatedListState] from the closest instance of [AnimatedList] that encloses the given
class
_ActiveItem
implements
Comparable
<
_ActiveItem
>
{
/// context.
_ActiveItem
.
incoming
(
this
.
controller
,
this
.
itemIndex
)
:
removedItemBuilder
=
null
;
///
_ActiveItem
.
outgoing
(
this
.
controller
,
this
.
itemIndex
,
this
.
removedItemBuilder
);
/// This method is typically used by [AnimatedList] item widgets that insert
_ActiveItem
.
index
(
this
.
itemIndex
)
/// or remove items in response to user input.
:
controller
=
null
,
///
removedItemBuilder
=
null
;
/// If no [AnimatedList] surrounds the context given, then this function will
/// return null.
///
/// This method can be expensive (it walks the element tree).
///
/// This method does not create a dependency, and so will not cause rebuilding
/// when the state changes.
///
/// See also:
///
/// * [of], a similar function that will throw if no [AnimatedList] ancestor
/// is found.
static
AnimatedListState
?
maybeOf
(
BuildContext
context
)
{
assert
(
context
!=
null
);
return
context
.
findAncestorStateOfType
<
AnimatedListState
>();
}
final
AnimationController
?
controller
;
@override
final
AnimatedRemovedItemBuilder
?
removedItemBuilder
;
AnimatedListState
createState
()
=>
AnimatedListState
();
int
itemIndex
;
}
/// The [AnimatedListState] for [AnimatedList], a scrolling list container that animates items when they are
/// inserted or removed.
///
/// When an item is inserted with [insertItem] an animation begins running. The
/// animation is passed to [AnimatedList.itemBuilder] whenever the item's widget
/// is needed.
///
/// When an item is removed with [removeItem] its animation is reversed.
/// The removed item's animation is passed to the [removeItem] builder
/// parameter.
///
/// An app that needs to insert or remove items in response to an event
/// can refer to the [AnimatedList]'s state with a global key:
///
/// ```dart
/// // (e.g. in a stateful widget)
/// GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
///
/// // ...
///
/// @override
/// Widget build(BuildContext context) {
/// return AnimatedList(
/// key: listKey,
/// itemBuilder: (BuildContext context, int index, Animation<double> animation) {
/// return const Placeholder();
/// },
/// );
/// }
///
/// // ...
///
/// void _updateList() {
/// // adds "123" to the AnimatedList
/// listKey.currentState!.insertItem(123);
/// }
/// ```
///
/// [AnimatedList] item input handlers can also refer to their [AnimatedListState]
/// with the static [AnimatedList.of] method.
class
AnimatedListState
extends
_AnimatedScrollViewState
<
AnimatedList
>
{
@override
@override
int
compareTo
(
_ActiveItem
other
)
=>
itemIndex
-
other
.
itemIndex
;
Widget
build
(
BuildContext
context
)
{
return
_wrap
(
SliverAnimatedList
(
key:
_sliverAnimatedMultiBoxKey
,
itemBuilder:
widget
.
itemBuilder
,
initialItemCount:
widget
.
initialItemCount
,
),
);
}
}
}
/// A scrolling container that animates items when they are inserted
or removed
/// A scrolling container that animates items when they are inserted
into or removed from a grid.
/// in a grid.
/// in a grid.
///
///
/// This widget's [AnimatedGridState] can be used to dynamically insert or
/// This widget's [AnimatedGridState] can be used to dynamically insert or
...
@@ -83,51 +202,200 @@ class _ActiveItem implements Comparable<_ActiveItem> {
...
@@ -83,51 +202,200 @@ class _ActiveItem implements Comparable<_ActiveItem> {
/// a list instead of a grid.
/// a list instead of a grid.
/// * [AnimatedList], which animates items added and removed from a list instead
/// * [AnimatedList], which animates items added and removed from a list instead
/// of a grid.
/// of a grid.
class
AnimatedGrid
extends
StatefulWidget
{
class
AnimatedGrid
extends
_AnimatedScrollView
{
/// Creates a scrolling container that animates items when they are inserted
/// Creates a scrolling container that animates items when they are inserted
/// or removed.
/// or removed.
const
AnimatedGrid
({
const
AnimatedGrid
({
super
.
key
,
super
.
key
,
required
this
.
itemBuilder
,
required
super
.
itemBuilder
,
required
this
.
gridDelegate
,
required
this
.
gridDelegate
,
super
.
initialItemCount
=
0
,
super
.
scrollDirection
=
Axis
.
vertical
,
super
.
reverse
=
false
,
super
.
controller
,
super
.
primary
,
super
.
physics
,
super
.
padding
,
super
.
clipBehavior
=
Clip
.
hardEdge
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
/// {@template flutter.widgets.AnimatedGrid.gridDelegate}
/// A delegate that controls the layout of the children within the
/// [AnimatedGrid].
///
/// See also:
///
/// * [SliverGridDelegateWithFixedCrossAxisCount], which creates a layout with
/// a fixed number of tiles in the cross axis.
/// * [SliverGridDelegateWithMaxCrossAxisExtent], which creates a layout with
/// tiles that have a maximum cross-axis extent.
/// {@endtemplate}
final
SliverGridDelegate
gridDelegate
;
/// The state from the closest instance of this class that encloses the given
/// context.
///
/// This method is typically used by [AnimatedGrid] item widgets that insert
/// or remove items in response to user input.
///
/// If no [AnimatedGrid] surrounds the context given, then this function will
/// assert in debug mode and throw an exception in release mode.
///
/// This method can be expensive (it walks the element tree).
///
/// This method does not create a dependency, and so will not cause rebuilding
/// when the state changes.
///
/// See also:
///
/// * [maybeOf], a similar function that will return null if no
/// [AnimatedGrid] ancestor is found.
static
AnimatedGridState
of
(
BuildContext
context
)
{
assert
(
context
!=
null
);
final
AnimatedGridState
?
result
=
AnimatedGrid
.
maybeOf
(
context
);
assert
(()
{
if
(
result
==
null
)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'AnimatedGrid.of() called with a context that does not contain an AnimatedGrid.'
),
ErrorDescription
(
'No AnimatedGrid ancestor could be found starting from the context that was passed to AnimatedGrid.of().'
,
),
ErrorHint
(
'This can happen when the context provided is from the same StatefulWidget that '
'built the AnimatedGrid. Please see the AnimatedGrid documentation for examples '
'of how to refer to an AnimatedGridState object:
\n
'
' https://api.flutter.dev/flutter/widgets/AnimatedGridState-class.html'
,
),
context
.
describeElement
(
'The context used was'
),
]);
}
return
true
;
}());
return
result
!;
}
/// The state from the closest instance of this class that encloses the given
/// context.
///
/// This method is typically used by [AnimatedGrid] item widgets that insert
/// or remove items in response to user input.
///
/// If no [AnimatedGrid] surrounds the context given, then this function will
/// return null.
///
/// This method can be expensive (it walks the element tree).
///
/// This method does not create a dependency, and so will not cause rebuilding
/// when the state changes.
///
/// See also:
///
/// * [of], a similar function that will throw if no [AnimatedGrid] ancestor
/// is found.
static
AnimatedGridState
?
maybeOf
(
BuildContext
context
)
{
assert
(
context
!=
null
);
return
context
.
findAncestorStateOfType
<
AnimatedGridState
>();
}
@override
AnimatedGridState
createState
()
=>
AnimatedGridState
();
}
/// The [State] for an [AnimatedGrid] that animates items when they are
/// inserted or removed.
///
/// When an item is inserted with [insertItem] an animation begins running. The
/// animation is passed to [AnimatedGrid.itemBuilder] whenever the item's widget
/// is needed.
///
/// When an item is removed with [removeItem] its animation is reversed.
/// The removed item's animation is passed to the [removeItem] builder
/// parameter.
///
/// An app that needs to insert or remove items in response to an event
/// can refer to the [AnimatedGrid]'s state with a global key:
///
/// ```dart
/// // (e.g. in a stateful widget)
/// GlobalKey<AnimatedGridState> gridKey = GlobalKey<AnimatedGridState>();
///
/// // ...
///
/// @override
/// Widget build(BuildContext context) {
/// return AnimatedGrid(
/// key: gridKey,
/// itemBuilder: (BuildContext context, int index, Animation<double> animation) {
/// return const Placeholder();
/// },
/// gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 100.0),
/// );
/// }
///
/// // ...
///
/// void _updateGrid() {
/// // adds "123" to the AnimatedGrid
/// gridKey.currentState!.insertItem(123);
/// }
/// ```
///
/// [AnimatedGrid] item input handlers can also refer to their [AnimatedGridState]
/// with the static [AnimatedGrid.of] method.
class
AnimatedGridState
extends
_AnimatedScrollViewState
<
AnimatedGrid
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
_wrap
(
SliverAnimatedGrid
(
key:
_sliverAnimatedMultiBoxKey
,
gridDelegate:
widget
.
gridDelegate
,
itemBuilder:
widget
.
itemBuilder
,
initialItemCount:
widget
.
initialItemCount
,
),
);
}
}
abstract
class
_AnimatedScrollView
extends
StatefulWidget
{
/// Creates a scrolling container that animates items when they are inserted
/// or removed.
const
_AnimatedScrollView
({
super
.
key
,
required
this
.
itemBuilder
,
this
.
initialItemCount
=
0
,
this
.
initialItemCount
=
0
,
this
.
scrollDirection
=
Axis
.
vertical
,
this
.
scrollDirection
=
Axis
.
vertical
,
this
.
reverse
=
false
,
this
.
reverse
=
false
,
this
.
controller
,
this
.
controller
,
this
.
primary
,
this
.
primary
,
this
.
physics
,
this
.
physics
,
this
.
shrinkWrap
=
false
,
this
.
padding
,
this
.
padding
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
})
:
assert
(
itemBuilder
!=
null
),
})
:
assert
(
itemBuilder
!=
null
),
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
/// Called, as needed, to build grid item widgets.
/// {@template flutter.widgets.AnimatedScrollView.itemBuilder}
/// Called, as needed, to build children widgets.
///
///
///
Grid items
are only built when they're scrolled into view.
///
Children
are only built when they're scrolled into view.
///
///
/// The [AnimatedItemBuilder] index parameter indicates the item's position in
/// The [AnimatedItemBuilder] index parameter indicates the item's
/// the grid. The value of the index parameter will be between 0 and
/// position in the scroll view. The value of the index parameter will be
/// [initialItemCount] plus the total number of items that have been inserted
/// between 0 and [initialItemCount] plus the total number of items that have
/// with [AnimatedGridState.insertItem] and less the total number of items
/// been inserted with [AnimatedListState.insertItem] or
/// that have been removed with [AnimatedGridState.removeItem].
/// [AnimatedGridState.insertItem] and less the total number of items that
/// have been removed with [AnimatedListState.removeItem] or
/// [AnimatedGridState.removeItem].
///
///
/// Implementations of this callback should assume that
/// Implementations of this callback should assume that
/// [AnimatedGridState.removeItem] removes an item immediately.
/// `removeItem` removes an item immediately.
/// {@endtemplate}
final
AnimatedItemBuilder
itemBuilder
;
final
AnimatedItemBuilder
itemBuilder
;
/// A delegate that controls the layout of the children within the
/// {@template flutter.widgets.AnimatedScrollView.initialItemCount}
/// [AnimatedGrid].
/// The number of items the [AnimatedList] or [AnimatedGrid] will start with.
///
/// See also:
///
/// * [SliverGridDelegateWithFixedCrossAxisCount], which creates a layout with
/// a fixed number of tiles in the cross axis.
/// * [SliverGridDelegateWithMaxCrossAxisExtent], which creates a layout with
/// tiles that have a maximum cross-axis extent.
final
SliverGridDelegate
gridDelegate
;
/// {@template flutter.widgets.AnimatedGrid.initialItemCount}
/// The number of items the grid will start with.
///
///
/// The appearance of the initial items is not animated. They
/// The appearance of the initial items is not animated. They
/// are created, as needed, by [itemBuilder] with an animation parameter
/// are created, as needed, by [itemBuilder] with an animation parameter
...
@@ -168,196 +436,320 @@ class AnimatedGrid extends StatefulWidget {
...
@@ -168,196 +436,320 @@ class AnimatedGrid extends StatefulWidget {
/// [ScrollController.animateTo]).
/// [ScrollController.animateTo]).
final
ScrollController
?
controller
;
final
ScrollController
?
controller
;
/// Whether this is the primary scroll view associated with the parent
/// Whether this is the primary scroll view associated with the parent
/// [PrimaryScrollController].
/// [PrimaryScrollController].
///
///
/// On iOS, this identifies the scroll view that will scroll to top in
/// On iOS, this identifies the scroll view that will scroll to top in
/// response to a tap in the status bar.
/// response to a tap in the status bar.
///
///
/// Defaults to true when [scrollDirection] is [Axis.vertical] and
/// Defaults to true when [scrollDirection] is [Axis.vertical] and
/// [controller] is null.
/// [controller] is null.
final
bool
?
primary
;
final
bool
?
primary
;
/// How the scroll view should respond to user input.
///
/// For example, this determines how the scroll view continues to animate after the
/// user stops dragging the scroll view.
///
/// Defaults to matching platform conventions.
final
ScrollPhysics
?
physics
;
/// Whether the extent of the scroll view in the [scrollDirection] should be
/// determined by the contents being viewed.
///
/// If the scroll view does not shrink wrap, then the scroll view will expand
/// to the maximum allowed size in the [scrollDirection]. If the scroll view
/// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must
/// be true.
///
/// Shrink wrapping the content of the scroll view is significantly more
/// expensive than expanding to the maximum allowed size because the content
/// can expand and contract during scrolling, which means the size of the
/// scroll view needs to be recomputed whenever the scroll position changes.
///
/// Defaults to false.
final
bool
shrinkWrap
;
/// The amount of space by which to inset the children.
final
EdgeInsetsGeometry
?
padding
;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.hardEdge].
final
Clip
clipBehavior
;
}
abstract
class
_AnimatedScrollViewState
<
T
extends
_AnimatedScrollView
>
extends
State
<
T
>
with
TickerProviderStateMixin
{
final
GlobalKey
<
_SliverAnimatedMultiBoxAdaptorState
<
_SliverAnimatedMultiBoxAdaptor
>>
_sliverAnimatedMultiBoxKey
=
GlobalKey
();
/// Insert an item at [index] and start an animation that will be passed
/// to [AnimatedGrid.itemBuilder] or [AnimatedList.itemBuilder] when the item
/// is visible.
///
/// This method's semantics are the same as Dart's [List.insert] method: it
/// increases the length of the list of items by one and shifts
/// all items at or after [index] towards the end of the list of items.
void
insertItem
(
int
index
,
{
Duration
duration
=
_kDuration
})
{
_sliverAnimatedMultiBoxKey
.
currentState
!.
insertItem
(
index
,
duration:
duration
);
}
/// Remove the item at `index` and start an animation that will be passed to
/// `builder` when the item is visible.
///
/// Items are removed immediately. After an item has been removed, its index
/// will no longer be passed to the `itemBuilder`. However, the
/// item will still appear for `duration` and during that time
/// `builder` must construct its widget as needed.
///
/// This method's semantics are the same as Dart's [List.remove] method: it
/// decreases the length of items by one and shifts all items at or before
/// `index` towards the beginning of the list of items.
///
/// See also:
///
/// * [AnimatedRemovedItemBuilder], which describes the arguments to the
/// `builder` argument.
void
removeItem
(
int
index
,
AnimatedRemovedItemBuilder
builder
,
{
Duration
duration
=
_kDuration
})
{
_sliverAnimatedMultiBoxKey
.
currentState
!.
removeItem
(
index
,
builder
,
duration:
duration
);
}
Widget
_wrap
(
Widget
sliver
)
{
return
CustomScrollView
(
scrollDirection:
widget
.
scrollDirection
,
reverse:
widget
.
reverse
,
controller:
widget
.
controller
,
primary:
widget
.
primary
,
physics:
widget
.
physics
,
clipBehavior:
widget
.
clipBehavior
,
slivers:
<
Widget
>[
SliverPadding
(
padding:
widget
.
padding
??
EdgeInsets
.
zero
,
sliver:
sliver
,
),
],
);
}
}
/// Signature for the builder callback used by [AnimatedList].
///
/// This is deprecated, use the identical [AnimatedItemBuilder] instead.
@Deprecated
(
'Use AnimatedItemBuilder instead. '
'This feature was deprecated after v3.5.0-4.0.pre.'
,
)
typedef
AnimatedListItemBuilder
=
Widget
Function
(
BuildContext
context
,
int
index
,
Animation
<
double
>
animation
);
/// Signature for the builder callback used by [AnimatedList] & [AnimatedGrid] to
/// build their animated children.
///
/// The `context` argument is the build context where the widget will be
/// created, the `index` is the index of the item to be built, and the
/// `animation` is an [Animation] that should be used to animate an entry
/// transition for the widget that is built.
///
/// See also:
///
/// * [AnimatedRemovedItemBuilder], a builder that is for removing items with
/// animations instead of adding them.
typedef
AnimatedItemBuilder
=
Widget
Function
(
BuildContext
context
,
int
index
,
Animation
<
double
>
animation
);
/// Signature for the builder callback used by [AnimatedListState.removeItem].
///
/// This is deprecated, use the identical [AnimatedRemovedItemBuilder]
/// instead.
@Deprecated
(
'Use AnimatedRemovedItemBuilder instead. '
'This feature was deprecated after v3.5.0-4.0.pre.'
,
)
typedef
AnimatedListRemovedItemBuilder
=
Widget
Function
(
BuildContext
context
,
Animation
<
double
>
animation
);
/// Signature for the builder callback used in [AnimatedListState.removeItem] and
/// [AnimatedGridState.removeItem] to animate their children after they have
/// been removed.
///
/// The `context` argument is the build context where the widget will be
/// created, and the `animation` is an [Animation] that should be used to
/// animate an exit transition for the widget that is built.
///
/// See also:
///
/// * [AnimatedItemBuilder], a builder that is for adding items with animations
/// instead of removing them.
typedef
AnimatedRemovedItemBuilder
=
Widget
Function
(
BuildContext
context
,
Animation
<
double
>
animation
);
// The default insert/remove animation duration.
const
Duration
_kDuration
=
Duration
(
milliseconds:
300
);
/// How the scroll view should respond to user input
.
// Incoming and outgoing animated items
.
///
class
_ActiveItem
implements
Comparable
<
_ActiveItem
>
{
/// For example, determines how the scroll view continues to animate after the
_ActiveItem
.
incoming
(
this
.
controller
,
this
.
itemIndex
)
:
removedItemBuilder
=
null
;
/// user stops dragging the scroll view.
_ActiveItem
.
outgoing
(
this
.
controller
,
this
.
itemIndex
,
this
.
removedItemBuilder
);
///
_ActiveItem
.
index
(
this
.
itemIndex
)
/// Defaults to matching platform conventions.
:
controller
=
null
,
final
ScrollPhysics
?
physics
;
removedItemBuilder
=
null
;
/// The amount of space by which to inset the children.
final
AnimationController
?
controller
;
final
EdgeInsetsGeometry
?
padding
;
final
AnimatedRemovedItemBuilder
?
removedItemBuilder
;
int
itemIndex
;
/// {@macro flutter.material.Material.clipBehavior}
@override
///
int
compareTo
(
_ActiveItem
other
)
=>
itemIndex
-
other
.
itemIndex
;
/// Defaults to [Clip.hardEdge].
}
final
Clip
clipBehavior
;
/// The state from the closest instance of this class that encloses the given
/// A [SliverList] that animates items when they are inserted or removed.
///
/// This widget's [SliverAnimatedListState] can be used to dynamically insert or
/// remove items. To refer to the [SliverAnimatedListState] either provide a
/// [GlobalKey] or use the static [SliverAnimatedList.of] method from a list item's
/// input callback.
///
/// {@tool dartpad}
/// This sample application uses a [SliverAnimatedList] to create an animated
/// effect when items are removed or added to the list.
///
/// ** See code in examples/api/lib/widgets/animated_list/sliver_animated_list.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [SliverList], which does not animate items when they are inserted or
/// removed.
/// * [AnimatedList], a non-sliver scrolling container that animates items when
/// they are inserted or removed.
/// * [SliverAnimatedGrid], a sliver which animates items when they are
/// inserted into or removed from a grid.
/// * [AnimatedGrid], a non-sliver scrolling container that animates items when
/// they are inserted into or removed from a grid.
class
SliverAnimatedList
extends
_SliverAnimatedMultiBoxAdaptor
{
/// Creates a [SliverList] that animates items when they are inserted or
/// removed.
const
SliverAnimatedList
({
super
.
key
,
required
super
.
itemBuilder
,
super
.
findChildIndexCallback
,
super
.
initialItemCount
=
0
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
@override
SliverAnimatedListState
createState
()
=>
SliverAnimatedListState
();
/// The [SliverAnimatedListState] from the closest instance of this class that encloses the given
/// context.
/// context.
///
///
/// This method is typically used by [
AnimatedGrid] item widgets that inser
t
/// This method is typically used by [
SliverAnimatedList] item widgets tha
t
/// or remove items in response to user input.
///
insert
or remove items in response to user input.
///
///
/// If no [
AnimatedGrid] surrounds the context given, then this function will
/// If no [
SliverAnimatedList] surrounds the context given, then this function
/// assert in debug mode and throw an exception in release mode.
///
will
assert in debug mode and throw an exception in release mode.
///
///
/// This method can be expensive (it walks the element tree).
/// This method can be expensive (it walks the element tree).
///
///
/// This method does not create a dependency, and so will not cause rebuilding
/// when the state changes.
///
/// See also:
/// See also:
///
///
/// * [maybeOf], a similar function that will return null if no
/// * [maybeOf], a similar function that will return null if no
/// [
AnimatedGrid
] ancestor is found.
/// [
SliverAnimatedList
] ancestor is found.
static
AnimatedGrid
State
of
(
BuildContext
context
)
{
static
SliverAnimatedList
State
of
(
BuildContext
context
)
{
assert
(
context
!=
null
);
assert
(
context
!=
null
);
final
AnimatedGridState
?
result
=
context
.
findAncestorStateOfType
<
AnimatedGridState
>(
);
final
SliverAnimatedListState
?
result
=
SliverAnimatedList
.
maybeOf
(
context
);
assert
(()
{
assert
(()
{
if
(
result
==
null
)
{
if
(
result
==
null
)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
throw
FlutterError
(
ErrorSummary
(
'AnimatedGrid.of() called with a context that does not contain an AnimatedGrid.'
),
'SliverAnimatedList.of() called with a context that does not contain a SliverAnimatedList.
\n
'
ErrorDescription
(
'No SliverAnimatedListState ancestor could be found starting from the '
'No AnimatedGrid ancestor could be found starting from the context that was passed to AnimatedGrid.of().'
,
'context that was passed to SliverAnimatedListState.of(). This can '
),
'happen when the context provided is from the same StatefulWidget that '
ErrorHint
(
'built the AnimatedList. Please see the SliverAnimatedList documentation '
'This can happen when the context provided is from the same StatefulWidget that '
'for examples of how to refer to an AnimatedListState object: '
'built the AnimatedGrid. Please see the AnimatedGrid documentation for examples '
'https://api.flutter.dev/flutter/widgets/SliverAnimatedListState-class.html
\n
'
'of how to refer to an AnimatedGridState object:
\n
'
'The context used was:
\n
'
' https://api.flutter.dev/flutter/widgets/AnimatedGridState-class.html'
,
'
$context
'
,
),
);
context
.
describeElement
(
'The context used was'
),
]);
}
}
return
true
;
return
true
;
}());
}());
return
result
!;
return
result
!;
}
}
/// The
state
from the closest instance of this class that encloses the given
/// The
[SliverAnimatedListState]
from the closest instance of this class that encloses the given
/// context.
/// context.
///
///
/// This method is typically used by [
AnimatedGrid] item widgets that inser
t
/// This method is typically used by [
SliverAnimatedList] item widgets tha
t
/// or remove items in response to user input.
///
insert
or remove items in response to user input.
///
///
/// If no [
AnimatedGrid] surrounds the context given, then this function will
/// If no [
SliverAnimatedList] surrounds the context given, then this function
/// return null.
///
will
return null.
///
///
/// This method can be expensive (it walks the element tree).
/// This method can be expensive (it walks the element tree).
///
///
/// This method does not create a dependency, and so will not cause rebuilding
/// when the state changes.
///
/// See also:
/// See also:
///
///
/// * [of], a similar function that will throw if no [
AnimatedGrid] ancestor
/// * [of], a similar function that will throw if no [
SliverAnimatedList]
/// is found.
///
ancestor
is found.
static
AnimatedGrid
State
?
maybeOf
(
BuildContext
context
)
{
static
SliverAnimatedList
State
?
maybeOf
(
BuildContext
context
)
{
assert
(
context
!=
null
);
assert
(
context
!=
null
);
return
context
.
findAncestorStateOfType
<
AnimatedGrid
State
>();
return
context
.
findAncestorStateOfType
<
SliverAnimatedList
State
>();
}
}
@override
AnimatedGridState
createState
()
=>
AnimatedGridState
();
}
}
/// The state for a
scrolling container
that animates items when they are
/// The state for a
[SliverAnimatedList]
that animates items when they are
/// inserted or removed.
/// inserted or removed.
///
///
/// When an item is inserted with [insertItem] an animation begins running. The
/// When an item is inserted with [insertItem] an animation begins running. The
/// animation is passed to [
AnimatedGrid.itemBuilder] whenever the item's widget
/// animation is passed to [
SliverAnimatedList.itemBuilder] whenever the item's
/// is needed.
///
widget
is needed.
///
///
/// When an item is removed with [removeItem] its animation is reversed.
/// When an item is removed with [removeItem] its animation is reversed.
/// The removed item's animation is passed to the [removeItem] builder
/// The removed item's animation is passed to the [removeItem] builder
/// parameter.
/// parameter.
///
///
/// An app that needs to insert or remove items in response to an event
/// An app that needs to insert or remove items in response to an event
/// can refer to the [
AnimatedGrid
]'s state with a global key:
/// can refer to the [
SliverAnimatedList
]'s state with a global key:
///
///
/// ```dart
/// ```dart
/// // (e.g. in a stateful widget)
/// // (e.g. in a stateful widget)
/// GlobalKey<Animated
GridState> gridKey = GlobalKey<AnimatedGrid
State>();
/// GlobalKey<Animated
ListState> listKey = GlobalKey<AnimatedList
State>();
///
///
/// // ...
/// // ...
///
///
/// @override
/// @override
/// Widget build(BuildContext context) {
/// Widget build(BuildContext context) {
/// return Animated
Grid
(
/// return Animated
List
(
/// key:
grid
Key,
/// key:
list
Key,
/// itemBuilder: (BuildContext context, int index, Animation<double> animation) {
/// itemBuilder: (BuildContext context, int index, Animation<double> animation) {
/// return const Placeholder();
/// return const Placeholder();
/// },
/// },
/// gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 100.0),
/// );
/// );
/// }
/// }
///
///
/// // ...
/// // ...
///
///
/// void _update
Grid
() {
/// void _update
List
() {
/// // adds "123" to the Animated
Grid
/// // adds "123" to the Animated
List
///
grid
Key.currentState!.insertItem(123);
///
list
Key.currentState!.insertItem(123);
/// }
/// }
/// ```
/// ```
///
///
/// [AnimatedGrid] item input handlers can also refer to their [AnimatedGridState]
/// [SliverAnimatedList] item input handlers can also refer to their
/// with the static [AnimatedGrid.of] method.
/// [SliverAnimatedListState] with the static [SliverAnimatedList.of] method.
class
AnimatedGridState
extends
State
<
AnimatedGrid
>
with
TickerProviderStateMixin
<
AnimatedGrid
>
{
class
SliverAnimatedListState
extends
_SliverAnimatedMultiBoxAdaptorState
<
SliverAnimatedList
>
{
final
GlobalKey
<
SliverAnimatedGridState
>
_sliverAnimatedGridKey
=
GlobalKey
();
/// Insert an item at [index] and start an animation that will be passed
/// to [AnimatedGrid.itemBuilder] when the item is visible.
///
/// This method's semantics are the same as Dart's [List.insert] method: it
/// increases the length of the list of items in the grid by one and shifts
/// all items at or after [index] towards the end of the list of items in the
/// grid.
void
insertItem
(
int
index
,
{
Duration
duration
=
_kDuration
})
{
_sliverAnimatedGridKey
.
currentState
!.
insertItem
(
index
,
duration:
duration
);
}
/// Remove the item at `index` and start an animation that will be passed to
/// `builder` when the item is visible.
///
/// Items are removed immediately. After an item has been removed, its index
/// will no longer be passed to the [AnimatedGrid.itemBuilder]. However, the
/// item will still appear in the grid for `duration` and during that time
/// `builder` must construct its widget as needed.
///
/// This method's semantics are the same as Dart's [List.remove] method: it
/// decreases the length of the list of items in the grid by one and shifts
/// all items at or before `index` towards the beginning of the list of items
/// in the grid.
///
/// See also:
///
/// - [AnimatedRemovedItemBuilder], which describes the arguments to the
/// `builder` argument.
void
removeItem
(
int
index
,
AnimatedRemovedItemBuilder
builder
,
{
Duration
duration
=
_kDuration
})
{
_sliverAnimatedGridKey
.
currentState
!.
removeItem
(
index
,
builder
,
duration:
duration
);
}
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
CustomScrollView
(
return
SliverList
(
scrollDirection:
widget
.
scrollDirection
,
delegate:
_createDelegate
(),
reverse:
widget
.
reverse
,
controller:
widget
.
controller
,
primary:
widget
.
primary
,
physics:
widget
.
physics
,
clipBehavior:
widget
.
clipBehavior
,
slivers:
<
Widget
>[
SliverPadding
(
padding:
widget
.
padding
??
EdgeInsets
.
zero
,
sliver:
SliverAnimatedGrid
(
key:
_sliverAnimatedGridKey
,
gridDelegate:
widget
.
gridDelegate
,
itemBuilder:
widget
.
itemBuilder
,
initialItemCount:
widget
.
initialItemCount
,
),
),
],
);
);
}
}
}
}
/// A
sliver that animates items when they are inserted or removed in a gri
d.
/// A
[SliverGrid] that animates items when they are inserted or remove
d.
///
///
/// This widget's [SliverAnimatedGridState] can be used to dynamically insert or
/// This widget's [SliverAnimatedGridState] can be used to dynamically insert or
/// remove items. To refer to the [SliverAnimatedGridState] either provide a
/// remove items. To refer to the [SliverAnimatedGridState] either provide a
...
@@ -380,51 +772,24 @@ class AnimatedGridState extends State<AnimatedGrid> with TickerProviderStateMixi
...
@@ -380,51 +772,24 @@ class AnimatedGridState extends State<AnimatedGrid> with TickerProviderStateMixi
/// * [SliverList], which displays a non-animated list of items.
/// * [SliverList], which displays a non-animated list of items.
/// * [SliverAnimatedList], which animates items added and removed from a list
/// * [SliverAnimatedList], which animates items added and removed from a list
/// instead of a grid.
/// instead of a grid.
class
SliverAnimatedGrid
extends
StatefulWidget
{
class
SliverAnimatedGrid
extends
_SliverAnimatedMultiBoxAdaptor
{
/// Creates a sliver that animates items when they are inserted or removed.
/// Creates a [SliverGrid] that animates items when they are inserted or
/// removed.
const
SliverAnimatedGrid
({
const
SliverAnimatedGrid
({
super
.
key
,
super
.
key
,
required
this
.
itemBuilder
,
required
super
.
itemBuilder
,
required
this
.
gridDelegate
,
required
this
.
gridDelegate
,
this
.
findChildIndexCallback
,
super
.
findChildIndexCallback
,
this
.
initialItemCount
=
0
,
super
.
initialItemCount
=
0
,
})
:
assert
(
itemBuilder
!=
null
),
})
:
assert
(
itemBuilder
!=
null
),
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
/// Called, as needed, to build grid item widgets.
///
/// Grid items are only built when they're scrolled into view.
///
/// The [AnimatedItemBuilder] index parameter indicates the item's position in
/// the grid. The value of the index parameter will be between 0 and
/// [initialItemCount] plus the total number of items that have been inserted
/// with [SliverAnimatedGridState.insertItem] and less the total number of
/// items that have been removed with [SliverAnimatedGridState.removeItem].
///
/// Implementations of this callback should assume that
/// [SliverAnimatedGridState.removeItem] removes an item immediately.
final
AnimatedItemBuilder
itemBuilder
;
/// A delegate that controls the layout of the children within the
/// [SliverAnimatedGrid].
///
/// See also:
///
/// * [SliverGridDelegateWithFixedCrossAxisCount], which creates a layout with
/// a fixed number of tiles in the cross axis.
/// * [SliverGridDelegateWithMaxCrossAxisExtent], which creates a layout with
/// tiles that have a maximum cross-axis extent.
final
SliverGridDelegate
gridDelegate
;
/// {@macro flutter.widgets.SliverChildBuilderDelegate.findChildIndexCallback}
final
ChildIndexGetter
?
findChildIndexCallback
;
/// {@macro flutter.widgets.AnimatedGrid.initialItemCount}
final
int
initialItemCount
;
@override
@override
SliverAnimatedGridState
createState
()
=>
SliverAnimatedGridState
();
SliverAnimatedGridState
createState
()
=>
SliverAnimatedGridState
();
/// {@macro flutter.widgets.AnimatedGrid.gridDelegate}
final
SliverGridDelegate
gridDelegate
;
/// The state from the closest instance of this class that encloses the given
/// The state from the closest instance of this class that encloses the given
/// context.
/// context.
///
///
...
@@ -483,7 +848,7 @@ class SliverAnimatedGrid extends StatefulWidget {
...
@@ -483,7 +848,7 @@ class SliverAnimatedGrid extends StatefulWidget {
}
}
}
}
/// The state for a
sliver
that animates items when they are
/// The state for a
[SliverAnimatedGrid]
that animates items when they are
/// inserted or removed.
/// inserted or removed.
///
///
/// When an item is inserted with [insertItem] an animation begins running. The
/// When an item is inserted with [insertItem] an animation begins running. The
...
@@ -524,10 +889,38 @@ class SliverAnimatedGrid extends StatefulWidget {
...
@@ -524,10 +889,38 @@ class SliverAnimatedGrid extends StatefulWidget {
///
///
/// [SliverAnimatedGrid] item input handlers can also refer to their
/// [SliverAnimatedGrid] item input handlers can also refer to their
/// [SliverAnimatedGridState] with the static [SliverAnimatedGrid.of] method.
/// [SliverAnimatedGridState] with the static [SliverAnimatedGrid.of] method.
class
SliverAnimatedGridState
extends
State
<
SliverAnimatedGrid
>
with
TickerProviderStateMixin
{
class
SliverAnimatedGridState
extends
_SliverAnimatedMultiBoxAdaptorState
<
SliverAnimatedGrid
>
{
final
List
<
_ActiveItem
>
_incomingItems
=
<
_ActiveItem
>[];
final
List
<
_ActiveItem
>
_outgoingItems
=
<
_ActiveItem
>[];
@override
int
_itemsCount
=
0
;
Widget
build
(
BuildContext
context
)
{
return
SliverGrid
(
gridDelegate:
widget
.
gridDelegate
,
delegate:
_createDelegate
(),
);
}
}
abstract
class
_SliverAnimatedMultiBoxAdaptor
extends
StatefulWidget
{
/// Creates a sliver that animates items when they are inserted or removed.
const
_SliverAnimatedMultiBoxAdaptor
({
super
.
key
,
required
this
.
itemBuilder
,
this
.
findChildIndexCallback
,
this
.
initialItemCount
=
0
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
initialItemCount
!=
null
&&
initialItemCount
>=
0
);
/// {@macro flutter.widgets.AnimatedScrollView.itemBuilder}
final
AnimatedItemBuilder
itemBuilder
;
/// {@macro flutter.widgets.SliverChildBuilderDelegate.findChildIndexCallback}
final
ChildIndexGetter
?
findChildIndexCallback
;
/// {@macro flutter.widgets.AnimatedScrollView.initialItemCount}
final
int
initialItemCount
;
}
abstract
class
_SliverAnimatedMultiBoxAdaptorState
<
T
extends
_SliverAnimatedMultiBoxAdaptor
>
extends
State
<
T
>
with
TickerProviderStateMixin
{
@override
@override
void
initState
()
{
void
initState
()
{
...
@@ -543,6 +936,10 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
...
@@ -543,6 +936,10 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
super
.
dispose
();
super
.
dispose
();
}
}
final
List
<
_ActiveItem
>
_incomingItems
=
<
_ActiveItem
>[];
final
List
<
_ActiveItem
>
_outgoingItems
=
<
_ActiveItem
>[];
int
_itemsCount
=
0
;
_ActiveItem
?
_removeActiveItemAt
(
List
<
_ActiveItem
>
items
,
int
itemIndex
)
{
_ActiveItem
?
_removeActiveItemAt
(
List
<
_ActiveItem
>
items
,
int
itemIndex
)
{
final
int
i
=
binarySearch
(
items
,
_ActiveItem
.
index
(
itemIndex
));
final
int
i
=
binarySearch
(
items
,
_ActiveItem
.
index
(
itemIndex
));
return
i
==
-
1
?
null
:
items
.
removeAt
(
i
);
return
i
==
-
1
?
null
:
items
.
removeAt
(
i
);
...
@@ -554,10 +951,11 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
...
@@ -554,10 +951,11 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
}
}
// The insertItem() and removeItem() index parameters are defined as if the
// The insertItem() and removeItem() index parameters are defined as if the
// removeItem() operation removed the corresponding grid entry immediately.
// removeItem() operation removed the corresponding list/grid entry
// The entry is only actually removed from the grid when the remove animation
// immediately. The entry is only actually removed from the
// finishes. The entry is added to _outgoingItems when removeItem is called
// ListView/GridView when the remove animation finishes. The entry is added
// and removed from _outgoingItems when the remove animation finishes.
// to _outgoingItems when removeItem is called and removed from
// _outgoingItems when the remove animation finishes.
int
_indexToItemIndex
(
int
index
)
{
int
_indexToItemIndex
(
int
index
)
{
int
itemIndex
=
index
;
int
itemIndex
=
index
;
...
@@ -597,14 +995,32 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
...
@@ -597,14 +995,32 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
);
);
}
}
Widget
_itemBuilder
(
BuildContext
context
,
int
itemIndex
)
{
final
_ActiveItem
?
outgoingItem
=
_activeItemAt
(
_outgoingItems
,
itemIndex
);
if
(
outgoingItem
!=
null
)
{
return
outgoingItem
.
removedItemBuilder
!(
context
,
outgoingItem
.
controller
!.
view
,
);
}
final
_ActiveItem
?
incomingItem
=
_activeItemAt
(
_incomingItems
,
itemIndex
);
final
Animation
<
double
>
animation
=
incomingItem
?.
controller
?.
view
??
kAlwaysCompleteAnimation
;
return
widget
.
itemBuilder
(
context
,
_itemIndexToIndex
(
itemIndex
),
animation
,
);
}
/// Insert an item at [index] and start an animation that will be passed to
/// Insert an item at [index] and start an animation that will be passed to
/// [SliverAnimatedGrid.itemBuilder] when the item is visible.
/// [SliverAnimatedGrid.itemBuilder] or [SliverAnimatedList.itemBuilder] when
/// the item is visible.
///
///
/// This method's semantics are the same as Dart's [List.insert] method: it
/// This method's semantics are the same as Dart's [List.insert] method: it
/// increases the length of the list of items in the grid by one and shifts
/// increases the length of the list of items by one and shifts
/// all items at or after [index] towards the end of the list of items in the
/// all items at or after [index] towards the end of the list of items.
/// grid.
void
insertItem
(
int
index
,
{
Duration
duration
=
_kDuration
})
{
void
insertItem
(
int
index
,
{
Duration
duration
=
_kDuration
})
{
assert
(
index
!=
null
&&
index
>=
0
);
assert
(
index
!=
null
&&
index
>=
0
);
assert
(
duration
!=
null
);
assert
(
duration
!=
null
);
...
@@ -648,15 +1064,15 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
...
@@ -648,15 +1064,15 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
/// to [builder] when the item is visible.
/// to [builder] when the item is visible.
///
///
/// Items are removed immediately. After an item has been removed, its index
/// Items are removed immediately. After an item has been removed, its index
/// will no longer be passed to the [SliverAnimatedGrid.itemBuilder]. However
/// will no longer be passed to the subclass' [SliverAnimatedGrid.itemBuilder]
/// the item will still appear in the grid for [duration] and during that time
/// or [SliverAnimatedList.itemBuilder]. However the item will still appear
/// [builder] must construct its widget as needed.
/// for [duration], and during that time [builder] must construct its widget
/// as needed.
///
///
/// This method's semantics are the same as Dart's [List.remove] method: it
/// This method's semantics are the same as Dart's [List.remove] method: it
/// decreases the length of the list of items in the grid by one and shifts
/// decreases the length of items by one and shifts
/// all items at or before [index] towards the beginning of the list of items
/// all items at or before [index] towards the beginning of the list of items.
/// in the grid.
void
removeItem
(
int
index
,
AnimatedRemovedItemBuilder
builder
,
{
Duration
duration
=
_kDuration
})
{
void
removeItem
(
int
index
,
AnimatedRemovedItemBuilder
builder
,
{
Duration
duration
=
_kDuration
})
{
assert
(
index
!=
null
&&
index
>=
0
);
assert
(
index
!=
null
&&
index
>=
0
);
assert
(
builder
!=
null
);
assert
(
builder
!=
null
);
assert
(
duration
!=
null
);
assert
(
duration
!=
null
);
...
@@ -694,30 +1110,4 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
...
@@ -694,30 +1110,4 @@ class SliverAnimatedGridState extends State<SliverAnimatedGrid> with TickerProvi
setState
(()
=>
_itemsCount
-=
1
);
setState
(()
=>
_itemsCount
-=
1
);
});
});
}
}
Widget
_itemBuilder
(
BuildContext
context
,
int
itemIndex
)
{
final
_ActiveItem
?
outgoingItem
=
_activeItemAt
(
_outgoingItems
,
itemIndex
);
if
(
outgoingItem
!=
null
)
{
return
outgoingItem
.
removedItemBuilder
!(
context
,
outgoingItem
.
controller
!.
view
,
);
}
final
_ActiveItem
?
incomingItem
=
_activeItemAt
(
_incomingItems
,
itemIndex
);
final
Animation
<
double
>
animation
=
incomingItem
?.
controller
?.
view
??
kAlwaysCompleteAnimation
;
return
widget
.
itemBuilder
(
context
,
_itemIndexToIndex
(
itemIndex
),
animation
,
);
}
@override
Widget
build
(
BuildContext
context
)
{
return
SliverGrid
(
gridDelegate:
widget
.
gridDelegate
,
delegate:
_createDelegate
(),
);
}
}
}
packages/flutter/lib/widgets.dart
View file @
0e70a97e
...
@@ -19,8 +19,7 @@ export 'foundation.dart' show UniqueKey;
...
@@ -19,8 +19,7 @@ export 'foundation.dart' show UniqueKey;
export
'rendering.dart'
show
TextSelectionHandleType
;
export
'rendering.dart'
show
TextSelectionHandleType
;
export
'src/widgets/actions.dart'
;
export
'src/widgets/actions.dart'
;
export
'src/widgets/animated_cross_fade.dart'
;
export
'src/widgets/animated_cross_fade.dart'
;
export
'src/widgets/animated_grid.dart'
;
export
'src/widgets/animated_scroll_view.dart'
;
export
'src/widgets/animated_list.dart'
;
export
'src/widgets/animated_size.dart'
;
export
'src/widgets/animated_size.dart'
;
export
'src/widgets/animated_switcher.dart'
;
export
'src/widgets/animated_switcher.dart'
;
export
'src/widgets/annotated_region.dart'
;
export
'src/widgets/annotated_region.dart'
;
...
...
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