A small Flutter example that demonstrates CRUD operations using the sqflite package and a simple Dish model.
This project is intended as a learning/demo app. It shows how to open and use an SQLite database with a DbHelper class, a minimal UI to create/read/update/delete records, and a few common layout fixes.
- Database helper:
lib/database/db_helper.dart - Model:
lib/model/dish.dart - App entry point / UI:
lib/main.dart
- Flutter SDK (stable channel)
- Platform-specific tooling for Android/iOS if you want to run on device/emulator
The app uses these packages (see pubspec.yaml):
- sqflite — SQLite plugin for Flutter
- path — for building platform-independent file paths
Install dependencies:
flutter pub getRun on a connected device or emulator:
flutter runlib/database/db_helper.dart exposes a small helper class to manage the SQLite database and provides convenience CRUD methods.
Key points:
- Lazy initialization: the
dbgetter (aFuture<Database> get db) opens the database on first access and caches theDatabaseinstance in a private_dbfield so subsequent calls reuse the same connection. initDb()builds the database path usinggetDatabasesPath()andjoin(...), then callsopenDatabase(...)and creates theDishestable if it doesn't exist.- CRUD methods call the getter then execute SQL using
rawInsert,rawUpdate,rawQuery,rawDelete.
Example: get a usable Database instance (from anywhere in the code):
final dbHelper = DbHelper();
final database = await dbHelper.db; // the getter opens/returns the DB
// now use `database` with sqflite APIsNote: using the helper's convenience methods is recommended (e.g. createDish, readAllDishes) rather than calling SQL directly from UI code.
The project creates a single table named Dishes with the following columns:
- name: TEXT
- description: TEXT
- price: DOUBLE
Tip: if you need an id primary key, change the SQL in onCreate to include id INTEGER PRIMARY KEY AUTOINCREMENT.
The lib/main.dart file contains a minimal UI used to test the DB operations. A few important points and fixes are documented here so you won't run into layout crashes:
-
Do not place
Expanded(or other flex children) directly inside aSingleChildScrollView's Column. The scroll view provides unbounded height, which causes flex widgets to fail with an error likeRenderFlex children have non-zero flex but incoming height constraints are unbounded.- Fix: replace
Expandedwith fixed-height containers or useWrap/IntrinsicHeight/ sizedContainers, or move the scrollable to a smaller subtree.
- Fix: replace
-
To show the newest log entries on top, prepend instead of append. Currently
log = newEntry + log;will ensure newest messages appear first.
- Create a dish (from
main.dart):
final dbHelper = DbHelper();
final dish = Dish('Pizza', 'Cheesy', 9.5);
await dbHelper.createDish(dish);- Read a dish by name:
final dish = await dbHelper.readDish('Pizza');
print(dish.description);- Read all dishes:
final list = await dbHelper.readAllDishes();- If you change the table schema you will need to handle migrations (increase the
versionargument toopenDatabaseand provide anonUpgradecallback) or uninstall the app to clear the old DB during development. - If
rawQueryreturns an empty list, your lookup returned no rows — methods inDbHelperwill throw or return empty lists accordingly.
Small fixes and improvements welcome. If you add features (like an id primary key, more fields, or migrations), please update DbHelper and the README schema section.
This project is an educational example. Use it as a starting point — production apps should use parameterized queries to avoid SQL injection and add proper error handling and migrations.