@@ -6,14 +6,21 @@ import 'package:flutter/material.dart';
66import 'package:provider/provider.dart' ;
77import 'package:sprintf/sprintf.dart' ;
88import 'settings_card.dart' ;
9+ import 'student_editor.dart' ;
10+ import 'package:sqflite_common_ffi/sqflite_ffi.dart' ;
11+ import 'student_db.dart' ;
12+ import 'student.dart' ;
13+ import 'package:shared_preferences/shared_preferences.dart' ;
914
1015// BIN 1 1111 1111 1111 0000 0000 0000 = DEC 33550336
1116// 众人将与一人离别,惟其人将觐见奇迹
1217
1318// 「在彩虹桥的尽头,天空之子将缝补晨昏」
14- final version = "v3.0.0ea2 " ;
19+ final version = "v3.0.0ea3 " ;
1520final codename = "Hyacine" ;
1621void main () {
22+ sqfliteFfiInit ();
23+ databaseFactory = databaseFactoryFfi;
1724 runApp (MyApp ());
1825}
1926
@@ -74,38 +81,118 @@ class MyApp extends StatelessWidget {
7481}
7582
7683class MyAppState extends ChangeNotifier {
77- var current = "0" ;
84+ MyAppState () {
85+ _loadSettings ();
86+ }
87+
88+ Future <void > _loadSettings () async {
89+ final prefs = await SharedPreferences .getInstance ();
90+ allowRepeat = prefs.getBool ('allowRepeat' ) ?? true ;
91+ themeMode = prefs.getInt ('themeMode' ) ?? 0 ;
92+ filterGender = prefs.getString ('filterGender' ) ?? "全部" ;
93+ filterNumberType = prefs.getString ('filterNumberType' ) ?? "全部" ;
94+ notifyListeners ();
95+ }
96+ // 是否允许重复抽取
97+ bool allowRepeat = true ;
98+ // 已抽过学生id列表
99+ List <int > pickedIds = [];
100+
101+ void setAllowRepeat (bool value) {
102+ allowRepeat = value;
103+ pickedIds.clear ();
104+ SharedPreferences .getInstance ().then ((prefs) {
105+ prefs.setBool ('allowRepeat' , value);
106+ });
107+ notifyListeners ();
108+ }
109+ var current = "别紧张..." ;
78110 var history = < String > [];
79111
80112 GlobalKey ? historyListKey;
81113
82114 // 0: 跟随系统 1: 亮色 2: 暗色
83115 int themeMode = 0 ;
84- int minValue = 0 ;
85- int maxValue = 20 ;
116+
117+ // 筛选条件
118+ String filterGender = "全部" ; // "全部" "男" "女"
119+ String filterNumberType = "全部" ; // "全部" "单号" "双号"
86120
87121 void setThemeMode (int mode) {
88122 themeMode = mode;
123+ SharedPreferences .getInstance ().then ((prefs) {
124+ prefs.setInt ('themeMode' , mode);
125+ });
89126 notifyListeners ();
90127 }
91128
92- void getNext () {
93- history.insert (0 , current);
94- var animatedList = historyListKey? .currentState as AnimatedListState ? ;
95- // var names = ["sunxiaochuan","fxpick","abcdccb"];
96- animatedList? .insertItem (0 );
97- current = sprintf ("%s" ,[randomGen (minValue, maxValue)]);
129+ void setFilterGender (String gender) {
130+ filterGender = gender;
131+ SharedPreferences .getInstance ().then ((prefs) {
132+ prefs.setString ('filterGender' , gender);
133+ });
98134 notifyListeners ();
99135 }
100136
101- void setRange (int min, int max) {
102- if (min > max) {
103- final tmp = min;
104- min = max;
105- max = tmp;
137+ void setFilterNumberType (String type) {
138+ filterNumberType = type;
139+ SharedPreferences .getInstance ().then ((prefs) {
140+ prefs.setString ('filterNumberType' , type);
141+ });
142+ notifyListeners ();
143+ }
144+
145+ Future <void > getNextStudent () async {
146+ // 获取所有学生
147+ final all = await StudentDatabase .instance.readAll ();
148+ // 按性别筛选
149+ List <Student > filtered = all;
150+ if (filterGender != "全部" ) {
151+ filtered = filtered.where ((s) => s.gender == filterGender).toList ();
152+ }
153+ // 按学号单双筛选
154+ if (filterNumberType != "全部" ) {
155+ filtered = filtered.where ((s) {
156+ final num = int .tryParse (s.studentId);
157+ if (num == null ) return false ;
158+ if (filterNumberType == "单号" ) return num % 2 == 1 ;
159+ if (filterNumberType == "双号" ) return num % 2 == 0 ;
160+ return true ;
161+ }).toList ();
162+ }
163+ // 不允许重复时,过滤已抽过
164+ if (! allowRepeat) {
165+ filtered = filtered.where ((s) => ! pickedIds.contains (s.id)).toList ();
166+ if (filtered.isEmpty && all.isNotEmpty) {
167+ // 所有人都抽过,重置
168+ pickedIds.clear ();
169+ filtered = all;
170+ if (filterGender != "全部" ) {
171+ filtered = filtered.where ((s) => s.gender == filterGender).toList ();
172+ }
173+ if (filterNumberType != "全部" ) {
174+ filtered = filtered.where ((s) {
175+ final num = int .tryParse (s.studentId);
176+ if (num == null ) return false ;
177+ if (filterNumberType == "单号" ) return num % 2 == 1 ;
178+ if (filterNumberType == "双号" ) return num % 2 == 0 ;
179+ return true ;
180+ }).toList ();
181+ }
182+ }
106183 }
107- minValue = min;
108- maxValue = max;
184+ if (filtered.isEmpty) {
185+ current = "无符合条件学生" ;
186+ } else {
187+ final picked = filtered[Random ().nextInt (filtered.length)];
188+ current = "${picked .name }(${picked .studentId })" ;
189+ if (! allowRepeat && picked.id != null ) {
190+ pickedIds.add (picked.id! );
191+ }
192+ }
193+ history.insert (0 , current);
194+ var animatedList = historyListKey? .currentState as AnimatedListState ? ;
195+ animatedList? .insertItem (0 );
109196 notifyListeners ();
110197 }
111198}
@@ -240,31 +327,7 @@ class GeneratorPage extends StatefulWidget {
240327}
241328
242329class _GeneratorPageState extends State <GeneratorPage > {
243- late TextEditingController minController;
244- late TextEditingController maxController;
245-
246- @override
247- void initState () {
248- super .initState ();
249- final appState = Provider .of <MyAppState >(context, listen: false );
250- minController = TextEditingController (text: appState.minValue.toString ());
251- maxController = TextEditingController (text: appState.maxValue.toString ());
252- }
253-
254- @override
255- void didChangeDependencies () {
256- super .didChangeDependencies ();
257- final appState = Provider .of <MyAppState >(context, listen: false );
258- minController.text = appState.minValue.toString ();
259- maxController.text = appState.maxValue.toString ();
260- }
261-
262- @override
263- void dispose () {
264- minController.dispose ();
265- maxController.dispose ();
266- super .dispose ();
267- }
330+ // 已移除范围相关控制器和生命周期方法
268331
269332 @override
270333 Widget build (BuildContext context) {
@@ -281,39 +344,22 @@ class _GeneratorPageState extends State<GeneratorPage> {
281344 SizedBox (height: 10 ),
282345 BigCard (pair: pair),
283346 SizedBox (height: 10 ),
284- // 数字范围选择
347+ // 筛选选项
285348 Row (
286349 mainAxisAlignment: MainAxisAlignment .center,
287350 children: [
288- Text ('范围:' ),
289- SizedBox (
290- width: 60 ,
291- child: TextField (
292- controller: minController,
293- keyboardType: TextInputType .number,
294- decoration: InputDecoration (
295- labelText: '最小' ,
296- ),
297- onSubmitted: (v) {
298- final min = int .tryParse (v) ?? appState.minValue;
299- appState.setRange (min, appState.maxValue);
300- },
301- ),
351+ Text ('性别:' ),
352+ DropdownButton <String >(
353+ value: appState.filterGender,
354+ items: ['全部' , '男' , '女' ].map ((g) => DropdownMenuItem (value: g, child: Text (g))).toList (),
355+ onChanged: (v) => appState.setFilterGender (v! ),
302356 ),
303- Text (' ~ ' ),
304- SizedBox (
305- width: 60 ,
306- child: TextField (
307- controller: maxController,
308- keyboardType: TextInputType .number,
309- decoration: InputDecoration (
310- labelText: '最大' ,
311- ),
312- onSubmitted: (v) {
313- final max = int .tryParse (v) ?? appState.maxValue;
314- appState.setRange (appState.minValue, max);
315- },
316- ),
357+ SizedBox (width: 20 ),
358+ Text ('学号类型:' ),
359+ DropdownButton <String >(
360+ value: appState.filterNumberType,
361+ items: ['全部' , '单号' , '双号' ].map ((t) => DropdownMenuItem (value: t, child: Text (t))).toList (),
362+ onChanged: (v) => appState.setFilterNumberType (v! ),
317363 ),
318364 ],
319365 ),
@@ -322,15 +368,8 @@ class _GeneratorPageState extends State<GeneratorPage> {
322368 mainAxisSize: MainAxisSize .min,
323369 children: [
324370 ElevatedButton (
325- onPressed: () {
326- // 先同步输入框内容到状态
327- final min = int .tryParse (minController.text) ?? appState.minValue;
328- final max = int .tryParse (maxController.text) ?? appState.maxValue;
329- appState.setRange (min, max);
330- appState.getNext ();
331- // 保证输入框内容和状态同步
332- minController.text = appState.minValue.toString ();
333- maxController.text = appState.maxValue.toString ();
371+ onPressed: () async {
372+ await appState.getNextStudent ();
334373 },
335374 child: Text ('点击抽选' ),
336375 ),
@@ -387,12 +426,7 @@ class NameListPage extends StatelessWidget {
387426 Widget build (BuildContext context) {
388427 var theme = Theme .of (context);
389428 var appState = context.watch <MyAppState >();
390- return Column (
391- crossAxisAlignment: CrossAxisAlignment .start,
392- children: [
393- Placeholder ()
394- ]
395- );
429+ return StudentEditorPage ();
396430 }
397431}
398432
@@ -455,6 +489,15 @@ class SettingsPage extends StatelessWidget {
455489 ],
456490 ),
457491 ),
492+ SettingsCard (
493+ title: Text ("允许重复抽取" ),
494+ leading: Icon (Icons .repeat),
495+ description: "关闭后,所有人都抽过才会重置名单" ,
496+ trailing: Switch (
497+ value: appState.allowRepeat,
498+ onChanged: (v) => appState.setAllowRepeat (v),
499+ ),
500+ ),
458501 ],
459502 );
460503 }
@@ -570,3 +613,4 @@ class HistoryCard extends StatelessWidget {
570613 );
571614 }
572615}
616+ // 文件结尾处补全所有类的闭合花括号
0 commit comments