workouttest_app/lib/library/tree_view.dart
2022-10-29 10:01:00 +02:00

213 lines
5.5 KiB
Dart

/// Tree view widget library
library tree_view;
import 'dart:async';
import 'package:aitrainer_app/util/common.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class TreeViewStream {
static final TreeViewStream _singleton = TreeViewStream._internal();
final StreamController<bool> streamController = StreamController<bool>.broadcast();
double positionY = 0;
Stream get stream => streamController.stream;
StreamController getStreamController() => streamController;
factory TreeViewStream() => _singleton;
TreeViewStream._internal();
void dispose() {
streamController.close();
}
}
class TreeView extends InheritedWidget {
final List<Widget> children;
final bool startExpanded;
TreeView({
Key? key,
required List<Widget> children,
bool startExpanded = false,
}) : this.children = children,
this.startExpanded = startExpanded,
super(
key: key,
child: _TreeViewData(
children: children,
),
);
static TreeView? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType(aspect: TreeView);
}
@override
bool updateShouldNotify(TreeView oldWidget) {
if (oldWidget.children == this.children && oldWidget.startExpanded == this.startExpanded) {
return false;
}
return true;
}
}
class _TreeViewData extends StatefulWidget {
final List<Widget> children;
_TreeViewData({
required this.children,
});
@override
__TreeViewDataState createState() => __TreeViewDataState();
}
class __TreeViewDataState extends State<_TreeViewData> {
final ScrollController _controller = ScrollController();
final Stream stream = TreeViewStream().stream;
late var subscription;
@override
void initState() {
super.initState();
/// We require the initializers to run after the loading screen is rendered
SchedulerBinding.instance.addPostFrameCallback((_) {
final double cHeight = MediaQuery.of(context).size.height;
subscription = stream.listen((value) {
if (value) {
final double positionY = TreeViewStream().positionY;
/* print("pos " +
positionY.toString() +
" height: " +
cHeight.toString() +
" controller offset " +
_controller.offset.toString() +
" controller initial " +
_controller.initialScrollOffset.toString()); */
if (positionY > cHeight - 190) {
final double offset = positionY + 40;
//print("antimateTo " + offset.toString());
_controller.animateTo(offset, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
}
}
});
});
}
@override
void dispose() {
_controller.dispose();
subscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection: Axis.vertical,
controller: _controller,
itemCount: widget.children.length,
itemBuilder: (context, index) {
return widget.children.elementAt(index);
},
);
}
}
class TreeViewChild extends StatefulWidget {
final bool? startExpanded;
final Widget parent;
final List<Widget> children;
final VoidCallback? onTap;
TreeViewChild({
required this.parent,
required this.children,
this.startExpanded,
this.onTap,
Key? key,
}) : super(key: key);
@override
TreeViewChildState createState() => TreeViewChildState();
TreeViewChild copyWith(
TreeViewChild source, {
bool? startExpanded,
Widget? parent,
List<Widget>? children,
VoidCallback? onTap,
}) {
return TreeViewChild(
parent: parent ?? source.parent,
children: children ?? source.children,
startExpanded: startExpanded ?? source.startExpanded,
onTap: onTap ?? source.onTap,
key: key,
);
}
}
class TreeViewChildState extends State<TreeViewChild> with Common {
late bool? isExpanded;
final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
@override
void initState() {
super.initState();
isExpanded = widget.startExpanded;
}
@override
void didChangeDependencies() {
isExpanded = widget.startExpanded ?? TreeView.of(context)!.startExpanded;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return (Column(
key: listKey,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(child: widget.parent, onTap: widget.onTap ?? () => toggleExpanded()),
Flexible(
child: Container(
child: AnimatedSwitcher(
duration: Duration(milliseconds: 200),
reverseDuration: Duration(milliseconds: 200),
switchInCurve: Curves.easeIn,
child: isExpanded!
? Column(
mainAxisSize: MainAxisSize.min,
children: widget.children,
)
: Offstage(),
),
),
)
],
));
}
void toggleExpanded() {
setState(() {
this.isExpanded = !this.isExpanded!;
TreeViewStream().positionY = getPosition();
TreeViewStream().getStreamController().add(this.isExpanded);
});
}
double getPosition() {
RenderBox? box = listKey.currentContext!.findRenderObject() as RenderBox;
Offset position = box.localToGlobal(Offset.zero); //this is global position
double y = position.dy;
return y;
}
}