hi-client/lib/app/widgets/hi_fixed_scrollbar.dart
2025-12-01 06:54:18 -08:00

124 lines
3.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class HiFixedScrollbar extends StatefulWidget {
final Widget child;
final ScrollController controller;
final bool isShowScrollbar;
/// 距离右边的间距(默认 18
final double right;
/// 滚动条宽度
final double thickness;
/// 滚动条颜色(默认白色 30%
final Color thumbColor;
/// 背景轨道颜色(默认白色 15%
final Color trackColor;
/// 滚动条固定高度(默认 50
final double thumbHeight;
const HiFixedScrollbar({
super.key,
required this.child,
required this.controller,
this.right = 18,
this.isShowScrollbar = true,
this.thickness = 5,
this.thumbHeight = 50,
this.thumbColor = const Color.fromRGBO(255, 255, 255, 0.3),
this.trackColor = const Color.fromRGBO(255, 255, 255, 0.15),
});
@override
State<HiFixedScrollbar> createState() => _HiFixedScrollbarState();
}
class _HiFixedScrollbarState extends State<HiFixedScrollbar> {
double _thumbOffset = 0.0;
bool _hasScrolled = false;
@override
void initState() {
super.initState();
widget.controller.addListener(_updateThumbPosition);
}
void _updateThumbPosition() {
if (!mounted) return;
final position = widget.controller.position;
if (!position.hasPixels || !position.hasContentDimensions) return;
final maxScrollExtent = position.maxScrollExtent;
final offset = position.pixels;
final viewport = position.viewportDimension;
// ✅ 固定高度滚动条,只根据滚动比例移动位置
final trackHeight = viewport - widget.thumbHeight;
final scrollRatio = maxScrollExtent == 0 ? 0 : offset / maxScrollExtent;
setState(() {
_thumbOffset = (trackHeight * scrollRatio).clamp(0, trackHeight);
_hasScrolled = offset > 0;
});
}
@override
void dispose() {
widget.controller.removeListener(_updateThumbPosition);
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (_, constraints) {
final showScrollbar = widget.isShowScrollbar &&
widget.controller.hasClients &&
widget.controller.position.maxScrollExtent > 0 &&
_hasScrolled;
return Stack(
children: [
// 滚动内容
widget.child,
if (showScrollbar) ...[
// 滚动条轨道
Positioned(
right: widget.right.w,
top: 0,
bottom: 0,
child: Container(
width: widget.thickness.w,
decoration: BoxDecoration(
color: widget.trackColor,
borderRadius: BorderRadius.circular(4),
),
),
),
// 滚动条拇指
Positioned(
right: widget.right.w,
top: _thumbOffset,
child: Container(
width: widget.thickness.w,
height: widget.thumbHeight,
decoration: BoxDecoration(
color: widget.thumbColor,
borderRadius: BorderRadius.circular(4),
),
),
),
]
],
);
},
);
}
}