Created
February 27, 2020 01:07
-
-
Save Hixie/ec3ca4ffcf858244a8244039dc37ec23 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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