Flutter 프로그래밍 Database 활용하기 preference, sqflite
안녕하세요 ARAYO IT (아라요이트) 입니다.
App 프로그래밍 개발을 하다 보면 앱이 종료되더라도 유지해야 할 데이터가 있습니다. 미세먼지 날씨 같은 경우 지역 좌표 정보나 설정 정보 또는 네트워크에 문제가 있으면 이전 백업 정보들을 저장해 둘 필요가 있습니다. 그래서 Flutter에서 지원하는 두 가지 대표 로컬 Database에 대해 알아보도록 하겠습니다.
구현 목표 - Local Database (preference, Sqflite)
Shared_preferences
단순 데이터(key value pair)를 각각의 플랫폼(Android, iOS) 별 영구 저장소 라이브러리에 래핑하여 저장해 주는 플러그인입니다. 데이터는 비동기식으로 유지되며, 항상 최신 데이터가 디스크에 저장되어 있다고 보장 할 수 없습니다. 중요한 데이터 저장은 권하지 않습니다.
pubspec.yaml 의존성 주입
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
shared_preferences: ^2.0.6
preference_service.dart
- PreferenceService을 singleton으로 정의
- initialize() : SharedPreference의 instatnce를 받아 _prefs 전역변수에 정의
- update() : key의 value를 업데이트
- select() : key의 value를 리턴
- _isValidate() : update시 데이터 유효성을 검사하여 update 할것인지 판단
import 'dart:convert';
import 'package:air_info/model/dto.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PreferenceService {
PreferenceService._();
static final PreferenceService instance = PreferenceService._();
factory PreferenceService() => instance;
static final String weatherHourKey = "weather_hour_key";
static final String weatherDayKey = "weather_day_key";
late SharedPreferences _prefs;
initialize() async {
_prefs = await SharedPreferences.getInstance();
}
update<T>(String key, dynamic data) async {
if (_isValidate<T>(data)) {
var value = jsonEncode(data);
await _prefs.setString(key, value);
}
}
select<T>(String key) async {
try {
var list = jsonDecode(_prefs.getString(key)!) as List<dynamic>;
if (T == WeatherHourDto) {
List<WeatherHourDto> dto = list.map((e) =>
WeatherHourDto.fromJson(e as Map<String, dynamic>)).toList();
return dto;
} else if (T == WeatherDayDto) {
List<WeatherDayDto> dto = list.map((e) =>
WeatherDayDto.fromJson(e as Map<String, dynamic>)).toList();
return dto;
}
} catch (e) {
print(e);
}
return null;
}
bool _isValidate<T>(dynamic data) {
try {
if (T == WeatherDayDto) {
if ((data as List<WeatherDayDto>).first.wf3Am?.isNotEmpty ?? false)
return true;
} else if (T == WeatherHourDto) {
if ((data as List<WeatherHourDto>).first.baseDate?.isNotEmpty ?? false)
return true;
}
} catch (e) {
print(e);
}
return false;
}
reset(String key) {
_prefs.remove(key);
}
}
PreferenceService 사용법
- API를 통해 getWheatherDay() 정보를 요청
- PreferenceSerivce.instance.update()를 통해 전단받은 list 정보를 저장
- PreferenceSerivce.instance.select()로 해당 키값의 value를 받아 dto에 할당
var list = await HttpService.instance.getWeatherDay();
await PreferenceService.instance.update<WeatherDayDto>(
PreferenceService.weatherDayKey, list);
print(list?.length ?? 0);
List<WeatherDayDto>? dto =
await PreferenceService.instance.select<WeatherDayDto>(
PreferenceService.weatherDayKey);
print(dto?.length ?? 0);
Sqflite
flutter용 SQLite 플로그인입니다. Android, iOS, MacOS를 지원합니다.
주요기능
- Transaction 과 Batch 지원
- 자동 버전관리
- Query 지원 (update, insert, replace, delete, select, create 등)
pubspec.yaml 의존성 주입
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
sqflite: ^2.0.0+3
database_helper.dart
- DatabaseHelper singleton으로 정의
- database () : DatabaseHelper의 _database를 리턴
- _initialDatabase() :생성된 데이터가 존재하지 않는다면 생성하거나, 이미 생성된 데이터를 복사
- _onCreate() : 새로운 database를 생성시 queryString 스크립트 수행
import 'dart:io';
import 'package:air_info/util/logger.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DatabaseHelper {
static final DatabaseHelper instance = DatabaseHelper._();
factory DatabaseHelper() => instance;
DatabaseHelper._();
static const databaseName = 'airinfo.db';
static const databaseVersion = 1;
static const tableStation = 'TBL_station';
static const tableWeatherPosition = 'TBL_weather_position';
Database? _database;
Future<Database> get database async {
if (_database != null && _database!.isOpen) {
return _database!;
}
_database = await _initialDatabase();
return _database!;
}
_initialDatabase() async {
var databasePath = await getDatabasesPath();
String path = join(databasePath, databaseName);
try {
if (FileSystemEntity.typeSync(path) == FileSystemEntityType.notFound) {
// Load database from asset and copy
ByteData data = await rootBundle.load(join('assets/data', databaseName));
List<int> bytes = data.buffer.asUint8List(
data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes);
}
} catch (e) {
logger.d(e);
}
Database db = await openDatabase(
path, version: databaseVersion, onCreate: _onCreate);
return db;
}
_onCreate(Database db, int version) async {
await db.execute(queryStringStationTable);
await db.execute(queryStringWeatherPositionTable);
logger.d('$tableStation is created');
}
String queryStringStationTable = '''
CREATE TABLE $tableStation(
dmX TEXT,
dmY TEXT,
item TEXT,
mangName TEXT,
year TEXT,
addr TEXT,
stationName TEXT
)
''';
String queryStringWeatherPositionTable = '''
CREATE TABLE $tableWeatherPosition(
sido TEXT,
sigun TEXT,
dong TEXT,
nx INTEGER,
ny INTEGER
)
''';
}
station_repository.dart
- StationRepository singleton으로 정의
- replace() : 데이터베이스 replace query
- insert() : 데이터베이스 insert query
- select() : 검색 조건에 맞는 데이터베이스 select query
- selectAll() : 전체 select query
import 'package:air_info/model/dto.dart';
import 'package:air_info/page/controller/controller.dart';
import 'package:air_info/util/util.dart';
import 'database_helper.dart';
class StationRepository {
insert(List<StationDto> list) async {}
replace(List<StationDto> list, {String? state}) async {
var db = await DatabaseHelper.instance.database;
var batch = db.batch();
HttpController.find.fetchStatus = 'database updating.. $state';
for (var d in list) {
batch.rawInsert('''
REPLACE INTO ${DatabaseHelper.tableStation}
(dmX, dmY, item, mangName, year, addr, stationName)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', [
d.dmX,
d.dmY,
d.item,
d.mangName,
d.year,
d.addr,
d.stationName,
]);
}
await batch.commit();
HttpController.find.fetchStatus = 'database completed.. $state';
}
select({String? sido, String? sigun, String? sigu}) async {
var db = await DatabaseHelper.instance.database;
String condition = '';
if (sido != null && sido.isNotEmpty)
condition += ' AND addr like \'%${trimAddress(sido)}%\' ';
if (sigun != null && sigun.isNotEmpty)
condition += ' AND addr like \'%$sigun%\' ';
if (sigu != null && sigu.isNotEmpty)
condition += ' AND addr like \'%$sigu%\' ';
List<Map> list =
await db.rawQuery(
'SELECT * FROM ${DatabaseHelper.tableStation} ${buildQueryCondition(condition)}');
return list.map((e) => StationDto.fromJson(e as Map<String, dynamic>)).toList();
}
selectAll() async {
var db = await DatabaseHelper.instance.database;
List<Map> list = await db.rawQuery('''
SELECT * FROM ${DatabaseHelper.tableStation}
LIMIT 5000
''');
return list.map((e) => StationDto.fromJson(e as Map<String, dynamic>)).toList();
}
}
station_repository 사용법
StationRepository repo = new StationRepository();
List<StationDto> list = await repo.selectAll();
print(list.length);

댓글
댓글 쓰기