티스토리 뷰
플러터에서 앱의 데이터를 저장하는 방법
- 공유 환경설정 - 비교적 적은 양의 간단한 데이터를 저장하는 용도로, shared Preferences라는 클래스를 이용한다. 이 클래스는 키-값 쌍으로 구성된 공유 환경설정 파일을 가리키며 이 파일에 데이터를 읽거나 쓰는 함수를 제공한다.
- 파일
- 데이터베이스
9-1 공유 환경설정에 데이터 저장하기
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _setData(int value) async {
var key = 'count';
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setInt(key, value)
}
void _loadData() async{
var key = 'count';
SharedPreferences pref = await SharedPreferences.getInstance();
setState((){
var value= pref.getInt(key);
if (value == null){
_counter = 0;
}else{
_counter = value;
}
});
}
공유 환경설정에 저장하는 데이터는 키-값 쌍으로 구성되어야 한다.
공유 환경설정에 저장할 수 있는 값의 유형에는 int, String, bool, double, StringList등이 있다.
_setData 함수에서 'count'라는 키 값을 저장한 key 변수를 선언했다. 그리고 SharedPreferences 클래스의 인스턴스를 생성한 후 key와 카운트 값 value를 인자로 setInt() 함수로 호출한다. setInt() 함수는 sharedPreferences 클래스가 제공하는 setter 함수이다.
공유 환경설정에서 저장된 데이터를 가져오는 코드는 _loadData() 함수에서 작성했다. SharedPreferences 클래스의 인스턴스를 생성한 후 이를 통해 getInt()라는 getter함수를 호출한다. 이때 키값으로 데이터를 찾기위해 key를 인자로 넣는다.
@override
void initState() {
super.initState();
_loadData();
}
void _incrementCounter() {
setState(() {
_counter++;
_setData(_counter);
});
}
카운트 값을 가져오는 _loadData 함수는 앱이 처음 실행될 때 호출하도록 initState()함수에 넣고 카운트 값을 저장하는 _setData() 함수는 버튼을 누를 때마다 호출되는 _incrementCounter()에 넣는다.
공유 환경설정을 이용하는 것도 결국 내부저장소에 파일 형태로 데이터를 저장하는 것이지만, 공유환경설정 파일은 앱에서만 접근할 수 있는 특수한 목적의 데이터 저장소라는 점에서 차이가 있다.
9-2 파일에 데이터 저장하기
int _count = 0;
@override
void initState() {
super.initState();
readCountFile();
}
void readCountFile() async {
try{
var dir = await getApplicationDocumentsDirectory();
var file = await File(dir.path+'/count.txt').readAsString();
print(file);
setState((){
_count = int.parse(file);
});
}catch(e){
print(e.toString());
}
}
void writeCountFile(int count) async{
var dir = await getApplicationDocumentsDirectory();
File(dir.path + '/count.txt').writeAsStringSync(count.toString());
}
// 내부 저장소의 경로를 가져올 때 getApplicationDocumentsDirectory() 함수 외에 getTemporaryDirectory()
//함수를 이용 가능. 이 함수는 임시 디렉터리로 경로를 가져온다. 하지만 임시 디렉터리는 캐시를 이용하므로 앱이 종료되고
//시간이 종료되면 사라질 수 있다.
}
파일 입출력 또한 언제 작업이 끝나는지 예측할 수 었기에 비동기 함수로 만든다.
path_provider 패키지에 들어있는 getApplicationDocuments Directory() 함수를 이용해 내부 저장소의 경로를 가져와서 그곳에 파일을 읽거나 쓴다.
writeCountFile() 함수는 매개변수로 전달받은 count값은 count.txt.라는 이름의 파일로 만들어 문자열 형태로 저장한다.
readCountFile() 함수는 이 파일을 읽어 다시 정수형으로 변환 후 _count 변수에 저장한다. 그리고 앱이 실행될 때 파일에서 가져온 데이터를 표시하기 위해 initSate() 함수에서 readCountFile() 함수를 호출한다.
앱에서 파일을 직접만들 수도 있지만 데이터의 양이 많고 또 자주 갱신하지 않아도되는 데이터라면 앱 안에 미리 파일로 만들어 놓고 이를 사용할 수도 있다.
Future<List<String>> readListFile() async {
List<String> itemList = new List.empty(growable: true);
var key = "first";
SharedPreferences pref = await SharedPreferences.getInstance();
bool? firstCheck = pref.getBool(key); // 파일을 처음 열었는지 확인하는 용도로 사용
var dir = await getApplicationDocumentsDirectory();
bool fileExist = await File(dir.path +'/fruit.txt').exists();
if(firstCheck == null || firstCheck == false || fileExist ==false ){
pref.setBool(key, true);
var file = await DefaultAssetBundle.of(context).loadString('repo/fruit.txt');
File(dir.path +'/fruit.txt').writeAsStringSync(file);
var array = file.split('\n');
for(var item in array){
print(item);
itemList.add(item);
}
return itemList;
} else{
var file = await File(dir.path+'/fruit.txt').readAsString();
var array = file.split('\n');
for (var item in array){
print(item);
itemList.add(item);
}
return itemList;
}
}
readListFile() 함수를 처음 실행하면 asset에 등록한 파일을 읽어서 내부 저장소에 다시 저장한다.
다음 실행부터는 내부 저장소에 있는 파일을 대상으로 데이터를 불러와 리스트를 만든다.
var key = "first";
SharedPreferences pref = await SharedPreferences.getInstance();
bool? firstCheck = pref.getBool(key);
'first' 라는 키를 이용해 bool값을 가져와 firstCheck변수에 저장하고 이후에 파일을 처음 열었는지 확인하는 용도로 사용한다.
bool fileExist = await File(dir.path +'/fruit.txt').exists();
지정한 경로에 파일이 있는지를 확인한다. File().exist()함수로 내부 저장소에 fruit.txt 파일이 있는지를 확인한 후 fileExist 변수에 bool값으로 저장한다.
if(firstCheck == null || firstCheck == false || fileExist ==false ){
pref.setBool(key, true);
var file = await DefaultAssetBundle.of(context).loadString('repo/fruit.txt');
File(dir.path +'/fruit.txt').writeAsStringSync(file);
var array = file.split('\n');
for(var item in array){
print(item);
itemList.add(item);
}
return itemList;
}
firstCheck, firstExist 변수는 파일이 있는지, 처음 열었는지 등을 확인해 로직을 둘로 나누어 처리하고자 만듦
파일을 처음 열었거나 없으면 공유 환경설정에 true값을 저장해 파일을 연것으로 기록하고 애셋에 등록한 파일을 읽어서 내부 저장소에 똑같은 파일을 만든다. 해당 파일의 데이터를 줄바꿈 문자를 기준으로 나누어 배열 형태로 만들고, 각 아이템을 리스트에 넣어서 반환한다.
else{
var file = await File(dir.path+'/fruit.txt').readAsString();
var array = file.split('\n');
for (var item in array){
print(item);
itemList.add(item);
}
return itemList;
}
파일을 처음 연 것이아니라면 파일의 데이터를 리스트로 만들어서 반한하되 대상 파일을 내부 저장소의 fruit.txt 파일로 지정한다.
int _count = 0;
List<String> itemList = new List.empty(growable: true);
TextEditingController controller = new TextEditingController();
@override
void initState() {
super.initState();
initData();
}
void initData() async{
var result = await readListFile();
setState(() {
itemList.addAll(result);
});
}
initState() 함수에는 async 키워드를 사용할 수 없으므로 별도의 initData() 함수를 만들어 async 처리를 해준다 .
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('File Example'),
),
body: Container(
child: Center(
child: Column(
children: <Widget>[
TextField(
controller: controller,
keyboardType: TextInputType.text,
),
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text(
itemList[index],
style: TextStyle(fontSize: 30),
),
));
},
itemCount: itemList.length,
))
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
},
child: Icon(Icons.add),
),
);
}
앞에서 준비한 list데이터를 ListView.bilder를 이용해 화면에 표시한다. 하지만 실행 창에 렌더링 오류가 발생한다. Row나 Column 위젯은 내용에 따라 알아서 크기를 조절해 배치하지만 ListView는 길이를 가늠할 수 없기때문이다.
따라서 Expended 위젯을 이용한다. 해당 위젯은 남은 공간을 모두 사용한다. 즉, 텍스트필드 이외에 나머지 부분은 모두 ListView로 사용하겠다는 의미이다.
void wirteFruit(String fruit) async{
var dir = await getApplicationDocumentsDirectory();
var file = await File(dir.path+'/fruit.txt').readAsString();
file = file+'\n'+fruit;
File(dir.path+'/fruit.txt').writeAsStringSync(file);
}
writeFruit 함수는 현재 fruit.txt 파일의 내용을 가져와서 매개변수로 전달받은 과일 이름 fruit를 추가한 후 다시 파일에 기록한다.
'플러터 앱 스터디 일지' 카테고리의 다른 글
플러터 스터디 chap 11 (0) | 2021.11.07 |
---|---|
플러터 스터디 chap 10 (0) | 2021.10.13 |
플러터 스터디 chap 8 (0) | 2021.10.06 |
플러터 스터디 chap 7 (0) | 2021.10.06 |
플러터 스터디 chap 6 - ios 스타일 Cupertino 위젯 사용 (0) | 2021.09.28 |