diff --git a/learning/tour-of-beam/frontend/lib/models/node.dart b/learning/tour-of-beam/frontend/lib/models/node.dart index ea8023a9b8fa..64529a51350d 100644 --- a/learning/tour-of-beam/frontend/lib/models/node.dart +++ b/learning/tour-of-beam/frontend/lib/models/node.dart @@ -60,7 +60,5 @@ abstract class NodeModel { } } - NodeModel getFirstUnit(); - NodeModel? getNodeByTreeIds(List treeIds); } diff --git a/learning/tour-of-beam/frontend/lib/models/parent_node.dart b/learning/tour-of-beam/frontend/lib/models/parent_node.dart index 53f3c7a17667..c29d6548aa20 100644 --- a/learning/tour-of-beam/frontend/lib/models/parent_node.dart +++ b/learning/tour-of-beam/frontend/lib/models/parent_node.dart @@ -30,9 +30,6 @@ abstract class ParentNodeModel extends NodeModel { required this.nodes, }); - @override - NodeModel getFirstUnit() => nodes[0].getFirstUnit(); - @override NodeModel? getNodeByTreeIds(List treeIds) { final firstId = treeIds.firstOrNull; diff --git a/learning/tour-of-beam/frontend/lib/models/unit.dart b/learning/tour-of-beam/frontend/lib/models/unit.dart index 08e1e270df99..7c0b13cd619d 100644 --- a/learning/tour-of-beam/frontend/lib/models/unit.dart +++ b/learning/tour-of-beam/frontend/lib/models/unit.dart @@ -29,9 +29,6 @@ class UnitModel extends NodeModel { title: unit.title, ); - @override - NodeModel getFirstUnit() => this; - @override NodeModel? getNodeByTreeIds(List treeIds) => this; } diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/controllers/content_tree.dart b/learning/tour-of-beam/frontend/lib/pages/tour/controllers/content_tree.dart index aa6118778219..bcb05d11568e 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/controllers/content_tree.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/controllers/content_tree.dart @@ -21,12 +21,14 @@ import 'package:get_it/get_it.dart'; import 'package:playground_components/playground_components.dart'; import '../../../cache/content_tree.dart'; -import '../../../models/group.dart'; + import '../../../models/node.dart'; +import '../../../models/parent_node.dart'; import '../../../models/unit.dart'; +import '../../../state.dart'; class ContentTreeController extends ChangeNotifier { - Sdk _sdk; + final Sdk initialSdk; List _treeIds; NodeModel? _currentNode; final _contentTreeCache = GetIt.instance.get(); @@ -35,29 +37,27 @@ class ContentTreeController extends ChangeNotifier { Set get expandedIds => _expandedIds; ContentTreeController({ - required Sdk initialSdk, + required this.initialSdk, List initialTreeIds = const [], - }) : _sdk = initialSdk, - _treeIds = initialTreeIds { + }) : _treeIds = initialTreeIds { _expandedIds.addAll(initialTreeIds); _contentTreeCache.addListener(_onContentTreeCacheChange); _onContentTreeCacheChange(); } - Sdk get sdk => _sdk; - - set sdk(Sdk newValue) { - _sdk = newValue; - notifyListeners(); - } - + Sdk get sdk => GetIt.instance.get().sdk ?? initialSdk; List get treeIds => _treeIds; NodeModel? get currentNode => _currentNode; void onNodePressed(NodeModel node) { - if (node is GroupModel) { - _onGroupPressed(node); + _toggleNode(node); + notifyListeners(); + } + + void _toggleNode(NodeModel node) { + if (node is ParentNodeModel) { + _onParentNodePressed(node); } else if (node is UnitModel) { if (node != _currentNode) { _currentNode = node; @@ -67,28 +67,28 @@ class ContentTreeController extends ChangeNotifier { if (_currentNode != null) { _treeIds = _getNodeAncestors(_currentNode!, [_currentNode!.id]); } - notifyListeners(); } - void _onGroupPressed(GroupModel group) { - if (_expandedIds.contains(group.id)) { - _expandedIds.remove(group.id); + void _onParentNodePressed(ParentNodeModel node) { + if (_expandedIds.contains(node.id)) { + _expandedIds.remove(node.id); notifyListeners(); } else { - _expandedIds.add(group.id); - final groupFirstUnit = group.nodes.first; - if (groupFirstUnit != _currentNode) { - onNodePressed(groupFirstUnit); + _expandedIds.add(node.id); + + final firstChildNode = node.nodes.first; + if (firstChildNode != _currentNode) { + _toggleNode(firstChildNode); } } } - void expandGroup(GroupModel group) { + void expandParentNode(ParentNodeModel group) { _expandedIds.add(group.id); notifyListeners(); } - void collapseGroup(GroupModel group) { + void collapseParentNode(ParentNodeModel group) { _expandedIds.remove(group.id); notifyListeners(); } @@ -104,13 +104,13 @@ class ContentTreeController extends ChangeNotifier { } void _onContentTreeCacheChange() { - final contentTree = _contentTreeCache.getContentTree(_sdk); + final contentTree = _contentTreeCache.getContentTree(sdk); if (contentTree == null) { return; } - onNodePressed( - contentTree.getNodeByTreeIds(_treeIds) ?? contentTree.getFirstUnit(), + _toggleNode( + contentTree.getNodeByTreeIds(_treeIds) ?? contentTree.modules.first, ); notifyListeners(); diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/state.dart b/learning/tour-of-beam/frontend/lib/pages/tour/state.dart index ea2d2ac113f8..e16e73772ed4 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/state.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/state.dart @@ -119,7 +119,6 @@ class TourNotifier extends ChangeNotifier with PageStateMixin { } Future _onAppNotifierChanged() async { - contentTreeController.sdk = currentSdk; playgroundController.setSdk(currentSdk); _listenToCurrentSnippetEditingController(); @@ -134,9 +133,8 @@ class TourNotifier extends ChangeNotifier with PageStateMixin { if (currentNode is! UnitModel) { await _emptyPlayground(); } else { - final sdk = contentTreeController.sdk; final content = await _unitContentCache.getUnitContent( - sdk.id, + currentSdk.id, currentNode.id, ); _setUnitContent(content); @@ -324,7 +322,7 @@ class TourNotifier extends ChangeNotifier with PageStateMixin { await playgroundController.examplesLoader.loadIfNew( ExamplesLoadingDescriptor( descriptors: [ - EmptyExampleLoadingDescriptor(sdk: contentTreeController.sdk), + EmptyExampleLoadingDescriptor(sdk: currentSdk), ], ), ); diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/content_tree.dart b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/content_tree.dart index caee4c9d9f3d..85f82eefbe7f 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/content_tree.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/content_tree.dart @@ -17,9 +17,11 @@ */ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:playground_components/playground_components.dart'; import '../../../components/builders/content_tree.dart'; +import '../../../state.dart'; import '../controllers/content_tree.dart'; import 'content_tree_title.dart'; import 'module.dart'; @@ -36,31 +38,34 @@ class ContentTreeWidget extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( width: 250, - child: ContentTreeBuilder( - sdk: controller.sdk, - builder: (context, contentTree, child) { - if (contentTree == null) { - return Container(); - } + child: AnimatedBuilder( + animation: GetIt.instance.get(), + builder: (context, child) => ContentTreeBuilder( + sdk: controller.sdk, + builder: (context, contentTree, child) { + if (contentTree == null) { + return Container(); + } - return SingleChildScrollView( - padding: const EdgeInsets.symmetric( - horizontal: BeamSizes.size12, - ), - child: Column( - children: [ - const ContentTreeTitleWidget(), - ...contentTree.modules.map( - (module) => ModuleWidget( - module: module, - contentTreeController: controller, + return SingleChildScrollView( + padding: const EdgeInsets.symmetric( + horizontal: BeamSizes.size12, + ), + child: Column( + children: [ + const ContentTreeTitleWidget(), + ...contentTree.modules.map( + (module) => ModuleWidget( + module: module, + contentTreeController: controller, + ), ), - ), - const SizedBox(height: BeamSizes.size12), - ], - ), - ); - }, + const SizedBox(height: BeamSizes.size12), + ], + ), + ); + }, + ), ), ); } diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group.dart b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group.dart index a3937e27d7a6..c4038a0407ce 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group.dart @@ -20,9 +20,8 @@ import 'package:flutter/material.dart'; import '../../../models/group.dart'; import '../controllers/content_tree.dart'; -import 'group_nodes.dart'; import 'group_title.dart'; -import 'stateless_expansion_tile.dart'; +import 'parent_node.dart'; class GroupWidget extends StatelessWidget { final GroupModel group; @@ -35,32 +34,15 @@ class GroupWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return AnimatedBuilder( - animation: contentTreeController, - builder: (context, child) { - final isExpanded = contentTreeController.expandedIds.contains(group.id); - - return StatelessExpansionTile( - isExpanded: isExpanded, - onExpansionChanged: (isExpanding) { - if (isExpanding) { - contentTreeController.expandGroup(group); - } else { - contentTreeController.collapseGroup(group); - } - }, - title: GroupTitleWidget( - group: group, - onTap: () { - contentTreeController.onNodePressed(group); - }, - ), - child: GroupNodesWidget( - nodes: group.nodes, - contentTreeController: contentTreeController, - ), - ); - }, + return ParentNodeWidget( + contentTreeController: contentTreeController, + node: group, + title: GroupTitleWidget( + group: group, + onTap: () { + contentTreeController.onNodePressed(group); + }, + ), ); } } diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group_title.dart b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group_title.dart index ce382b2e0311..894a37f55aa7 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group_title.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group_title.dart @@ -42,9 +42,11 @@ class GroupTitleWidget extends StatelessWidget { child: Row( children: [ _GroupProgressIndicator(group: group), - Text( - group.title, - style: Theme.of(context).textTheme.headlineMedium, + Expanded( + child: Text( + group.title, + style: Theme.of(context).textTheme.headlineMedium, + ), ), ], ), diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/module.dart b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/module.dart index 383fa54925ad..e2d90a9e7f96 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/module.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/module.dart @@ -17,12 +17,11 @@ */ import 'package:flutter/material.dart'; -import 'package:playground_components/playground_components.dart'; import '../../../models/module.dart'; import '../controllers/content_tree.dart'; import 'module_title.dart'; -import 'node.dart'; +import 'parent_node.dart'; class ModuleWidget extends StatelessWidget { final ModuleModel module; @@ -35,24 +34,13 @@ class ModuleWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - children: [ - ModuleTitleWidget( - module: module, - onTap: () => contentTreeController.onNodePressed(module), - ), - ...module.nodes - .map( - (node) => NodeWidget( - node: node, - contentTreeController: contentTreeController, - ), - ) - .toList(growable: false), - const BeamDivider( - margin: EdgeInsets.symmetric(vertical: BeamSizes.size10), - ), - ], + return ParentNodeWidget( + contentTreeController: contentTreeController, + node: module, + title: ModuleTitleWidget( + module: module, + onTap: () => contentTreeController.onNodePressed(module), + ), ); } } diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/module_title.dart b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/module_title.dart index 4d64e8157931..62de25826988 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/module_title.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/module_title.dart @@ -37,16 +37,17 @@ class ModuleTitleWidget extends StatelessWidget { child: Padding( padding: const EdgeInsets.only(bottom: BeamSizes.size6), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - module.title, - style: Theme.of(context).textTheme.headlineMedium, - ), Padding( padding: const EdgeInsets.only(right: BeamSizes.size4), child: ComplexityWidget(complexity: module.complexity), ), + Expanded( + child: Text( + module.title, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), ], ), ), diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group_nodes.dart b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/nodes.dart similarity index 94% rename from learning/tour-of-beam/frontend/lib/pages/tour/widgets/group_nodes.dart rename to learning/tour-of-beam/frontend/lib/pages/tour/widgets/nodes.dart index 304dbd329b03..69b8802777b9 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/group_nodes.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/nodes.dart @@ -22,11 +22,11 @@ import '../../../models/node.dart'; import '../controllers/content_tree.dart'; import 'node.dart'; -class GroupNodesWidget extends StatelessWidget { +class NodesWidget extends StatelessWidget { final List nodes; final ContentTreeController contentTreeController; - const GroupNodesWidget({ + const NodesWidget({ required this.nodes, required this.contentTreeController, }); diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/parent_node.dart b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/parent_node.dart new file mode 100644 index 000000000000..c5a93166ad83 --- /dev/null +++ b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/parent_node.dart @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'package:flutter/material.dart'; + +import '../../../models/parent_node.dart'; +import '../controllers/content_tree.dart'; +import 'nodes.dart'; +import 'stateless_expansion_tile.dart'; + +class ParentNodeWidget extends StatelessWidget { + final ContentTreeController contentTreeController; + final ParentNodeModel node; + final Widget title; + + const ParentNodeWidget({ + required this.contentTreeController, + required this.node, + required this.title, + }); + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: contentTreeController, + builder: (context, child) { + final isExpanded = contentTreeController.expandedIds.contains(node.id); + + return StatelessExpansionTile( + isExpanded: isExpanded, + onExpansionChanged: (isExpanding) { + if (isExpanding) { + contentTreeController.expandParentNode(node); + } else { + contentTreeController.collapseParentNode(node); + } + }, + title: title, + child: NodesWidget( + nodes: node.nodes, + contentTreeController: contentTreeController, + ), + ); + }, + ); + } +} diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/stateless_expansion_tile.dart b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/stateless_expansion_tile.dart index 149bd04a586e..b22aafcbe301 100644 --- a/learning/tour-of-beam/frontend/lib/pages/tour/widgets/stateless_expansion_tile.dart +++ b/learning/tour-of-beam/frontend/lib/pages/tour/widgets/stateless_expansion_tile.dart @@ -44,7 +44,7 @@ class StatelessExpansionTile extends StatelessWidget { onExpansionChanged: onExpansionChanged, title: title, childrenPadding: const EdgeInsets.only( - left: BeamSizes.size24, + left: BeamSizes.size12, ), children: [child], ),