Chuyển đến nội dung chính

第 10 課:本地儲存和離線優先

SharedPreferences、Hive、Isar 資料庫。 SQL 的漂移(停泊)。離線優先架構,同步策略。快取管理。

💻 程式設計 — 第 10 課 第 10 課:本地儲存和離線優先

Flutter 和 Dart:從基礎到高級

第 3 部分:網路與數據

亞洲開發網

1. 共享首選項

class SettingsService {
  Future<void> setThemeMode(ThemeMode mode) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setInt('theme_mode', mode.index);
  }

  Future<ThemeMode> getThemeMode() async {
    final prefs = await SharedPreferences.getInstance();
    final index = prefs.getInt('theme_mode') ?? 0;
    return ThemeMode.values[index];
  }
}

2. Hive——NoSQL本地資料庫

@HiveType(typeId: 0)
class CachedProduct extends HiveObject {
  @HiveField(0)
  final String id;
  @HiveField(1)
  final String name;
  @HiveField(2)
  final double price;
  @HiveField(3)
  final DateTime cachedAt;

  CachedProduct({required this.id, required this.name, required this.price, required this.cachedAt});
}

// Sử dụng
Future<void> main() async {
  await Hive.initFlutter();
  Hive.registerAdapter(CachedProductAdapter());
  await Hive.openBox<CachedProduct>('products');
  runApp(const MyApp());
}

class ProductCache {
  final Box<CachedProduct> _box = Hive.box('products');

  List<CachedProduct> getAll() => _box.values.toList();

  Future<void> saveAll(List<CachedProduct> products) async {
    await _box.clear();
    for (final p in products) {
      await _box.put(p.id, p);
    }
  }

  bool isStale(Duration maxAge) {
    if (_box.isEmpty) return true;
    final oldest = _box.values.first.cachedAt;
    return DateTime.now().difference(oldest) > maxAge;
  }
}

3. Drift-類型安全的 SQL

@DriftDatabase(tables: [Products, Orders])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(_openConnection());

  @override
  int get schemaVersion => 1;

  Future<List<Product>> getAllProducts() => select(products).get();

  Stream<List<Product>> watchProducts() => select(products).watch();

  Future<void> insertProduct(ProductsCompanion product) =>
      into(products).insert(product);
}

class Products extends Table {
  TextColumn get id => text()();
  TextColumn get name => text().withLength(min: 1, max: 255)();
  RealColumn get price => real()();
  DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();

  @override
  Set<Column> get primaryKey => {id};
}

4. 離線優先架構

class OfflineFirstRepository {
  final ApiClient _api;
  final ProductCache _cache;
  final ConnectivityService _connectivity;

  OfflineFirstRepository(this._api, this._cache, this._connectivity);

  Future<List<Product>> getProducts() async {
    if (await _connectivity.isOnline) {
      try {
        final products = await _api.getProducts();
        await _cache.saveAll(products);
        return products;
      } catch (_) {
        return _cache.getAll(); // Fallback to cache
      }
    }
    return _cache.getAll(); // Offline — trả cache
  }
}

下一篇: Firebase 集成。