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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ jobs:
fi
apt-get update --allow-releaseinfo-change
apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev libasound2-dev libunwind-dev
apt-get install -y pkg-config libsecret-1-0 libsecret-1-dev
apt-get install -y \
libmpv-dev mpv \
libgstreamer1.0-dev \
Expand Down Expand Up @@ -718,6 +719,7 @@ jobs:
flet-map
flet-permission-handler
flet-rive
flet-secure-storage
flet-video
flet-webview
)
Expand Down Expand Up @@ -835,6 +837,7 @@ jobs:
flet_map \
flet_permission_handler \
flet_rive \
flet_secure_storage \
flet_video \
flet_webview; do
uv publish dist/**/${pkg}-*
Expand Down
4 changes: 3 additions & 1 deletion client/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<!-- Google TV -->
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />

<application
android:label="flet_client"
android:name="${applicationName}"
Expand Down
9 changes: 5 additions & 4 deletions client/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import 'package:flet_lottie/flet_lottie.dart' as flet_lottie;
import 'package:flet_map/flet_map.dart' as flet_map;
import 'package:flet_permission_handler/flet_permission_handler.dart'
as flet_permission_handler;
import 'package:flet_secure_storage/flet_secure_storage.dart'
as flet_secure_storage;
// --FAT_CLIENT_START--
// --RIVE_IMPORT_START--
import 'package:flet_rive/flet_rive.dart' as flet_rive;
Expand Down Expand Up @@ -48,6 +50,7 @@ void main([List<String>? args]) async {
flet_flashlight.Extension(),
flet_datatable2.Extension(),
flet_charts.Extension(),
flet_secure_storage.Extension(),
// --FAT_CLIENT_START--
// --RIVE_EXTENSION_START--
flet_rive.Extension(),
Expand Down Expand Up @@ -90,10 +93,8 @@ void main([List<String>? args]) async {
assetsDir = args[2];
debugPrint("Args contain a path assets directory: $assetsDir}");
}
} else if (!kDebugMode &&
(Platform.isWindows || Platform.isMacOS || Platform.isLinux)) {
throw Exception(
'In desktop mode Flet app URL must be provided as a first argument.');
} else if (!kDebugMode && (Platform.isWindows || Platform.isMacOS || Platform.isLinux)) {
throw Exception('In desktop mode Flet app URL must be provided as a first argument.');
}
}

Expand Down
9 changes: 6 additions & 3 deletions client/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ dependencies:
flet_audio_recorder:
path: ../sdk/python/packages/flet-audio-recorder/src/flutter/flet_audio_recorder

flet_charts:
path: ../sdk/python/packages/flet-charts/src/flutter/flet_charts

flet_datatable2:
path: ../sdk/python/packages/flet-datatable2/src/flutter/flet_datatable2

Expand All @@ -70,12 +73,12 @@ dependencies:
flet_permission_handler:
path: ../sdk/python/packages/flet-permission-handler/src/flutter/flet_permission_handler

flet_secure_storage:
path: ../sdk/python/packages/flet-secure-storage/src/flutter/flet_secure_storage

flet_webview:
path: ../sdk/python/packages/flet-webview/src/flutter/flet_webview

flet_charts:
path: ../sdk/python/packages/flet-charts/src/flutter/flet_charts

