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 keydynamic dataasync {
    if (_isValidate<T>(data)) {
      var value = jsonEncode(data);
      await _prefs.setString(keyvalue);
    }
  }

  select<T>(String keyasync {
    try {
      var list = jsonDecode(_prefs.getString(key)!) as List<dynamic>;
      if (T == WeatherHourDto) {
        List<WeatherHourDtodto = list.map((e) => 
            WeatherHourDto.fromJson(e as Map<Stringdynamic>)).toList();
        return dto;
      } else if (T == WeatherDayDto) {
        List<WeatherDayDtodto = list.map((e) => 
            WeatherDayDto.fromJson(e as Map<Stringdynamic>)).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.weatherDayKeylist);
    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<Databaseget database async {
    if (_database != null && _database!.isOpen) {
      return _database!;
    }

    _database = await _initialDatabase();
    return _database!;
  }

  _initialDatabase() async {
    var databasePath = await getDatabasesPath();
    String path = join(databasePathdatabaseName);
    try {
      if (FileSystemEntity.typeSync(path) == FileSystemEntityType.notFound) {
        // Load database from asset and copy
        ByteData data = await rootBundle.load(join('assets/data'databaseName));
        List<intbytes = data.buffer.asUint8List(
            data.offsetInBytesdata.lengthInBytes);

        await new File(path).writeAsBytes(bytes);
      }
    } catch (e) {
      logger.d(e);
    }

    Database db = await openDatabase(
        pathversiondatabaseVersiononCreate_onCreate);
    return db;
  }

  _onCreate(Database dbint versionasync {
    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<StationDtolistasync {}

  replace(List<StationDtolist, {Stringstate}) 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({StringsidoStringsigunStringsigu}) 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<Maplist =
        await db.rawQuery(
   'SELECT * FROM ${DatabaseHelper.tableStation} ${buildQueryCondition(condition)}');

    return list.map((e) => StationDto.fromJson(e as Map<Stringdynamic>)).toList();
  }

  selectAll() async {
    var db = await DatabaseHelper.instance.database;
    List<Maplist = await db.rawQuery('''
        SELECT * FROM ${DatabaseHelper.tableStation} 
        LIMIT 5000
      ''');
    return list.map((e) => StationDto.fromJson(e as Map<Stringdynamic>)).toList();
  }
}



station_repository 사용법


    StationRepository repo = new StationRepository();
    List<StationDtolist = await repo.selectAll();
    print(list.length);
    



댓글