mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Replace Scrollbar with Scrollbar2 (#8133)
This commit is contained in:
parent
ef004ae97d
commit
fd30f63161
|
@ -190,7 +190,7 @@ class ListDemoState extends State<ListDemo> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: new Scrollbar2(
|
body: new Scrollbar(
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
padding: new EdgeInsets.symmetric(vertical: _dense ? 4.0 : 8.0),
|
padding: new EdgeInsets.symmetric(vertical: _dense ? 4.0 : 8.0),
|
||||||
children: listItems.toList(),
|
children: listItems.toList(),
|
||||||
|
|
|
@ -447,7 +447,7 @@ class _LicensePageState extends State<LicensePage> {
|
||||||
),
|
),
|
||||||
body: new DefaultTextStyle(
|
body: new DefaultTextStyle(
|
||||||
style: Theme.of(context).textTheme.caption,
|
style: Theme.of(context).textTheme.caption,
|
||||||
child: new Scrollbar2(
|
child: new Scrollbar(
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
|
|
|
@ -184,7 +184,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
|
||||||
textStyle: route.style,
|
textStyle: route.style,
|
||||||
child: new ScrollConfiguration(
|
child: new ScrollConfiguration(
|
||||||
delegate: new _DropdownScrollConfigurationDelegate(Theme.of(context).platform),
|
delegate: new _DropdownScrollConfigurationDelegate(Theme.of(context).platform),
|
||||||
child: new Scrollbar2(
|
child: new Scrollbar(
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
controller: config.route.scrollController,
|
controller: config.route.scrollController,
|
||||||
padding: _kMenuVerticalPadding,
|
padding: _kMenuVerticalPadding,
|
||||||
|
|
|
@ -10,22 +10,22 @@ import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
class Scrollbar2 extends StatefulWidget {
|
class Scrollbar extends StatefulWidget {
|
||||||
Scrollbar2({
|
Scrollbar({
|
||||||
Key key,
|
Key key,
|
||||||
this.child,
|
this.child,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// The subtree to place inside the [Scrollbar2]. This should include
|
/// The subtree to place inside the [Scrollbar]. This should include
|
||||||
/// a source of [ScrollNotification2] notifications, typically a [Scrollable2]
|
/// a source of [ScrollNotification2] notifications, typically a [Scrollable2]
|
||||||
/// widget.
|
/// widget.
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_Scrollbar2State createState() => new _Scrollbar2State();
|
_ScrollbarState createState() => new _ScrollbarState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Scrollbar2State extends State<Scrollbar2> with TickerProviderStateMixin {
|
class _ScrollbarState extends State<Scrollbar> with TickerProviderStateMixin {
|
||||||
_ScrollbarController _controller;
|
_ScrollbarController _controller;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -179,193 +179,3 @@ class _ScrollbarPainter extends CustomPainter {
|
||||||
return oldDelegate.controller != controller;
|
return oldDelegate.controller != controller;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// DELETE EVERYTHING BELOW THIS LINE WHEN REMOVING LEGACY SCROLLING CODE
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const double _kMinScrollbarThumbExtent = 18.0;
|
|
||||||
const double _kScrollbarThumbGirth = 6.0;
|
|
||||||
const Duration _kScrollbarThumbFadeDuration = const Duration(milliseconds: 300);
|
|
||||||
|
|
||||||
class _Painter extends CustomPainter {
|
|
||||||
_Painter({
|
|
||||||
this.scrollOffset,
|
|
||||||
this.scrollDirection,
|
|
||||||
this.contentExtent,
|
|
||||||
this.containerExtent,
|
|
||||||
this.color
|
|
||||||
});
|
|
||||||
|
|
||||||
final double scrollOffset;
|
|
||||||
final Axis scrollDirection;
|
|
||||||
final double contentExtent;
|
|
||||||
final double containerExtent;
|
|
||||||
final Color color;
|
|
||||||
|
|
||||||
void paintScrollbar(Canvas canvas, Size size) {
|
|
||||||
Point thumbOrigin;
|
|
||||||
Size thumbSize;
|
|
||||||
|
|
||||||
switch (scrollDirection) {
|
|
||||||
case Axis.vertical:
|
|
||||||
double thumbHeight = size.height * containerExtent / contentExtent;
|
|
||||||
thumbHeight = thumbHeight.clamp(_kMinScrollbarThumbExtent, size.height);
|
|
||||||
final double maxThumbTop = size.height - thumbHeight;
|
|
||||||
double thumbTop = (scrollOffset / (contentExtent - containerExtent)) * maxThumbTop;
|
|
||||||
thumbTop = thumbTop.clamp(0.0, maxThumbTop);
|
|
||||||
thumbOrigin = new Point(size.width - _kScrollbarThumbGirth, thumbTop);
|
|
||||||
thumbSize = new Size(_kScrollbarThumbGirth, thumbHeight);
|
|
||||||
break;
|
|
||||||
case Axis.horizontal:
|
|
||||||
double thumbWidth = size.width * containerExtent / contentExtent;
|
|
||||||
thumbWidth = thumbWidth.clamp(_kMinScrollbarThumbExtent, size.width);
|
|
||||||
final double maxThumbLeft = size.width - thumbWidth;
|
|
||||||
double thumbLeft = (scrollOffset / (contentExtent - containerExtent)) * maxThumbLeft;
|
|
||||||
thumbLeft = thumbLeft.clamp(0.0, maxThumbLeft);
|
|
||||||
thumbOrigin = new Point(thumbLeft, size.height - _kScrollbarThumbGirth);
|
|
||||||
thumbSize = new Size(thumbWidth, _kScrollbarThumbGirth);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Paint paint = new Paint()..color = color;
|
|
||||||
canvas.drawRect(thumbOrigin & thumbSize, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
if (scrollOffset == null || color.alpha == 0)
|
|
||||||
return;
|
|
||||||
paintScrollbar(canvas, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(_Painter oldPainter) {
|
|
||||||
return oldPainter.scrollOffset != scrollOffset
|
|
||||||
|| oldPainter.scrollDirection != scrollDirection
|
|
||||||
|| oldPainter.contentExtent != contentExtent
|
|
||||||
|| oldPainter.containerExtent != containerExtent
|
|
||||||
|| oldPainter.color != color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Displays a scrollbar that tracks the scrollOffset of its child's [Scrollable]
|
|
||||||
/// descendant. If the Scrollbar's child has more than one Scrollable descendant
|
|
||||||
/// the scrollableKey parameter can be used to identify the one the Scrollbar
|
|
||||||
/// should track.
|
|
||||||
class Scrollbar extends StatefulWidget {
|
|
||||||
/// Creates a scrollbar.
|
|
||||||
///
|
|
||||||
/// The child argument must not be null.
|
|
||||||
Scrollbar({ Key key, this.scrollableKey, this.child }) : super(key: key) {
|
|
||||||
assert(child != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Identifies the [Scrollable] descendant of child that the scrollbar will
|
|
||||||
/// track. Can be null if there's only one [Scrollable] descendant.
|
|
||||||
final Key scrollableKey;
|
|
||||||
|
|
||||||
/// The scrollbar will be stacked on top of this child. The scrollbar will
|
|
||||||
/// display when child's [Scrollable] descendant is scrolled.
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
@override
|
|
||||||
_ScrollbarState createState() => new _ScrollbarState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ScrollbarState extends State<Scrollbar> with SingleTickerProviderStateMixin {
|
|
||||||
AnimationController _fade;
|
|
||||||
CurvedAnimation _opacity;
|
|
||||||
double _scrollOffset;
|
|
||||||
Axis _scrollDirection;
|
|
||||||
double _containerExtent;
|
|
||||||
double _contentExtent;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_fade = new AnimationController(duration: _kScrollbarThumbFadeDuration, vsync: this);
|
|
||||||
_opacity = new CurvedAnimation(parent: _fade, curve: Curves.fastOutSlowIn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_fade.stop();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateState(ScrollableState scrollable) {
|
|
||||||
if (scrollable.scrollBehavior is! ExtentScrollBehavior)
|
|
||||||
return;
|
|
||||||
if (_scrollOffset != scrollable.scrollOffset)
|
|
||||||
setState(() { _scrollOffset = scrollable.scrollOffset; });
|
|
||||||
if (_scrollDirection != scrollable.config.scrollDirection)
|
|
||||||
setState(() { _scrollDirection = scrollable.config.scrollDirection; });
|
|
||||||
final ExtentScrollBehavior scrollBehavior = scrollable.scrollBehavior;
|
|
||||||
if (_contentExtent != scrollBehavior.contentExtent)
|
|
||||||
setState(() { _contentExtent = scrollBehavior.contentExtent; });
|
|
||||||
if (_containerExtent != scrollBehavior.containerExtent)
|
|
||||||
setState(() { _containerExtent = scrollBehavior.containerExtent; });
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onScrollStarted(ScrollableState scrollable) {
|
|
||||||
_updateState(scrollable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onScrollUpdated(ScrollableState scrollable) {
|
|
||||||
_updateState(scrollable);
|
|
||||||
if (_fade.status != AnimationStatus.completed)
|
|
||||||
_fade.forward();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onScrollEnded(ScrollableState scrollable) {
|
|
||||||
_updateState(scrollable);
|
|
||||||
_fade.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _handleScrollNotification(ScrollNotification notification) {
|
|
||||||
if (config.scrollableKey == null) {
|
|
||||||
if (notification.depth != 0)
|
|
||||||
return false;
|
|
||||||
} else if (config.scrollableKey != notification.scrollable.config.key) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ScrollableState scrollable = notification.scrollable;
|
|
||||||
switch(notification.kind) {
|
|
||||||
case ScrollNotificationKind.started:
|
|
||||||
_onScrollStarted(scrollable);
|
|
||||||
break;
|
|
||||||
case ScrollNotificationKind.updated:
|
|
||||||
_onScrollUpdated(scrollable);
|
|
||||||
break;
|
|
||||||
case ScrollNotificationKind.ended:
|
|
||||||
_onScrollEnded(scrollable);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return new NotificationListener<ScrollNotification>(
|
|
||||||
onNotification: _handleScrollNotification,
|
|
||||||
child: new AnimatedBuilder(
|
|
||||||
animation: _opacity,
|
|
||||||
builder: (BuildContext context, Widget child) {
|
|
||||||
return new CustomPaint(
|
|
||||||
foregroundPainter: new _Painter(
|
|
||||||
scrollOffset: _scrollOffset,
|
|
||||||
scrollDirection: _scrollDirection,
|
|
||||||
containerExtent: _containerExtent,
|
|
||||||
contentExtent: _contentExtent,
|
|
||||||
color: Theme.of(context).highlightColor.withOpacity(_opacity.value)
|
|
||||||
),
|
|
||||||
child: child
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: config.child
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,13 +9,13 @@ import '../rendering/mock_canvas.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Viewport2 basic test', (WidgetTester tester) async {
|
testWidgets('Viewport2 basic test', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(new Scrollbar2(
|
await tester.pumpWidget(new Scrollbar(
|
||||||
child: new SingleChildScrollView(
|
child: new SingleChildScrollView(
|
||||||
child: const SizedBox(width: 4000.0, height: 4000.0),
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
expect(find.byType(Scrollbar2), isNot(paints..rect()));
|
expect(find.byType(Scrollbar), isNot(paints..rect()));
|
||||||
await tester.fling(find.byType(SingleChildScrollView), const Offset(0.0, -10.0), 10.0);
|
await tester.fling(find.byType(SingleChildScrollView), const Offset(0.0, -10.0), 10.0);
|
||||||
expect(find.byType(Scrollbar2), paints..rect());
|
expect(find.byType(Scrollbar), paints..rect());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ void main() {
|
||||||
),
|
),
|
||||||
height: 200.0,
|
height: 200.0,
|
||||||
width: 300.0,
|
width: 300.0,
|
||||||
child: new Scrollbar2(
|
child: new Scrollbar(
|
||||||
child: new ListView(
|
child: new ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
new Container(height: 40.0, child: new Text('0')),
|
new Container(height: 40.0, child: new Text('0')),
|
||||||
|
|
|
@ -74,7 +74,7 @@ void main() {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new ScrollConfiguration2(
|
new ScrollConfiguration2(
|
||||||
behavior: new TestBehavior(),
|
behavior: new TestBehavior(),
|
||||||
child: new Scrollbar2(
|
child: new Scrollbar(
|
||||||
child: new Scrollable2(
|
child: new Scrollable2(
|
||||||
axisDirection: AxisDirection.down,
|
axisDirection: AxisDirection.down,
|
||||||
physics: const TestScrollPhysics(),
|
physics: const TestScrollPhysics(),
|
||||||
|
|
Loading…
Reference in a new issue