Files
MWClash/lib/widgets/scroll.dart
chen08209 b44cef071d Optimize default option
Optimize computed text size
2025-04-30 14:47:19 +08:00

227 lines
5.9 KiB
Dart

import 'package:collection/collection.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
class CommonScrollBar extends StatelessWidget {
final ScrollController? controller;
final Widget child;
const CommonScrollBar({
super.key,
required this.child,
required this.controller,
});
@override
Widget build(BuildContext context) {
return Scrollbar(
controller: controller,
thumbVisibility: true,
trackVisibility: true,
thickness: 8,
radius: const Radius.circular(8),
interactive: true,
child: child,
);
}
}
class CommonAutoHiddenScrollBar extends StatelessWidget {
final ScrollController? controller;
final Widget child;
const CommonAutoHiddenScrollBar({
super.key,
required this.child,
required this.controller,
});
@override
Widget build(BuildContext context) {
return Scrollbar(
controller: controller,
thickness: 8,
radius: const Radius.circular(8),
interactive: true,
child: child,
);
}
}
class ScrollToEndBox<T> extends StatefulWidget {
final ScrollController controller;
final List<T> dataSource;
final Widget child;
final CacheTag tag;
const ScrollToEndBox({
super.key,
required this.child,
required this.controller,
required this.tag,
required this.dataSource,
});
@override
State<ScrollToEndBox<T>> createState() => _ScrollToEndBoxState<T>();
}
class _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {
final equals = ListEquality<T>();
_handleTryToEnd() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final double offset = globalState.cacheScrollPosition[widget.tag] ?? -1;
if (offset < 0) {
widget.controller.animateTo(
duration: kThemeAnimationDuration,
widget.controller.position.maxScrollExtent,
curve: Curves.easeOut,
);
}
});
}
@override
void didUpdateWidget(ScrollToEndBox<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (!equals.equals(oldWidget.dataSource, widget.dataSource)) {
_handleTryToEnd();
}
}
@override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (details) {
globalState.cacheScrollPosition[widget.tag] =
details.metrics.pixels == details.metrics.maxScrollExtent
? -1
: details.metrics.pixels;
return false;
},
child: widget.child,
);
}
}
class CacheItemExtentListView extends StatefulWidget {
final NullableIndexedWidgetBuilder itemBuilder;
final int itemCount;
final String Function(int index) keyBuilder;
final double Function(int index) itemExtentBuilder;
final ScrollPhysics? physics;
final bool shrinkWrap;
final bool reverse;
final ScrollController controller;
final CacheTag tag;
const CacheItemExtentListView({
super.key,
this.physics,
this.reverse = false,
this.shrinkWrap = false,
required this.itemBuilder,
required this.controller,
required this.keyBuilder,
required this.itemCount,
required this.itemExtentBuilder,
required this.tag,
});
@override
State<CacheItemExtentListView> createState() =>
CacheItemExtentListViewState();
}
class CacheItemExtentListViewState extends State<CacheItemExtentListView> {
@override
void initState() {
super.initState();
_updateCacheHeightMap();
}
_updateCacheHeightMap() {
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
globalState.cacheHeightMap[widget.tag] ??= FixedMap(widget.itemCount);
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: widget.itemBuilder,
itemCount: widget.itemCount,
physics: widget.physics,
reverse: widget.reverse,
shrinkWrap: widget.shrinkWrap,
controller: widget.controller,
itemExtentBuilder: (index, __) {
_updateCacheHeightMap();
return globalState.cacheHeightMap[widget.tag]?.updateCacheValue(
widget.keyBuilder(index),
() => widget.itemExtentBuilder(index),
);
},
);
}
}
class CacheItemExtentSliverReorderableList extends StatefulWidget {
final IndexedWidgetBuilder itemBuilder;
final int itemCount;
final String Function(int index) keyBuilder;
final double Function(int index) itemExtentBuilder;
final ReorderCallback onReorder;
final ReorderItemProxyDecorator? proxyDecorator;
final CacheTag tag;
const CacheItemExtentSliverReorderableList({
super.key,
required this.itemBuilder,
required this.keyBuilder,
required this.itemCount,
required this.itemExtentBuilder,
required this.onReorder,
this.proxyDecorator,
required this.tag,
});
@override
State<CacheItemExtentSliverReorderableList> createState() =>
CacheItemExtentSliverReorderableListState();
}
class CacheItemExtentSliverReorderableListState
extends State<CacheItemExtentSliverReorderableList> {
@override
void initState() {
super.initState();
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
globalState.cacheHeightMap[widget.tag] ??= FixedMap(widget.itemCount);
}
@override
Widget build(BuildContext context) {
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
return SliverReorderableList(
itemBuilder: widget.itemBuilder,
itemCount: widget.itemCount,
itemExtentBuilder: (index, __) {
return globalState.cacheHeightMap[widget.tag]?.updateCacheValue(
widget.keyBuilder(index),
() => widget.itemExtentBuilder(index),
);
},
onReorder: widget.onReorder,
proxyDecorator: widget.proxyDecorator,
);
}
@override
void dispose() {
super.dispose();
}
}