You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm trying to abstract my Hive box opening logic in a Flutter app using a custom helper function and an abstract class, but I'm running into issues when passing types. The non-abstracted, verbose code works, but my abstracted approach fails. How can I properly pass types to my abstracted helper function?
In all other places, I have been explicitly calling Hive boxes with their types, such as Hive.openBox(), and this works fine. However, I'm trying to refactor my code to be more dynamic and avoid repetition.
Why am I facing this issue here? I have tried numerous variations of type checking and different approaches, but nothing seems to work. Any guidance on how to achieve this abstraction correctly would be greatly appreciated.
I suspect this is a dart framework issue but im not experienced enough to make that judgement call. So...
I have created a minimum reproducible example with two simple custom classes to feed hive and its causing the exact same issues as my massive project:
abstract class HiveDB {
static Future<Box<dynamic>> secureEncryptedBox(
dynamic object, {
required String boxName,
}) async {
const secureStorage = FlutterSecureStorage();
// if key not exists return null
final encryptionKeyString = await secureStorage.read(key: 'key');
if (encryptionKeyString == null) {
final key = Hive.generateSecureKey();
await secureStorage.write(
key: 'key',
value: base64UrlEncode(key),
);
}
final key = await secureStorage.read(key: 'key');
final encryptionKeyUint8List = base64Url.decode(key!);
// Check if the box is already open, and open it with the correct type
if (Hive.isBoxOpen(boxName)) {
return TypeFilter.openHiveBoxExplicitly(object, boxName, encryptionKeyUint8List);
}
return TypeFilter.openHiveBoxExplicitlyAlternate(object, boxName, encryptionKeyUint8List);
}
}
void main() async {
await Hive.initFlutter();
Hive.registerAdapter(UserAdapter());
Hive.registerAdapter(ItemAdapter());
await Hive.openBox('secureStorage'); // This box is used for secure keys
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<void> _saveData() async {
final user = User(name: 'John Doe', age: 30);
final item = Item(description: 'Sample Item', price: 19.99);
Box<dynamic> userBox = await HiveDB.secureEncryptedBox(user, boxName: 'user_box');
Box<dynamic> itemBox = await HiveDB.secureEncryptedBox(item, boxName: 'item_box');
userBox.add(user);
itemBox.add(item);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hive Example'),
),
body: Center(
child: ElevatedButton(
onPressed: _saveData,
child: Text('Save Data'),
),
),
);
}
}
// Abstract class TypeFilter
abstract class TypeFilter {
static Future<Box<dynamic>> openHiveBoxExplicitly<T>(
T object,
String boxName,
List<int> encryptionKey,
) async {
Box<dynamic> encryptedBox;
if (object == User) {
encryptedBox = await Hive.openBox<User>(
boxName,
encryptionCipher: HiveAesCipher(encryptionKey),
);
} else if (object == Item) {
encryptedBox = await Hive.openBox<Item>(
boxName,
encryptionCipher: HiveAesCipher(encryptionKey),
);
} else {
throw Exception("Unknown object type");
}
return encryptedBox;
}
static Future<Box<dynamic>> openHiveBoxExplicitlyAlternate(
Type object,
String boxName,
List<int> encryptionKey,
) async {
late Box<dynamic> encryptedBox;
switch (object) {
case User:
encryptedBox = await Hive.openBox<User>(
boxName,
encryptionCipher: HiveAesCipher(encryptionKey),
);
break;
case Item:
encryptedBox = await Hive.openBox<Item>(
boxName,
encryptionCipher: HiveAesCipher(encryptionKey),
);
break;
default:
throw Exception("Unknown object type");
}
return encryptedBox;
}
}
Error:
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: type 'User' is not a subtype of type 'Type'
#0 HiveDB.secureEncryptedBox
main.dart:30
<asynchronous suspension>
#1 _MyHomePageState._saveData
main.dart:61
<asynchronous suspension>
WORKING NON-ABSTRACTED HIVE DB CODE:
note - in my actual project i have a massive if and switch since I've got over 15 classes Im trying to abstract into this method. Also, for info, in my main project, i need both the if statements with "is" operator as well as the switch with object switching. Why I don't know, thats just what turned out to work. I tried using reflectable with no luck, thought that could solve this. It seems in my main project the object switch works when initializing the app and when saving in a different config the "is" operator works since only when saving or performing actual actions am i passing an actual object. Nonetheless, same issue here so I'm doing something wrong or theres an issue somewhere...
abstract class HiveDB {
static Future<Box<dynamic>> secureEncryptedBox(
dynamic object, {
required String boxName,
}) async {
const secureStorage = FlutterSecureStorage();
// if key not exists return null
final encryptionKeyString = await secureStorage.read(key: 'key');
if (encryptionKeyString == null) {
final key = Hive.generateSecureKey();
await secureStorage.write(
key: 'key',
value: base64UrlEncode(key),
);
}
final key = await secureStorage.read(key: 'key');
final encryptionKeyUint8List = base64Url.decode(key!);
late Box<dynamic> encryptedBox;
// Check if the box is already open, and open it with the correct type
if (Hive.isBoxOpen(boxName)) {
if (object is User) {
encryptedBox = await Hive.openBox<User>(
boxName,
encryptionCipher: HiveAesCipher(encryptionKeyUint8List),
);
} else if (object is Item) {
encryptedBox = await Hive.openBox<Item>(
boxName,
encryptionCipher: HiveAesCipher(encryptionKeyUint8List),
);
}
} else {
// Open the box with the correct type if not already open
switch (object.runtimeType) {
case User:
encryptedBox = await Hive.openBox<User>(
boxName,
encryptionCipher: HiveAesCipher(encryptionKeyUint8List),
);
break;
case Item:
encryptedBox = await Hive.openBox<Item>(
boxName,
encryptionCipher: HiveAesCipher(encryptionKeyUint8List),
);
break;
default:
throw Exception("Unknown object type");
}
}
log(encryptedBox.runtimeType.toString());
return encryptedBox;
}
}
CLASSES FOR MIN EXAMPLE:
import 'package:hive/hive.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
part 'item.g.dart';
@HiveType(typeId: 1)
class Item {
@HiveField(0)
final String description;
@HiveField(1)
final double price;
Item({required this.description, required this.price});
}
// user.dart
import 'package:hive/hive.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
part 'user.g.dart';
@HiveType(typeId: 0)
class User {
@HiveField(0)
final String name;
@HiveField(1)
final int age;
User({required this.name, required this.age});
}
The text was updated successfully, but these errors were encountered:
I'm trying to abstract my Hive box opening logic in a Flutter app using a custom helper function and an abstract class, but I'm running into issues when passing types. The non-abstracted, verbose code works, but my abstracted approach fails. How can I properly pass types to my abstracted helper function?
In all other places, I have been explicitly calling Hive boxes with their types, such as Hive.openBox(), and this works fine. However, I'm trying to refactor my code to be more dynamic and avoid repetition.
Why am I facing this issue here? I have tried numerous variations of type checking and different approaches, but nothing seems to work. Any guidance on how to achieve this abstraction correctly would be greatly appreciated.
I suspect this is a dart framework issue but im not experienced enough to make that judgement call. So...
I have created a minimum reproducible example with two simple custom classes to feed hive and its causing the exact same issues as my massive project:
Error:
WORKING NON-ABSTRACTED HIVE DB CODE:
note - in my actual project i have a massive if and switch since I've got over 15 classes Im trying to abstract into this method. Also, for info, in my main project, i need both the if statements with "is" operator as well as the switch with object switching. Why I don't know, thats just what turned out to work. I tried using reflectable with no luck, thought that could solve this. It seems in my main project the object switch works when initializing the app and when saving in a different config the "is" operator works since only when saving or performing actual actions am i passing an actual object. Nonetheless, same issue here so I'm doing something wrong or theres an issue somewhere...
CLASSES FOR MIN EXAMPLE:
The text was updated successfully, but these errors were encountered: