Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions hive_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## 2.4.0

- Adds `MaterialUiTimeOfDayAdapter` for the `TimeOfDay` class from `package:material_ui`
- Adds `useMaterialUi` flag to `initFlutter` to register `MaterialUiTimeOfDayAdapter` instead of `TimeOfDayAdapter`
- Updates minimum Flutter version to 3.44.0
- `Hive.initFlutter` uses absolute paths as-is without calling `getApplicationDocumentsDirectory` (by [@jheld](https://github.com/jheld) in [#293](https://github.com/IO-Design-Team/hive_ce/pull/293))
- `IsolatedHive.initFlutter` also uses absolute paths as-is

## 2.3.4

Expand Down
61 changes: 52 additions & 9 deletions hive_flutter/lib/src/adapters/time_adapter.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter/material.dart' as flutter show TimeOfDay;
import 'package:hive_ce/hive_ce.dart';
import 'package:material_ui/material_ui.dart' as material_ui show TimeOfDay;

/// TODO: Document this!
class TimeOfDayAdapter extends TypeAdapter<TimeOfDay> {
abstract class _TimeOfDayAdapter<T> extends TypeAdapter<T> {
static const _defaultTypeId = 201;

/// Constructor
const TimeOfDayAdapter({int? typeId}) : typeId = typeId ?? _defaultTypeId;
const _TimeOfDayAdapter({int? typeId}) : typeId = typeId ?? _defaultTypeId;

@override
final int typeId;

T _create(int hour, int minute);

int _hour(T obj);

int _minute(T obj);

@override
TimeOfDay read(BinaryReader reader) {
T read(BinaryReader reader) {
final totalMinutes = reader.readInt();
return TimeOfDay(hour: totalMinutes ~/ 60, minute: totalMinutes % 60);
return _create(totalMinutes ~/ 60, totalMinutes % 60);
}

@override
void write(BinaryWriter writer, TimeOfDay obj) {
writer.writeInt(obj.hour * 60 + obj.minute);
void write(BinaryWriter writer, T obj) {
writer.writeInt(_hour(obj) * 60 + _minute(obj));
}
}

/// A [TypeAdapter] for the `TimeOfDay` class from `package:flutter/material.dart`
///
/// Uses the same binary format and default type id as
/// [MaterialUiTimeOfDayAdapter]
class TimeOfDayAdapter extends _TimeOfDayAdapter<flutter.TimeOfDay> {
/// Constructor
const TimeOfDayAdapter({super.typeId});

@override
flutter.TimeOfDay _create(int hour, int minute) =>
flutter.TimeOfDay(hour: hour, minute: minute);

@override
int _hour(flutter.TimeOfDay obj) => obj.hour;

@override
int _minute(flutter.TimeOfDay obj) => obj.minute;
}

/// A [TypeAdapter] for the `TimeOfDay` class from `package:material_ui`
///
/// Uses the same binary format and default type id as [TimeOfDayAdapter]
class MaterialUiTimeOfDayAdapter
extends _TimeOfDayAdapter<material_ui.TimeOfDay> {
/// Constructor
const MaterialUiTimeOfDayAdapter({super.typeId});

@override
material_ui.TimeOfDay _create(int hour, int minute) =>
material_ui.TimeOfDay(hour: hour, minute: minute);

@override
int _hour(material_ui.TimeOfDay obj) => obj.hour;

@override
int _minute(material_ui.TimeOfDay obj) => obj.minute;
}
41 changes: 12 additions & 29 deletions hive_flutter/lib/src/hive_extensions.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:hive_ce_flutter/adapters.dart';
import 'package:hive_ce_flutter/src/type_registry_extensions.dart';
import 'package:hive_ce_flutter/src/wrapper/path_provider.dart';
import 'package:hive_ce_flutter/src/wrapper/path.dart' as path_helper;

/// Flutter extensions for Hive.
extension HiveX on HiveInterface {
Expand All @@ -15,38 +13,23 @@ extension HiveX on HiveInterface {
/// Also registers the flutter type adapters
/// - [colorAdapterTypeId] - The type id for the color adapter (default: 200)
/// - [timeOfDayAdapterTypeId] - The type id for the time of day adapter (default: 201)
///
/// If [useMaterialUi] is true, [MaterialUiTimeOfDayAdapter] is registered
/// instead of [TimeOfDayAdapter]
Future<void> initFlutter([
String? dir,
HiveStorageBackendPreference backendPreference =
HiveStorageBackendPreference.native,
int? colorAdapterTypeId,
int? timeOfDayAdapterTypeId,
bool useMaterialUi = false,
]) async {
WidgetsFlutterBinding.ensureInitialized();

String? path;
if (!kIsWeb) {
// Root-relative Windows paths still take the drive from the app directory
if (dir != null &&
path_helper.isAbsolute(dir) &&
!path_helper.isRootRelative(dir)) {
path = dir;
} else {
final appDir = await getApplicationDocumentsDirectory();
path = path_helper.join(appDir.path, dir);
}
}

init(path, backendPreference: backendPreference);

final colorAdapter = ColorAdapter(typeId: colorAdapterTypeId);
if (!isAdapterRegistered(colorAdapter.typeId)) {
registerAdapter(colorAdapter);
}

final timeOfDayAdapter = TimeOfDayAdapter(typeId: timeOfDayAdapterTypeId);
if (!isAdapterRegistered(timeOfDayAdapter.typeId)) {
registerAdapter(timeOfDayAdapter);
}
await initFlutterCommon(
dir: dir,
initHive: (path) => init(path, backendPreference: backendPreference),
colorAdapterTypeId: colorAdapterTypeId,
timeOfDayAdapterTypeId: timeOfDayAdapterTypeId,
useMaterialUi: useMaterialUi,
);
}
}
43 changes: 16 additions & 27 deletions hive_flutter/lib/src/isolated_hive_extensions.dart
Original file line number Diff line number Diff line change
@@ -1,46 +1,35 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:hive_ce_flutter/adapters.dart'
hide IsolatedHive, IsolateNameServer;
import 'package:hive_ce_flutter/src/isolate/isolate_name_server.dart';
import 'package:hive_ce_flutter/src/type_registry_extensions.dart';
import 'package:hive_ce_flutter/src/wrapper/path_provider.dart';
import 'package:hive_ce_flutter/src/wrapper/path.dart' as path_helper;

/// Flutter extensions for [IsolatedHiveInterface]
extension IsolatedHiveX on IsolatedHiveInterface {
/// Initializes [IsolatedHive] with the path from
/// [getApplicationDocumentsDirectory] and the Flutter [IsolateNameServer]
///
/// You can provide a [subDirectory] where the boxes should be stored
/// You can provide a [subDirectory] where the boxes should be stored.
/// Relative paths are resolved against [getApplicationDocumentsDirectory].
/// Absolute paths are used as-is.
///
/// Also registers the flutter type adapters
///
/// If [useMaterialUi] is true, [MaterialUiTimeOfDayAdapter] is registered
/// instead of [TimeOfDayAdapter]
Future<void> initFlutter({
String? subDirectory,
int? colorAdapterTypeId,
int? timeOfDayAdapterTypeId,
bool useMaterialUi = false,
}) async {
try {
WidgetsFlutterBinding.ensureInitialized();
} catch (_) {
// This will fail if the Flutter engine is not available
}

String? path;
if (!kIsWeb) {
final appDir = await getApplicationDocumentsDirectory();
path = path_helper.join(appDir.path, subDirectory);
}

await init(path, isolateNameServer: const IsolateNameServer());

final colorAdapter = ColorAdapter(typeId: colorAdapterTypeId);
if (!isAdapterRegistered(colorAdapter.typeId)) {
registerAdapter(colorAdapter);
}

final timeOfDayAdapter = TimeOfDayAdapter(typeId: timeOfDayAdapterTypeId);
if (!isAdapterRegistered(timeOfDayAdapter.typeId)) {
registerAdapter(timeOfDayAdapter);
}
await initFlutterCommon(
dir: subDirectory,
initHive: (path) =>
init(path, isolateNameServer: const IsolateNameServer()),
colorAdapterTypeId: colorAdapterTypeId,
timeOfDayAdapterTypeId: timeOfDayAdapterTypeId,
useMaterialUi: useMaterialUi,
);
}
}
54 changes: 54 additions & 0 deletions hive_flutter/lib/src/type_registry_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:hive_ce_flutter/adapters.dart';
import 'package:hive_ce_flutter/src/wrapper/path_provider.dart';
import 'package:hive_ce_flutter/src/wrapper/path.dart' as path_helper;

/// Flutter extensions for [TypeRegistry]
extension TypeRegistryX on TypeRegistry {
/// Common Flutter initialization code
Future<void> initFlutterCommon({
String? dir,
required FutureOr<void> Function(String? path) initHive,
int? colorAdapterTypeId,
int? timeOfDayAdapterTypeId,
bool useMaterialUi = false,
}) async {
try {
WidgetsFlutterBinding.ensureInitialized();
} catch (_) {
// This will fail if the Flutter engine is not available
}

String? path;
if (!kIsWeb) {
// Root-relative Windows paths still take the drive from the app directory
if (dir != null &&
path_helper.isAbsolute(dir) &&
!path_helper.isRootRelative(dir)) {
path = dir;
} else {
final appDir = await getApplicationDocumentsDirectory();
path = path_helper.join(appDir.path, dir);
}
}

await initHive(path);

_registerAdapter(ColorAdapter(typeId: colorAdapterTypeId));
if (useMaterialUi) {
_registerAdapter(
MaterialUiTimeOfDayAdapter(typeId: timeOfDayAdapterTypeId),
);
} else {
_registerAdapter(TimeOfDayAdapter(typeId: timeOfDayAdapterTypeId));
}
Comment thread
Rexios80 marked this conversation as resolved.
}

void _registerAdapter<T>(TypeAdapter<T> adapter) {
if (isAdapterRegistered(adapter.typeId)) return;
registerAdapter(adapter);
}
}
4 changes: 3 additions & 1 deletion hive_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ homepage: https://github.com/IO-Design-Team/hive_ce/tree/main/hive_flutter

environment:
sdk: ^3.4.0
flutter: ">=3.27.0"
flutter: ">=3.44.0"

dependencies:
flutter:
sdk: flutter

hive_ce: ^2.16.0
material_ui: ^1.0.0
path_provider: ^2.0.10
path: ^1.8.2

dev_dependencies:
test: ^1.21.1
path_provider_platform_interface: ^2.0.0
rexios_lints: ^19.0.0
mockito: ^5.4.1
build_runner: ^2.5.4
33 changes: 30 additions & 3 deletions hive_flutter/test/adapters/time_adapter_test.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import 'package:flutter/material.dart' show TimeOfDay;
import 'package:flutter/material.dart' as flutter show TimeOfDay;
import 'package:hive_ce/src/binary/binary_reader_impl.dart';
import 'package:hive_ce/src/binary/binary_writer_impl.dart';
import 'package:hive_ce/src/registry/type_registry_impl.dart';
import 'package:hive_ce_flutter/adapters.dart';
import 'package:material_ui/material_ui.dart' as material_ui show TimeOfDay;
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';

import '../mocks.dart';

void main() {
group('TimeOfDayAdapter', () {
late TimeOfDay time;
late flutter.TimeOfDay time;
late int totalMinutes;

setUp(() {
time = const TimeOfDay(hour: 8, minute: 0);
time = const flutter.TimeOfDay(hour: 8, minute: 0);
totalMinutes = time.hour * 60 + time.minute;
});

Expand All @@ -31,4 +35,27 @@ void main() {
verify(binaryWriter.writeInt(totalMinutes));
});
});

group('TimeOfDayAdapter and MaterialUiTimeOfDayAdapter compatibility', () {
final flutterRegistry = TypeRegistryImpl()
..registerAdapter(const TimeOfDayAdapter());
final materialUiRegistry = TypeRegistryImpl()
..registerAdapter(const MaterialUiTimeOfDayAdapter());

test('flutter to material_ui', () {
const time = flutter.TimeOfDay(hour: 13, minute: 37);
final writer = BinaryWriterImpl(flutterRegistry)..write(time);

final reader = BinaryReaderImpl(writer.toBytes(), materialUiRegistry);
expect(reader.read(), const material_ui.TimeOfDay(hour: 13, minute: 37));
});

test('material_ui to flutter', () {
const time = material_ui.TimeOfDay(hour: 13, minute: 37);
final writer = BinaryWriterImpl(materialUiRegistry)..write(time);

final reader = BinaryReaderImpl(writer.toBytes(), flutterRegistry);
expect(reader.read(), const flutter.TimeOfDay(hour: 13, minute: 37));
});
});
}
53 changes: 53 additions & 0 deletions hive_flutter/test/type_registry_extensions_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'package:hive_ce/src/registry/type_registry_impl.dart';
import 'package:hive_ce_flutter/src/type_registry_extensions.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'package:test/test.dart';

final _documentsPath = p.absolute('documents');

class _FakePathProviderPlatform extends PathProviderPlatform {
var documentsPathCalls = 0;

@override
Future<String?> getApplicationDocumentsPath() async {
documentsPathCalls++;
return _documentsPath;
}
}

void main() {
late _FakePathProviderPlatform pathProvider;

setUp(() {
pathProvider = _FakePathProviderPlatform();
PathProviderPlatform.instance = pathProvider;
});

Future<String?> initPath(String? dir) async {
String? initPath;
await TypeRegistryImpl().initFlutterCommon(
dir: dir,
initHive: (path) => initPath = path,
);
return initPath;
}

group('TypeRegistryX.initFlutterCommon()', () {
test('uses the documents directory when dir is null', () async {
expect(await initPath(null), _documentsPath);
expect(pathProvider.documentsPathCalls, 1);
});

test('resolves a relative dir against the documents directory', () async {
expect(await initPath('boxes'), p.join(_documentsPath, 'boxes'));
expect(pathProvider.documentsPathCalls, 1);
});

test('uses an absolute dir as-is', () async {
final dir = p.absolute('boxes');
expect(await initPath(dir), dir);
expect(pathProvider.documentsPathCalls, 0);
});
});
}
Loading