| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- 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<KnowledgeTreeWidget> createState() => _KnowledgeTreeWidgetState();
- }
- class _KnowledgeTreeWidgetState extends ConsumerState<KnowledgeTreeWidget> {
- List<KnowledgeNode> _tree = [];
- String? _selectedCode;
- bool _isLoading = true;
- @override
- void initState() {
- super.initState();
- _load();
- }
- Future<void> _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(),
- );
- }
- }
|