import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/app_provider.dart'; import '../models/knowledge.dart'; class KnowledgeTreeWidget extends ConsumerStatefulWidget { final Function(String? code, String? name) onSelectNode; const KnowledgeTreeWidget({super.key, required this.onSelectNode}); @override ConsumerState createState() => _KnowledgeTreeWidgetState(); } class _KnowledgeTreeWidgetState extends ConsumerState { List _tree = []; String? _selectedCode; bool _isLoading = true; @override void initState() { super.initState(); _load(); } Future _load() async { final api = ref.read(apiServiceProvider); try { final data = await api.knowledgeTree(); setState(() { _tree = (data['tree'] as List?) ?.map((j) => KnowledgeNode.fromJson(j)) .toList() ?? []; _isLoading = false; }); } catch (_) { setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: _isLoading ? const Center(child: CircularProgressIndicator()) : ListView( children: [ ListTile( leading: const Icon(Icons.select_all, size: 18), title: const Text('全部题目'), selected: _selectedCode == null, onTap: () { setState(() => _selectedCode = null); widget.onSelectNode(null, null); }, ), ..._tree.map((node) => _buildNode(node, 0)), ], ), ), ], ); } Widget _buildNode(KnowledgeNode node, int depth) { if (node.children.isEmpty) { // Leaf node: click to filter return ListTile( contentPadding: EdgeInsets.only(left: 16.0 + depth * 16.0), dense: true, leading: const Icon(Icons.circle, size: 6), title: Text(node.name, style: const TextStyle(fontSize: 13)), selected: _selectedCode == node.code, onTap: () { setState(() => _selectedCode = node.code); widget.onSelectNode(node.code, node.name); }, ); } // Branch node: expand to browse, click filter icon to filter return ExpansionTile( tilePadding: EdgeInsets.only(left: depth * 16.0), leading: GestureDetector( onTap: () { setState(() => _selectedCode = node.code); widget.onSelectNode(node.code, node.name); }, child: Icon( Icons.filter_alt_outlined, size: 16, color: _selectedCode == node.code ? Theme.of(context).colorScheme.primary : Colors.grey, ), ), title: Text(node.name, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: _selectedCode == node.code ? Theme.of(context).colorScheme.primary : null, )), initiallyExpanded: false, children: node.children.map((c) => _buildNode(c, depth + 1)).toList(), ); } }