0. Demo Video
(프로그램을 종료 후 재수행해도 저장된 데이터는 조회됨)
https://tv.kakao.com/channel/3793022/cliplink/417983265
1. Project Sturture
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