Bird
Raised Fist0
Fluttermobile~20 mins

SQLite with sqflite package in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Notes List
A simple notes app screen that stores notes locally using SQLite via the sqflite package. Users can add new notes and see the list of saved notes.
Target UI
-------------------------
| Notes List            |
|-----------------------|
| + Add Note            |
|-----------------------|
| - Note 1              |
| - Note 2              |
| - Note 3              |
|                       |
-------------------------
Use sqflite package to create and manage a local SQLite database.
Create a table 'notes' with columns 'id' (integer primary key) and 'content' (text).
Display a list of saved notes from the database.
Add a FloatingActionButton with '+' icon to open a dialog for entering a new note.
Save the new note to the database and update the list immediately.
Handle database initialization asynchronously.
Use simple UI with a Scaffold, AppBar, ListView, and FloatingActionButton.
Starter Code
Flutter
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: NotesListScreen(),
    );
  }
}

class NotesListScreen extends StatefulWidget {
  const NotesListScreen({super.key});

  @override
  State<NotesListScreen> createState() => _NotesListScreenState();
}

class _NotesListScreenState extends State<NotesListScreen> {
  late Database db;
  List<String> notes = [];

  @override
  void initState() {
    super.initState();
    // TODO: Initialize database and load notes
  }

  Future<void> _addNote() async {
    // TODO: Show dialog to add note and save it
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Notes List'),
      ),
      body: ListView.builder(
        itemCount: notes.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(notes[index]),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addNote,
        child: const Icon(Icons.add),
      ),
    );
  }
}
Task 1
Task 2
Task 3
Task 4
Solution
Flutter
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: NotesListScreen(),
    );
  }
}

class NotesListScreen extends StatefulWidget {
  const NotesListScreen({super.key});

  @override
  State<NotesListScreen> createState() => _NotesListScreenState();
}

class _NotesListScreenState extends State<NotesListScreen> {
  late Database db;
  List<String> notes = [];

  @override
  void initState() {
    super.initState();
    _initDb();
  }

  Future<void> _initDb() async {
    final databasePath = await getDatabasesPath();
    final path = join(databasePath, 'notes.db');

    db = await openDatabase(
      path,
      version: 1,
      onCreate: (db, version) async {
        await db.execute('''
          CREATE TABLE notes(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            content TEXT
          )
        ''');
      },
    );

    await _loadNotes();
  }

  Future<void> _loadNotes() async {
    final List<Map<String, dynamic>> maps = await db.query('notes');
    setState(() {
      notes = maps.map((map) => map['content'] as String).toList();
    });
  }

  Future<void> _addNote() async {
    final TextEditingController controller = TextEditingController();

    final result = await showDialog<String?>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Add Note'),
        content: TextField(
          controller: controller,
          autofocus: true,
          decoration: const InputDecoration(hintText: 'Enter note content'),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              final text = controller.text.trim();
              if (text.isNotEmpty) {
                Navigator.pop(context, text);
              }
            },
            child: const Text('Save'),
          ),
        ],
      ),
    );

    if (result != null && result.isNotEmpty) {
      await db.insert('notes', {'content': result});
      await _loadNotes();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Notes List'),
      ),
      body: ListView.builder(
        itemCount: notes.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(notes[index]),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addNote,
        child: const Icon(Icons.add),
      ),
    );
  }
}

This app uses the sqflite package to manage a local SQLite database. In initState, it opens or creates the database file and the notes table if it doesn't exist. Then it loads all saved notes into a list.

The _addNote method shows a dialog with a text field for the user to enter a new note. When saved, the note is inserted into the database, and the list is refreshed to show the new note immediately.

The UI uses a ListView to display notes and a floating button with a plus icon to add new notes. This simple structure helps beginners understand how to connect UI with persistent local storage.

Final Result
Completed Screen
-------------------------
| Notes List            |
|-----------------------|
| + Add Note            |
|-----------------------|
| - Buy groceries       |
| - Call mom            |
| - Read Flutter docs   |
|                       |
-------------------------
Tap the '+' floating button to open a dialog with a text field.
Enter note text and tap 'Save' to add it to the list and database.
Tap 'Cancel' to close the dialog without saving.
Notes list updates immediately after adding a note.
Stretch Goal
Add the ability to delete a note by swiping it left or right in the list.
💡 Hint
Use Dismissible widget around each ListTile and remove the note from the database and list on dismiss.

Practice

(1/5)
1. What is the primary purpose of the sqflite package in Flutter?
easy
A. To handle network requests and APIs
B. To manage SQLite databases for storing app data locally
C. To create user interfaces with widgets
D. To manage app navigation between screens

Solution

  1. Step 1: Understand sqflite package role

    The sqflite package is designed to work with SQLite databases in Flutter apps.
  2. Step 2: Identify main functionality

    It helps create, open, and manage local databases for storing app data persistently.
  3. Final Answer:

    To manage SQLite databases for storing app data locally -> Option B
  4. Quick Check:

    sqflite = SQLite database management [OK]
Hint: sqflite = SQLite database helper in Flutter [OK]
Common Mistakes:
  • Confusing sqflite with UI or navigation packages
  • Thinking sqflite handles network or API calls
  • Assuming sqflite is for animations or gestures
2. Which of the following is the correct way to open a database using sqflite?
easy
A. db.open('my_db.db');
B. openDB('my_db.db');
C. await openDatabase('my_db.db');
D. Database.open('my_db.db');

Solution

  1. Step 1: Recall sqflite openDatabase syntax

    The sqflite package provides an async function called openDatabase to open or create a database.
  2. Step 2: Match correct syntax

    The correct usage is: await openDatabase('filename'); other options are not valid sqflite methods.
  3. Final Answer:

    await openDatabase('my_db.db'); -> Option C
  4. Quick Check:

    openDatabase is async and correct method [OK]
Hint: Use await openDatabase('filename') to open DB [OK]
Common Mistakes:
  • Using non-existent methods like openDB or Database.open
  • Forgetting to await the async openDatabase call
  • Calling openDatabase without parentheses
3. Given this code snippet, what will be the output printed?
var db = await openDatabase('test.db');
await db.execute('CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT)');
await db.insert('users', {'id': 1, 'name': 'Alice'});
var result = await db.query('users');
print(result);
medium
A. SyntaxError
B. []
C. null
D. [{'id': 1, 'name': 'Alice'}]

Solution

  1. Step 1: Analyze database operations

    The code opens a database, creates a table if missing, inserts one user, then queries all users.
  2. Step 2: Understand query result

    The query returns a list of maps representing rows. Since one user was inserted, result contains one map with id and name.
  3. Final Answer:

    [{'id': 1, 'name': 'Alice'}] -> Option D
  4. Quick Check:

    Insert then query returns inserted row [OK]
Hint: Insert then query returns list with inserted rows [OK]
Common Mistakes:
  • Expecting empty list if table was just created
  • Confusing query result with null or error
  • Not awaiting async calls causing runtime errors
4. What is wrong with this sqflite code snippet?
var db = await openDatabase('app.db');
await db.execute('CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT)');
await db.insert('users', {'id': 1, 'name': 'Bob'});
await db.insert('users', {'id': 1, 'name': 'Carol'});
medium
A. It tries to insert two rows with the same primary key, causing a conflict error
B. The table creation SQL syntax is incorrect
C. The insert method requires a third argument for conflictAlgorithm
D. openDatabase must be called with a version number

Solution

  1. Step 1: Check table and inserts

    The table has id as PRIMARY KEY. Two inserts use the same id=1.
  2. Step 2: Understand primary key constraint

    Inserting duplicate primary keys causes a conflict error unless handled explicitly.
  3. Final Answer:

    It tries to insert two rows with the same primary key, causing a conflict error -> Option A
  4. Quick Check:

    Duplicate primary keys cause insert conflict error [OK]
Hint: Primary key must be unique; duplicates cause errors [OK]
Common Mistakes:
  • Assuming insert silently overwrites existing rows
  • Thinking version is mandatory for openDatabase
  • Believing insert always needs conflictAlgorithm argument
5. You want to update a user's name in the database using sqflite. Which code snippet correctly updates the user with id = 2 to have the name 'David'?
hard
A. await db.update('users', {'name': 'David'}, where: 'id = ?', whereArgs: [2]);
B. await db.insert('users', {'id': 2, 'name': 'David'});
C. await db.execute('UPDATE users SET name = 'David' WHERE id = 2');
D. await db.update('users', {'name': 'David'}, where: 'id = 2');

Solution

  1. Step 1: Understand update method parameters

    The update method requires the table name, values map, and a where clause with arguments to avoid SQL injection.
  2. Step 2: Check correct usage of where and whereArgs

    await db.update('users', {'name': 'David'}, where: 'id = ?', whereArgs: [2]); uses where: 'id = ?' and whereArgs: [2], which is the safe and correct way.
  3. Final Answer:

    await db.update('users', {'name': 'David'}, where: 'id = ?', whereArgs: [2]); -> Option A
  4. Quick Check:

    Use whereArgs with ? placeholders for safe updates [OK]
Hint: Use where with ? and whereArgs list for updates [OK]
Common Mistakes:
  • Using raw SQL string without parameter binding
  • Using insert instead of update to change data
  • Omitting whereArgs causing errors or unsafe queries
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard