Changed questions, added export.

This commit is contained in:
Raktbastr 2026-01-23 15:52:29 -06:00
parent a83bf0d533
commit 78618aed9a
8 changed files with 277 additions and 173 deletions

View file

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum ScoutingView { match, pit }
class NotesPage extends StatefulWidget {
final String teamCode;
final String eventCode;
@ -18,10 +20,19 @@ class NotesPage extends StatefulWidget {
}
class _NotesPageState extends State<NotesPage> {
final TextEditingController _checkboxes = TextEditingController();
final TextEditingController _controller2 = TextEditingController();
final TextEditingController _switchvalue = TextEditingController();
double _slidervalue = 0.0;
late SharedPreferences _prefs;
ScoutingView _selectedView = ScoutingView.match;
bool _isLoading = true;
final _autonRundownController = TextEditingController();
final _botPositionController = TextEditingController();
final _generalObservationsController = TextEditingController();
final _driveTrainTypeController = TextEditingController();
bool _hasVision = false;
double _climbLevel = 100.0;
bool _trenchable = false;
double _fuelCapacity = 0.0;
@override
void initState() {
@ -31,40 +42,50 @@ class _NotesPageState extends State<NotesPage> {
@override
void dispose() {
_saveNotes();
_checkboxes.dispose();
_controller2.dispose();
_switchvalue.dispose();
_autonRundownController.dispose();
_botPositionController.dispose();
_driveTrainTypeController.dispose();
_generalObservationsController.dispose();
super.dispose();
}
String _generateKey(String field) {
return '${widget.teamCode}_${widget.eventCode}_$field';
}
Future<void> _loadNotes() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_prefs = await SharedPreferences.getInstance();
_autonRundownController.text = _prefs.getString(_generateKey('autonRundown')) ?? '';
_botPositionController.text = _prefs.getString(_generateKey('botPosition')) ?? '';
_generalObservationsController.text = _prefs.getString(_generateKey('generalObservations')) ?? '';
_driveTrainTypeController.text = _prefs.getString(_generateKey('driveTrainType')) ?? '';
_hasVision = _prefs.getBool(_generateKey('hasVision')) ?? false;
_climbLevel = _prefs.getDouble(_generateKey('climbLevel')) ?? 0.0;
_trenchable = _prefs.getBool(_generateKey('trenchable')) ?? false;
_fuelCapacity = _prefs.getDouble(_generateKey('fuelCapacity')) ?? 0.0;
_autonRundownController.addListener(() => _saveString('autonRundown', _autonRundownController.text));
_botPositionController.addListener(() => _saveString('botPosition', _botPositionController.text));
_driveTrainTypeController.addListener(() => _saveString('driveTrainType', _driveTrainTypeController.text));
_generalObservationsController.addListener(() => _saveString('generalObservations', _generalObservationsController.text));
if (mounted) {
setState(() {
_checkboxes.text =
prefs.getString('${widget.teamCode}_${widget.eventCode}_note1') ?? '';
_controller2.text =
prefs.getString('${widget.teamCode}_${widget.eventCode}_note2') ?? '';
_switchvalue.text =
prefs.getString('${widget.teamCode}_${widget.eventCode}_note3') ?? '';
_slidervalue = double.tryParse(prefs
.getString('${widget.teamCode}_${widget.eventCode}_note4') ??
'0.0') ?? 0.0;
_isLoading = false;
});
}
}
Future<void> _saveNotes() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(
'${widget.teamCode}_${widget.eventCode}_note1', _checkboxes.text);
await prefs.setString(
'${widget.teamCode}_${widget.eventCode}_note2', _controller2.text);
await prefs.setString(
'${widget.teamCode}_${widget.eventCode}_note3', _switchvalue.text);
await prefs.setString(
'${widget.teamCode}_${widget.eventCode}_note4', _slidervalue.toString());
Future<void> _saveString(String field, String value) async {
await _prefs.setString(_generateKey(field), value);
}
Future<void> _saveBool(String field, bool value) async {
await _prefs.setBool(_generateKey(field), value);
}
Future<void> _saveDouble(String field, double value) async {
await _prefs.setDouble(_generateKey(field), value);
}
@override
@ -73,108 +94,166 @@ class _NotesPageState extends State<NotesPage> {
appBar: AppBar(
title: Text('Notes'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: ListView(
children: [
Text(widget.teamName, style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center),
Text('Bot Starting Position',
style: Theme.of(context).textTheme.titleMedium),
CheckboxListTile(
title: const Text('Left'),
value: _checkboxes.text.contains('Left'),
onChanged: (bool? value) {
body: _isLoading
? const Center(child: CircularProgressIndicator())
: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: Text(widget.teamName, style: Theme.of(context).textTheme.titleLarge)
),
Padding(
padding: const EdgeInsets.all(16.0),
child: SegmentedButton<ScoutingView>(
segments: const [
ButtonSegment(value: ScoutingView.match, label: Text('Match Scouting')),
ButtonSegment(value: ScoutingView.pit, label: Text('Pit Scouting')),
],
selected: {_selectedView},
onSelectionChanged: (newSelection) {
setState(() {
if (value == true) {
_checkboxes.text += 'Left ';
} else {
_checkboxes.text =
_checkboxes.text.replaceAll('Left ', '');
}
_saveNotes();
_selectedView = newSelection.first;
});
},
),
CheckboxListTile(
title: const Text('Mid'),
value: _checkboxes.text.contains('Mid'),
onChanged: (bool? value) {
setState(() {
if (value == true) {
_checkboxes.text += 'Mid ';
} else {
_checkboxes.text = _checkboxes.text.replaceAll('Mid ', '');
}
_saveNotes();
});
},
),
Expanded(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: _selectedView == ScoutingView.match
? _buildMatchScoutingView()
: _buildPitScoutingView(),
),
CheckboxListTile(
title: const Text('Right'),
value: _checkboxes.text.contains('Right'),
onChanged: (bool? value) {
setState(() {
if (value == true) {
_checkboxes.text += 'Right ';
} else {
_checkboxes.text =
_checkboxes.text.replaceAll('Right ', '');
}
_saveNotes();
});
},
),
const Divider(),
TextField(
controller: _controller2,
decoration: const InputDecoration(labelText: 'Auton Rundown'),
maxLines: null,
keyboardType: TextInputType.multiline,
onChanged: (text) => _saveNotes(),
),
const Divider(),
CheckboxListTile(
title: const Text('Can Score Algae'),
value: _switchvalue.text.contains('Can Score Algae'),
onChanged: (bool? value) {
setState(() {
_switchvalue.text =
value == true ? 'Can Score Algae' : '';
_saveNotes();
});
},
),
CheckboxListTile(
title: const Text('Cannot Score Algae'),
value: _switchvalue.text.contains('Cannot Score Algae'),
onChanged: (bool? value) {
setState(() {
_switchvalue.text =
value == true ? 'Cannot Score Algae' : '';
_saveNotes();
});
},
),
const Divider(),
Text('Coral Level', style: Theme.of(context).textTheme.titleMedium),
Slider(
value: _slidervalue,
onChanged: (double value) {
setState(() {
_slidervalue = value;
});
},
onChangeEnd: (double value) {
_saveNotes();
},
min: 0,
max: 3,
divisions: 3,
label: _slidervalue.round().toString(),
),
],
),
),
],
),
);
}
Widget _buildMatchScoutingView() {
return ListView(
key: const ValueKey('match'),
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: [
Text('Match-Specific Observations', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 16),
TextField(
controller: _botPositionController,
decoration: const InputDecoration(
labelText: 'Bot Position / Role',
hintText: 'e.g., Shooter, Defense, Collector',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: _autonRundownController,
decoration: const InputDecoration(
labelText: 'Autonomous Rundown',
hintText: 'Describe their auto',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: _generalObservationsController,
decoration: const InputDecoration(
labelText: 'Observations',
hintText: 'General observations',
border: OutlineInputBorder(),
),
)
],
);
}
Widget _buildPitScoutingView() {
return ListView(
key: const ValueKey('pit'),
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: [
Text('Robot Technical Details', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 16),
TextField(
controller: _driveTrainTypeController,
decoration: const InputDecoration(
labelText: 'Drivetrain Type',
hintText: 'e.g., Swerve, Tank',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
SwitchListTile(
title: Text('Has Vision/AprilTags', style: Theme.of(context).textTheme.titleSmall),
value: _hasVision,
onChanged: (bool value) {
setState(() {
_hasVision = value;
});
_saveBool('hasVision', value);
},
),
SwitchListTile(
title: Text('Can Go Under Trench', style: Theme.of(context).textTheme.titleSmall),
value: _trenchable,
onChanged: (bool value) {
setState(() {
_trenchable = value;
});
_saveBool('trenchable', value);
},
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Max Climb Level - ${_climbLevel.round()}', style: Theme.of(context).textTheme.titleSmall),
Slider(
value: _climbLevel,
min: 0,
max: 3,
divisions: 3,
label: _climbLevel.round().toString(),
onChanged: (double value) {
setState(() {
_climbLevel = value;
});
},
onChangeEnd: (double value) {
_saveDouble('climbLevel', value);
},
),
],
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Fuel Capacity - ${_fuelCapacity.round()}', style: Theme.of(context).textTheme.titleSmall),
Slider(
value: _fuelCapacity,
min: 0,
max: 50,
divisions: 50,
label: _fuelCapacity.round().toString(),
onChanged: (double value) {
setState(() {
_fuelCapacity = value;
});
},
onChangeEnd: (double value) {
_saveDouble('fuelCapacity', value);
},
),
],
),
),
],
);
}
}