cupertino_icons: ^1.0.6
wakelock_plus: ^1.4.0
package_info_plus: ^9.0.0
Expand Down
36 changes: 20 additions & 16 deletions packages/flet/lib/src/models/control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -371,35 +371,39 @@ class Control extends ChangeNotifier {
var node = getPatchTarget(op[1]);
var index = op[2];
var value = op[3];
if (node.obj is! List) {
throw Exception("Add operation can be applied to lists only: $op");
}
node.obj
.insert(index, _transformIfControl(value, node.control, backend));
if (shouldNotify) {
node.control.notify();
if (node.obj is Map) {
node.obj[index] = _transformIfControl(value, node.control, backend);
} else if (node.obj is List) {
node.obj.insert(index, _transformIfControl(value, node.control, backend));
} else {
throw Exception("Add operation can be applied to lists or maps: $op");
}
if (shouldNotify) node.control.notify();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What did the changes in control.dart fix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7576457/flet-secure-storage#1

I was getting an exception from line 378 when calling the .readAll method, which returns a map. When I deleted something from storage, I got the same error. This means that at runtime I'm working with a map, while control.dart expects to work only with a list.

These changes make it possible to work with a map at runtime, which is what FlutterSecureStorage actually uses.

} else if (opType == OperationType.remove) {
// REMOVE
var node = getPatchTarget(op[1]);
var index = op[2];
if (node.obj is! List) {
throw Exception("Remove operation can be applied to lists only: $op");
}
node.obj.removeAt(index);
if (shouldNotify) {
node.control.notify();
if (node.obj is List) {
node.obj.removeAt(index);
} else if (node.obj is Map) {
node.obj.remove(index);
} else {
throw Exception("Remove operation can be applied to lists or maps: $op");
}
if (shouldNotify) node.control.notify();
} else if (opType == OperationType.move) {
// MOVE
var fromNode = getPatchTarget(op[1]);
var fromIndex = op[2];
var toNode = getPatchTarget(op[3]);
var toIndex = op[4];
if (fromNode.obj is! List || toNode.obj is! List) {
throw Exception("Move operation can be applied to lists only: $op");
if (fromNode.obj is List && toNode.obj is List) {
toNode.obj.insert(toIndex, fromNode.obj.removeAt(fromIndex));
} else if (fromNode.obj is Map && toNode.obj is Map) {
toNode.obj[toIndex] = fromNode.obj.remove(fromIndex);
} else {
throw Exception("Move operation can only be applied to lists or maps: $op");
}
toNode.obj.insert(toIndex, fromNode.obj.removeAt(fromIndex));
if (shouldNotify) {
if (fromNode.control.id != toNode.control.id) {
fromNode.control.notify();
Expand Down
94 changes: 94 additions & 0 deletions sdk/python/examples/services/secure_storage/basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import base64
import os

import flet as ft
import flet_secure_storage as fss


def main(page: ft.Page):
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
storage = fss.SecureStorage(
web_options=fss.WebOptions(
db_name="customstorage",
public_key="publickey",
wrap_key=base64.urlsafe_b64encode(os.urandom(32)).decode(),
wrap_key_iv=base64.urlsafe_b64encode(os.urandom(16)).decode(),
),
android_options=fss.AndroidOptions(
reset_on_error=True,
migrate_on_algorithm_change=True,
enforce_biometrics=True,
key_cipher_algorithm=fss.KeyCipherAlgorithm.AES_GCM_NO_PADDING,
storage_cipher_algorithm=fss.StorageCipherAlgorithm.AES_GCM_NO_PADDING,
),
)

key = ft.TextField(label="Key", value="example_key")
value = ft.TextField(label="Value", value="secret_value")
result = ft.Text(theme_style=ft.TextThemeStyle.TITLE_LARGE)

async def set_value(e):
await storage.set(key.value, value.value)
result.value = "Value saved"
page.update()

async def get_value(e):
result.value = await storage.get(key.value)
page.update()

async def remove_value(e):
await storage.remove(key.value)
result.value = "Value removed"
page.update()

async def clear_storage(e):
await storage.clear()
result.value = "Storage cleared"
page.update()

async def contains_key(e):
exists = await storage.contains_key(key.value)
result.value = f"Key exists: {exists}"
page.update()

async def get_availability(e):
is_availability = await storage.get_availability()
page.show_dialog(
ft.SnackBar(
content=ft.Text(
value=f"Protected data available: {is_availability}"
if is_availability
else "Protected data available: None"
)
)
)
page.update()

page.add(
ft.Column(
alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
spacing=10,
controls=[
result,
key,
value,
ft.Row(
width=300,
wrap=True,
controls=[
ft.Button("Set", on_click=set_value),
ft.Button("Get", on_click=get_value),
ft.Button("Contains key", on_click=contains_key),
ft.Button("Remove", on_click=remove_value),
ft.Button("Clear", on_click=clear_storage),
ft.Button("Check Data Availability", on_click=get_availability),
],
),
],
),
)


ft.run(main)
Loading
Loading