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
1568b827
Commit
1568b827
authored
Mar 07, 2016
by
Hans Muller
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2467 from HansMuller/leave_behind
Added support for Dismissable leave-behind list items
parents
a5887b6f
2662ea52
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
217 additions
and
22 deletions
+217
-22
leave_behind_demo.dart
examples/material_gallery/lib/demo/leave_behind_demo.dart
+149
-0
home.dart
examples/material_gallery/lib/gallery/home.dart
+2
-0
section.dart
examples/material_gallery/lib/gallery/section.dart
+1
-1
card_collection.dart
examples/widgets/card_collection.dart
+1
-0
list_item.dart
packages/flutter/lib/src/material/list_item.dart
+1
-2
dismissable.dart
packages/flutter/lib/src/widgets/dismissable.dart
+62
-19
dismissable_test.dart
packages/flutter/test/widget/dismissable_test.dart
+1
-0
No files found.
examples/material_gallery/lib/demo/leave_behind_demo.dart
0 → 100644
View file @
1568b827
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
enum
LeaveBehindDemoAction
{
reset
,
horizontalSwipe
,
leftSwipe
,
rightSwipe
}
class
LeaveBehindItem
{
LeaveBehindItem
({
this
.
index
,
this
.
name
,
this
.
subject
,
this
.
body
});
LeaveBehindItem
.
from
(
LeaveBehindItem
item
)
:
index
=
item
.
index
,
name
=
item
.
name
,
subject
=
item
.
subject
,
body
=
item
.
body
;
final
int
index
;
final
String
name
;
final
String
subject
;
final
String
body
;
}
class
LeaveBehindDemo
extends
StatefulComponent
{
LeaveBehindDemo
({
Key
key
})
:
super
(
key:
key
);
LeaveBehindDemoState
createState
()
=>
new
LeaveBehindDemoState
();
}
class
LeaveBehindDemoState
extends
State
<
LeaveBehindDemo
>
{
final
GlobalKey
<
ScaffoldState
>
_scaffoldKey
=
new
GlobalKey
<
ScaffoldState
>();
DismissDirection
_dismissDirection
=
DismissDirection
.
horizontal
;
List
<
LeaveBehindItem
>
leaveBehindItems
;
void
initListItems
()
{
leaveBehindItems
=
new
List
.
generate
(
16
,
(
int
index
)
{
return
new
LeaveBehindItem
(
index:
index
,
name:
'Item
$index
Sender'
,
subject:
'Subject:
$index
'
,
body:
"[
$index
] first line of the message's body..."
);
});
}
void
initState
()
{
super
.
initState
();
initListItems
();
}
void
handleDemoAction
(
LeaveBehindDemoAction
action
)
{
switch
(
action
)
{
case
LeaveBehindDemoAction
.
reset
:
initListItems
();
break
;
case
LeaveBehindDemoAction
.
horizontalSwipe
:
_dismissDirection
=
DismissDirection
.
horizontal
;
break
;
case
LeaveBehindDemoAction
.
leftSwipe
:
_dismissDirection
=
DismissDirection
.
left
;
break
;
case
LeaveBehindDemoAction
.
rightSwipe
:
_dismissDirection
=
DismissDirection
.
right
;
break
;
}
}
Widget
buildItem
(
LeaveBehindItem
item
)
{
final
ThemeData
theme
=
Theme
.
of
(
context
);
return
new
Dismissable
(
key:
new
ObjectKey
(
item
),
direction:
_dismissDirection
,
onDismissed:
(
DismissDirection
direction
)
{
setState
(()
{
leaveBehindItems
.
remove
(
item
);
});
final
String
action
=
(
direction
==
DismissDirection
.
left
)
?
'archived'
:
'deleted'
;
_scaffoldKey
.
currentState
.
showSnackBar
(
new
SnackBar
(
content:
new
Text
(
'You
$action
item
${item.index}
'
)
));
},
background:
new
Container
(
decoration:
new
BoxDecoration
(
backgroundColor:
theme
.
primaryColor
),
child:
new
ListItem
(
left:
new
Icon
(
icon:
Icons
.
delete
,
color:
Colors
.
white
,
size:
36.0
)
)
),
secondaryBackground:
new
Container
(
decoration:
new
BoxDecoration
(
backgroundColor:
theme
.
primaryColor
),
child:
new
ListItem
(
right:
new
Icon
(
icon:
Icons
.
archive
,
color:
Colors
.
white
,
size:
36.0
)
)
),
child:
new
Container
(
decoration:
new
BoxDecoration
(
backgroundColor:
theme
.
canvasColor
,
border:
new
Border
(
bottom:
new
BorderSide
(
color:
theme
.
dividerColor
))
),
child:
new
ListItem
(
primary:
new
Text
(
item
.
name
),
secondary:
new
Text
(
'
${item.subject}
\n
${item.body}
'
),
isThreeLine:
true
)
)
);
}
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
key:
_scaffoldKey
,
toolBar:
new
ToolBar
(
center:
new
Text
(
'Swipe Items to Dismiss'
),
right:
<
Widget
>[
new
PopupMenuButton
<
LeaveBehindDemoAction
>(
onSelected:
handleDemoAction
,
items:
<
PopupMenuEntry
>[
new
PopupMenuItem
<
LeaveBehindDemoAction
>(
value:
LeaveBehindDemoAction
.
reset
,
child:
new
Text
(
'Reset the list'
)
),
new
PopupMenuDivider
(),
new
CheckedPopupMenuItem
<
LeaveBehindDemoAction
>(
value:
LeaveBehindDemoAction
.
horizontalSwipe
,
checked:
_dismissDirection
==
DismissDirection
.
horizontal
,
child:
new
Text
(
'Hoizontal swipe'
)
),
new
CheckedPopupMenuItem
<
LeaveBehindDemoAction
>(
value:
LeaveBehindDemoAction
.
leftSwipe
,
checked:
_dismissDirection
==
DismissDirection
.
left
,
child:
new
Text
(
'Only swipe left'
)
),
new
CheckedPopupMenuItem
<
LeaveBehindDemoAction
>(
value:
LeaveBehindDemoAction
.
rightSwipe
,
checked:
_dismissDirection
==
DismissDirection
.
right
,
child:
new
Text
(
'Only swipe right'
)
)
]
)
]
),
body:
new
Block
(
padding:
new
EdgeDims
.
all
(
4.0
),
children:
leaveBehindItems
.
map
(
buildItem
).
toList
()
)
);
}
}
examples/material_gallery/lib/gallery/home.dart
View file @
1568b827
...
...
@@ -19,6 +19,7 @@ import '../demo/drop_down_demo.dart';
import
'../demo/fitness_demo.dart'
;
import
'../demo/grid_list_demo.dart'
;
import
'../demo/icons_demo.dart'
;
import
'../demo/leave_behind_demo.dart'
;
import
'../demo/list_demo.dart'
;
import
'../demo/modal_bottom_sheet_demo.dart'
;
import
'../demo/menu_demo.dart'
;
...
...
@@ -107,6 +108,7 @@ class GalleryHomeState extends State<GalleryHome> {
new
GalleryDemo
(
title:
'Floating Action Button'
,
builder:
()
=>
new
TabsFabDemo
()),
new
GalleryDemo
(
title:
'Grid'
,
builder:
()
=>
new
GridListDemo
()),
new
GalleryDemo
(
title:
'Icons'
,
builder:
()
=>
new
IconsDemo
()),
new
GalleryDemo
(
title:
'Leave-behind List Items'
,
builder:
()
=>
new
LeaveBehindDemo
()),
new
GalleryDemo
(
title:
'List'
,
builder:
()
=>
new
ListDemo
()),
new
GalleryDemo
(
title:
'Modal Bottom Sheet'
,
builder:
()
=>
new
ModalBottomSheetDemo
()),
new
GalleryDemo
(
title:
'Menus'
,
builder:
()
=>
new
MenuDemo
()),
...
...
examples/material_gallery/lib/gallery/section.dart
View file @
1568b827
...
...
@@ -67,7 +67,7 @@ class GallerySection extends StatelessComponent {
primarySwatch:
colors
);
final
TextStyle
titleTextStyle
=
theme
.
text
.
title
.
copyWith
(
color:
theme
.
brightness
==
ThemeBrightness
.
dark
?
Colors
.
black
:
Colors
.
white
color:
Colors
.
white
);
return
new
Flexible
(
child:
new
GestureDetector
(
...
...
examples/widgets/card_collection.dart
View file @
1568b827
...
...
@@ -296,6 +296,7 @@ class CardCollectionState extends State<CardCollection> {
CardModel
cardModel
=
_cardModels
[
index
];
Widget
card
=
new
Dismissable
(
key:
new
ObjectKey
(
cardModel
),
direction:
_dismissDirection
,
onResized:
()
{
_invalidator
(<
int
>[
index
]);
},
onDismissed:
(
DismissDirection
direction
)
{
dismissCard
(
cardModel
);
},
...
...
packages/flutter/lib/src/material/list_item.dart
View file @
1568b827
...
...
@@ -28,7 +28,6 @@ class ListItem extends StatelessComponent {
this
.
onTap
,
this
.
onLongPress
})
:
super
(
key:
key
)
{
assert
(
primary
!=
null
);
assert
(
isThreeLine
?
secondary
!=
null
:
true
);
}
...
...
@@ -117,7 +116,7 @@ class ListItem extends StatelessComponent {
final
Widget
primaryLine
=
new
DefaultTextStyle
(
style:
primaryTextStyle
(
context
),
child:
primary
child:
primary
??
new
Container
()
);
Widget
center
=
primaryLine
;
if
(
isTwoLine
||
isThreeLine
)
{
...
...
packages/flutter/lib/src/widgets/dismissable.dart
View file @
1568b827
...
...
@@ -38,24 +38,47 @@ enum DismissDirection {
down
}
/// Can be dismissed by dragging in
one or more directions
.
/// Can be dismissed by dragging in
the indicated [direction]
.
///
///
The child is draggable in the indicated direction(s). When released (or
///
flung), the child disappears off the edge and the d
ismissable widget
///
Dragging or flinging this widget in the [DismissDirection] causes the child
///
to slide out of view. Following the slide animation, the D
ismissable widget
/// animates its height (or width, whichever is perpendicular to the dismiss
/// direction) to zero.
///
/// Backgrounds can be used to implement the "leave-behind" idiom. If a background
/// is specified it is stacked behind the Dismissable's child and is exposed when
/// the child moves.
///
/// The [onDimissed] callback runs after Dismissable's size has collapsed to zero.
/// If the Dismissable is a list item, it must have a key that distinguishes it from
/// the other items and its onDismissed callback must remove the item from the list.
class
Dismissable
extends
StatefulComponent
{
Dismissable
({
Key
key
,
this
.
child
,
this
.
background
,
this
.
secondaryBackground
,
this
.
onResized
,
this
.
onDismissed
,
this
.
direction
:
DismissDirection
.
horizontal
})
:
super
(
key:
key
);
})
:
super
(
key:
key
)
{
assert
(
key
!=
null
);
assert
(
secondaryBackground
!=
null
?
background
!=
null
:
true
);
}
final
Widget
child
;
/// Called when the widget changes size (i.e., when contracting after being dismissed).
/// A widget that is stacked behind the child. If secondaryBackground is also
/// specified then this widget only appears when the child has been dragged
/// down or to the right.
final
Widget
background
;
/// A widget that is stacked behind the child and is exposed when the child
/// has been dragged up or to the left. It may only be specified when background
/// has also been specified.
final
Widget
secondaryBackground
;
/// Called when the widget changes size (i.e., when contracting before being dismissed).
final
VoidCallback
onResized
;
/// Called when the widget has been dismissed, after finishing resizing.
...
...
@@ -96,6 +119,12 @@ class _DismissableState extends State<Dismissable> {
||
config
.
direction
==
DismissDirection
.
right
;
}
DismissDirection
get
_dismissDirection
{
if
(
_directionIsXAxis
)
return
_dragExtent
>
0
?
DismissDirection
.
right
:
DismissDirection
.
left
;
return
_dragExtent
>
0
?
DismissDirection
.
down
:
DismissDirection
.
up
;
}
bool
get
_isActive
{
return
_dragUnderway
||
_moveController
.
isAnimating
;
}
...
...
@@ -235,12 +264,7 @@ class _DismissableState extends State<Dismissable> {
void
_handleResizeProgressChanged
()
{
if
(
_resizeController
.
isCompleted
)
{
if
(
config
.
onDismissed
!=
null
)
{
DismissDirection
direction
;
if
(
_directionIsXAxis
)
direction
=
_dragExtent
>
0
?
DismissDirection
.
right
:
DismissDirection
.
left
;
else
direction
=
_dragExtent
>
0
?
DismissDirection
.
down
:
DismissDirection
.
up
;
config
.
onDismissed
(
direction
);
config
.
onDismissed
(
_dismissDirection
);
}
}
else
{
if
(
config
.
onResized
!=
null
)
...
...
@@ -249,31 +273,53 @@ class _DismissableState extends State<Dismissable> {
}
Widget
build
(
BuildContext
context
)
{
Widget
background
=
config
.
background
;
if
(
config
.
secondaryBackground
!=
null
)
{
final
DismissDirection
direction
=
_dismissDirection
;
if
(
direction
==
DismissDirection
.
left
||
direction
==
DismissDirection
.
up
)
background
=
config
.
secondaryBackground
;
}
if
(
_resizeAnimation
!=
null
)
{
// we've been dragged aside, and are now resizing.
assert
(()
{
if
(
_resizeAnimation
.
status
!=
AnimationStatus
.
forward
)
{
assert
(
_resizeAnimation
.
status
==
AnimationStatus
.
completed
);
throw
new
WidgetError
(
'
Dismissable widget completed its resize animation without being removed from the tree.
\n
'
'Make sure to implement the onDismissed handler and to immediately remove the Dismissable
'
'
A dismissed Dismissable widget is still part of the tree.
\n
'
+
'Make sure to implement the onDismissed handler and to immediately remove the Dismissable
\n
'
+
'widget from the application once that handler has fired.'
);
}
return
true
;
});
return
new
AnimatedBuilder
(
animation:
_resizeAnimation
,
builder:
(
BuildContext
context
,
Widget
child
)
{
return
new
SizedBox
(
width:
!
_directionIsXAxis
?
_resizeAnimation
.
value
:
null
,
height:
_directionIsXAxis
?
_resizeAnimation
.
value
:
null
height:
_directionIsXAxis
?
_resizeAnimation
.
value
:
null
,
child:
background
);
}
);
}
// we are not resizing. (we may be being dragged aside.)
Widget
backgroundAndChild
=
new
SlideTransition
(
position:
_moveAnimation
,
child:
config
.
child
);
if
(
background
!=
null
)
{
backgroundAndChild
=
new
Stack
(
children:
<
Widget
>[
new
Positioned
(
left:
0.0
,
top:
0.0
,
bottom:
0.0
,
right:
0.0
,
child:
background
),
new
Viewport
(
child:
backgroundAndChild
)
]
);
}
// We are not resizing but we may be being dragging in config.direction.
return
new
GestureDetector
(
onHorizontalDragStart:
_directionIsXAxis
?
_handleDragStart
:
null
,
onHorizontalDragUpdate:
_directionIsXAxis
?
_handleDragUpdate
:
null
,
...
...
@@ -282,10 +328,7 @@ class _DismissableState extends State<Dismissable> {
onVerticalDragUpdate:
_directionIsXAxis
?
null
:
_handleDragUpdate
,
onVerticalDragEnd:
_directionIsXAxis
?
null
:
_handleDragEnd
,
behavior:
HitTestBehavior
.
opaque
,
child:
new
SlideTransition
(
position:
_moveAnimation
,
child:
config
.
child
)
child:
backgroundAndChild
);
}
}
packages/flutter/test/widget/dismissable_test.dart
View file @
1568b827
...
...
@@ -110,6 +110,7 @@ class Test1215DismissableComponent extends StatelessComponent {
final
String
text
;
Widget
build
(
BuildContext
context
)
{
return
new
Dismissable
(
key:
new
ObjectKey
(
text
),
child:
new
AspectRatio
(
aspectRatio:
1.0
,
child:
new
Text
(
this
.
text
)
...
...
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