简介
用于绘制滚动条的CustomPainter。
- 与CustomPainter子类化的CustomPainters不同,只有当shouldRepaint返回true时才重绘
- 当滚动条消失和滚动位置发生变化的时候painter需要重绘而不是重新构建
基本使用
当ScrollbarPainter不再使用的时候,必须在ScrollbarPainter上调用dispose
- 除非自定制Scrollbar的需求,基本上我们都可以使用Scrollbar
- Scrollbar的源码其实就是基于ScrollbarPainter封装的
- demo中将Scrollbar源码进行提取使用
- CupertinoScrollbar 同样也是使用ScrollbarPainter的最好学习示例
实例演示
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
const double _kScrollbarThickness = 16.0;
const Duration _kScrollbarFadeDuration = Duration(milliseconds: 300);
const Duration _kScrollbarTimeToFade = Duration(milliseconds: 600);
class PaintDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 300.0,
child: ScrollbarPainterDemo(
child: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return Container(
height: 100.0,
color: Colors.blue,
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: Text(
'test$index',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.white),
),
);
},
)),
);
}
}
class ScrollbarPainterDemo extends StatefulWidget {
const ScrollbarPainterDemo({
Key key,
@required this.child,
}) : super(key: key);
final Widget child;
@override
_ScrollbarPainterDemoState createState() => _ScrollbarPainterDemoState();
}
class _ScrollbarPainterDemoState extends State<ScrollbarPainterDemo>
with TickerProviderStateMixin {
ScrollbarPainter _materialPainter;
TargetPlatform _currentPlatform;
TextDirection _textDirection;
Color _themeColor;
AnimationController _fadeoutAnimationController;
Animation<double> _fadeoutOpacityAnimation;
Timer _fadeoutTimer;
@override
void initState() {
super.initState();
_fadeoutAnimationController = AnimationController(
vsync: this,
duration: _kScrollbarFadeDuration,
);
_fadeoutOpacityAnimation = CurvedAnimation(
parent: _fadeoutAnimationController, curve: Curves.fastOutSlowIn);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
final ThemeData theme = Theme.of(context);
_currentPlatform = theme.platform;
switch (_currentPlatform) {
case TargetPlatform.iOS:
// On iOS, stop all local animations. CupertinoScrollbar has its own
// animations.
_fadeoutTimer?.cancel();
_fadeoutTimer = null;
_fadeoutAnimationController.reset();
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
_themeColor = theme.primaryColor.withOpacity(1.0);
_textDirection = Directionality.of(context);
_materialPainter = _buildMaterialScrollbarPainter();
break;
}
}
ScrollbarPainter _buildMaterialScrollbarPainter() {
return ScrollbarPainter(
color: _themeColor,
textDirection: _textDirection,
thickness: _kScrollbarThickness,
fadeoutOpacityAnimation: _fadeoutOpacityAnimation,
);
}
bool _handleScrollNotification(ScrollNotification notification) {
// iOS sub-delegates to the CupertinoScrollbar instead and doesn't handle
// scroll notifications here.
if (_currentPlatform != TargetPlatform.iOS &&
(notification is ScrollUpdateNotification ||
notification is OverscrollNotification)) {
if (_fadeoutAnimationController.status != AnimationStatus.forward) {
_fadeoutAnimationController.forward();
}
_materialPainter.update(
notification.metrics, notification.metrics.axisDirection);
_fadeoutTimer?.cancel();
_fadeoutTimer = Timer(_kScrollbarTimeToFade, () {
_fadeoutAnimationController.reverse();
_fadeoutTimer = null;
});
}
return false;
}
@override
void dispose() {
_fadeoutAnimationController.dispose();
_fadeoutTimer?.cancel();
_materialPainter?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
switch (_currentPlatform) {
case TargetPlatform.iOS:
return CupertinoScrollbar(
child: widget.child,
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification,
child: RepaintBoundary(
child: CustomPaint(
foregroundPainter: _materialPainter,
child: RepaintBoundary(
child: widget.child,
),
),
),
);
}
throw FlutterError('Unknown platform for scrollbar insertion');
}
}