In this tutorial we will see how to save an image as a string in preferences.
Watch more flutter videos on my youtube channel here.
Watch Video Tutorial
Add Dependencies
First thing we have to do is to add the plugins.
Open pubspec.yaml file and add the below Dependencies.
shared_preferences: ^0.5.0 // save the data in preferences. image_picker: ^0.6.0+3 // to select image from the gallery or camera. sqflite: ^1.1.3 // For Sqlite operations
So Let’s start…
First we will write a Utility class to save the image as a String and retreive it.
import 'dart:typed_data'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/material.dart'; import 'dart:async'; import 'dart:convert'; class Utility { static Image imageFromBase64String(String base64String) { return Image.memory( base64Decode(base64String), fit: BoxFit.fill, ); } static Uint8List dataFromBase64String(String base64String) { return base64Decode(base64String); } static String base64String(Uint8List data) { return base64Encode(data); } }
Select Image from gallery
pickImageFromGallery() { ImagePicker.pickImage(source: ImageSource.gallery).then((imgFile) { String imgString = Utility.base64String(imgFile.readAsBytesSync()); }); }
imageFile will be a Future<file> and this will be set when the user selects an image from gallery or Camera.
DBHelper
We will write a simple Utility class to save the image in the database.
Before that we need a model class to save in the database.
Create a file named “Photo.dart”
class Photo { int id; String photo_name; Photo(this.id, this.photo_name); Map<String, dynamic> toMap() { var map = <String, dynamic>{ 'id': id, 'photo_name': photo_name, }; return map; } Photo.fromMap(Map<String, dynamic> map) { id = map['id']; photo_name = map['photo_name']; } }
Now the DBHelper class.
import 'dart:async'; import 'dart:io' as io; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart'; import 'Photo.dart'; class DBHelper { static Database _db; static const String ID = 'id'; static const String NAME = 'photo_name'; static const String TABLE = 'PhotosTable'; static const String DB_NAME = 'photos.db'; Future<Database> get db async { if (null != _db) { return _db; } _db = await initDb(); return _db; } initDb() async { io.Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, DB_NAME); var db = await openDatabase(path, version: 1, onCreate: _onCreate); return db; } _onCreate(Database db, int version) async { await db.execute("CREATE TABLE $TABLE ($ID INTEGER, $NAME TEXT)"); } Future<Photo> save(Photo employee) async { var dbClient = await db; employee.id = await dbClient.insert(TABLE, employee.toMap()); return employee; } Future<List<Photo>> getPhotos() async { var dbClient = await db; List<Map> maps = await dbClient.query(TABLE, columns: [ID, NAME]); List<Photo> employees = []; if (maps.length > 0) { for (int i = 0; i < maps.length; i++) { employees.add(Photo.fromMap(maps[i])); } } return employees; } Future close() async { var dbClient = await db; dbClient.close(); } }
The above class will save the image in the Photo object in the Sqlite database and getPhotos will return the list of records as list of photos.
Show the Selected Images in a GridView
gridView() { return Padding( padding: EdgeInsets.all(5.0), child: GridView.count( crossAxisCount: 2, childAspectRatio: 1.0, mainAxisSpacing: 4.0, crossAxisSpacing: 4.0, children: images.map((photo) { return Utility.imageFromBase64String(photo.photoName); }).toList(), ), ); }
To Update the images array, we will call the refreshImages() function.
refreshImages() { dbHelper.getPhotos().then((imgs) { setState(() { images.clear(); images.addAll(imgs); }); }); }
Complete Code
import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'dart:io'; import 'Utility.dart'; import 'DBHelper.dart'; import 'Photo.dart'; import 'dart:async'; class SaveImageDemoSQLite extends StatefulWidget { // SaveImageDemoSQLite() : super(); final String title = "Flutter Save Image"; @override _SaveImageDemoSQLiteState createState() => _SaveImageDemoSQLiteState(); } class _SaveImageDemoSQLiteState extends State<SaveImageDemoSQLite> { // Future<File> imageFile; Image image; DBHelper dbHelper; List<Photo> images; @override void initState() { super.initState(); images = []; dbHelper = DBHelper(); refreshImages(); } refreshImages() { dbHelper.getPhotos().then((imgs) { setState(() { images.clear(); images.addAll(imgs); }); }); } pickImageFromGallery() { ImagePicker.pickImage(source: ImageSource.gallery).then((imgFile) { String imgString = Utility.base64String(imgFile.readAsBytesSync()); Photo photo = Photo(0, imgString); dbHelper.save(photo); refreshImages(); }); } gridView() { return Padding( padding: EdgeInsets.all(5.0), child: GridView.count( crossAxisCount: 2, childAspectRatio: 1.0, mainAxisSpacing: 4.0, crossAxisSpacing: 4.0, children: images.map((photo) { return Utility.imageFromBase64String(photo.photoName); }).toList(), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), actions: <Widget>[ IconButton( icon: Icon(Icons.add), onPressed: () { pickImageFromGallery(); }, ) ], ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ Flexible( child: gridView(), ) ], ), ), ); } }
Thanks for reading…
Please watch the video tutorial to see it in action.
I did this, and it worked great for small images. Then I took a couple of high resolution photos from my camera, and they broke my db. Is there a way to have the images automatically resized or compressed first? Or should I have the images save to the device instead? Thanks for the video!
Pingback: Google's Flutter Tutorial - Save Image as String in SQLite. (coderzheaven.com) - TutsFx