workouttest_app/lib/library/tree_view.dart
2021-02-20 16:48:01 +01:00

218 lines
5.6 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({
this.children,
});
@override
__TreeViewDataState createState() => __TreeViewDataState();
}
class __TreeViewDataState extends State<_TreeViewData> {
final ScrollController _controller = ScrollController();
final Stream stream = TreeViewStream().stream;
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) {
assert(parent != null);
assert(children != null);
}
@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,
);
}
}
class TreeViewChildState extends State<TreeViewChild> with Common {
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();
Offset position = box.localToGlobal(Offset.zero); //this is global position
double y = position.dy;
return y;
}
}