You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
decky/lib/screens/deck_config_screen.dart

242 lines
7.7 KiB

import 'package:flutter/material.dart';
import 'package:practice_engine/practice_engine.dart';
class DeckConfigScreen extends StatefulWidget {
const DeckConfigScreen({super.key});
@override
State<DeckConfigScreen> createState() => _DeckConfigScreenState();
}
class _DeckConfigScreenState extends State<DeckConfigScreen> {
Deck? _deck;
DeckConfig? _config;
late TextEditingController _consecutiveController;
late TextEditingController _attemptSizeController;
late TextEditingController _priorityIncreaseController;
late TextEditingController _priorityDecreaseController;
late bool _immediateFeedback;
@override
void initState() {
super.initState();
_deck = null;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_deck == null) {
// Get deck from route arguments
final args = ModalRoute.of(context)?.settings.arguments;
_deck = args is Deck ? args : _createSampleDeck();
_config = _deck!.config;
_consecutiveController = TextEditingController(
text: _config!.requiredConsecutiveCorrect.toString(),
);
_attemptSizeController = TextEditingController(
text: _config!.defaultAttemptSize.toString(),
);
_priorityIncreaseController = TextEditingController(
text: _config!.priorityIncreaseOnIncorrect.toString(),
);
_priorityDecreaseController = TextEditingController(
text: _config!.priorityDecreaseOnCorrect.toString(),
);
_immediateFeedback = _config!.immediateFeedbackEnabled;
}
}
Deck _createSampleDeck() {
return Deck(
id: 'sample',
title: 'Sample',
description: 'Sample',
questions: [],
config: const DeckConfig(),
);
}
@override
void dispose() {
_consecutiveController.dispose();
_attemptSizeController.dispose();
_priorityIncreaseController.dispose();
_priorityDecreaseController.dispose();
super.dispose();
}
void _save() {
if (_deck == null || _config == null) return;
final consecutive = int.tryParse(_consecutiveController.text);
final attemptSize = int.tryParse(_attemptSizeController.text);
final priorityIncrease = int.tryParse(_priorityIncreaseController.text);
final priorityDecrease = int.tryParse(_priorityDecreaseController.text);
if (consecutive == null ||
attemptSize == null ||
priorityIncrease == null ||
priorityDecrease == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please enter valid numbers for all fields'),
backgroundColor: Colors.red,
),
);
return;
}
if (consecutive < 1 ||
attemptSize < 1 ||
priorityIncrease < 0 ||
priorityDecrease < 0) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Values must be positive (priority changes >= 0)'),
backgroundColor: Colors.red,
),
);
return;
}
final updatedConfig = _config!.copyWith(
requiredConsecutiveCorrect: consecutive,
defaultAttemptSize: attemptSize,
priorityIncreaseOnIncorrect: priorityIncrease,
priorityDecreaseOnCorrect: priorityDecrease,
immediateFeedbackEnabled: _immediateFeedback,
);
final updatedDeck = _deck!.copyWith(config: updatedConfig);
Navigator.pop(context, updatedDeck);
}
void _cancel() {
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
if (_deck == null || _config == null) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Deck Configuration'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Practice Settings',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 24),
// Required Consecutive Correct
TextField(
controller: _consecutiveController,
decoration: const InputDecoration(
labelText: 'Required Consecutive Correct',
helperText: 'Number of correct answers in a row to mark as known',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 16),
// Default Attempt Size
TextField(
controller: _attemptSizeController,
decoration: const InputDecoration(
labelText: 'Default Questions Per Attempt',
helperText: 'Number of questions to include in each attempt',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 16),
// Priority Increase
TextField(
controller: _priorityIncreaseController,
decoration: const InputDecoration(
labelText: 'Priority Increase on Incorrect',
helperText: 'Priority points added when answered incorrectly',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 16),
// Priority Decrease
TextField(
controller: _priorityDecreaseController,
decoration: const InputDecoration(
labelText: 'Priority Decrease on Correct',
helperText: 'Priority points removed when answered correctly',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 16),
// Immediate Feedback Toggle
SwitchListTile(
title: const Text('Immediate Feedback'),
subtitle: const Text(
'Show correct/incorrect immediately after answering',
),
value: _immediateFeedback,
onChanged: (value) {
setState(() {
_immediateFeedback = value;
});
},
),
],
),
),
),
const SizedBox(height: 24),
// Action Buttons
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: _cancel,
child: const Text('Cancel'),
),
),
const SizedBox(width: 16),
Expanded(
child: FilledButton(
onPressed: _save,
child: const Text('Save'),
),
),
],
),
],
),
),
);
}
}

Powered by TurnKey Linux.