Skip to content

Commit 000c123

Browse files
Client storage (#288)
* Create client_storage.py * Return results from a method * ClientStorage.get, .set * All client storage methods implemented * Added prefix to get_keys() * Saving any types in the client storage
1 parent 4673a19 commit 000c123

File tree

8 files changed

+311
-0
lines changed

8 files changed

+311
-0
lines changed

client/macos/Flutter/GeneratedPluginRegistrant.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import FlutterMacOS
66
import Foundation
77

88
import screen_retriever
9+
import shared_preferences_macos
910
import url_launcher_macos
1011
import window_manager
1112

1213
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
1314
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
15+
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
1416
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
1517
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
1618
}

client/pubspec.lock

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ packages:
8585
url: "https://pub.dartlang.org"
8686
source: hosted
8787
version: "2.0.1"
88+
file:
89+
dependency: transitive
90+
description:
91+
name: file
92+
url: "https://pub.dartlang.org"
93+
source: hosted
94+
version: "6.1.4"
8895
file_picker:
8996
dependency: transitive
9097
description:
@@ -219,20 +226,55 @@ packages:
219226
url: "https://pub.dartlang.org"
220227
source: hosted
221228
version: "1.8.2"
229+
path_provider_linux:
230+
dependency: transitive
231+
description:
232+
name: path_provider_linux
233+
url: "https://pub.dartlang.org"
234+
source: hosted
235+
version: "2.1.7"
236+
path_provider_platform_interface:
237+
dependency: transitive
238+
description:
239+
name: path_provider_platform_interface
240+
url: "https://pub.dartlang.org"
241+
source: hosted
242+
version: "2.0.4"
243+
path_provider_windows:
244+
dependency: transitive
245+
description:
246+
name: path_provider_windows
247+
url: "https://pub.dartlang.org"
248+
source: hosted
249+
version: "2.1.3"
222250
petitparser:
223251
dependency: transitive
224252
description:
225253
name: petitparser
226254
url: "https://pub.dartlang.org"
227255
source: hosted
228256
version: "5.0.0"
257+
platform:
258+
dependency: transitive
259+
description:
260+
name: platform
261+
url: "https://pub.dartlang.org"
262+
source: hosted
263+
version: "3.1.0"
229264
plugin_platform_interface:
230265
dependency: transitive
231266
description:
232267
name: plugin_platform_interface
233268
url: "https://pub.dartlang.org"
234269
source: hosted
235270
version: "2.1.2"
271+
process:
272+
dependency: transitive
273+
description:
274+
name: process
275+
url: "https://pub.dartlang.org"
276+
source: hosted
277+
version: "4.2.4"
236278
redux:
237279
dependency: transitive
238280
description:
@@ -247,6 +289,62 @@ packages:
247289
url: "https://pub.dartlang.org"
248290
source: hosted
249291
version: "0.1.2"
292+
shared_preferences:
293+
dependency: transitive
294+
description:
295+
name: shared_preferences
296+
url: "https://pub.dartlang.org"
297+
source: hosted
298+
version: "2.0.15"
299+
shared_preferences_android:
300+
dependency: transitive
301+
description:
302+
name: shared_preferences_android
303+
url: "https://pub.dartlang.org"
304+
source: hosted
305+
version: "2.0.13"
306+
shared_preferences_ios:
307+
dependency: transitive
308+
description:
309+
name: shared_preferences_ios
310+
url: "https://pub.dartlang.org"
311+
source: hosted
312+
version: "2.1.1"
313+
shared_preferences_linux:
314+
dependency: transitive
315+
description:
316+
name: shared_preferences_linux
317+
url: "https://pub.dartlang.org"
318+
source: hosted
319+
version: "2.1.1"
320+
shared_preferences_macos:
321+
dependency: transitive
322+
description:
323+
name: shared_preferences_macos
324+
url: "https://pub.dartlang.org"
325+
source: hosted
326+
version: "2.0.4"
327+
shared_preferences_platform_interface:
328+
dependency: transitive
329+
description:
330+
name: shared_preferences_platform_interface
331+
url: "https://pub.dartlang.org"
332+
source: hosted
333+
version: "2.1.0"
334+
shared_preferences_web:
335+
dependency: transitive
336+
description:
337+
name: shared_preferences_web
338+
url: "https://pub.dartlang.org"
339+
source: hosted
340+
version: "2.0.4"
341+
shared_preferences_windows:
342+
dependency: transitive
343+
description:
344+
name: shared_preferences_windows
345+
url: "https://pub.dartlang.org"
346+
source: hosted
347+
version: "2.1.1"
250348
sky_engine:
251349
dependency: transitive
252350
description: flutter
@@ -392,6 +490,13 @@ packages:
392490
url: "https://pub.dartlang.org"
393491
source: hosted
394492
version: "0.2.6"
493+
xdg_directories:
494+
dependency: transitive
495+
description:
496+
name: xdg_directories
497+
url: "https://pub.dartlang.org"
498+
source: hosted
499+
version: "0.2.0+2"
395500
xml:
396501
dependency: transitive
397502
description:
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import 'dart:convert';
2+
3+
import 'package:flet/src/flet_app_services.dart';
4+
import 'package:flutter/widgets.dart';
5+
import 'package:shared_preferences/shared_preferences.dart';
6+
7+
import '../models/control.dart';
8+
9+
class ClientStorageControl extends StatefulWidget {
10+
final Control? parent;
11+
final Control control;
12+
13+
const ClientStorageControl(
14+
{Key? key, required this.parent, required this.control})
15+
: super(key: key);
16+
17+
@override
18+
State<ClientStorageControl> createState() => _ClientStorageControlState();
19+
}
20+
21+
class _ClientStorageControlState extends State<ClientStorageControl> {
22+
String? _method;
23+
24+
@override
25+
Widget build(BuildContext context) {
26+
debugPrint("Client storage build: ${widget.control.id}");
27+
28+
var method = widget.control.attrString("method");
29+
30+
if (method != null && method != _method) {
31+
_method = method;
32+
debugPrint("Client storage JSON value: $_method");
33+
34+
var mj = json.decode(method);
35+
var i = mj["i"] as int;
36+
var name = mj["n"] as String;
37+
var params = List<String>.from(mj["p"] as List);
38+
39+
sendResult(Object? result, String? error) {
40+
FletAppServices.of(context).ws.pageEventFromWeb(
41+
eventTarget: widget.control.id,
42+
eventName: "result",
43+
eventData: json.encode({
44+
"i": i,
45+
"r": result != null ? json.encode(result) : null,
46+
"e": error
47+
}));
48+
}
49+
50+
() async {
51+
var prefs = await SharedPreferences.getInstance();
52+
switch (name) {
53+
case "set":
54+
var result = await prefs.setString(params[0], params[1]);
55+
sendResult(result, null);
56+
break;
57+
case "get":
58+
sendResult(prefs.getString(params[0]), null);
59+
break;
60+
case "containskey":
61+
sendResult(prefs.containsKey(params[0]), null);
62+
break;
63+
case "getkeys":
64+
sendResult(
65+
prefs
66+
.getKeys()
67+
.where((key) => key.startsWith(params[0]))
68+
.toList(),
69+
null);
70+
break;
71+
case "remove":
72+
sendResult(await prefs.remove(params[0]), null);
73+
break;
74+
case "clear":
75+
sendResult(await prefs.clear(), null);
76+
break;
77+
}
78+
}();
79+
}
80+
81+
return const SizedBox.shrink();
82+
}
83+
}

package/lib/src/controls/create_control.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'banner.dart';
1515
import 'card.dart';
1616
import 'checkbox.dart';
1717
import 'circle_avatar.dart';
18+
import 'client_storage.dart';
1819
import 'clipboard.dart';
1920
import 'column.dart';
2021
import 'container.dart';
@@ -90,6 +91,9 @@ Widget createControl(Control? parent, String id, bool parentDisabled) {
9091
return MarkdownControl(parent: parent, control: controlView.control);
9192
case ControlType.clipboard:
9293
return ClipboardControl(parent: parent, control: controlView.control);
94+
case ControlType.clientstorage:
95+
return ClientStorageControl(
96+
parent: parent, control: controlView.control);
9397
case ControlType.launchUrl:
9498
return LaunchUrlControl(parent: parent, control: controlView.control);
9599
case ControlType.image:

package/lib/src/models/control_type.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ enum ControlType {
66
card,
77
checkbox,
88
circleAvatar,
9+
clientstorage,
910
clipboard,
1011
column,
1112
container,

package/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies:
2222
url_launcher: ^6.1.5
2323
flutter_markdown: ^0.6.10+3
2424
file_picker: ^5.0.1
25+
shared_preferences: ^2.0.15
2526

2627
dev_dependencies:
2728
flutter_test:

sdk/python/flet/client_storage.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import dataclasses
2+
import json
3+
import threading
4+
from multiprocessing import Event
5+
from typing import Any, Dict, List, Optional
6+
from unittest.main import main
7+
8+
from beartype import beartype
9+
10+
from flet.control import Control
11+
from flet.ref import Ref
12+
13+
14+
@dataclasses.dataclass
15+
class ClientStorageMethodCall:
16+
i: int
17+
n: str
18+
p: List[str]
19+
20+
21+
@dataclasses.dataclass
22+
class ClientStorageMethodResults:
23+
i: int
24+
r: Optional[str]
25+
e: Optional[str]
26+
27+
28+
class ClientStorage(Control):
29+
def __init__(
30+
self,
31+
ref: Optional[Ref] = None,
32+
data: Any = None,
33+
):
34+
35+
Control.__init__(
36+
self,
37+
ref=ref,
38+
data=data,
39+
)
40+
41+
self.__call_counter = 0
42+
self.__calls: Dict[int, threading.Event] = {}
43+
self.__results: Dict[threading.Event, tuple[Optional[str], Optional[str]]] = {}
44+
self._add_event_handler("result", self._on_result)
45+
46+
def _get_control_name(self):
47+
return "clientstorage"
48+
49+
def _is_isolated(self):
50+
return True
51+
52+
def set(self, key: str, value: Any):
53+
jv = self._convert_attr_json(value)
54+
assert jv is not None
55+
self._call_method("set", [key, jv])
56+
57+
def get(self, key: str):
58+
jv = self._call_method("get", [key])
59+
if jv != None:
60+
return json.loads(jv)
61+
return None
62+
63+
def contains_key(self, key: str) -> bool:
64+
return self._call_method("containskey", [key])
65+
66+
def remove(self, key: str) -> bool:
67+
return self._call_method("remove", [key])
68+
69+
def get_keys(self, key_prefix: str) -> List[str]:
70+
return self._call_method("getkeys", [key_prefix])
71+
72+
def clear(self) -> bool:
73+
return self._call_method("clear", [])
74+
75+
def _call_method(self, name: str, params: List[str]) -> Any:
76+
m = ClientStorageMethodCall(i=self.__call_counter, n=name, p=params)
77+
self.__call_counter += 1
78+
self._set_attr_json("method", m)
79+
evt = threading.Event()
80+
self.__calls[m.i] = evt
81+
self.update()
82+
if not evt.wait(5):
83+
del self.__calls[m.i]
84+
raise Exception(
85+
f"Timeout waiting for ClientStorage.{name}({params}) method call"
86+
)
87+
result, err = self.__results.pop(evt)
88+
if err != None:
89+
raise Exception(err)
90+
if result == None:
91+
return None
92+
return json.loads(result)
93+
94+
def _on_result(self, e):
95+
d = json.loads(e.data)
96+
result = ClientStorageMethodResults(**d)
97+
evt = self.__calls.pop(result.i, None)
98+
if evt == None:
99+
return
100+
self.__results[evt] = (result.r, result.e)
101+
evt.set()

0 commit comments

Comments
 (0)