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
b4777c35
Unverified
Commit
b4777c35
authored
Jan 13, 2022
by
Casey Rogers
Committed by
GitHub
Jan 13, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make `DraggableScrollableController` a `ChangeNotifier` (#96089)
parent
f01556ab
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
167 additions
and
1 deletion
+167
-1
draggable_scrollable_sheet.dart
...s/flutter/lib/src/widgets/draggable_scrollable_sheet.dart
+26
-1
draggable_scrollable_sheet_test.dart
...flutter/test/widgets/draggable_scrollable_sheet_test.dart
+141
-0
No files found.
packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart
View file @
b4777c35
...
...
@@ -44,7 +44,14 @@ typedef ScrollableWidgetBuilder = Widget Function(
///
/// The controller's methods cannot be used until after the controller has been
/// passed into a [DraggableScrollableSheet] and the sheet has run initState.
class
DraggableScrollableController
{
///
/// A [DraggableScrollableController] is a [Listenable]. It notifies its
/// listeners whenever an attached sheet changes sizes. It does not notify its
/// listeners when a sheet is first attached or when an attached sheet's
/// parameters change without affecting the sheet's current size. It does not
/// fire when [pixels] changes without [size] changing. For example, if the
/// constraints provided to an attached sheet change.
class
DraggableScrollableController
extends
ChangeNotifier
{
_DraggableScrollableSheetScrollController
?
_attachedController
;
/// Get the current size (as a fraction of the parent height) of the attached sheet.
...
...
@@ -160,9 +167,23 @@ class DraggableScrollableController {
void
_attach
(
_DraggableScrollableSheetScrollController
scrollController
)
{
assert
(
_attachedController
==
null
,
'Draggable scrollable controller is already attached to a sheet.'
);
_attachedController
=
scrollController
;
_attachedController
!.
extent
.
_currentSize
.
addListener
(
notifyListeners
);
}
void
_onExtentReplaced
(
_DraggableSheetExtent
previousExtent
)
{
// When the extent has been replaced, the old extent is already disposed and
// the controller will point to a new extent. We have to add our listener to
// the new extent.
_attachedController
!.
extent
.
_currentSize
.
addListener
(
notifyListeners
);
if
(
previousExtent
.
currentSize
!=
_attachedController
!.
extent
.
currentSize
)
{
// The listener won't fire for a change in size between two extent
// objects so we have to fire it manually here.
notifyListeners
();
}
}
void
_detach
()
{
_attachedController
?.
extent
.
_currentSize
.
removeListener
(
notifyListeners
);
_attachedController
=
null
;
}
}
...
...
@@ -635,6 +656,7 @@ class _DraggableScrollableSheetState extends State<DraggableScrollableSheet> {
}
void
_replaceExtent
()
{
final
_DraggableSheetExtent
previousExtent
=
_extent
;
_extent
.
dispose
();
_extent
=
_extent
.
copyWith
(
minSize:
widget
.
minChildSize
,
...
...
@@ -647,6 +669,9 @@ class _DraggableScrollableSheetState extends State<DraggableScrollableSheet> {
// Modify the existing scroll controller instead of replacing it so that
// developers listening to the controller do not have to rebuild their listeners.
_scrollController
.
extent
=
_extent
;
// If an external facing controller was provided, let it know that the
// extent has been replaced.
widget
.
controller
?.
_onExtentReplaced
(
previousExtent
);
if
(
widget
.
snap
)
{
// Trigger a snap in case snap or snapSizes has changed. We put this in a
// post frame callback so that `build` can update `_extent.availablePixels`
...
...
packages/flutter/test/widgets/draggable_scrollable_sheet_test.dart
View file @
b4777c35
...
...
@@ -976,6 +976,147 @@ void main() {
expect
(
tester
.
takeException
(),
isAssertionError
);
});
testWidgets
(
'Can listen for changes in sheet size'
,
(
WidgetTester
tester
)
async
{
const
Key
stackKey
=
ValueKey
<
String
>(
'stack'
);
const
Key
containerKey
=
ValueKey
<
String
>(
'container'
);
final
List
<
double
>
loggedSizes
=
<
double
>[];
final
DraggableScrollableController
controller
=
DraggableScrollableController
();
controller
.
addListener
(()
{
loggedSizes
.
add
(
controller
.
size
);
});
await
tester
.
pumpWidget
(
_boilerplate
(
null
,
controller:
controller
,
stackKey:
stackKey
,
containerKey:
containerKey
,
));
await
tester
.
pumpAndSettle
();
final
double
screenHeight
=
tester
.
getSize
(
find
.
byKey
(
stackKey
))
.
height
;
// The initial size shouldn't be logged because no change has occurred yet.
expect
(
loggedSizes
.
isEmpty
,
true
);
await
tester
.
drag
(
find
.
text
(
'Item 1'
),
Offset
(
0
,
.
1
*
screenHeight
),
touchSlopY:
0
);
await
tester
.
pumpAndSettle
();
expect
(
loggedSizes
,
<
double
>[.
4
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
await
tester
.
timedDrag
(
find
.
text
(
'Item 1'
),
Offset
(
0
,
-.
1
*
screenHeight
),
const
Duration
(
seconds:
1
),
frequency:
2
);
await
tester
.
pumpAndSettle
();
expect
(
loggedSizes
,
<
double
>[.
45
,
.
5
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
controller
.
jumpTo
(.
6
);
await
tester
.
pumpAndSettle
();
expect
(
loggedSizes
,
<
double
>[.
6
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
controller
.
animateTo
(
1
,
duration:
const
Duration
(
milliseconds:
400
),
curve:
Curves
.
linear
);
await
tester
.
pumpAndSettle
();
expect
(
loggedSizes
,
<
double
>[.
7
,
.
8
,
.
9
,
1
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
DraggableScrollableActuator
.
reset
(
tester
.
element
(
find
.
byKey
(
containerKey
)));
await
tester
.
pumpAndSettle
();
expect
(
loggedSizes
,
<
double
>[.
5
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
});
testWidgets
(
'Listener does not fire on parameter change and persists after change'
,
(
WidgetTester
tester
)
async
{
const
Key
stackKey
=
ValueKey
<
String
>(
'stack'
);
const
Key
containerKey
=
ValueKey
<
String
>(
'container'
);
final
List
<
double
>
loggedSizes
=
<
double
>[];
final
DraggableScrollableController
controller
=
DraggableScrollableController
();
controller
.
addListener
(()
{
loggedSizes
.
add
(
controller
.
size
);
});
await
tester
.
pumpWidget
(
_boilerplate
(
null
,
controller:
controller
,
stackKey:
stackKey
,
containerKey:
containerKey
,
));
await
tester
.
pumpAndSettle
();
final
double
screenHeight
=
tester
.
getSize
(
find
.
byKey
(
stackKey
))
.
height
;
expect
(
loggedSizes
.
isEmpty
,
true
);
await
tester
.
drag
(
find
.
text
(
'Item 1'
),
Offset
(
0
,
.
1
*
screenHeight
),
touchSlopY:
0
);
await
tester
.
pumpAndSettle
();
expect
(
loggedSizes
,
<
double
>[.
4
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
// Update a parameter without forcing a change in the current size.
await
tester
.
pumpWidget
(
_boilerplate
(
null
,
minChildSize:
.
1
,
controller:
controller
,
stackKey:
stackKey
,
containerKey:
containerKey
,
));
expect
(
loggedSizes
.
isEmpty
,
true
);
await
tester
.
drag
(
find
.
text
(
'Item 1'
),
Offset
(
0
,
.
1
*
screenHeight
),
touchSlopY:
0
);
await
tester
.
pumpAndSettle
();
expect
(
loggedSizes
,
<
double
>[.
3
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
});
testWidgets
(
'Listener fires if a parameter change forces a change in size'
,
(
WidgetTester
tester
)
async
{
const
Key
stackKey
=
ValueKey
<
String
>(
'stack'
);
const
Key
containerKey
=
ValueKey
<
String
>(
'container'
);
final
List
<
double
>
loggedSizes
=
<
double
>[];
final
DraggableScrollableController
controller
=
DraggableScrollableController
();
controller
.
addListener
(()
{
loggedSizes
.
add
(
controller
.
size
);
});
await
tester
.
pumpWidget
(
_boilerplate
(
null
,
controller:
controller
,
stackKey:
stackKey
,
containerKey:
containerKey
,
));
await
tester
.
pumpAndSettle
();
final
double
screenHeight
=
tester
.
getSize
(
find
.
byKey
(
stackKey
))
.
height
;
expect
(
loggedSizes
.
isEmpty
,
true
);
// Set a new `initialChildSize` which will trigger a size change because we
// haven't moved away initial size yet.
await
tester
.
pumpWidget
(
_boilerplate
(
null
,
initialChildSize:
.
6
,
controller:
controller
,
stackKey:
stackKey
,
containerKey:
containerKey
,
));
expect
(
loggedSizes
,
<
double
>[.
6
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
// Move away from initial child size.
await
tester
.
drag
(
find
.
text
(
'Item 1'
),
Offset
(
0
,
.
3
*
screenHeight
),
touchSlopY:
0
);
await
tester
.
pumpAndSettle
();
expect
(
loggedSizes
,
<
double
>[.
3
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
// Set a `minChildSize` greater than the current size.
await
tester
.
pumpWidget
(
_boilerplate
(
null
,
minChildSize:
.
4
,
controller:
controller
,
stackKey:
stackKey
,
containerKey:
containerKey
,
));
expect
(
loggedSizes
,
<
double
>[.
4
].
map
((
double
v
)
=>
closeTo
(
v
,
precisionErrorTolerance
)));
loggedSizes
.
clear
();
});
testWidgets
(
'Invalid controller interactions throw assertion errors'
,
(
WidgetTester
tester
)
async
{
final
DraggableScrollableController
controller
=
DraggableScrollableController
();
// Can't use a controller before attaching it.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment