본문 바로가기

카테고리 없음

[Flutter] SQLite plugin for Flutter(iOS)

0. Demo Video

(프로그램을 종료 후 재수행해도 저장된 데이터는 조회됨)

https://tv.kakao.com/channel/3793022/cliplink/417983265

1. Project Sturture

Image, Text 내부 Database Project 구조

2. Dependency

dependencies:
  flutter:
    sdk: flutter
  provider: ^4.0.0
  image_picker: ^0.6.5+1
  path_provider: ^2.0.1
  path: ^1.8.0
  sqflite: ^1.3.0

 

2-1. Image Picker plugin for Flutter

Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:

(* Android의 경우에는 별도로 세팅하지 않아도 된다.)

<key>NSCameraUsageDescription</key>
<string>We need to take a picture</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need to take a picture</string>

Refernece : https://pub.dev/packages/image_picker

 

2-2. sqflite Guide

 -  하나의 클래스로 생성하여 다른 클래스에서 호출해서 사용한다. (Singleton  Pattern)

 -  각 함수는 static으로 선언하여 모든 클래스에서 접근 가능하도록 설정한다.

import 'package:sqflite/sqflite.dart' as sql;
import 'package:path/path.dart' as path;
import 'package:sqflite/sqlite_api.dart';

class DBHelper {

  static Future<Database> database() async {
    final dbPath = await sql.getDatabasesPath();

	/*
    	DB Scheme : places.db
    
    */
    return await sql.openDatabase(
      path.join(dbPath, 'places.db'),
      onCreate: (db, version) {
        return db.execute(
          'CREATE TABLE user_places(id TEXT PRIMARY KEY, title TEXT, image TEXT)');
      }, 
      version: 1
    );
  }

  static Future<void> insert(String table, Map<String, Object> data) async {

    final db = await DBHelper.database();
    
    /*
    	conflict가 발생하면 안되지만,
       	conflict가 발생했을 경우 Error Handling
    */
    
    db.insert(
      table, 
      data, 
      conflictAlgorithm: sql.ConflictAlgorithm.replace
    );
    
  }

  static Future<List<Map<String, dynamic>>> getData(String table) async {
    final db = await DBHelper.database();
    /*
    	Adding Query Conditions and Arguments
    */
    return db.query(
      table, 
      where: "id = ?",
      whereArgs: ['args']
    );
  }
  
}

 

3. Usage

3-1. Provider : 사용자가 입력한 데이터 Insert

- 사용자가 저장한 데이터는 GreatPlaces List에서 관리된다.

- 사용자가 addPlace 함수를 호출하여 List에 데이터를 Insert하고자 할때, List에 객체를 넣어주는 동시에 위에 선언한 DBHelper.insert함수를 호출한다. (이때 insert 함수가 static으로 선언되어 있지 않다면 호출이 불가능하다.)

import 'dart:io';
import 'package:flutter/material.dart';
import '../models/place.dart';
import '../helpers/db_helper.dart';

class GreatPlaces with ChangeNotifier {
  List<Place> _items = [];

  List<Place> get items {
    return [..._items];
  }

  void addPlace(String pickedTitle, File pickedImage) {

    final newPlace = Place(
      id: DateTime.now().toString(),
      image: pickedImage,
      title: pickedTitle,
      location: null,
    );

    _items.add(newPlace);
    notifyListeners();

    DBHelper.insert('user_places', {
      'id' : newPlace.id, 
      'title' : newPlace.title, 
      'image': newPlace.image.path
    });

  }

  Future<void> fetchAndSetPlaces() async {

    final dataList = await DBHelper.getData('user_places');

    _items = dataList.map(
      (item) => Place(
        id: item['id'],
        title : item['title'],
        image: File(item['image']),
        location : null
      )
    ).toList();

    notifyListeners();
  }

}

 

3-2. Consumer : 사용자가 저장한 데이터 조회

 - 사용자가 저장한 정보는 Provider인 GreatPlaces의 fetchAndSetPlaces() 함수에서 호출된다.

 - 호출 순서 :  Consumer -> GreatePlaces . fetchAndSetPlaces() -> DBHelper . getData()

 - 조회 조건

    A. DBHelper의 getData()에서 조회 조건을 설정

    B. GreatePlaces fetchAndSetPlaces에서 List를 return할 때 조건 설정

 

import 'package:flutter/material.dart';
import 'package:native_features/providers/great_places.dart';
import 'package:native_features/screens/add_place_screen.dart';
import 'package:provider/provider.dart';

class PlacesListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Your Places'),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.add),
              onPressed: () {
                Navigator.of(context).pushNamed(AddPlaceScreen.routeName);
              },
            )
          ],
        ),
        body: FutureBuilder(
          future: Provider.of<GreatPlaces>(context, listen: false)
              .fetchAndSetPlaces(),
          builder: (ctx, snapshot) => snapshot.connectionState ==
                  ConnectionState.waiting
              ? Center(
                  child: CircularProgressIndicator(),
                )
              : Consumer<GreatPlaces>(
                  child: Center(
                    child: const Text('Got no places yet, start adding Some!'),
                  ),
                  builder: (ctx, greatPlaces, ch) =>
                      greatPlaces.items.length <= 0
                          ? ch
                          // ch: child const text의 내용 노출
                          : ListView.builder(
                              itemCount: greatPlaces.items.length,
                              itemBuilder: (ctx, i) => ListTile(
                                leading: CircleAvatar(
                                  backgroundImage:
                                      FileImage(greatPlaces.items[i].image),
                                ),
                                title: Text(greatPlaces.items[i].title),
                                onTap: () {
                                  // Go to detail pages...
                                },
                              ),
                            ),
                ),
        ));
  }
}

 

soruce code @

https://github.com/92phantom/flutter_template/tree/main/native_features