Toy project to learn Flutter/Dart.
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.

158 lines
5.4 KiB

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:path_provider/path_provider.dart';
import 'utils.dart';
// type of sorting
enum Sort {
alphabetic,
numerical,
}
// pokemon list provider
// contains the current index and the status of the pokemons
class PokemonProvider with ChangeNotifier {
int currentIndex = 0; // index of pokemon being reviewed
List<String> pokemonNames = ["null"]; // list of pokemon names
List<int> alphaIndices = []; // list of indices in alphabetic order
List<Status> statusList = List.filled(802, Status.undefined); // list of pokemon status
RandomAccessFile? storage;
Sort sorting = Sort.numerical;
// default constructor
PokemonProvider();
// "getters"
int currentId() {return index2id(currentIndex);}
int nextId() {return index2id(currentIndex+1);}
String currentAsString() {return id2str(currentId());} // formated int
String nextAsString() {return id2str(nextId());} // formated int
String currentName() {return name(currentId());} // name of current
String currentImage() {return id2image(currentId());} // asset of current
String nextImage() {return id2image(nextId());} // asset of next
Status currentStatus() {return status(currentId());} // status of current
Status previousStatus() { // status of previous (edge case for first pokemon)
if(currentIndex>0) {
return statusList[currentIndex-1];
} else {
return Status.undefined;
}
}
// util functions
// from id to feature
String name(int id) {return pokemonNames[id-1];} // name of pokemon by id
Status status(int id) {return statusList[id-1];} // status of pokemon by id
String id2str(int id) {return padint(id);} // id to string format (for assets)
String id2image(int id) {return "assets/pokemon/${id2str(id)}.png";} // path of asset
String id2mini(int id) {return "assets/pokemon-mini/${id2str(id)}.png";} // path of asset
// index to id conversion
int index2id(int index) {
switch(sorting) {
case Sort.numerical: { return index+1;}
case Sort.alphabetic: { return alphaIndices[index]+1;}
}
}
// id to index conversion
int id2index(int id) {return id-1;}
// from index to feature
String index2name(int index) {return name(index2id(index));} // name of pokemon by id
Status index2status(int index) {return status(index2id(index));} // status of pokemon by id
String index2str(int index) {return id2str(index2id(index));} // id to string format (for assets)
String index2image(int index) {return id2image(index2id(index));} // path of asset
String index2mini(int index) {return id2mini(index2id(index));} // path of asset
// status "setters"
void setCurrentStatus(Status status) {
int index = id2index(currentId());
statusList[index] = status;
notifyListeners();
writeStatus(status, index); // trigger write to file
}
// index "setters"
void setCurrentIndex(int index) {
currentIndex = index;
notifyListeners();
}
void next() {
setCurrentIndex(currentIndex + 1);
notifyListeners();
}
void previous() {
if(currentIndex>0) {
setCurrentIndex(currentIndex - 1);
notifyListeners();
}
}
void setSorting(Sort sort) {
sorting = sort;
notifyListeners();
}
// read pokemon names from assets (only at start of the app)
Future<List<String>> readPokemonNames() async {
return rootBundle.loadString("assets/pokemonNames.txt")
.then((s) => s.split("\n"));
}
// initialize data in provider
void initialize() async {
// print("initializing provider"); // only happens at start
readPokemonNames().then((names){
pokemonNames = names; // TODO store length apart from the list
alphaIndices = List<int>.generate(names.length, (int i) => i); // range from 0 to n-1
alphaIndices.sort((i,j)=>pokemonNames[i].compareTo(pokemonNames[j])); // sort them into alphabetic order
loadStatus(names.length);
// notifyListeners();
});
}
// get position of local path in filesystem
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
// get position of status file
Future<File> get _statusFile async {
final path = await _localPath;
return File('$path/status');
}
// load status from status file
void loadStatus(int length) async {
_statusFile.then((file) {
file.exists().then((exists) {
// DELETE file for debug purpose
// file.deleteSync();
// exists = false;
file.open(mode: FileMode.append).then((f) {
storage ??= f;
if(exists) { // reads it
storage?.setPosition(0).then((s) {
s.read(length).then((bytes) {
// print("read $bytes");
statusList = bytes.map<Status>(byteToStatus).toList();
notifyListeners();
});
});
} else { // initialize it
// print("initializing with $length zeros ${Uint8List(3)}");
f.writeFrom(Uint8List(length));
}
});
});
});
}
// write status at given position in file
void writeStatus(Status status, int index) {
storage?.setPosition(index).then((f) {f.writeByte(statusToByte(status));});
// print("storage $storage, status ${statusToByte(status)}");
}
}