Создание приложения с поддержкой работы в оффлайне на Flutter & SQLite — лучшие практики сохранения данных

Создание приложения с поддержкой работы в оффлайне на Flutter & SQLite — лучшие практики сохранения данных

FlutterPulse

Эта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

В сегодняшнем мобильном мире функция работы оффлайн является критически важной для плавного пользовательского опыта. Приложение с приоритетом оффлайн обеспечивает, что пользователи могут продолжать использовать приложение даже без подключения к интернету.

В сегодняшнем мобильном мире, функция работы оффлайн является критически важной для плавного пользовательского опыта. Приложение с приоритетом оффлайн обеспечивает, что пользователи могут продолжать использовать приложение даже без подключения к интернету.

Flutter предоставляет несколько решений для сохранения данных, но SQLite остается наиболее мощным вариантом для хранения структурированных данных локально. В этом руководстве мы создадим приложение Flutter с приоритетом оффлайн с SQLite, охватывая:

Почему SQLite?
Настройка SQLite в Flutter
Создание базы данных и таблиц
Выполнение операций CRUD
Синхронизация оффлайн-данных с API

🔹 Почему выбрать SQLite для оффлайн-хранения?

Постоянное хранение — Данные сохраняются даже после закрытия приложения.
Структурированные данные — Использует таблицы и связи, как традиционные базы данных.
Быстрое и легковесное — Идеально для мобильных приложений.
Работает оффлайн — Интернет не требуется для доступа к данным.
Лучшая производительность — Быстрее, чем shared preferences или локальные JSON-файлы.

📌 Шаг 1: Добавление зависимостей

Чтобы использовать SQLite в Flutter, установите пакет sqflite:

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.3.0
  path_provider: ^2.1.2

Запустите:

flutter pub get

📌 Шаг 2: Настройка базы данных SQLite

🛠 Создание класса помощника базы данных

Мы создадим файл database_helper.dart для инициализации базы данных:

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
  static final DatabaseHelper _instance = DatabaseHelper._internal();
  factory DatabaseHelper() => _instance;

  Database? _database;
  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(), 'app_database.db');
    return await openDatabase(
      path,
      version: 1,
      onCreate: _onCreate,
    );
  }
  Future<void> _onCreate(Database db, int version) async {
    await db.execute('''
      CREATE TABLE notes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT,
        content TEXT,
        createdAt TEXT
      )
    ''');
  }
}

Использует path_provider для хранения базы данных в локальной директории приложения.
Создает таблицу notes с полями id, title, content, и createdAt.
Обрабатывает инициализацию базы данных только один раз (singleton pattern).

📌 Шаг 3: Выполнение операций CRUD

🔹 Вставка данных

Добавьте новую запись в базу данных:

Future<int> addNote(String title, String content) async {
  final db = await DatabaseHelper().database;
  return await db.insert('notes', {
    'title': title,
    'content': content,
    'createdAt': DateTime.now().toIso8601String(),
  });
}

🔹 Получение данных

Получите все заметки из базы данных:

Future<List<Map<String, dynamic>>> getNotes() async {
  final db = await DatabaseHelper().database;
  return await db.query('notes', orderBy: 'createdAt DESC');
}

🔹 Обновление данных

Редактирование существующей заметки:

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

🔹 Удаление данных

Удаление заметки из базы данных:

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

📌 Шаг 4: Отображение данных в интерфейсе

Создание модели заметки

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

Получение и отображение заметок в ListView

import 'package:flutter/material.dart';

class NotesScreen extends StatefulWidget {
  @override
  _NotesScreenState createState() => _NotesScreenState();
}
class _NotesScreenState extends State<NotesScreen> {
  List<Note> _notes = [];
  @override
  void initState() {
    super.initState();
    _loadNotes();
  }
  Future<void> _loadNotes() async {
    final notesList = await DatabaseHelper().getNotes();
    setState(() {
      _notes = notesList.map((note) => Note.fromMap(note)).toList();
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Offline Notes')),
      body: ListView.builder(
        itemCount: _notes.length,
        itemBuilder: (context, index) {
          final note = _notes[index];
          return ListTile(
            title: Text(note.title),
            subtitle: Text(note.content),
            trailing: Text(note.createdAt),
          );
        },
      ),
    );
  }
}

📌 Шаг 5: Синхронизация офлайн-данных с API

Для поддержания синхронизации локальных данных с удалённым сервером реализуйте:

Фоновая синхронизация — Периодическая синхронизация офлайн-данных с API.
Разрешение конфликтов — Обработка изменений, когда приложение онлайн.
Обнаружение состояния сети — Используйте connectivity_plus для проверки подключения.

Обнаружение изменений в сети

Установите:

dependencies:
  connectivity_plus: ^5.0.2
import 'package:connectivity_plus/connectivity_plus.dart';

Future<bool> isConnected() async {
  var result = await Connectivity().checkConnectivity();
  return result != ConnectivityResult.none;
}

Синхронизация данных при наличии подключения

Future<void> syncData() async {
  if (await isConnected()) {
    final localNotes = await DatabaseHelper().getNotes();

    for (var note in localNotes) {
      await uploadToServer(note); // Предполагаемый вызов API
    }

    print('Синхронизация с сервером завершена!');
  }
}

📌 Шаг 6: Обработка обновлений базы данных (версионирование)

Если вам нужно изменить структуру базы данных, обновите функцию onUpgrade:

Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
  if (oldVersion < 2) {
    await db.execute("ALTER TABLE notes ADD COLUMN isSynced INTEGER DEFAULT 0");
  }
}

Сохраняет существующие данные пользователя.
Предотвращает потерю данных при обновлении приложения.

🚀 Заключение

SQLite — это мощное решение для создания приложений с приоритетом офлайн-режима в Flutter.
Локальное хранение данных базы для сохранения состояния.
Эффективные операции CRUD для управления данными.
Синхронизация данных офлайн с API при наличии подключения.
Оптимизировано для производительности и версии.

🎯 Что дальше? Оставайтесь на связи за дополнительными лучшими практиками Flutter, оптимизацией производительности и продвинутыми техниками управления состоянием! 🚀🔥

Report Page