Ders İçeriği

Mobil uygulamalar genellikle internet bağlantısı olmadığında bile çalışabilmek veya kullanıcı tercihlerini kaydetmek için yerel veri depolama yöntemlerine ihtiyaç duyar. Flutter, bu amaçla çeşitli seçenekler sunar.

1. Shared Preferences

shared_preferences paketi, basit anahtar-değer çiftlerini depolamak için kullanılır. Genellikle kullanıcı ayarları, oturum bilgileri veya küçük miktardaki verileri kaydetmek için idealdir. Veriler, platforma özgü kalıcı depolama mekanizmalarında (iOS'ta NSUserDefaults, Android'de SharedPreferences) saklanır.
Öncelikle shared_preferences paketini pubspec.yaml dosyanıza ekleyin:
dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.2 # En güncel sürümü kullanın

Sonra flutter pub get komutunu çalıştırın.
Kullanım Örneği:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({Key? key}) : super(key: key);

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool _isDarkMode = false;
  int _fontSize = 16;

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

  // Ayarları yükle
  Future<void> _loadSettings() async {
    final prefs = await SharedPreferences.getInstance();
    setState(() {
      _isDarkMode = prefs.getBool('isDarkMode') ?? false;
      _fontSize = prefs.getInt('fontSize') ?? 16;
    });
  }

  // Karanlık mod ayarını kaydet
  Future<void> _setDarkMode(bool value) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('isDarkMode', value);
    setState(() {
      _isDarkMode = value;
    });
  }

  // Yazı tipi boyutunu kaydet
  Future<void> _setFontSize(int value) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setInt('fontSize', value);
    setState(() {
      _fontSize = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Ayarlar')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                const Text('Karanlık Mod'),
                Switch(
                  value: _isDarkMode,
                  onChanged: _setDarkMode,
                ),
              ],
            ),
            const SizedBox(height: 20),
            Text('Yazı Tipi Boyutu: $_fontSize'),
            Slider(
              value: _fontSize.toDouble(),
              min: 10,
              max: 30,
              divisions: 20,
              label: _fontSize.toString(),
              onChanged: (double value) {
                _setFontSize(value.toInt());
              },
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                final prefs = await SharedPreferences.getInstance();
                await prefs.clear(); // Tüm ayarları temizle
                _loadSettings(); // Ayarları yeniden yükle
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('Ayarlar temizlendi.')),
                );
              },
              child: const Text('Ayarları Temizle'),
            ),
          ],
        ),
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: const SettingsScreen(),
  ));
}
SharedPreferences ile bool, int, double, String ve List<String> gibi temel veri tiplerini depolayabilirsiniz. Veriler senkronize olarak değil, asenkron olarak kaydedilir ve okunur, bu nedenle await anahtar kelimesini kullanmak önemlidir.

5.2. SQLite (sqflite paketi)

Daha yapılandırılmış ve ilişkisel veri depolama ihtiyaçları için SQLite veritabanı kullanılabilir. Flutter'da SQLite ile etkileşim kurmak için sqflite paketi popüler bir seçimdir. sqflite, platforma özgü SQLite veritabanı işlemlerini Dart katmanına taşır.
Öncelikle sqflite ve path_provider paketlerini pubspec.yaml dosyanıza ekleyin:
dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.3.0 # En güncel sürümü kullanın
  path_provider: ^2.1.1 # En güncel sürümü kullanın

Sonra flutter pub get komutunu çalıştırın.
Kullanım Örneği: Basit Bir Not Uygulaması
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

// Not modeli
class Note {
  final int? id;
  final String title;
  final String content;

  Note({this.id, required this.title, required this.content});

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'title': title,
      'content': content,
    };
  }

  factory Note.fromMap(Map<String, dynamic> map) {
    return Note(
      id: map['id'] as int,
      title: map['title'] as String,
      content: map['content'] as String,
    );
  }

  @override
  String toString() {
    return 'Note{id: $id, title: $title, content: $content}';
  }
}

// Veritabanı Yardımcı Sınıfı
class DatabaseHelper {
  static final DatabaseHelper _instance = DatabaseHelper._internal();
  static Database? _database;

  factory DatabaseHelper() {
    return _instance;
  }

  DatabaseHelper._internal();

  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  Future<Database> _initDatabase() async {
    String path = join(await getDatabasesPath(), 'notes_database.db');
    return await openDatabase(
      path,
      version: 1,
      onCreate: (db, version) async {
        await db.execute(
          'CREATE TABLE notes(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, content TEXT)',
        );
      },
    );
  }

  Future<int> insertNote(Note note) async {
    final db = await database;
    return await db.insert('notes', note.toMap(), conflictAlgorithm: ConflictAlgorithm.replace);
  }

  Future<List<Note>> getNotes() async {
    final db = await database;
    final List<Map<String, dynamic>> maps = await db.query('notes');
    return List.generate(maps.length, (i) {
      return Note.fromMap(maps[i]);
    });
  }

  Future<int> updateNote(Note note) async {
    final db = await database;
    return await db.update(
      'notes',
      note.toMap(),
      where: 'id = ?',
      whereArgs: [note.id],
    );
  }

  Future<int> deleteNote(int id) async {
    final db = await database;
    return await db.delete(
      'notes',
      where: 'id = ?',
      whereArgs: [id],
    );
  }
}

class NotesScreen extends StatefulWidget {
  const NotesScreen({Key? key}) : super(key: key);

  @override
  State<NotesScreen> createState() => _NotesScreenState();
}

class _NotesScreenState extends State<NotesScreen> {
  final DatabaseHelper _dbHelper = DatabaseHelper();
  List<Note> _notes = [];
  final TextEditingController _titleController = TextEditingController();
  final TextEditingController _contentController = TextEditingController();
  Note? _selectedNote;

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

  Future<void> _loadNotes() async {
    final notes = await _dbHelper.getNotes();
    setState(() {
      _notes = notes;
    });
  }

  void _saveNote() async {
    if (_titleController.text.isEmpty || _contentController.text.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Başlık ve içerik boş olamaz.')),
      );
      return;
    }

    if (_selectedNote == null) {
      // Yeni not ekle
      final newNote = Note(
        title: _titleController.text,
        content: _contentController.text,
      );
      await _dbHelper.insertNote(newNote);
    } else {
      // Notu güncelle
      final updatedNote = Note(
        id: _selectedNote!.id,
        title: _titleController.text,
        content: _contentController.text,
      );
      await _dbHelper.updateNote(updatedNote);
      _selectedNote = null; // Seçimi kaldır
    }
    _titleController.clear();
    _contentController.clear();
    _loadNotes();
  }

  void _editNote(Note note) {
    setState(() {
      _selectedNote = note;
      _titleController.text = note.title;
      _contentController.text = note.content;
    });
  }

  void _deleteNote(int id) async {
    await _dbHelper.deleteNote(id);
    _loadNotes();
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Not silindi.')),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Not Uygulaması (SQLite)')),
      body: Column(
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              children: [
                TextField(
                  controller: _titleController,
                  decoration: const InputDecoration(labelText: 'Başlık'),
                ),
                TextField(
                  controller: _contentController,
                  decoration: const InputDecoration(labelText: 'İçerik'),
                  maxLines: 3,
                ),
                const SizedBox(height: 10),
                ElevatedButton(
                  onPressed: _saveNote,
                  child: Text(_selectedNote == null ? 'Not Ekle' : 'Notu Güncelle'),
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _notes.length,
              itemBuilder: (context, index) {
                final note = _notes[index];
                return Card(
                  margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
                  child: ListTile(
                    title: Text(note.title),
                    subtitle: Text(note.content),
                    trailing: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        IconButton(
                          icon: const Icon(Icons.edit),
                          onPressed: () => _editNote(note),
                        ),
                        IconButton(
                          icon: const Icon(Icons.delete),
                          onPressed: () => _deleteNote(note.id!),
                        ),
                      ],
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: const NotesScreen(),
  ));
}
SQLite, daha karmaşık ve yapılandırılmış verileri yerel olarak depolamak için güçlü bir çözümdür. İlişkisel veritabanı yapısı sayesinde, veriler arasında ilişkiler kurabilir ve SQL sorguları ile esnek bir şekilde verilere erişebilirsiniz.
Bu bölümde orta seviye Flutter derslerini tamamlamış bulunmaktayız. Artık Flutter'da daha karmaşık kullanıcı arayüzleri oluşturmayı, farklı durum yönetimi yaklaşımlarını, navigasyonu ve ağ ile yerel veri işlemlerini biliyorsunuz. Bir sonraki bölümde ileri seviye konulara geçeceğiz.