mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-10 06:52:43 +01:00
reorg ui into app
This commit is contained in:
175
lib/app/util/custom_scroll_fix.dart
Normal file
175
lib/app/util/custom_scroll_fix.dart
Normal file
@@ -0,0 +1,175 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Fixes scroll postion being reset in a tab when scrolling up inside a
|
||||
// NestedScrollView.
|
||||
//
|
||||
// https://dartpad.dev/?id=5d1a0c7c4b55281ccff377fc8ea90b60
|
||||
// https://github.com/flutter/flutter/issues/159123#issuecomment-2614343654
|
||||
|
||||
class CustomScrollProvider extends StatefulWidget {
|
||||
const CustomScrollProvider({
|
||||
super.key,
|
||||
required this.tabController,
|
||||
required this.parent,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final TabController tabController;
|
||||
final ScrollController parent;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
State<CustomScrollProvider> createState() => _CustomScrollProviderState();
|
||||
}
|
||||
|
||||
class _CustomScrollProviderState extends State<CustomScrollProvider> {
|
||||
late final List<CustomScrollController> scrollControllers;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final activeIndex = widget.tabController.index;
|
||||
|
||||
scrollControllers = List.generate(
|
||||
widget.tabController.length,
|
||||
(index) => CustomScrollController(
|
||||
isActive: index == activeIndex,
|
||||
parent: widget.parent,
|
||||
debugLabel: 'CustomScrollController/$index',
|
||||
),
|
||||
);
|
||||
|
||||
widget.tabController.addListener(() {
|
||||
changeActiveIndex(widget.tabController.index);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final scrollController in scrollControllers) {
|
||||
scrollController.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void changeActiveIndex(int value) {
|
||||
for (var i = 0; i < scrollControllers.length; i++) {
|
||||
final scrollController = scrollControllers[i];
|
||||
final isActive = i == value;
|
||||
scrollController.isActive = isActive;
|
||||
|
||||
if (isActive) {
|
||||
scrollController.forceAttach();
|
||||
} else {
|
||||
scrollController.forceDetach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomScrollProviderData(
|
||||
scrollControllers: scrollControllers,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomScrollProviderData extends InheritedWidget {
|
||||
const CustomScrollProviderData({
|
||||
super.key,
|
||||
required super.child,
|
||||
required this.scrollControllers,
|
||||
});
|
||||
|
||||
static CustomScrollProviderData of(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<CustomScrollProviderData>()!;
|
||||
}
|
||||
|
||||
final List<CustomScrollController> scrollControllers;
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(CustomScrollProviderData oldWidget) {
|
||||
return scrollControllers != oldWidget.scrollControllers;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomScrollController extends ScrollController {
|
||||
CustomScrollController({
|
||||
required this.isActive,
|
||||
required this.parent,
|
||||
String debugLabel = 'CustomScrollController',
|
||||
}) : super(
|
||||
debugLabel: parent.debugLabel == null
|
||||
? null
|
||||
: '${parent.debugLabel}/$debugLabel',
|
||||
initialScrollOffset: parent.initialScrollOffset,
|
||||
keepScrollOffset: parent.keepScrollOffset,
|
||||
);
|
||||
|
||||
bool isActive;
|
||||
final ScrollController parent;
|
||||
|
||||
@override
|
||||
ScrollPosition createScrollPosition(
|
||||
ScrollPhysics physics,
|
||||
ScrollContext context,
|
||||
ScrollPosition? oldPosition,
|
||||
) {
|
||||
debugPrint('$debugLabel-createScrollPosition: $isActive');
|
||||
|
||||
return parent.createScrollPosition(physics, context, oldPosition);
|
||||
}
|
||||
|
||||
@override
|
||||
void attach(ScrollPosition position) {
|
||||
debugPrint('$debugLabel-attach: $isActive');
|
||||
|
||||
super.attach(position);
|
||||
if (isActive && !parent.positions.contains(position)) {
|
||||
parent.attach(position);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void detach(ScrollPosition position) {
|
||||
debugPrint('$debugLabel-detach: $isActive');
|
||||
|
||||
if (parent.positions.contains(position)) {
|
||||
parent.detach(position);
|
||||
}
|
||||
|
||||
super.detach(position);
|
||||
}
|
||||
|
||||
void forceDetach() {
|
||||
debugPrint('$debugLabel-forceDetach: $isActive');
|
||||
|
||||
for (final position in positions) {
|
||||
if (parent.positions.contains(position)) {
|
||||
parent.detach(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void forceAttach() {
|
||||
debugPrint('$debugLabel-forceAttach: $isActive');
|
||||
|
||||
for (final position in positions) {
|
||||
if (!parent.positions.contains(position)) {
|
||||
parent.attach(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
debugPrint('$debugLabel-dispose: $isActive');
|
||||
|
||||
forceDetach();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user