Skip to content

Commit 7db9882

Browse files
committed
refactor(word): refactor word data structure
Signed-off-by: OctagonalStar <[email protected]>
1 parent ff11cd7 commit 7db9882

File tree

11 files changed

+303
-150
lines changed

11 files changed

+303
-150
lines changed

lib/funcs/ui.dart

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:convert';
22
import 'dart:ui';
33

4+
import 'package:arabic_learning/vars/config_structure.dart' show ClassItem, SourceItem, WordItem;
45
import 'package:flutter/material.dart';
56
import 'package:provider/provider.dart';
67

@@ -35,20 +36,30 @@ import 'package:arabic_learning/funcs/utili.dart';
3536
/// List<List<String>> value = await popSelectClasses(context);
3637
/// List<Map<String, dynamic>> words = getSelectedWords(context , forceSelectClasses: value);
3738
/// ```
38-
Future<List<List<String>>> popSelectClasses(BuildContext context, {bool withCache = false}) async {
39+
Future<List<ClassItem>> popSelectClasses(BuildContext context, {bool withCache = false}) async {
3940
context.read<Global>().uiLogger.info("弹出课程选择(ClassSelectPage),withCache: $withCache");
40-
late final List<List<String>> beforeSelectedClasses;
41+
final List<ClassItem> beforeSelectedClasses = [];
4142
if(withCache) {
4243
final String tpcPrefs = context.read<Global>().prefs.getString("tempConfig") ?? jsonEncode(StaticsVar.tempConfig);
43-
beforeSelectedClasses = (jsonDecode(tpcPrefs)["SelectedClasses"] as List)
44+
final List<List<String>> cacheList = (jsonDecode(tpcPrefs)["SelectedClasses"] as List)
4445
.cast<List>()
4546
.map((e) => e.cast<String>().toList())
4647
.toList();
48+
for(List<String> cachedClass in cacheList) {
49+
for(SourceItem sourceItem in context.read<Global>().wordData.classes){
50+
if(sourceItem.sourceJsonFileName != cachedClass[0]){
51+
continue;
52+
}
53+
if(sourceItem.subClasses.any((ClassItem classItem) => classItem.className == cachedClass[1])){
54+
beforeSelectedClasses.add(
55+
sourceItem.subClasses.firstWhere((ClassItem classItem) => classItem.className == cachedClass[1])
56+
);
57+
}
58+
}
59+
}
4760
context.read<Global>().uiLogger.fine("已缓存课程选择: $beforeSelectedClasses");
48-
} else {
49-
beforeSelectedClasses = [];
5061
}
51-
List<List<String>>? selectedClasses = await showModalBottomSheet<List<List<String>>>(
62+
List<ClassItem>? selectedClasses = await showModalBottomSheet<List<ClassItem>>(
5263
context: context,
5364
// 假装圆角... :)
5465
shape: RoundedRectangleBorder(side: BorderSide(width: 1.0, color: Theme.of(context).colorScheme.onSurface.withAlpha(150)), borderRadius: StaticsVar.br),
@@ -67,7 +78,7 @@ Future<List<List<String>>> popSelectClasses(BuildContext context, {bool withCach
6778
context.read<Global>().uiLogger.info("课程选择缓存完成");
6879
}
6980
if(context.mounted) context.read<Global>().uiLogger.fine("选择的课程: $selectedClasses");
70-
return selectedClasses ?? [];
81+
return selectedClasses??[];
7182
}
7283

7384

@@ -82,11 +93,11 @@ Future<List<List<String>>> popSelectClasses(BuildContext context, {bool withCach
8293
/// [isClassSelected] :需要一个函数判断该课程是否被选中
8394
///
8495
/// 一般情况下该函数只会在 [ClassSelectPage] 中被使用,若非必要你不应该使用此函数
85-
List<Widget> classesSelectionList(BuildContext context, Function (List<String>) onChanged, bool Function (List<String>) isClassSelected) {
96+
List<Widget> classesSelectionList(BuildContext context, Function (ClassItem) onChanged, bool Function (ClassItem) isClassSelected) {
8697
context.read<Global>().uiLogger.fine("构建课程选择列表");
87-
Map<String, dynamic> wordData = context.read<Global>().wordData;
98+
List<SourceItem> sourcesList = context.read<Global>().wordData.classes;
8899
List<Widget> widgetList = [];
89-
for (String sourceName in wordData["Classes"].keys) {
100+
for (SourceItem source in sourcesList) {
90101
widgetList.add(
91102
Container(
92103
margin: EdgeInsets.all(16.0),
@@ -96,7 +107,7 @@ List<Widget> classesSelectionList(BuildContext context, Function (List<String>)
96107
borderRadius: StaticsVar.br,
97108
),
98109
child: Text(
99-
sourceName,
110+
source.sourceJsonFileName,
100111
style: TextStyle(
101112
fontSize: 16.0,
102113
fontWeight: FontWeight.bold,
@@ -105,7 +116,7 @@ List<Widget> classesSelectionList(BuildContext context, Function (List<String>)
105116
),
106117
);
107118
bool isEven = true;
108-
for(String className in wordData["Classes"][sourceName].keys){
119+
for(ClassItem classItem in source.subClasses){
109120
widgetList.add(
110121
Container(
111122
margin: EdgeInsets.all(2),
@@ -116,11 +127,11 @@ List<Widget> classesSelectionList(BuildContext context, Function (List<String>)
116127
child: StatefulBuilder(
117128
builder: (context, setLocalState) {
118129
return CheckboxListTile(
119-
title: Text(className),
120-
value: isClassSelected([sourceName, className]),
130+
title: Text(classItem.className),
131+
value: isClassSelected(classItem),
121132
onChanged: (value) {
122133
setLocalState(() {
123-
onChanged([sourceName, className]);
134+
onChanged(classItem);
124135
});
125136
},
126137
);
@@ -190,7 +201,7 @@ void alart(BuildContext context, String e, {Function? onConfirmed, Duration dela
190201
}
191202

192203
/// 弹出详解页面
193-
void viewAnswer(BuildContext context, Map<String, dynamic> wordData) async {
204+
void viewAnswer(BuildContext context, WordItem wordData) async {
194205
context.read<Global>().uiLogger.info("弹出详解页面");
195206
MediaQueryData mediaQuery = MediaQuery.of(context);
196207
showBottomSheet(
@@ -445,7 +456,7 @@ class _ChooseButtonBoxState extends State<ChooseButtonBox> {
445456
///
446457
/// [useMask] :是否显示高斯遮罩
447458
class WordCard extends StatelessWidget {
448-
final Map<String, dynamic> word;
459+
final WordItem word;
449460
final double? width;
450461
final double? height;
451462
final bool useMask;
@@ -468,9 +479,9 @@ class WordCard extends StatelessWidget {
468479
padding: const EdgeInsets.all(16.0),
469480
),
470481
icon: const Icon(Icons.volume_up, size: 24.0),
471-
label: FittedBox(child: Text(word["arabic"], style: TextStyle(fontSize: 64.0, fontFamily: context.read<Global>().arFont))),
482+
label: FittedBox(child: Text(word.arabic, style: TextStyle(fontSize: 64.0, fontFamily: context.read<Global>().arFont))),
472483
onPressed: (){
473-
playTextToSpeech(word["arabic"], context);
484+
playTextToSpeech(word.arabic, context);
474485
},
475486
),
476487
Stack(
@@ -483,7 +494,7 @@ class WordCard extends StatelessWidget {
483494
borderRadius: BorderRadius.vertical(bottom: Radius.circular(25.0)),
484495
),
485496
child: Center(
486-
child: Text(' 中文:${word["chinese"]}\n 示例:${word["explanation"]}\n 归属课程:${word["subClass"]}',
497+
child: Text(' 中文:${word.chinese}\n 示例:${word.explanation}\n 归属课程:${word.className}',
487498
style: Theme.of(context).textTheme.bodyLarge,
488499
textAlign: TextAlign.left,
489500
),
@@ -541,22 +552,22 @@ class WordCard extends StatelessWidget {
541552
///
542553
/// 注意:如果你要进行课程选择,请先考虑 [popSelectClasses] 函数,这是一个已经基本成熟的实现
543554
class ClassSelectPage extends StatelessWidget {
544-
final List<List<String>> beforeSelectedClasses;
555+
final List<ClassItem> beforeSelectedClasses;
545556
const ClassSelectPage({super.key, this.beforeSelectedClasses = const []});
546557
@override
547558
Widget build(BuildContext context) {
548-
final mediaQuery = MediaQuery.of(context);
549-
List<List<String>> selectedClass = beforeSelectedClasses.toList();
550-
void addClass(List<String> classInfo) {
559+
final MediaQueryData mediaQuery = MediaQuery.of(context);
560+
List<ClassItem> selectedClass = beforeSelectedClasses.toList();
561+
void addClass(ClassItem classInfo) {
551562
selectedClass.add(classInfo);
552563
}
553-
void removeClass(List<String> classInfo) {
554-
selectedClass.removeWhere((e) => e[0] == classInfo[0] && e[1] == classInfo[1]);
564+
void removeClass(ClassItem classInfo) {
565+
selectedClass.remove(classInfo);
555566
}
556-
bool isClassSelected(List<String> classInfo) {
557-
return selectedClass.any((e) => e[0] == classInfo[0] && e[1] == classInfo[1]);
567+
bool isClassSelected(ClassItem classInfo) {
568+
return selectedClass.any((e) => e==classInfo);
558569
}
559-
void onClassChanged(List<String> classInfo) {
570+
void onClassChanged(ClassItem classInfo) {
560571
if(isClassSelected(classInfo)) {
561572
removeClass(classInfo);
562573
} else {
@@ -800,14 +811,14 @@ class _ChoiceQuestions extends State<ChoiceQuestions> {
800811
///
801812
/// [bottomWidget] :题目下方的组件,可用于翻页之类的其他功能,自行设置
802813
class WordCardQuestion extends StatelessWidget {
803-
final Map<String, dynamic> word;
814+
final WordItem word;
804815
final String? hint;
805816
final Widget? bottomWidget;
806817
const WordCardQuestion({super.key, required this.word, this.hint, this.bottomWidget});
807818

808819
@override
809820
Widget build(BuildContext context) {
810-
context.read<Global>().uiLogger.info("构建单词卡片页面,主单词: ${word["arabic"]}");
821+
context.read<Global>().uiLogger.info("构建单词卡片页面,主单词: ${word.arabic}");
811822
MediaQueryData mediaQuery = MediaQuery.of(context);
812823
return Material(
813824
child: Column(

lib/funcs/utili.dart

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,31 @@ Future<void> downloadFile(String url, String savePath, {ProgressCallback? onDown
3030
);
3131
}
3232

33-
List<Map<String, dynamic>> getSelectedWords(BuildContext context , {List<List<String>>? forceSelectClasses, bool doShuffle = false, bool doDouble = false}) {
34-
final wordData = context.read<Global>().wordData;
35-
late final List<List<String>> courseList;
33+
List<WordItem> getSelectedWords(BuildContext context , {List<ClassItem>? forceSelectClasses, bool doShuffle = false, bool doDouble = false}) {
34+
final List<ClassItem> courseList = forceSelectClasses??[];
3635
if(forceSelectClasses == null) {
3736
final tpcPrefs = context.read<Global>().prefs.getString("tempConfig") ?? jsonEncode(StaticsVar.tempConfig);
38-
courseList = (jsonDecode(tpcPrefs)["SelectedClasses"] as List)
39-
.cast<List>()
40-
.map((e) => e.cast<String>().toList())
41-
.toList();
42-
} else {
43-
courseList = forceSelectClasses;
37+
final List<List<String>> cacheList = (jsonDecode(tpcPrefs)["SelectedClasses"] as List)
38+
.cast<List>()
39+
.map((e) => e.cast<String>().toList())
40+
.toList();
41+
for(List<String> cachedClass in cacheList) {
42+
for(SourceItem sourceItem in context.read<Global>().wordData.classes){
43+
if(sourceItem.sourceJsonFileName != cachedClass[0]){
44+
continue;
45+
}
46+
if(sourceItem.subClasses.any((ClassItem classItem) => classItem.className == cachedClass[1])){
47+
courseList.add(
48+
sourceItem.subClasses.firstWhere((ClassItem classItem) => classItem.className == cachedClass[1])
49+
);
50+
}
51+
}
52+
}
4453
}
45-
List<Map<String, dynamic>> ans = [];
46-
for(List<String> c in courseList) {
47-
for (int x in wordData["Classes"][c[0]][c[1]].cast<int>()){
48-
ans.add({...wordData["Words"][x], "id": x}); // 保留id方便后面进度保存
54+
List<WordItem> ans = [];
55+
for(ClassItem c in courseList) {
56+
for (int wordIndex in c.wordIndexs){
57+
ans.add(context.read<Global>().wordData.words[wordIndex]); // 保留id方便后面进度保存
4958
}
5059
}
5160
if(doDouble) ans = [...ans, ...ans];

lib/pages/home_page.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:math';
33
import 'package:arabic_learning/funcs/ui.dart';
44
import 'package:arabic_learning/funcs/utili.dart';
55
import 'package:arabic_learning/pages/setting_page.dart';
6+
import 'package:arabic_learning/vars/config_structure.dart';
67
import 'package:arabic_learning/vars/global.dart';
78
import 'package:arabic_learning/vars/statics_var.dart';
89
import 'package:flutter/material.dart';
@@ -159,11 +160,11 @@ class _DailyWord extends State<DailyWord> {
159160
final now = DateTime.now();
160161
final seed = now.year * 10000 + now.month * 100 + now.day;
161162
Random rnd = Random(seed);
162-
late Map<String, dynamic> data;
163+
late WordItem data;
163164
late String dailyWord;
164165
if(context.read<Global>().wordCount != 0) {
165-
data = context.read<Global>().wordData["Words"][rnd.nextInt(context.read<Global>().wordCount)];
166-
dailyWord = data["arabic"];
166+
data = context.read<Global>().wordData.words[rnd.nextInt(context.read<Global>().wordCount)];
167+
dailyWord = data.arabic;
167168
}
168169

169170
return ElevatedButton(
@@ -203,12 +204,12 @@ class _DailyWord extends State<DailyWord> {
203204
children: context.read<Global>().wordCount == 0 ? [Text("当前未导入词库数据\n请点此以跳转设置页面导入")]
204205
: [
205206
Text(
206-
data["arabic"] ?? "读取出错 data:${data.toString()}",
207+
data.arabic,
207208
style: TextStyle(fontSize: 52.0, fontFamily: context.read<Global>().arFont),
208209
),
209210
SizedBox(height: mediaQuery.size.height * 0.005),
210211
Text(
211-
data["chinese"] ?? "读取出错 data:${data.toString()}",
212+
data.chinese,
212213
style: TextStyle(fontSize: 18.0),
213214
textAlign: TextAlign.center,
214215
),

lib/pages/learning_page.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:arabic_learning/vars/config_structure.dart';
12
import 'package:flutter/material.dart';
23
import 'package:provider/provider.dart';
34

@@ -144,9 +145,9 @@ class LearningPage extends StatelessWidget {
144145

145146
Future<void> shiftToStudy(BuildContext context, int studyType) async {
146147
context.read<Global>().uiLogger.info("准备转向学习页面: studyType: $studyType");
147-
final List<List<String>> selectedClasses = await popSelectClasses(context, withCache: false);
148+
final List<ClassItem> selectedClasses = await popSelectClasses(context, withCache: false);
148149
if(selectedClasses.isEmpty || !context.mounted) return;
149-
final List<Map<String, dynamic>> words = getSelectedWords(context, doShuffle: false, doDouble: false, forceSelectClasses: selectedClasses);
150+
final List<WordItem> words = getSelectedWords(context, doShuffle: false, doDouble: false, forceSelectClasses: selectedClasses);
150151
context.read<Global>().uiLogger.info("完成单词挑拣,共${words.length}个");
151152
if(words.isEmpty) return;
152153
context.read<Global>().uiLogger.info("跳转: LearningPage => InLearningPage");

0 commit comments

Comments
 (0)