fix: reduce visibility of attributes and methods to a minimum

This commit is contained in:
Orangerot 2025-01-14 18:09:58 +01:00
parent ff0517d435
commit deab0ecfaf
5 changed files with 92 additions and 97 deletions

View file

@ -20,7 +20,7 @@ class Level extends StatefulWidget {
} }
class _LevelState extends State<Level> with SingleTickerProviderStateMixin { class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
final player = AudioPlayer(); final _player = AudioPlayer();
bool _isPlaying = true; bool _isPlaying = true;
Duration? _duration; Duration? _duration;
Duration? _position; Duration? _position;
@ -30,11 +30,11 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
StreamSubscription? _buttonSubscription; StreamSubscription? _buttonSubscription;
final FocusNode _focusNode = FocusNode(); final FocusNode _focusNode = FocusNode();
InputDirection inputDirection = InputDirection(); final InputDirection _inputDirection = InputDirection();
String hitOrMissMessage = 'Play!'; String _hitOrMissMessage = 'Play!';
List<Note> notes = []; final List<Note> _notes = [];
late AnimationController _animationController; late AnimationController _animationController;
late Animation<double> _animation; late Animation<double> _animation;
@ -62,12 +62,12 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
_animationController.forward(); _animationController.forward();
// Use initial values from player // Use initial values from player
player.getDuration().then( _player.getDuration().then(
(value) => setState(() { (value) => setState(() {
_duration = value; _duration = value;
}), }),
); );
player.getCurrentPosition().then( _player.getCurrentPosition().then(
(value) => setState(() { (value) => setState(() {
_position = value; _position = value;
}), }),
@ -75,24 +75,24 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
// listen for new values from player // listen for new values from player
_durationSubscription = _durationSubscription =
player.onDurationChanged.listen((Duration duration) { _player.onDurationChanged.listen((Duration duration) {
setState(() => _duration = duration); setState(() => _duration = duration);
}); });
_positionSubscription = _positionSubscription =
player.onPositionChanged.listen((Duration position) { _player.onPositionChanged.listen((Duration position) {
setState(() => _position = position); setState(() => _position = position);
for (final note in notes) { for (final note in _notes) {
_noteHitCheck(note, position); _noteHitCheck(note, position);
} }
}); });
// go to GameOverStats when level finishes // go to GameOverStats when level finishes
player.onPlayerComplete.listen((void _) { _player.onPlayerComplete.listen((void _) {
Route route = MaterialPageRoute( Route route = MaterialPageRoute(
builder: (context) => GameOverStats( builder: (context) => GameOverStats(
simfile: widget.simfile, simfile: widget.simfile,
notes: notes, notes: _notes,
)); ));
Navigator.pushReplacement(context, route); Navigator.pushReplacement(context, route);
}); });
@ -112,10 +112,10 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
if (arrowIndex < 0 || arrowIndex > 3) { if (arrowIndex < 0 || arrowIndex > 3) {
return; return;
} }
notes.add(Note(time: time, direction: ArrowDirection.values[arrowIndex])); _notes.add(Note(time: time, direction: ArrowDirection.values[arrowIndex]));
}); });
player.play(DeviceFileSource(widget.simfile.audioPath!)); _player.play(DeviceFileSource(widget.simfile.audioPath!));
} }
@override @override
@ -124,19 +124,19 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
_durationSubscription?.cancel(); _durationSubscription?.cancel();
_positionSubscription?.cancel(); _positionSubscription?.cancel();
_buttonSubscription?.cancel(); _buttonSubscription?.cancel();
player.dispose(); _player.dispose();
super.dispose(); super.dispose();
} }
/// toggle between pause and resume /// toggle between pause and resume
void _pauseResume() { void _pauseResume() {
if (_isPlaying) { if (_isPlaying) {
player.pause(); _player.pause();
setState(() { setState(() {
_isPlaying = false; _isPlaying = false;
}); });
} else { } else {
player.resume(); _player.resume();
setState(() { setState(() {
_isPlaying = true; _isPlaying = true;
}); });
@ -155,25 +155,25 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
// combine keyboard and esense input // combine keyboard and esense input
InputDirection esenseDirection = InputDirection esenseDirection =
ESenseInput.instance.getInputDirection(note.direction); ESenseInput.instance.getInputDirection(note.direction);
inputDirection.up |= esenseDirection.up; _inputDirection.up |= esenseDirection.up;
inputDirection.down |= esenseDirection.down; _inputDirection.down |= esenseDirection.down;
inputDirection.left |= esenseDirection.left; _inputDirection.left |= esenseDirection.left;
inputDirection.right |= esenseDirection.right; _inputDirection.right |= esenseDirection.right;
// check if input matches arrow direction // check if input matches arrow direction
bool keypressCorrect = false; bool keypressCorrect = false;
switch (note.direction) { switch (note.direction) {
case ArrowDirection.up: case ArrowDirection.up:
keypressCorrect = inputDirection.up; keypressCorrect = _inputDirection.up;
break; break;
case ArrowDirection.down: case ArrowDirection.down:
keypressCorrect = inputDirection.down; keypressCorrect = _inputDirection.down;
break; break;
case ArrowDirection.right: case ArrowDirection.right:
keypressCorrect = inputDirection.right; keypressCorrect = _inputDirection.right;
break; break;
case ArrowDirection.left: case ArrowDirection.left:
keypressCorrect = inputDirection.left; keypressCorrect = _inputDirection.left;
break; break;
} }
if (keypressCorrect) { if (keypressCorrect) {
@ -181,9 +181,9 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
note.wasHit = true; note.wasHit = true;
_animationController.reset(); _animationController.reset();
_animationController.forward(); _animationController.forward();
inputDirection.reset(); _inputDirection.reset();
setState(() { setState(() {
hitOrMissMessage = 'Great!'; _hitOrMissMessage = 'Great!';
}); });
} }
} else if (note.position < -0.5 * 1.0 / 60.0) { } else if (note.position < -0.5 * 1.0 / 60.0) {
@ -191,9 +191,9 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
note.wasHit = false; note.wasHit = false;
_animationController.reset(); _animationController.reset();
_animationController.forward(); _animationController.forward();
inputDirection.reset(); _inputDirection.reset();
setState(() { setState(() {
hitOrMissMessage = 'Missed'; _hitOrMissMessage = 'Missed';
}); });
} }
} }
@ -210,16 +210,16 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
} }
switch (event.logicalKey) { switch (event.logicalKey) {
case LogicalKeyboardKey.arrowUp: case LogicalKeyboardKey.arrowUp:
inputDirection.up = isDown; _inputDirection.up = isDown;
break; break;
case LogicalKeyboardKey.arrowDown: case LogicalKeyboardKey.arrowDown:
inputDirection.down = isDown; _inputDirection.down = isDown;
break; break;
case LogicalKeyboardKey.arrowLeft: case LogicalKeyboardKey.arrowLeft:
inputDirection.left = isDown; _inputDirection.left = isDown;
break; break;
case LogicalKeyboardKey.arrowRight: case LogicalKeyboardKey.arrowRight:
inputDirection.right = isDown; _inputDirection.right = isDown;
break; break;
} }
} }
@ -254,7 +254,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
)), )),
), ),
body: Stack(children: [ body: Stack(children: [
Arrows(notes: notes), Arrows(notes: _notes),
Positioned( Positioned(
top: 50, top: 50,
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
@ -262,7 +262,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
child: FadeTransition( child: FadeTransition(
opacity: _animation, opacity: _animation,
child: Text( child: Text(
hitOrMissMessage, _hitOrMissMessage,
textScaler: TextScaler.linear(4), textScaler: TextScaler.linear(4),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),

View file

@ -17,36 +17,35 @@ class LevelSelection extends StatefulWidget {
} }
class _LevelSelectionState extends State<LevelSelection> { class _LevelSelectionState extends State<LevelSelection> {
String? stepmaniaCoursesPath; String? _stepmaniaCoursesPath;
List<Simfile> stepmaniaCoursesFolders = []; List<Simfile> _stepmaniaCoursesFolders = [];
List<Simfile> stepmaniaCoursesFoldersFiltered = []; List<Simfile> _stepmaniaCoursesFoldersFiltered = [];
String searchString = '';
@override @override
void initState() { void initState() {
super.initState(); super.initState();
loadFolderPath(); _loadFolderPath();
} }
/// gets folder path from persistent storage and updates state with loaded simfiles /// gets folder path from persistent storage and updates state with loaded simfiles
Future<void> loadFolderPath() async { Future<void> _loadFolderPath() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
final String? stepmaniaCoursesPathSetting = final String? stepmaniaCoursesPathSetting =
prefs.getString('stepmania_courses'); prefs.getString('stepmania_courses');
if (stepmaniaCoursesPathSetting == null) return; if (stepmaniaCoursesPathSetting == null) return;
List<Simfile> stepmaniaCoursesFoldersFuture = List<Simfile> stepmaniaCoursesFoldersFuture =
await listFilesAndFolders(stepmaniaCoursesPathSetting); await _listFilesAndFolders(stepmaniaCoursesPathSetting);
setState(() { setState(() {
stepmaniaCoursesPath = stepmaniaCoursesPathSetting; _stepmaniaCoursesPath = stepmaniaCoursesPathSetting;
stepmaniaCoursesFolders = stepmaniaCoursesFoldersFuture; _stepmaniaCoursesFolders = stepmaniaCoursesFoldersFuture;
stepmaniaCoursesFoldersFiltered = stepmaniaCoursesFoldersFuture; _stepmaniaCoursesFoldersFiltered = stepmaniaCoursesFoldersFuture;
}); });
} }
/// open folder selection dialog and save selected folder in persistent storage /// open folder selection dialog and save selected folder in persistent storage
Future<void> selectFolder() async { Future<void> _selectFolder() async {
await Permission.manageExternalStorage.request(); await Permission.manageExternalStorage.request();
String? selectedFolder = await FilePicker.platform.getDirectoryPath(); String? selectedFolder = await FilePicker.platform.getDirectoryPath();
@ -55,12 +54,12 @@ class _LevelSelectionState extends State<LevelSelection> {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('stepmania_courses', selectedFolder); await prefs.setString('stepmania_courses', selectedFolder);
loadFolderPath(); _loadFolderPath();
} }
} }
/// load all simfiles from a [directoryPath] /// load all simfiles from a [directoryPath]
Future<List<Simfile>> listFilesAndFolders(String directoryPath) async { Future<List<Simfile>> _listFilesAndFolders(String directoryPath) async {
final directory = Directory(directoryPath); final directory = Directory(directoryPath);
try { try {
// List all files and folders in the directory // List all files and folders in the directory
@ -90,9 +89,9 @@ class _LevelSelectionState extends State<LevelSelection> {
} }
/// filter stepmaniaCoursesFolders based on [input] /// filter stepmaniaCoursesFolders based on [input]
void filterLevels(String input) { void _filterLevels(String input) {
setState(() { setState(() {
stepmaniaCoursesFoldersFiltered = stepmaniaCoursesFolders _stepmaniaCoursesFoldersFiltered = _stepmaniaCoursesFolders
.where((simfile) => simfile.tags["TITLE"]! .where((simfile) => simfile.tags["TITLE"]!
.toLowerCase() .toLowerCase()
.contains(input.toLowerCase())) .contains(input.toLowerCase()))
@ -119,9 +118,9 @@ class _LevelSelectionState extends State<LevelSelection> {
], ],
), ),
body: Builder(builder: (context) { body: Builder(builder: (context) {
if (stepmaniaCoursesPath == null) { if (_stepmaniaCoursesPath == null) {
return Text('Add a Directory with Stepmania Songs on \'+\''); return Text('Add a Directory with Stepmania Songs on \'+\'');
} else if (stepmaniaCoursesFolders.isEmpty) { } else if (_stepmaniaCoursesFolders.isEmpty) {
return Text( return Text(
'Folder empty. Add Stepmania Songs to Folder or select a different folder on \'+\''); 'Folder empty. Add Stepmania Songs to Folder or select a different folder on \'+\'');
} else { } else {
@ -131,7 +130,7 @@ class _LevelSelectionState extends State<LevelSelection> {
padding: padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0),
child: TextField( child: TextField(
onChanged: filterLevels, onChanged: _filterLevels,
decoration: InputDecoration( decoration: InputDecoration(
// icon: Icon(Icons.search), // icon: Icon(Icons.search),
hintText: 'Search'), hintText: 'Search'),
@ -139,11 +138,11 @@ class _LevelSelectionState extends State<LevelSelection> {
), ),
Expanded( Expanded(
child: ListView.separated( child: ListView.separated(
itemCount: stepmaniaCoursesFoldersFiltered.length, itemCount: _stepmaniaCoursesFoldersFiltered.length,
separatorBuilder: (BuildContext context, int index) => separatorBuilder: (BuildContext context, int index) =>
const Divider(), const Divider(),
itemBuilder: (context, index) { itemBuilder: (context, index) {
Simfile simfile = stepmaniaCoursesFoldersFiltered[index]; Simfile simfile = _stepmaniaCoursesFoldersFiltered[index];
return LevelListEntry(simfile: simfile); return LevelListEntry(simfile: simfile);
}, },
), ),
@ -154,7 +153,7 @@ class _LevelSelectionState extends State<LevelSelection> {
}), }),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () { onPressed: () {
selectFolder(); _selectFolder();
}, },
child: Icon(Icons.add)), child: Icon(Icons.add)),
); );

View file

@ -15,18 +15,17 @@ class ESenseInput {
ESenseManager eSenseManager = ESenseManager('unknown'); ESenseManager eSenseManager = ESenseManager('unknown');
// valuenotifier allows widgets to rerender when the value changes // valuenotifier allows widgets to rerender when the value changes
ValueNotifier<String> deviceStatus = ValueNotifier('Disconnected'); ValueNotifier<String> deviceStatus = ValueNotifier('Disconnected');
StreamSubscription? subscription; StreamSubscription? _subscription;
String eSenseDeviceName = ''; String eSenseDeviceName = '';
bool connected = false; bool connected = false;
bool sampling = false;
int sampleRate = 20; final int _sampleRate = 20;
InputDirection inputDirection = InputDirection(); final InputDirection _inputDirection = InputDirection();
int x = 0; int _x = 0;
int y = 0; int _y = 0;
int z = 0; int _z = 0;
ESenseInput._() { ESenseInput._() {
_listenToESense(); _listenToESense();
@ -83,7 +82,6 @@ class ESenseInput {
break; break;
case ConnectionType.disconnected: case ConnectionType.disconnected:
deviceStatus.value = 'Disconnected'; deviceStatus.value = 'Disconnected';
sampling = false;
_pauseListenToSensorEvents(); _pauseListenToSensorEvents();
break; break;
case ConnectionType.device_found: case ConnectionType.device_found:
@ -107,7 +105,7 @@ class ESenseInput {
void _startListenToSensorEvents() async { void _startListenToSensorEvents() async {
// // any changes to the sampling frequency must be done BEFORE listening to sensor events // // any changes to the sampling frequency must be done BEFORE listening to sensor events
print('setting sampling frequency...'); print('setting sampling frequency...');
bool successs = await eSenseManager.setSamplingRate(sampleRate); bool successs = await eSenseManager.setSamplingRate(_sampleRate);
if (successs) { if (successs) {
print('setSamplingRate success'); print('setSamplingRate success');
} else { } else {
@ -115,19 +113,17 @@ class ESenseInput {
} }
// subscribe to sensor event from the eSense device // subscribe to sensor event from the eSense device
subscription = eSenseManager.sensorEvents.listen((event) { _subscription = eSenseManager.sensorEvents.listen((event) {
// print('SENSOR event: $event'); // print('SENSOR event: $event');
if (event.gyro != null) { if (event.gyro != null) {
_parseGyroData(event.gyro!); _parseGyroData(event.gyro!);
} }
}); });
sampling = true;
} }
/// cancels the sensorEvents listening /// cancels the sensorEvents listening
void _pauseListenToSensorEvents() async { void _pauseListenToSensorEvents() async {
subscription?.cancel(); _subscription?.cancel();
sampling = false;
} }
/// add up all new gyro [data] in the form of deg/s multiplied by scaling factor /// add up all new gyro [data] in the form of deg/s multiplied by scaling factor
@ -135,42 +131,42 @@ class ESenseInput {
void _parseGyroData(List<int> data) { void _parseGyroData(List<int> data) {
// Float value in deg/s = Gyro value / Gyro scale factor // Float value in deg/s = Gyro value / Gyro scale factor
// The default configuration is +- 500deg/s for the gyroscope. // The default configuration is +- 500deg/s for the gyroscope.
x = (x + (15 * data[0] ~/ (500 * sampleRate))) % 360; _x = (_x + (15 * data[0] ~/ (500 * _sampleRate))) % 360;
y = (y + (15 * data[1] ~/ (500 * sampleRate))) % 360; _y = (_y + (15 * data[1] ~/ (500 * _sampleRate))) % 360;
z = (z + (15 * data[2] ~/ (500 * sampleRate))) % 360; _z = (_z + (15 * data[2] ~/ (500 * _sampleRate))) % 360;
print('$x, $y, $z'); print('$_x, $_y, $_z');
// print('${(z.toDouble() / 500.0 * (1.0 / sampleRate.toDouble())) * 7.5}'); // print('${(z.toDouble() / 500.0 * (1.0 / sampleRate.toDouble())) * 7.5}');
// print('${z.toDouble() / 500.0 * (1.0 / 10.0)}'); // print('${z.toDouble() / 500.0 * (1.0 / 10.0)}');
} }
/// nulls all angles and reset inputDirection /// nulls all angles and reset inputDirection
void resetAngles() { void resetAngles() {
inputDirection.reset(); _inputDirection.reset();
x = 0; _x = 0;
y = 0; _y = 0;
z = 0; _z = 0;
} }
/// get InputDirection by checking if angels are in defined ranges and /// get InputDirection by checking if angels are in defined ranges and
/// calibrating based on the [expect]ed direction from ArrowDirection /// calibrating based on the [expect]ed direction from ArrowDirection
InputDirection getInputDirection(ArrowDirection expect) { InputDirection getInputDirection(ArrowDirection expect) {
// check if angle is in range // check if angle is in range
inputDirection.up = z > 180 && z < 340; _inputDirection.up = _z > 180 && _z < 340;
inputDirection.down = z > 20 && z < 180; _inputDirection.down = _z > 20 && _z < 180;
inputDirection.left = y > 0 && y < 180; _inputDirection.left = _y > 0 && _y < 180;
inputDirection.right = y > 180 && y < 360; _inputDirection.right = _y > 180 && _y < 360;
// calibrate based on expected directoin from ArrowDirection // calibrate based on expected directoin from ArrowDirection
if (expect == ArrowDirection.up && inputDirection.up || if (expect == ArrowDirection.up && _inputDirection.up ||
expect == ArrowDirection.down && inputDirection.down) { expect == ArrowDirection.down && _inputDirection.down) {
y = 0; _y = 0;
} }
if (expect == ArrowDirection.left && inputDirection.left || if (expect == ArrowDirection.left && _inputDirection.left ||
expect == ArrowDirection.right && inputDirection.right) { expect == ArrowDirection.right && _inputDirection.right) {
z = 0; _z = 0;
} }
return inputDirection; return _inputDirection;
} }
/// connect to ESense with [deviceName] by first asking for permissions /// connect to ESense with [deviceName] by first asking for permissions

View file

@ -17,7 +17,7 @@ class ESenseConnectDialog extends StatefulWidget {
} }
class _ESenseConnectDialogState extends State<ESenseConnectDialog> { class _ESenseConnectDialogState extends State<ESenseConnectDialog> {
String eSenseDeviceName = ''; String _eSenseDeviceName = '';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -31,7 +31,7 @@ class _ESenseConnectDialogState extends State<ESenseConnectDialog> {
TextField( TextField(
onChanged: (input) { onChanged: (input) {
setState(() { setState(() {
eSenseDeviceName = input; _eSenseDeviceName = input;
}); });
}, },
decoration: InputDecoration( decoration: InputDecoration(
@ -54,7 +54,7 @@ class _ESenseConnectDialogState extends State<ESenseConnectDialog> {
child: const Text('Disconnect'), child: const Text('Disconnect'),
) )
: TextButton( : TextButton(
onPressed: () => widget.connect(eSenseDeviceName), onPressed: () => widget.connect(_eSenseDeviceName),
child: const Text('Connect'), child: const Text('Connect'),
), ),
], ],

View file

@ -17,13 +17,13 @@ class LevelListEntry extends StatelessWidget {
final Simfile simfile; final Simfile simfile;
/// navigates to level screen /// navigates to level screen
void navigateToLevel(BuildContext context) { void _navigateToLevel(BuildContext context) {
Navigator.push(context, Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) => Level(simfile))); MaterialPageRoute(builder: (BuildContext context) => Level(simfile)));
} }
/// opens ESenseConnectDialog /// opens ESenseConnectDialog
void openESenseConnectDialog(context) { void _openESenseConnectDialog(context) {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -41,19 +41,19 @@ class LevelListEntry extends StatelessWidget {
} }
/// when clocked on the level, warn if not connected to ESense /// when clocked on the level, warn if not connected to ESense
void tapHandler(BuildContext context) { void _tapHandler(BuildContext context) {
if (ESenseInput.instance.connected) { if (ESenseInput.instance.connected) {
navigateToLevel(context); _navigateToLevel(context);
} else { } else {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return ESenseNotConnectedDialog( return ESenseNotConnectedDialog(
onCancel: () { onCancel: () {
openESenseConnectDialog(context); _openESenseConnectDialog(context);
}, },
onContinue: () { onContinue: () {
navigateToLevel(context); _navigateToLevel(context);
}, },
); );
}, },
@ -88,7 +88,7 @@ class LevelListEntry extends StatelessWidget {
), ),
), ),
onTap: () { onTap: () {
tapHandler(context); _tapHandler(context);
}, },
); );
} }