tree_tab.dart 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import 'package:flutter/material.dart';
  2. import '../design.dart';
  3. import '../models/inventory.dart';
  4. import '../state/inventory_store.dart';
  5. import 'resource_detail_screen.dart';
  6. class TreeTab extends StatelessWidget {
  7. const TreeTab({super.key, required this.store});
  8. final InventoryStore store;
  9. @override
  10. Widget build(BuildContext context) {
  11. if (store.loadError != null) {
  12. return ScribeSheet(
  13. children: [
  14. InlineNotice(
  15. message: store.loadError.toString(),
  16. isError: true,
  17. onRetry: store.refresh,
  18. ),
  19. ],
  20. );
  21. }
  22. final document = store.document;
  23. if (document == null) return const ScribeSpinner();
  24. final roots = document.resources
  25. .where((r) => r.isHardware || (r.runsOn.isEmpty))
  26. .toList()
  27. ..sort((a, b) => a.name.compareTo(b.name));
  28. if (roots.isEmpty) {
  29. return const ScribeSpinner();
  30. }
  31. return RefreshIndicator(
  32. onRefresh: store.refresh,
  33. child: ScribeSheet(
  34. children: [
  35. const Text('Containment', style: ScribeType.hero),
  36. SurfacePanel(
  37. padding: const EdgeInsets.symmetric(
  38. vertical: 8, horizontal: ScribeSpacing.panelPadding),
  39. child: Column(
  40. crossAxisAlignment: CrossAxisAlignment.stretch,
  41. children: [
  42. for (final root in roots)
  43. _Branch(
  44. store: store,
  45. resource: root,
  46. depth: 0,
  47. ancestors: const {},
  48. ),
  49. ],
  50. ),
  51. ),
  52. ],
  53. ),
  54. );
  55. }
  56. }
  57. class _Branch extends StatelessWidget {
  58. const _Branch({
  59. required this.store,
  60. required this.resource,
  61. required this.depth,
  62. required this.ancestors,
  63. });
  64. final InventoryStore store;
  65. final Resource resource;
  66. final int depth;
  67. final Set<String> ancestors;
  68. @override
  69. Widget build(BuildContext context) {
  70. final nextAncestors = {...ancestors, resource.name.toLowerCase()};
  71. final children = (store.document?.dependantsOf(resource.name) ?? [])
  72. .where((c) => !nextAncestors.contains(c.name.toLowerCase()))
  73. .toList()
  74. ..sort((a, b) => a.name.compareTo(b.name));
  75. return Column(
  76. crossAxisAlignment: CrossAxisAlignment.stretch,
  77. children: [
  78. InkWell(
  79. onTap: () => Navigator.of(context).push(MaterialPageRoute(
  80. builder: (_) =>
  81. ResourceDetailScreen(store: store, name: resource.name),
  82. )),
  83. child: Padding(
  84. padding: EdgeInsets.only(left: depth * 16.0, top: 8, bottom: 8),
  85. child: Row(
  86. children: [
  87. CategoryMark(icon: resource.glyph),
  88. const SizedBox(width: 10),
  89. Expanded(
  90. child: Text(resource.name, style: ScribeType.title),
  91. ),
  92. ],
  93. ),
  94. ),
  95. ),
  96. for (final child in children)
  97. _Branch(
  98. store: store,
  99. resource: child,
  100. depth: depth + 1,
  101. ancestors: nextAncestors,
  102. ),
  103. ],
  104. );
  105. }
  106. }