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

Optimize proxies page and access page

Update flutter and pub dependencies

Update go version

Optimize more details
2025-09-23 15:23:58 +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: 6,
radius: const Radius.circular(6),
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();
}
}