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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file.

### Fixed

- **`Pick.saveFile` is source-compatible with file_picker 12.** file_picker 12 made `FilePicker.saveFile`'s `fileName` and `bytes` parameters required and non-null, which broke the analyzer build (`argument_type_not_assignable`) under a fresh `flutter pub get` that resolved the newer file_picker. `Pick.saveFile` keeps its nullable facade surface but now guards both arguments before forwarding, so the call type-checks against file_picker 11 and 12 and a null argument fails with a clear `ArgumentError` instead of an unhelpful type error. Touches `lib/src/facades/pick.dart`.
- **`Crypt` now accepts the `base64:` app key that `key:generate` produces.** `key:generate` writes `APP_KEY=base64:<base64 of 32 random bytes>`, but `EncryptionServiceProvider` required `app.key` to be a raw 32-character string and threw `App Key must be 32 characters for AES-256` on the generated key, so `Crypt.encrypt`/`decrypt` were unusable out of the box. Added `MagicEncrypter.fromAppKey(appKey)` which base64-decodes a `base64:`-prefixed key to its 32 bytes (and still accepts a raw 32-character key); `EncryptionServiceProvider` now binds through it. Touches `lib/src/encryption/magic_encrypter.dart`, `lib/src/encryption/encryption_service_provider.dart`; adds three `fromAppKey` cases to `test/encryption/magic_encrypter_test.dart`.
- **`MagicStatefulView` now calls the controller's `onInit()` lifecycle hook.** `MagicStatefulViewState.initState` listened to the controller and called the VIEW's own `onInit()` hook, but never invoked the CONTROLLER's `onInit()`, despite the documented contract. A controller that bootstraps in `onInit` (initial data load, table creation, subscriptions) silently never ran it when backed by a `MagicStatefulView`, so the screen rendered against uninitialized state (e.g. a query against a table the controller's `onInit` was supposed to create). It now calls `_controller.onInit()` guarded by `MagicController.initialized`, so a `SimpleMagicController` that already initialized in its constructor is not double-initialized and a singleton controller reused across re-mounts initializes exactly once per lifetime. Touches `lib/src/ui/magic_view.dart`; adds `test/ui/magic_view_controller_oninit_test.dart`.
- **Auth no longer warns on every boot of a fresh app.** `AuthServiceProvider.boot()` logged a `userFactory not registered` warning (blaming provider order) whenever no userFactory was set, even for apps with no stored session to restore. It now only warns when a stored session actually exists (`Auth.hasToken()`) but cannot be rebuilt; a fresh app or a logged-out user stays quiet (debug-level). The stored-session check is guarded so a misconfigured Auth (for example, no Vault registered) cannot crash boot from this warning-verbosity path. Touches `lib/src/auth/auth_service_provider.dart`; adds three cases to `test/auth/auth_test.dart`.
Expand Down
11 changes: 9 additions & 2 deletions lib/src/facades/pick.dart
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ class Pick {
///
/// **Parameters:**
/// - [dialogTitle]: Title of the save dialog.
/// - [fileName]: Default file name.
/// - [bytes]: Optional bytes to write to the file.
/// - [fileName]: File name to save as. Required; throws [ArgumentError] if null.
/// - [bytes]: Bytes to write to the file. Required; throws [ArgumentError] if null.
///
/// ```dart
/// final savePath = await Pick.saveFile(
Expand All @@ -363,6 +363,13 @@ class Pick {
String? fileName,
Uint8List? bytes,
}) async {
// file_picker 12 made fileName and bytes required and non-null on
// FilePicker.saveFile; guard here so the nullable facade surface stays
// source-compatible across file_picker 11 and 12 and fails with a clear
// error instead of an unhelpful type error.
if (fileName == null || bytes == null) {
throw ArgumentError('Pick.saveFile requires both fileName and bytes.');
Comment thread
anilcancakir marked this conversation as resolved.
}
return FilePicker.saveFile(
dialogTitle: dialogTitle,
fileName: fileName,
Expand Down
23 changes: 23 additions & 0 deletions test/facades/pick_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'dart:typed_data';

import 'package:flutter_test/flutter_test.dart';
import 'package:magic/src/facades/pick.dart';

void main() {
group('Pick.saveFile null guard', () {
test('throws ArgumentError when fileName is null', () {
expect(
() => Pick.saveFile(bytes: Uint8List.fromList([1, 2, 3])),
throwsArgumentError,
);
});

test('throws ArgumentError when bytes is null', () {
expect(() => Pick.saveFile(fileName: 'report.pdf'), throwsArgumentError);
});

test('throws ArgumentError when both are null', () {
expect(() => Pick.saveFile(), throwsArgumentError);
});
});
}
Loading