@@ -2,6 +2,8 @@ import 'dart:convert';
22import 'dart:typed_data' ;
33import 'package:file_selector_platform_interface/file_selector_platform_interface.dart' ;
44import 'package:cookethflow/core/services/platform_file_service.dart' ;
5+ import 'package:flutter/foundation.dart' ;
6+ import 'package:universal_html/html.dart' as html;
57
68class FileServices {
79 final FileSelectorPlatform fileSelector = FileSelectorPlatform .instance;
@@ -11,38 +13,51 @@ class FileServices {
1113 required String jsonString,
1214 }) async {
1315 try {
14- const XTypeGroup typeGroup = XTypeGroup (
15- label: 'JSON' ,
16- extensions: ['json' ],
17- mimeTypes: ['application/json' ],
18- );
19-
2016 // Ensure the default name ends with .json
21- if (! defaultName.toLowerCase ().endsWith ('.json' )) {
22- defaultName = '$defaultName .json' ;
23- }
24-
25- final String ? path = await fileSelector.getSavePath (
26- acceptedTypeGroups: [typeGroup],
27- suggestedName: defaultName,
28- );
29-
30- if (path == null ) {
31- return 'Save operation cancelled' ;
17+ final sanitizedName = defaultName.toLowerCase ().endsWith ('.json' )
18+ ? defaultName
19+ : '$defaultName .json' ;
20+
21+ if (kIsWeb) {
22+ // Web: Use browser download API
23+ final bytes = Uint8List .fromList (utf8.encode (jsonString));
24+ final blob = html.Blob ([bytes], 'application/json' );
25+ final url = html.Url .createObjectUrlFromBlob (blob);
26+ final anchor = html.AnchorElement (href: url)
27+ ..setAttribute ('download' , sanitizedName)
28+ ..click ();
29+ html.Url .revokeObjectUrl (url);
30+ return 'success' ;
31+ } else {
32+ // Desktop: Use file_selector
33+ const XTypeGroup typeGroup = XTypeGroup (
34+ label: 'JSON' ,
35+ extensions: ['json' ],
36+ mimeTypes: ['application/json' ],
37+ );
38+
39+ final String ? path = await fileSelector.getSavePath (
40+ acceptedTypeGroups: [typeGroup],
41+ suggestedName: sanitizedName,
42+ );
43+
44+ if (path == null ) {
45+ return 'Save operation cancelled' ;
46+ }
47+
48+ // Ensure the selected path ends with .json
49+ final String savePath =
50+ path.toLowerCase ().endsWith ('.json' ) ? path : '$path .json' ;
51+
52+ final XFile file = XFile .fromData (
53+ Uint8List .fromList (utf8.encode (jsonString)),
54+ mimeType: 'application/json' ,
55+ name: savePath.split ('/' ).last,
56+ );
57+
58+ await file.saveTo (savePath);
59+ return 'success' ;
3260 }
33-
34- // Ensure the selected path ends with .json
35- final String savePath =
36- path.toLowerCase ().endsWith ('.json' ) ? path : '$path .json' ;
37-
38- final XFile file = XFile .fromData (
39- Uint8List .fromList (utf8.encode (jsonString)),
40- mimeType: 'application/json' ,
41- name: savePath.split ('/' ).last,
42- );
43-
44- await file.saveTo (savePath);
45- return 'success' ;
4661 } catch (e) {
4762 return e.toString ();
4863 }
@@ -53,30 +68,50 @@ class FileServices {
5368 required Uint8List pngBytes,
5469 }) async {
5570 try {
56- const XTypeGroup typeGroup = XTypeGroup (
57- label: 'PNG Images' ,
58- extensions: ['png' ],
59- mimeTypes: ['image/png' ],
60- );
61-
62- final String ? path = await fileSelector.getSavePath (
63- acceptedTypeGroups: [typeGroup],
64- suggestedName: defaultName,
65- );
66-
67- if (path == null ) {
68- return 'Save operation cancelled' ;
71+ // Ensure the default name ends with .png
72+ final sanitizedName = defaultName.toLowerCase ().endsWith ('.png' )
73+ ? defaultName
74+ : '$defaultName .png' ;
75+
76+ if (kIsWeb) {
77+ // Web: Use browser download API
78+ final blob = html.Blob ([pngBytes], 'image/png' );
79+ final url = html.Url .createObjectUrlFromBlob (blob);
80+ final anchor = html.AnchorElement (href: url)
81+ ..setAttribute ('download' , sanitizedName)
82+ ..click ();
83+ html.Url .revokeObjectUrl (url);
84+ return 'success' ;
85+ } else {
86+ // Desktop: Use file_selector
87+ const XTypeGroup typeGroup = XTypeGroup (
88+ label: 'PNG Images' ,
89+ extensions: ['png' ],
90+ mimeTypes: ['image/png' ],
91+ );
92+
93+ final String ? path = await fileSelector.getSavePath (
94+ acceptedTypeGroups: [typeGroup],
95+ suggestedName: sanitizedName,
96+ );
97+
98+ if (path == null ) {
99+ return 'Save operation cancelled' ;
100+ }
101+
102+ // Ensure the selected path ends with .png
103+ final String savePath =
104+ path.toLowerCase ().endsWith ('.png' ) ? path : '$path .png' ;
105+
106+ final XFile file = XFile .fromData (
107+ pngBytes,
108+ mimeType: 'image/png' ,
109+ name: savePath.split ('/' ).last,
110+ );
111+
112+ await file.saveTo (savePath);
113+ return 'success' ;
69114 }
70-
71- final XFile file = XFile .fromData (
72- pngBytes,
73- mimeType: 'image/png' ,
74- name: path.split ('/' ).last,
75- path: path,
76- );
77-
78- await file.saveTo (path);
79- return 'success' ;
80115 } catch (e) {
81116 return e.toString ();
82117 }
@@ -87,30 +122,51 @@ class FileServices {
87122 required String svgString,
88123 }) async {
89124 try {
90- const XTypeGroup typeGroup = XTypeGroup (
91- label: 'SVG Images' ,
92- extensions: ['svg' ],
93- mimeTypes: ['image/svg+xml' ],
94- );
95-
96- final String ? path = await fileSelector.getSavePath (
97- acceptedTypeGroups: [typeGroup],
98- suggestedName: defaultName,
99- );
100-
101- if (path == null ) {
102- return 'Save operation cancelled' ;
125+ // Ensure the default name ends with .svg
126+ final sanitizedName = defaultName.toLowerCase ().endsWith ('.svg' )
127+ ? defaultName
128+ : '$defaultName .svg' ;
129+
130+ if (kIsWeb) {
131+ // Web: Use browser download API
132+ final bytes = Uint8List .fromList (utf8.encode (svgString));
133+ final blob = html.Blob ([bytes], 'image/svg+xml' );
134+ final url = html.Url .createObjectUrlFromBlob (blob);
135+ final anchor = html.AnchorElement (href: url)
136+ ..setAttribute ('download' , sanitizedName)
137+ ..click ();
138+ html.Url .revokeObjectUrl (url);
139+ return 'success' ;
140+ } else {
141+ // Desktop: Use file_selector
142+ const XTypeGroup typeGroup = XTypeGroup (
143+ label: 'SVG Images' ,
144+ extensions: ['svg' ],
145+ mimeTypes: ['image/svg+xml' ],
146+ );
147+
148+ final String ? path = await fileSelector.getSavePath (
149+ acceptedTypeGroups: [typeGroup],
150+ suggestedName: sanitizedName,
151+ );
152+
153+ if (path == null ) {
154+ return 'Save operation cancelled' ;
155+ }
156+
157+ // Ensure the selected path ends with .svg
158+ final String savePath =
159+ path.toLowerCase ().endsWith ('.svg' ) ? path : '$path .svg' ;
160+
161+ final XFile file = XFile .fromData (
162+ Uint8List .fromList (utf8.encode (svgString)),
163+ mimeType: 'image/svg+xml' ,
164+ name: savePath.split ('/' ).last,
165+ );
166+
167+ await file.saveTo (savePath);
168+ return 'success' ;
103169 }
104-
105- final XFile file = XFile .fromData (
106- Uint8List .fromList (utf8.encode (svgString)),
107- mimeType: 'image/svg+xml' ,
108- name: path.split ('/' ).last,
109- path: path,
110- );
111-
112- await file.saveTo (path);
113- return 'success' ;
114170 } catch (e) {
115171 return e.toString ();
116172 }
@@ -126,13 +182,9 @@ class FileServices {
126182 }
127183
128184 Future <XFile ?> selectImages () async {
129- // For Linux, you can specify the MIME types or extensions
130- // final fileSelector = FileSelectorPlatform.instance;
131185 const XTypeGroup typeGroup = XTypeGroup (
132186 label: 'Images' ,
133- mimeTypes: ['image/*' ], // All image types
134- // Alternatively, you can specify extensions:
135- // extensions: ['jpg', 'jpeg', 'png', 'gif', 'webp'],
187+ mimeTypes: ['image/*' ],
136188 );
137189
138190 try {
@@ -141,8 +193,6 @@ class FileServices {
141193
142194 if (file != null ) {
143195 print ('Selected file: ${file .path }' );
144- // You can now read the file or display the image
145- // For example, with Image.file(File(file.path))
146196 }
147197 return file;
148198 } catch (e) {
@@ -152,7 +202,6 @@ class FileServices {
152202 }
153203
154204 Future <XFile ?> importJsonFiles () async {
155- // final fileSelector = FileSelectorPlatform.instance;
156205 const XTypeGroup typeGroup = XTypeGroup (
157206 label: 'JSON' ,
158207 mimeTypes: ['application/json' ],
@@ -171,4 +220,4 @@ class FileServices {
171220 return null ;
172221 }
173222 }
174- }
223+ }
0 commit comments