fluttersharedpreferencesdata-persistence

Best way to store objects with images locally on disk in Flutter?


I'm making a super simple cookbook (like literally, for food) app for one single person, so I haven't been thinking that I want/need to use a database or any internet connectivity. When the user creates a recipe, the recipe object contains a bunch of strings and an image, like this:

class Recipe {
 String _title;
  Image _picture;
  Uint8List _imageBytes;
  int _prepTime;
  int _cookTime;
  int _totalTime;
  String _description;
  List<String> _ingredientList;
  List<String> _directionsList;


....
}


The user will probably create around 20 recipes. There need not be any profiles, since I'm literally just making the app for one person. The recipes are the only app data that need to persist.

So far, I have tried to encode each recipe as a JSON and then save it to shared_preferences (note the _imageBytes field that I tried to use for this purpose). But not only does this seem like a really inefficient way to do things, I haven't even been able to get it to work. It seems confusingly difficult to get information on this: how would one normally store such information locally in a Flutter app, without using a database? Or is a database the way to go here?


Solution

  • To persist data efficiently in Flutter using a local database, such as SQLite, is the way to go:

    1. Import the right dependecies:
    import 'dart:async';
    
    import 'package:path/path.dart';
    import 'package:sqflite/sqflite.dart';
    
    1. Open the DB:
    WidgetsFlutterBinding.ensureInitialized();
    // Open the database and store the reference.
    final Future<Database> database = openDatabase(
      join(await getDatabasesPath(), 'recipes_database.db'),
    );
    
    1. Create the table:
    final Future<Database> database = openDatabase(
      join(await getDatabasesPath(), 'doggie_database.db'),
      onCreate: (db, version) {
        return db.execute(
          "CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TITLE)", // all the properties here
        );
      },
      version: 1,
    );
    
    1. Create methods to insert and retrieve entries:
    Future<void> inserRecipe(Recipe recipe) async {
      final Database db = await database;
      await db.insert(
        'recipe',
        recipe.toMap(),
        conflictAlgorithm: ConflictAlgorithm.replace,
      );
    }
    Future<List<Recipe>> recipes() async {
      final Database db = await database;
    
      final List<Map<String, dynamic>> maps = await db.query('recipes');
    
      return List.generate(maps.length, (i) {
        return Recipe(
          title: maps[i]['title'],
          // ... all the properties here
        );
      });
    }
    

    Since our class, Recipe, contains files, and more specifically images, we could store the Image as an URL and take advantage of the Cache to retrieve it:

    var image = await DefaultCacheManager().getSingleFile(recipe.picture);