Skip to content

Instantly share code, notes, and snippets.

@Hixie
Created February 27, 2020 01:07
Show Gist options
  • Save Hixie/ec3ca4ffcf858244a8244039dc37ec23 to your computer and use it in GitHub Desktop.
Save Hixie/ec3ca4ffcf858244a8244039dc37ec23 to your computer and use it in GitHub Desktop.
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void _enablePlatformOverrideForDesktop() {
if (!kIsWeb && (Platform.isWindows || Platform.isLinux)) {
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
}
}
void main() {
_enablePlatformOverrideForDesktop();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: TreeDemo(),
);
}
}
List<SingleNode> _kTestNodes = [
GroupNode('0', [1, 2, 3, 4, 5, 6]),
SingleNode('1'),
SingleNode('2'),
SingleNode('3'),
SingleNode('4'),
SingleNode('5'),
GroupNode('6', [7, 8]),
SingleNode('7'),
SingleNode('8'),
];
class TreeDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _TreeDemoState();
}
}
class _TreeDemoState extends State<TreeDemo> {
TestTreeModel model = TestTreeModel(_kTestNodes);
@override
Widget build(BuildContext context) {
return Column(
children: [
Tree(
model: model,
),
GestureDetector(
child: Icon(Icons.refresh),
onTap: () {
setState(() {
// Move node 6 to index 0 within node 0's children.
model.moveNode(6, 0, 0, 0);
});
},
),
GestureDetector(
child: Icon(Icons.description),
onTap: () {
debugDumpApp();
},
)
],
);
}
}
enum BorderType { box, line, none }
/// A widget with a tree structure. Grabs its structure and data from a [model].
class Tree extends StatefulWidget {
Tree({
Key key,
@required this.model,
}) : assert(model != null),
super(key: key);
/// The model backing the [Tree].
final TestTreeModel model;
@override
_TreeState createState() => _TreeState();
}
class _TreeState extends State<Tree> {
Map<Widget, int> nodesByWidget = {};
/// Map of nodes to GlobalKeys attached to the nodes' Widget representation.
Map<int, Key> rowKeys = {};
TestTreeModel get _model => widget.model;
@override
Widget build(BuildContext context) {
List<Widget> treeRows = _populateRows(
nodes: _model.getTopLevel(),
);
return Column(children: treeRows);
}
List<Widget> _populateRows({
@required List<int> nodes,
double leftIndent = 0,
}) {
List<Widget> rows = [];
for (int i = 0; i < nodes.length; i++) {
int node = nodes[i];
double indent = leftIndent;
print('Adding row for $node');
rowKeys[node] ??= GlobalKey(debugLabel: 'Node $node');
Widget row = TreeRow(key: rowKeys[node], node: node, indent: indent, model: _model);
rows.add(row);
nodesByWidget[row] = node;
rows.addAll(
_populateRows(
nodes: _model.getChildrenList(node),
leftIndent: indent + 32,
),
);
}
return rows;
}
}
class TreeRow extends StatelessWidget {
TreeRow({Key key, this.node, this.indent, this.model}) : super(key: key);
final int node;
final double indent;
final TestTreeModel model;
@override
Widget build(BuildContext context) {
assert(node != null);
assert(indent != null);
final Widget nodeWidget = model.getNodeWidget(node, context);
final List<Widget> rowChildren = [];
// Indent
rowChildren.add(
SizedBox(
width: indent,
height: 28,
),
);
// Node content
rowChildren.add(nodeWidget);
return Row(children: rowChildren);
}
}
class TestTreeModel {
TestTreeModel(this.nodes);
List<SingleNode> nodes;
List<int> getTopLevel() {
return [0];
}
List<int> getChildrenList(int node) {
return nodes[node] is GroupNode ? (nodes[node] as GroupNode).children : [];
}
void moveNode(int source, int sourceParent, int target, int targetParent) {
getChildrenList(sourceParent).remove(source);
List<int> targetChildren = getChildrenList(targetParent);
if (targetParent == target) {
targetChildren.insert(0, source);
return;
}
for (int i = 0; i < targetChildren.length; ++i) {
if (targetChildren[i] == target) {
targetChildren.insert(i, source);
return;
}
}
}
Widget getNodeWidget(int node, BuildContext context) {
return Text(nodes[node].name);
}
}
class SingleNode {
const SingleNode(this.name);
final String name;
}
class GroupNode extends SingleNode {
const GroupNode(String name, this.children) : super(name);
final List<int> children;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment