Files
MWClash/lib/widgets/scroll.dart
chen08209 b987c2376a Add android separates the core process
Support core status check and force restart

Optimize proxies page and access page

Update flutter and pub dependencies
2025-09-03 17:05:46 +08:00

247 lines
6.3 KiB
Dart

import 'dart:async';
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;
final bool trackVisibility;
final bool thumbVisibility;
const CommonScrollBar({
super.key,
required this.child,
required this.controller,
this.trackVisibility = false,
this.thumbVisibility = false,
});
@override
Widget build(BuildContext context) {
return Scrollbar(
controller: controller,
thumbVisibility: thumbVisibility,
trackVisibility: trackVisibility,
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 bool enable;
final VoidCallback? onCancelToEnd;
const ScrollToEndBox({
super.key,
required this.child,
required this.controller,
required this.dataSource,
this.onCancelToEnd,
this.enable = true,
});
@override
State<ScrollToEndBox<T>> createState() => _ScrollToEndBoxState<T>();
}
class _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {
final equals = ListEquality<T>();
bool _isFastToEnd = false;
@override
void initState() {
super.initState();
}
Future<bool> _handleTryToEnd() {
final completer = Completer<bool>();
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (mounted &&
widget.controller.hasClients &&
widget.controller.position.pixels !=
widget.controller.position.maxScrollExtent) {
await widget.controller.animateTo(
duration: kThemeAnimationDuration,
widget.controller.position.maxScrollExtent,
curve: Curves.easeOut,
);
}
completer.complete(true);
});
return completer.future;
}
@override
void didUpdateWidget(ScrollToEndBox<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.enable == true && oldWidget.enable != true) {
_handleFastToEnd();
return;
}
if (widget.enable &&
!equals.equals(oldWidget.dataSource, widget.dataSource)) {
_handleTryToEnd();
}
}
Future<void> _handleFastToEnd() async {
_isFastToEnd = true;
await _handleTryToEnd();
_isFastToEnd = false;
}
@override
Widget build(BuildContext context) {
return NotificationListener<UserScrollNotification>(
onNotification: (details) {
if (_isFastToEnd) {
return false;
}
if (widget.onCancelToEnd != null) {
widget.onCancelToEnd!();
}
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();
}
void _updateCacheHeightMap() {
globalState.computeHeightMapCache[widget.tag]?.updateMaxLength(
widget.itemCount,
);
globalState.computeHeightMapCache[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.computeHeightMapCache[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.computeHeightMapCache[widget.tag]?.updateMaxLength(
widget.itemCount,
);
globalState.computeHeightMapCache[widget.tag] ??= FixedMap(
widget.itemCount,
);
}
@override
Widget build(BuildContext context) {
globalState.computeHeightMapCache[widget.tag]?.updateMaxLength(
widget.itemCount,
);
return SliverReorderableList(
itemBuilder: widget.itemBuilder,
itemCount: widget.itemCount,
itemExtentBuilder: (index, _) {
return globalState.computeHeightMapCache[widget.tag]?.updateCacheValue(
widget.keyBuilder(index),
() => widget.itemExtentBuilder(index),
);
},
onReorder: widget.onReorder,
proxyDecorator: widget.proxyDecorator,
);
}
@override
void dispose() {
super.dispose();
}
}