본문 바로가기

그 땐 App했지/그 땐 Flutter했지

[TAVE/Study]Do it! Flutter 9장 내부 저장소 이용하기②

728x90

211008 금

 

파일 입출력 연습하기

📌 이번에는 파일에 데이터를 저장하고 불러오는 방법을 보자!

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  path_provider: ^2.0.2

👉🏻 패키지 등록!

import 'package:flutter/material.dart';

class FileApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _FileApp();
}

class _FileApp extends State<FileApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('File Example'),
      ),
      body: Container(),
    );
  }
}

👉🏻 fileApp.dart라는 새로운 다트 파일을 만든다.

import 'dart:io';
import 'package:path_provider/path_provider.dart';

class _FileApp extends State<FileApp> {
  int _count = 0;

  @override
  void initState() {
    super.initState();
    readCountFile();
  }
  
  void WriteCountFile(int count) async {
    var dir = await getApplicationDocumentsDirectory();
    File(dir.path + '/count.txt').writeAsStringSync(count.toString());
  }

  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());
    }
  }
}

👉🏻 _FileApp 클래스에서 파일을 읽고 쓴다.

👉🏻 임시 디렉터리를 이용하기 위해서는 getTemporaryDirectory함수를 이용할 수 있다.

✍🏻 getApplicationDocumentsDirectory함수는 path_provider 패키지에 들어 있는 함수이다. 내부 저장소의 경로를 가져와 그곳에 파일을 읽거나 쓴다.

✍🏻 writeCountFile함수는 count값을 매개변수로 받아 count.txt 이름의 파일로 만들어 문자열 형태로 저장한다. 

✍🏻 readCountFile함수는 파일을 다시 정수형으로 변환해 _count 변수에 저장한다.

      body: Container(
        child: Center(
          child: Text(
          '$_count',
          style: TextStyle(fontSize: 40),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _count++;
          });
          writeCountFile(_count);
        },
        child: Icon(Icons.add),
      ),
    );

👉🏻 버튼을 누를 때마다 _count값이 올라가고 writeCountFile 함수를 실행해 그 값을 저장한다.

import 'package:flutter/material.dart';
import 'fileApp.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: FileApp(),
    );
  }
}

👉🏻 main.dart에 연결

(영상)

 

좋아하는 과일 표시하기 - 파일 읽기

📌 데이터 양이 많고 자주 갱신될 필요가 없다면 앱 안에 미리 파일을 만들어 저장해두고 사용하는 방법도 있다.

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  path_provider: ^2.0.2
  shared_preferences: 2.0.6

flutter:
  uses-material-design: true
  assets:
    - repo/fruit.txt

👉🏻 fruit.txt 파일을 만들어 좋아하는 과일을 써둔다.

👉🏻 shared_preferences 패키지를 불러오고 fruit.txt 파일도 불러온다.

import 'package:shared_preferences/shared_preferences.dart';

  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;
    }
  }

👉🏻 readFile함수는 애셋에 등록한 파일을 읽어 내부 저장소에서 다시 저장한다.

✍🏻 first 키를 이용해 bool값을 가져와 firstCheck 변수에 저장한다. 이 변수는 이후에 파일을 처음 열었는지 확인한다.

✍🏻 fileExist에는 지정한 경로에 파일이 있는지 확인한다. 있는지 여부에 따라 bool값이 결정된다. 

✍🏻 위의 변수들을 확인해서 처음 열었거나, 파일이 없다면 공유 환경설정에서 true를 저장해 파일을 열었다고 기록하고 파일을 읽어 내부 저장소에 똑같은 파일을 만든다. 그리고 해당 파일 데이터를 리스트에 저장한다.

✍🏻 파일을 처음 연 것이 아니면 파일 데이터를 리스트로 만들어 반환하되 대상 파일을 내부 저장소의 fruit.txt 파일로 지정한다.

class _FileApp extends State<FileApp> {
  int _count = 0;
  List<String> itemList = new List.empty(growable: true);
  TextEditingController controller = new TextEditingController();

  @override
  void initState() {
    super.initState();
    readCountFile();
    initData();
  }
  
  void initData() async {
    var result = await readListFile();
    setState(() {
      itemList.addAll(result);
    });
  }

👉🏻 앱을 실행하면 readListFile함수를 실행한다.

✍🏻 initState는 비동기 방식으로 처리할 수 없어 별도의 initData 함수를 만들어 비동기 처리를 한다. 이 함수는 readFile함수를 호출하고 setState로 리스트를 만든다.

              Expanded(
                child: ListView.builder(
                  itemBuilder: (context, index) {
                    return Card(
                      child: Center(
                        child: Text(
                          itemList[index],
                          style: TextStyle(fontSize: 30),
                        ),
                      ),
                    );
                  },
                  itemCount: itemList.length,
                ),
              )

✍🏻 Expanded 위젯: 남은 공간을 모두 사용하는 것이다. 이 코드는 텍스트 필드 이외의 나머지 부분을 ListView로 사용한다.

 

좋아하는 과일 추가하기 - 파일 쓰기
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          writeFruit(controller.value.text);
          setState(() {
            itemList.add(controller.value.text);
          });
        },
        
  (...생략...)
        
  void writeFruit(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);
  }

👉🏻 버튼 누를 때만다 사용자가 등록한 과일이름을 내부 저장소의 fruit.txt 파일에 추가한다.

(영상)

 

내려받은 이미지를 로고로 사용하기

📌 인트로 화면을 바꿔보겠다! 이때 인트로 화면을 바꿀때마다 업데이트를 하는 것이 아닌 서버에서 이미지를 내려받는 방법을 보겠다!

import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'main.dart';

class IntroPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _IntroPage();
}

class _IntroPage extends State<IntroPage> {
  Widget logo = Icon(
    Icons.info,
    size: 50,
  );

  @override
  void initState() {
    super.initState();
    initData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Center(
          child: Column(
            children: [
              logo,
              ElevatedButton(
                onPressed: () {
                  Navigator.of(context)
                      .pushReplacement(MaterialPageRoute(builder:(context){
                    return MyHomePage(title: '')
                  }));
                },
                child: Text('다음으로 가기'),
              )
            ],
            mainAxisAlignment: MainAxisAlignment.center,
          ),
        ),
      ),
    );
  }

  void initData() async {
    var dir = await getApplicationDocumentsDirectory();
    bool fileExist = await File(dir.path + '/myimage.jpg').exists();
    if (fileExist) {
      setState(() {
        logo = Image.file(
          File(dir.path + '/myimage.jpg'),
          height: 200,
          width: 200,
          fit: BoxFit.contain,
        );
      });
    }
  }
}

👉🏻 내려받는 이미지를 자동으로 내부 저장소에 이미지 파일로 저장한다.

✍🏻 initData로 파일이 있는지 확인하고 logo 위젯에 이미지를 넣는다.

import 'introPage.dart';

      home: IntroPage(),

👉🏻 메인 파일에 연결한다.

(영상)

728x90