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
f05c409a
Unverified
Commit
f05c409a
authored
Mar 25, 2021
by
Kate Lovett
Committed by
GitHub
Mar 25, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve error messaging and documentation for Scrollbar.isAlwaysShown (#77107)
parent
fed35b4d
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
170 additions
and
6 deletions
+170
-6
scrollbar.dart
packages/flutter/lib/src/widgets/scrollbar.dart
+82
-6
scrollbar_test.dart
packages/flutter/test/material/scrollbar_test.dart
+59
-0
scrollbar_test.dart
packages/flutter/test/widgets/scrollbar_test.dart
+29
-0
No files found.
packages/flutter/lib/src/widgets/scrollbar.dart
View file @
f05c409a
...
...
@@ -578,8 +578,9 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
///
/// By default, the thumb will fade in and out as the child scroll view
/// scrolls. When [isAlwaysShown] is true, the scrollbar thumb will remain
/// visible without the fade animation. This requires that a [ScrollController]
/// is provided to [controller], or that the [PrimaryScrollController] is available.
/// visible without the fade animation. This requires that the [ScrollController]
/// associated with the Scrollable widget is provided to [controller], or that
/// the [PrimaryScrollController] is being used by that Scrollable widget.
///
/// If the scrollbar is wrapped around multiple [ScrollView]s, it only responds to
/// the nearest scrollView and shows the corresponding scrollbar thumb by default.
...
...
@@ -705,9 +706,18 @@ class RawScrollbar extends StatefulWidget {
/// When false, the scrollbar will be shown during scrolling
/// and will fade out otherwise.
///
/// When true, the scrollbar will always be visible and never fade out. If the
/// [controller] property has not been set, the [PrimaryScrollController] will
/// be used.
/// When true, the scrollbar will always be visible and never fade out. This
/// requires that the Scrollbar can access the [ScrollController] of the
/// associated Scrollable widget. This can either be the provided [controller],
/// or the [PrimaryScrollController] of the current context.
///
/// * When providing a controller, the same ScrollController must also be
/// provided to the associated Scrollable widget.
/// * The [PrimaryScrollController] is used by default for a [ScrollView]
/// that has not been provided a [ScrollController] and that has an
/// [Axis.vertical] [ScrollDirection]. This automatic behavior does not
/// apply to those with a ScrollDirection of Axis.horizontal. To explicitly
/// use the PrimaryScrollController, set [ScrollView.primary] to true.
///
/// Defaults to false when null.
///
...
...
@@ -759,6 +769,10 @@ class RawScrollbar extends StatefulWidget {
///
/// * [RawScrollbarState.showScrollbar], an overridable getter which uses
/// this value to override the default behavior.
/// * [ScrollView.primary], which indicates whether the ScrollView is the primary
/// scroll view associated with the parent [PrimaryScrollController].
/// * [PrimaryScrollController], which associates a [ScrollController] with
/// a subtree.
/// {@endtemplate}
final
bool
?
isAlwaysShown
;
...
...
@@ -916,11 +930,73 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
// thumb to show immediately when isAlwaysShown is true. A scroll
// event is required in order to paint the thumb.
final
ScrollController
?
scrollController
=
widget
.
controller
??
PrimaryScrollController
.
of
(
context
);
final
bool
tryPrimary
=
widget
.
controller
==
null
;
final
String
controllerForError
=
tryPrimary
?
'provided ScrollController'
:
'PrimaryScrollController'
;
assert
(
scrollController
!=
null
,
'A ScrollController is required when Scrollbar.isAlwaysShown is true. '
'Either Scrollbar.controller was not provided, or a PrimaryScrollController could not be found.'
,
'
${tryPrimary ? 'The Scrollbar was not provided a ScrollController, '
'and attempted to use the PrimaryScrollController, but none was found.' :''}
'
,
);
assert
(()
{
if
(!
scrollController
!.
hasClients
)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'The Scrollbar
\'
s ScrollController has no ScrollPosition attached.'
),
ErrorDescription
(
'A Scrollbar cannot be painted without a ScrollPosition. '
),
ErrorHint
(
'The Scrollbar attempted to use the
$controllerForError
. This '
'ScrollController should be associated with the ScrollView that '
'the Scrollbar is being applied to. '
'
${tryPrimary
? 'A ScrollView with an Axis.vertical '
'ScrollDirection will automatically use the '
'PrimaryScrollController if the user has not provided a '
'ScrollController, but a ScrollDirection of Axis.horizontal will '
'not. To use the PrimaryScrollController explicitly, set ScrollView.primary '
'to true for the Scrollable widget.'
: 'When providing your own ScrollController, ensure both the '
'Scrollbar and the Scrollable widget use the same one.'
}
'
),
]);
}
return
true
;
}());
assert
(()
{
try
{
scrollController
!.
position
;
}
catch
(
_
)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'The
$controllerForError
is currently attached to more than one '
'ScrollPosition.'
),
ErrorDescription
(
'The Scrollbar requires a single ScrollPosition in order to be painted.'
),
ErrorHint
(
'When Scrollbar.isAlwaysShown is true, the associated Scrollable '
'widgets must have unique ScrollControllers. '
'
${tryPrimary
? 'The PrimaryScrollController is used by default for '
'ScrollViews with an Axis.vertical ScrollDirection, '
'unless the ScrollView has been provided its own '
'ScrollController. More than one Scrollable may have tried '
'to use the PrimaryScrollController of the current context.'
: 'The provided ScrollController must be unique to a '
'Scrollable widget.'
}
'
),
]);
}
return
true
;
}());
scrollController
!.
position
.
didUpdateScrollPositionBy
(
0
);
}
});
...
...
packages/flutter/test/material/scrollbar_test.dart
View file @
f05c409a
...
...
@@ -1275,4 +1275,63 @@ void main() {
// The offset should not have changed.
expect
(
scrollController
.
offset
,
scrollAmount
);
});
testWidgets
(
'Scrollbar.isAlwaysShown triggers assertion when multiple ScrollPositions are attached.'
,
(
WidgetTester
tester
)
async
{
Widget
_getTabContent
({
ScrollController
?
scrollController
})
{
return
Scrollbar
(
isAlwaysShown:
true
,
controller:
scrollController
,
child:
ListView
.
builder
(
controller:
scrollController
,
itemCount:
200
,
itemBuilder:
(
BuildContext
context
,
int
index
)
=>
const
Text
(
'Test'
),
),
);
}
Widget
_buildApp
({
ScrollController
?
scrollController
})
{
return
MaterialApp
(
home:
DefaultTabController
(
length:
2
,
child:
Scaffold
(
body:
TabBarView
(
children:
<
Widget
>[
_getTabContent
(
scrollController:
scrollController
),
_getTabContent
(
scrollController:
scrollController
),
],
),
),
),
);
}
// Asserts when using the PrimaryScrollController.
await
tester
.
pumpWidget
(
_buildApp
());
// Swipe to the second tab, resulting in two attached ScrollPositions during
// the transition.
try
{
await
tester
.
drag
(
find
.
text
(
'Test'
).
first
,
const
Offset
(
10.0
,
0.0
));
}
on
FlutterError
catch
(
error
)
{
expect
(
error
.
message
,
contains
(
'The Scrollbar attempted to paint using the position attached to the PrimaryScrollController.'
),
);
}
// Asserts when using the ScrollController provided by the user.
final
ScrollController
scrollController
=
ScrollController
();
await
tester
.
pumpWidget
(
_buildApp
(
scrollController:
scrollController
));
// Swipe to the second tab, resulting in two attached ScrollPositions during
// the transition.
try
{
await
tester
.
drag
(
find
.
text
(
'Test'
).
first
,
const
Offset
(
10.0
,
0.0
));
}
on
AssertionError
catch
(
error
)
{
expect
(
error
.
message
,
contains
(
'The Scrollbar attempted to paint using the position attached to the provided ScrollController.'
),
);
}
});
}
packages/flutter/test/widgets/scrollbar_test.dart
View file @
f05c409a
...
...
@@ -955,4 +955,33 @@ void main() {
),
);
});
testWidgets
(
'RawScrollbar.isAlwaysShown asserts that a ScrollPosition is attached'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
RawScrollbar
(
isAlwaysShown:
true
,
controller:
ScrollController
(),
thumbColor:
const
Color
(
0x11111111
),
child:
const
SingleChildScrollView
(
child:
SizedBox
(
height:
1000.0
,
width:
50.0
,
),
),
),
),
),
);
await
tester
.
pumpAndSettle
();
final
dynamic
exception
=
tester
.
takeException
();
expect
(
exception
,
isAssertionError
);
expect
(
(
exception
as
AssertionError
).
message
,
contains
(
'The Scrollbar
\'
s ScrollController has no ScrollPosition attached.'
),
);
});
}
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