Skip to content

Conversation

@wellgenio
Copy link

@wellgenio wellgenio commented Jul 12, 2025

📄 Description

Essse PR é uma proposta inicial para criação de um "retrofit" para armazenamento local.
Visando padrinizar qualquer acesso a fonte de dados usando o retrofit.

É necessario criar um adapter implementando a interface ILocalStorage para o seu local storage favorito.

🔄 Changes Made

  • Foi criado um setup para preferences em vaden_class_scanner
  • Foi adicionado novos tipos em vaden_core

✅ Checklist

  • Tests have been added or updated.
  • Documentation has been updated (if necessary).
  • Code review completed.

⚠ Ainda não foi feito testes e etc. Esse PR foi aberto a titulo de discutir a solução e sua implementação.

Exemplo de uso:

Adapter:

class SharedPreferencesAdapter implements ILocalStorage {
  final Future<SharedPreferences> _sharedPreferences;

  SharedPreferencesAdapter(this._sharedPreferences);

  @override
  Future<bool?> getBool(String key) async {
    final prefs = await _sharedPreferences;
    return prefs.getBool(key);
  }

  @override
  Future<double?> getDouble(String key) async {
    final prefs = await _sharedPreferences;
    return prefs.getDouble(key);
  }

  @override
  Future<int?> getInt(String key) async {
    final prefs = await _sharedPreferences;
    return prefs.getInt(key);
  }

  @override
  Future<String?> getString(String key) async {
    final prefs = await _sharedPreferences;
    return prefs.getString(key);
  }

  @override
  Future<bool> setBool(String key, bool value) async {
    final prefs = await _sharedPreferences;
    return prefs.setBool(key, value);
  }

  @override
  Future<bool> setDouble(String key, double value) async {
    final prefs = await _sharedPreferences;
    return prefs.setDouble(key, value);
  }

  @override
  Future<bool> setInt(String key, int value) async {
    final prefs = await _sharedPreferences;
    return prefs.setInt(key, value);
  }

  @override
  Future<bool> setString(String key, String value) async {
    final prefs = await _sharedPreferences;
    return prefs.setString(key, value);
  }
}

Configuration

@Configuration()
class AppConfiguration {
  @Bean()
  ILocalStorage preferencesFactory() {
    final sharedPreferences = SharedPreferences.getInstance();
    return SharedPreferencesAdapter(sharedPreferences);
  }
}

LocalStorage (Retrofit)

@LocalStorage()
abstract class ProductLocal {
  @StorageKey('products')
  @StorageObject()
  Future<List<ProductModel>> setProducts(List<ProductModel> products);

  @StorageKey('products')
  @StorageObject()
  Future<List<ProductModel>?> getProducts();

  @StorageKey('product')
  @StorageObject()
  Future<ProductModel> setProduct(ProductModel product);

  @StorageKey('product')
  @StorageObject()
  Future<ProductModel?> getProduct();

  @StorageKey('dark_mode')
  Future<void> setDarkMode(bool enabled);

  @StorageKey('dark_mode')
  Future<bool?> getDarkMode();
}

class gerada:

class _ProductLocal implements ProductLocal {
  final DSON dson;
  _ProductLocal(this.dson);
  @override
  Future<List<ProductModel>> setProducts(List<ProductModel> products) async {
    final prefs = _injector.tryGet<ILocalStorage>();

    if (prefs == null) {
      throw Exception("ILocalStorage not found");
    }

    await prefs.setString(
      'products',
      json.encode(dson.toJsonList<ProductModel>(products)),
    );
    return products;
  }

  @override
  Future<List<ProductModel>?> getProducts() async {
    final prefs = _injector.tryGet<ILocalStorage>();

    if (prefs == null) {
      throw Exception("ILocalStorage not found");
    }

    final result = await prefs.getString('products');
    if (result == null || result.isEmpty) {
      return null;
    }
    return dson.fromJsonList<ProductModel>(json.decode(result));
  }

