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
7619c686
Unverified
Commit
7619c686
authored
Jan 23, 2019
by
Hans Muller
Committed by
GitHub
Jan 23, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add Dismissible.confirmDismiss callback (#26901)
parent
c37b7c53
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
232 additions
and
7 deletions
+232
-7
dismissible.dart
packages/flutter/lib/src/widgets/dismissible.dart
+34
-6
dialog_test.dart
packages/flutter/test/material/dialog_test.dart
+131
-0
dismissible_test.dart
packages/flutter/test/widgets/dismissible_test.dart
+67
-1
No files found.
packages/flutter/lib/src/widgets/dismissible.dart
View file @
7619c686
...
...
@@ -24,6 +24,12 @@ const double _kDismissThreshold = 0.4;
/// Used by [Dismissible.onDismissed].
typedef
DismissDirectionCallback
=
void
Function
(
DismissDirection
direction
);
/// Signature used by [Dismissible] to give the application an opportunity to
/// confirm or veto a dismiss gesture.
///
/// Used by [Dismissible.confirmDismiss].
typedef
ConfirmDismissCallback
=
Future
<
bool
>
Function
(
DismissDirection
direction
);
/// The direction in which a [Dismissible] can be dismissed.
enum
DismissDirection
{
/// The [Dismissible] can be dismissed by dragging either up or down.
...
...
@@ -77,6 +83,7 @@ class Dismissible extends StatefulWidget {
@required
this
.
child
,
this
.
background
,
this
.
secondaryBackground
,
this
.
confirmDismiss
,
this
.
onResize
,
this
.
onDismissed
,
this
.
direction
=
DismissDirection
.
horizontal
,
...
...
@@ -105,6 +112,15 @@ class Dismissible extends StatefulWidget {
/// has also been specified.
final
Widget
secondaryBackground
;
/// Gives the app an opportunity to confirm or veto a pending dismissal.
///
/// If the returned Future<bool> completes true, then this widget will be
/// dismissed, otherwise it will be moved back to its original location.
///
/// If the returned Future<bool> completes to false or null the [onResize]
/// and [onDismissed] callbacks will not run.
final
ConfirmDismissCallback
confirmDismiss
;
/// Called when the widget changes size (i.e., when contracting before being dismissed).
final
VoidCallback
onResize
;
...
...
@@ -386,11 +402,11 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
return
_FlingGestureKind
.
reverse
;
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
Future
<
void
>
_handleDragEnd
(
DragEndDetails
details
)
async
{
if
(!
_isActive
||
_moveController
.
isAnimating
)
return
;
_dragUnderway
=
false
;
if
(
_moveController
.
isCompleted
)
{
if
(
_moveController
.
isCompleted
&&
await
_confirmStartResizeAnimation
()
==
true
)
{
_startResizeAnimation
();
return
;
}
...
...
@@ -424,12 +440,25 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
}
}
void
_handleDismissStatusChanged
(
AnimationStatus
status
)
{
if
(
status
==
AnimationStatus
.
completed
&&
!
_dragUnderway
)
_startResizeAnimation
();
Future
<
void
>
_handleDismissStatusChanged
(
AnimationStatus
status
)
async
{
if
(
status
==
AnimationStatus
.
completed
&&
!
_dragUnderway
)
{
if
(
await
_confirmStartResizeAnimation
()
==
true
)
_startResizeAnimation
();
else
_moveController
.
reverse
();
}
updateKeepAlive
();
}
Future
<
bool
>
_confirmStartResizeAnimation
()
async
{
if
(
widget
.
confirmDismiss
!=
null
)
{
final
DismissDirection
direction
=
_dismissDirection
;
assert
(
direction
!=
null
);
return
widget
.
confirmDismiss
(
direction
);
}
return
true
;
}
void
_startResizeAnimation
()
{
assert
(
_moveController
!=
null
);
assert
(
_moveController
.
isCompleted
);
...
...
@@ -550,4 +579,3 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
);
}
}
packages/flutter/test/material/dialog_test.dart
View file @
7619c686
...
...
@@ -476,4 +476,135 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'Dismissable.confirmDismiss defers to an AlertDialog'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
ScaffoldState
>
_scaffoldKey
=
GlobalKey
<
ScaffoldState
>();
final
List
<
int
>
dismissedItems
=
<
int
>[];
// Dismiss is confirmed IFF confirmDismiss() returns true.
Future
<
bool
>
confirmDismiss
(
DismissDirection
dismissDirection
)
{
return
showDialog
<
bool
>(
context:
_scaffoldKey
.
currentContext
,
barrierDismissible:
true
,
// showDialog() returns null if tapped outside the dialog
builder:
(
BuildContext
context
)
{
return
AlertDialog
(
actions:
<
Widget
>[
FlatButton
(
child:
const
Text
(
'TRUE'
),
onPressed:
()
{
Navigator
.
pop
(
context
,
true
);
// showDialog() returns true
},
),
FlatButton
(
child:
const
Text
(
'FALSE'
),
onPressed:
()
{
Navigator
.
pop
(
context
,
false
);
// showDialog() returns false
},
),
],
);
},
);
}
Widget
buildDismissibleItem
(
int
item
,
StateSetter
setState
)
{
return
Dismissible
(
key:
ValueKey
<
int
>(
item
),
confirmDismiss:
confirmDismiss
,
onDismissed:
(
DismissDirection
direction
)
{
setState
(()
{
expect
(
dismissedItems
.
contains
(
item
),
isFalse
);
dismissedItems
.
add
(
item
);
});
},
child:
SizedBox
(
height:
100.0
,
child:
Text
(
item
.
toString
()),
),
);
}
Widget
buildFrame
()
{
return
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
Scaffold
(
key:
_scaffoldKey
,
body:
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
ListView
(
itemExtent:
100.0
,
children:
<
int
>[
0
,
1
,
2
,
3
,
4
]
.
where
((
int
i
)
=>
!
dismissedItems
.
contains
(
i
))
.
map
<
Widget
>((
int
item
)
=>
buildDismissibleItem
(
item
,
setState
)).
toList
(),
),
),
);
},
),
);
}
Future
<
void
>
dismissItem
(
WidgetTester
tester
,
int
item
)
async
{
await
tester
.
fling
(
find
.
text
(
item
.
toString
()),
const
Offset
(
300.0
,
0.0
),
1000.0
);
// fling to the right
await
tester
.
pump
();
// start the slide
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the slide and start shrinking...
await
tester
.
pump
();
// first frame of shrinking animation
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the shrinking and call the callback...
await
tester
.
pump
();
// rebuild after the callback removes the entry
}
// Dismiss item 0 is confirmed via the AlertDialog
await
tester
.
pumpWidget
(
buildFrame
());
expect
(
dismissedItems
,
isEmpty
);
await
dismissItem
(
tester
,
0
);
// Causes the AlertDialog to appear per confirmDismiss
await
tester
.
pumpAndSettle
();
await
tester
.
tap
(
find
.
text
(
'TRUE'
));
// AlertDialog action
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'TRUE'
),
findsNothing
);
// Dialog was dismissed
expect
(
find
.
text
(
'FALSE'
),
findsNothing
);
expect
(
dismissedItems
,
<
int
>[
0
]);
expect
(
find
.
text
(
'0'
),
findsNothing
);
// Dismiss item 1 is not confirmed via the AlertDialog
await
tester
.
pumpWidget
(
buildFrame
());
expect
(
dismissedItems
,
<
int
>[
0
]);
await
dismissItem
(
tester
,
1
);
// Causes the AlertDialog to appear per confirmDismiss
await
tester
.
pumpAndSettle
();
await
tester
.
tap
(
find
.
text
(
'FALSE'
));
// AlertDialog action
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'TRUE'
),
findsNothing
);
// Dialog was dismissed
expect
(
find
.
text
(
'FALSE'
),
findsNothing
);
expect
(
dismissedItems
,
<
int
>[
0
]);
expect
(
find
.
text
(
'0'
),
findsNothing
);
expect
(
find
.
text
(
'1'
),
findsOneWidget
);
// Dismiss item 1 is not confirmed via the AlertDialog
await
tester
.
pumpWidget
(
buildFrame
());
expect
(
dismissedItems
,
<
int
>[
0
]);
await
dismissItem
(
tester
,
1
);
// Causes the AlertDialog to appear per confirmDismiss
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'FALSE'
),
findsOneWidget
);
expect
(
find
.
text
(
'TRUE'
),
findsOneWidget
);
await
tester
.
tapAt
(
Offset
.
zero
);
// Tap outside of the AlertDialog
await
tester
.
pumpAndSettle
();
expect
(
dismissedItems
,
<
int
>[
0
]);
expect
(
find
.
text
(
'0'
),
findsNothing
);
expect
(
find
.
text
(
'1'
),
findsOneWidget
);
expect
(
find
.
text
(
'TRUE'
),
findsNothing
);
// Dialog was dismissed
expect
(
find
.
text
(
'FALSE'
),
findsNothing
);
// Dismiss item 1 is confirmed via the AlertDialog
await
tester
.
pumpWidget
(
buildFrame
());
expect
(
dismissedItems
,
<
int
>[
0
]);
await
dismissItem
(
tester
,
1
);
// Causes the AlertDialog to appear per confirmDismiss
await
tester
.
pumpAndSettle
();
await
tester
.
tap
(
find
.
text
(
'TRUE'
));
// AlertDialog action
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'TRUE'
),
findsNothing
);
// Dialog was dismissed
expect
(
find
.
text
(
'FALSE'
),
findsNothing
);
expect
(
dismissedItems
,
<
int
>[
0
,
1
]);
expect
(
find
.
text
(
'0'
),
findsNothing
);
expect
(
find
.
text
(
'1'
),
findsNothing
);
});
}
packages/flutter/test/widgets/dismissible_test.dart
View file @
7619c686
...
...
@@ -15,7 +15,11 @@ List<int> dismissedItems = <int>[];
Widget
background
;
const
double
crossAxisEndOffset
=
0.5
;
Widget
buildTest
(
{
double
startToEndThreshold
,
TextDirection
textDirection
=
TextDirection
.
ltr
})
{
Widget
buildTest
(
{
double
startToEndThreshold
,
TextDirection
textDirection
=
TextDirection
.
ltr
,
Future
<
bool
>
Function
(
BuildContext
context
,
DismissDirection
direction
)
confirmDismiss
,
})
{
return
Directionality
(
textDirection:
textDirection
,
child:
StatefulBuilder
(
...
...
@@ -25,6 +29,9 @@ Widget buildTest({ double startToEndThreshold, TextDirection textDirection = Tex
dragStartBehavior:
DragStartBehavior
.
down
,
key:
ValueKey
<
int
>(
item
),
direction:
dismissDirection
,
confirmDismiss:
confirmDismiss
==
null
?
null
:
(
DismissDirection
direction
)
{
return
confirmDismiss
(
context
,
direction
);
},
onDismissed:
(
DismissDirection
direction
)
{
setState
(()
{
reportedDismissDirection
=
direction
;
...
...
@@ -660,4 +667,63 @@ void main() {
expect
(
find
.
text
(
'1'
),
findsOneWidget
);
expect
(
dismissedItems
,
isEmpty
);
});
testWidgets
(
'confirmDismiss returns values: true, false, null'
,
(
WidgetTester
tester
)
async
{
scrollDirection
=
Axis
.
vertical
;
dismissDirection
=
DismissDirection
.
horizontal
;
DismissDirection
confirmDismissDirection
;
Widget
buildFrame
(
bool
confirmDismissValue
)
{
return
buildTest
(
confirmDismiss:
(
BuildContext
context
,
DismissDirection
dismissDirection
)
{
confirmDismissDirection
=
dismissDirection
;
return
Future
<
bool
>.
value
(
confirmDismissValue
);
}
);
}
// Dismiss is confirmed IFF confirmDismiss() returns true.
await
tester
.
pumpWidget
(
buildFrame
(
true
));
expect
(
dismissedItems
,
isEmpty
);
await
dismissItem
(
tester
,
0
,
gestureDirection:
AxisDirection
.
right
,
mechanism:
flingElement
);
expect
(
find
.
text
(
'0'
),
findsNothing
);
expect
(
dismissedItems
,
equals
(<
int
>[
0
]));
expect
(
reportedDismissDirection
,
DismissDirection
.
startToEnd
);
expect
(
confirmDismissDirection
,
DismissDirection
.
startToEnd
);
await
dismissItem
(
tester
,
1
,
gestureDirection:
AxisDirection
.
left
,
mechanism:
flingElement
);
expect
(
find
.
text
(
'1'
),
findsNothing
);
expect
(
dismissedItems
,
equals
(<
int
>[
0
,
1
]));
expect
(
reportedDismissDirection
,
DismissDirection
.
endToStart
);
expect
(
confirmDismissDirection
,
DismissDirection
.
endToStart
);
// Dismiss is not confirmed if confirmDismiss() returns false
dismissedItems
=
<
int
>[];
await
tester
.
pumpWidget
(
buildFrame
(
false
));
await
dismissItem
(
tester
,
0
,
gestureDirection:
AxisDirection
.
right
,
mechanism:
flingElement
);
expect
(
find
.
text
(
'0'
),
findsOneWidget
);
expect
(
dismissedItems
,
isEmpty
);
expect
(
confirmDismissDirection
,
DismissDirection
.
startToEnd
);
await
dismissItem
(
tester
,
1
,
gestureDirection:
AxisDirection
.
left
,
mechanism:
flingElement
);
expect
(
find
.
text
(
'1'
),
findsOneWidget
);
expect
(
dismissedItems
,
isEmpty
);
expect
(
confirmDismissDirection
,
DismissDirection
.
endToStart
);
// Dismiss is not confirmed if confirmDismiss() returns null
dismissedItems
=
<
int
>[];
await
tester
.
pumpWidget
(
buildFrame
(
null
));
await
dismissItem
(
tester
,
0
,
gestureDirection:
AxisDirection
.
right
,
mechanism:
flingElement
);
expect
(
find
.
text
(
'0'
),
findsOneWidget
);
expect
(
dismissedItems
,
isEmpty
);
expect
(
confirmDismissDirection
,
DismissDirection
.
startToEnd
);
await
dismissItem
(
tester
,
1
,
gestureDirection:
AxisDirection
.
left
,
mechanism:
flingElement
);
expect
(
find
.
text
(
'1'
),
findsOneWidget
);
expect
(
dismissedItems
,
isEmpty
);
expect
(
confirmDismissDirection
,
DismissDirection
.
endToStart
);
});
}
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