  @override
  Future<ProductModel> setProduct(ProductModel product) async {
    final prefs = _injector.tryGet<ILocalStorage>();

    if (prefs == null) {
      throw Exception("ILocalStorage not found");
    }

    await prefs.setString(
      'product',
      json.encode(dson.toJson<ProductModel>(product)),
    );
    return product;
  }

  @override
  Future<ProductModel?> getProduct() async {
    final prefs = _injector.tryGet<ILocalStorage>();

    if (prefs == null) {
      throw Exception("ILocalStorage not found");
    }

    final result = await prefs.getString('product');
    if (result == null || result.isEmpty) {
      return null;
    }
    return dson.fromJson<ProductModel?>(json.decode(result));
  }

  @override
  Future<void> setDarkMode(bool enabled) async {
    final prefs = _injector.tryGet<ILocalStorage>();

    if (prefs == null) {
      throw Exception("ILocalStorage not found");
    }

    await prefs.setBool('dark_mode', enabled);
  }

  @override
  Future<bool?> getDarkMode() async {
    final prefs = _injector.tryGet<ILocalStorage>();

    if (prefs == null) {
      throw Exception("ILocalStorage not found");
    }

    return await prefs.getBool('dark_mode');
  }
}

@wellgenio wellgenio requested a review from jacobaraujo7 July 12, 2025 13:41
@wellgenio
Copy link
Author

Daria para evoluir para trabalhar com valores default em getters, para não haver nullables...

@jacobaraujo7
Copy link
Contributor

Temos que achar uma maneira de não acoplar o shared_preferences

@wellgenio
Copy link
Author

@jacobaraujo7 isso faz mais sentido pra você?

@jacobaraujo7
Copy link
Contributor

@tganzerli Pode analisar isso por favor?

@jacobaraujo7 jacobaraujo7 requested a review from tganzerli July 22, 2025 11:44
@tganzerli
Copy link
Contributor

Eu acho que esta muito fora do padrão aplicado no Vaden.

  • Nuca forçamos o uso de um Adapter, até o momento todas as implicações padrão é fazer uma classe de configuration para LocalStorage e nela o único local onde os pacotes externos são observados.
  • Sobre as classes a notadas com @LocalStorage(), o que voce acha de ela ser um pouco mais similar aos @Controller()? Onde cada classe é responsável por um registro especifico. O nome da Key poderia ficar na própria anotação LocalStorage, e ao invés de @StorageKey('products') poderíamos ter anotação que identificaria qual é o tipo da função ex: @Set, @Get, etc... No casso da @StorageObject() poderíamos tentar observar se a classe é um DTO diretamente do gerador, para não precisar atribuir esta anotação.
  • Um ponto para avaliar: se por acaso a pessoa queira ter mais de um tipo de LocalStorage. Como faríamos?

@wellgenio
Copy link
Author

wellgenio commented Jul 24, 2025

@tganzerli acho que faz bastante sentido... apenas para ilustrar, a API de uso ficaria assim:

@LocalStorage(key: 'products')
class ProductLocal {
  @Set()
  Future<List<ProductModel>> setProducts(List<ProductModel> products);

  @Get(defaultValue: []) /// sem o `defaultValue` caso não exista retorna null
  Future<List<ProductModel>> getProducts();

  @Set()
  Future<ProductModel> setProduct(ProductModel product);

  @Get()
  Future<ProductModel?> getProduct();
}

@LocalStorage(key: 'theme')
class ThemeLocal {
  @Set()
  Future<void> setDarkMode(bool enabled);

  @Get(defaultValue: false)
  Future<bool> getDarkMode();
}

Que me agrada muito!
Agora creio que o principal ponto a discutir é a adoção ou não do adapter... e/ou uma outra forma de fazer isso sem acoplar o vaden a uma lib de local storage e a possibilidade de multiplos storages.

@jacobaraujo7 teria alguma ideia?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants