How to access methods in a Flutter for desktop (macOS) app - flutter

I have been trying to write a Flutter desktop app which can communicate with Go methods.
Go file:
package main
import "C"
import "fmt"
func PrintHello() {
fmt.Print("Hello,World")
}
func main() {}
import 'dart:ffi' as ffi;
typedef PrintHello_func = ffi.Void Function();
typedef PrintHello = void Function();
void ffi_test(List<String> arguments) {
var path = '/path/to/libhello_ffi.dylib';
final ffi.DynamicLibrary dylib = ffi.DynamicLibrary.open(path);
final PrintHello hello = dylib
.lookup<ffi.NativeFunction<PrintHello_func>>('PrintHello')
.asFunction();
hello();
}
The execution of the above Flutter code fails with an error:
The following ArgumentError was thrown while handling a gesture:
Invalid argument(s): Failed to load dynamic library
(dlopen(/path/to/libhello_ffi.dylib, 1):
no suitable image found. Did find:
file system sandbox blocked open() of
'/path/to/libhello_ffi.dylib')
But it works just fine if I execute the dart directly instead of flutter run.
I even tried to create a separate FFI package but unfortunately it failed as well.
FFI package name: /my_app/packages/libhello_ffi
I placed the libhello_ffi.dylib file under /my_app/packages/libhello_ffi/macos directory
Flutter code:
import 'dart:ffi';
import 'dart:io';
final DynamicLibrary _dl = _open();
DynamicLibrary _open() {
if (Platform.isMacOS) return DynamicLibrary.executable();
throw UnsupportedError('This platform is not supported.');
}
typedef sayhello_C = Void Function();
typedef sayhello_Dart = void Function();
void sayhello() {
_dl.lookup<NativeFunction<sayhello_C>>('PrintHello')
.asFunction();
}
Error:
The following ArgumentError was thrown while handling a gesture:
Invalid argument(s): Failed to lookup symbol (dlsym(RTLD_DEFAULT, PrintHello): symbol not found)
When the exception was thrown, this was the stack:
#0 DynamicLibrary.lookup (dart:ffi-patch/ffi_dynamic_library_patch.dart:31:29)
#1 sayhello (package:libhello_ffi/ffi.dart:17:7)
#2 LibhelloFfi.sayhello (package:libhello_ffi/libhello_ffi.dart:5:12)
#3 ffi_test (package:squash_archiver/features/home/ui/widgets/ffi_test.dart:10:13)
#4 _HomeScreenState._buildTestFfi.<anonymous closure> (package:squash_archiver/features/home/ui/pages/home_screen.dart:73:13)
#5 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
#6 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
There isn't any proper article on the Go and Flutter for Desktop integration available online.

But it works just fine if I execute the dart directly instead of flutter run
Dart isn't a sandboxed app, while a Flutter app is sandboxed by default. That's why you are only getting a sandbox error in your Flutter app.
I placed the libhello_ffi.dylib file under /my_app/packages/libhello_ffi/macos directory
This sounds like a location in your source tree, not your application. You can't reference arbitrary files outside your application unless you disable the sandbox.
If you are just testing locally, turning off the sandbox is the easy solution. If you want something you can distribute, you need to package the library in your bundle (by adding it as a bundled resource in your Xcode project) anyway, at which point I would expect loading it to work even with the sandbox.

Related

Flutter js unit test

I have a flutter test project which depends on futter js (javascript environment) this, I wrote a unit test to it. But I keep getting error on this line
jsRuntime = getJavascriptRuntime();
Invalid argument(s): Failed to load dynamic library 'quickjs_c_bridge.dll': error code 126
dart:ffi new DynamicLibrary.open
package:flutter_js/quickjs/ffi.dart 118:26 _qjsLib
package:flutter_js/quickjs/ffi.dart _qjsLib
package:flutter_js/quickjs/ffi.dart 163:19 _jsNewRuntime
package:flutter_js/quickjs/ffi.dart _jsNewRuntime
package:flutter_js/quickjs/ffi.dart 206:14 jsNewRuntime
package:flutter_js/quickjs/quickjs_runtime2.dart 51:16 QuickJsRuntime2._ensureEngine
package:flutter_js/quickjs/quickjs_runtime2.dart 165:5 QuickJsRuntime2.evaluate
package:flutter_js/quickjs/quickjs_runtime2.dart 230:9 QuickJsRuntime2.initChannelFunctions
package:flutter_js/javascript_runtime.dart 63:5 JavascriptRuntime.init
package:flutter_js/quickjs/quickjs_runtime2.dart 46:10 new QuickJsRuntime2
package:flutter_js/flutter_js.dart 40:15
getJavascriptRuntime
can't figure out how to do flutter js unit test. I want some tips and if possible
Here is the reference but it is written only for windows and linux but I need it for android apk.If possible with eg:
https://github.com/abner/flutter_js
Inside your build directory, copy file runner/Release/quickjs_c_bridge.dll to runner/Debug directory.

Flutter: how do I write a file to local directory with path_provider?

I'm working on a Flutter app and need to write files to the local system (iOS, Android but also Web). I have logically followed the documentation on Read and Write Files, and used path_provider to make a small example to test functionality:
import 'package:path_provider/path_provider.dart';
...
void _testPathProvider() async {
// Directory appDocDir = await getTemporaryDirectory(); // Using a different dir. not working either
// String appDocPath = appDocDir.path;
Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;
debugPrint('$appDocPath/my_file.txt');
// Save in local file system
var file = await File('$appDocPath/my_file.txt').writeAsString('hello!');
}
This test function is simply called by a button, nothing crazy there.
But when I run flutter in browser & press button, I get following error:
Error: MissingPluginException(No implementation found for method getApplicationDocumentsDirectory on channel plugins.flutter.io/path_provider)
at Object.throw_ [as throw] (http://localhost:42561/dart_sdk.js:5067:11)
at MethodChannel._invokeMethod (http://localhost:42561/packages/flutter/src/services/restoration.dart.lib.js:1560:21)
at _invokeMethod.next (<anonymous>)
at http://localhost:42561/dart_sdk.js:40571:33
at _RootZone.runUnary (http://localhost:42561/dart_sdk.js:40441:59)
at _FutureListener.thenAwait.handleValue (http://localhost:42561/dart_sdk.js:35363:29)
at handleValueCallback (http://localhost:42561/dart_sdk.js:35931:49)
at Function._propagateToListeners (http://localhost:42561/dart_sdk.js:35969:17)
at _Future.new.[_completeWithValue] (http://localhost:42561/dart_sdk.js:35817:23)
at async._AsyncCallbackEntry.new.callback (http://localhost:42561/dart_sdk.js:35838:35)
at Object._microtaskLoop (http://localhost:42561/dart_sdk.js:40708:13)
at _startMicrotaskLoop (http://localhost:42561/dart_sdk.js:40714:13)
at http://localhost:42561/dart_sdk.js:36191:9
Seems like the issue is well-know, have followed advice both here and here, including :
running flutter clean and re-getting packages
closing app completely and re-running
upgrading Flutter to latest stable
re-adding path_provider to my pubspec.yaml
I'm using latest version of the dependency (path_provider: ^2.0.9).
Any help much appreciated.
Here's an example of the data uri method mentioned in the comments:
import 'dart:html' as html;
final bytes = utf8.encode('hello!');
final dataUri = 'data:text/plain;base64,${base64.encode(bytes)}';
html.document.createElement('a') as html.AnchorElement
..href = dataUri
..download = 'my_file.txt'
..dispatchEvent(html.Event.eventType('MouseEvent', 'click'));
Make sure that's in a dart file that's only imported when html is available, or preferably, use package:universal_html.

Flutter: Build web app with file other than 'lib/main.dart'

I use more than one dart main file in my lib folder for passing different app configuration depending on server each will use.
Example of main_dev.dart
void main() async {
FlavorConfig(
flavorName: 'test',
values: {
'BASE_URL': 'http://$MY_IP:9002/graphql',
'WS_URL': 'ws://$MY_IP:9002/graphql',
},
);
await runApp(ProviderApp());
}
For now I have main_test.dart and main_live.dart and everything works fine when building android app.
Example I use the following command to build test app.
flutter build apk --obfuscate --split-debug-info=./build/app/outputs/symbols lib/main_test.dart
Note the last part lib/main_test.dart I specify which file I intend to use and it works really well for building android app.
But I tried similar thing for web and I get the following error as it seems to me building web app needs main.dart.
Target dart2js failed: Exception:
.dart_tool/flutter_build/11c1feed0a867bb072ab6c7b64967a31/main.dart:8:8:
Error: Error when reading 'lib/main.dart': Error reading
'lib/main.dart' (The system cannot find the file specified. ) import
'package:my_amazing_app/main.dart' as entrypoint;
^ .dart_tool/flutter_build/11c1feed0a867bb072ab6c7b64967a31/main.dart:13:3:
Error: Method not found: 'main'. entrypoint.main(); ^^^^ Error:
Compilation failed.
Compiling lib\main.dart for the Web...
20.3s Exception: Failed to compile application for the Web.
How can can specify the file I want in command for web too?.
I have tried the following without success flutter build web lib/main_test.dart
Rather than using different entrypoints, you can use compile-time constants. When running flutter build, you can pass in --dart-define=foo=bar to set a compile-time constant, and you can then use it from your code using:
const foo = String.fromEnvironment('foo');
print(foo); // prints "bar"
const fooExists = bool.hasEnvironment('foo');
print(fooExists); // prints "true"
Note that this is a const value, so the compiler can still use this information for tree shaking.
In your case, assuming you have 2 environments: prod and test, you could do something like this:
// lib/main.dart
void main() {
const env = String.fromEnvironment('env');
if (env == 'test') {
runApp(TestApp());
} else if (env == 'prod') {
runApp(ProdApp());
} else {
throw 'unknown environment: $env';
}
}
Then when you build your app, you can use:
flutter build web --dart-define=env=prod <other-args>

Test failed to load while creating a Store

I try to unit-test, but it fails to load:
dart:ffi new DynamicLibrary.open
package:objectbox/src/native/bindings/bindings.dart 21:28 loadObjectBoxLib
package:objectbox/src/native/bindings/bindings.dart 50:41 C
package:objectbox/src/native/model.dart 18:31 new Model
package:objectbox/src/native/store.dart 63:17 new Store
package:productivitie/features/to_do_listing/data/datasource/project_database_data_source_object_box.dart 23:15 new ProjectDataBaseDataSourceObjectBox.<fn>
dart:async _completeOnAsyncReturn
package:path_provider/path_provider.dart getApplicationDocumentsDirectory
Failed to load "F:\Programme\gitProgramme\productivitie\test\features\to_do_listing\data\datasource\project_database_data_source_object_box_test.dart": Invalid argument(s): Failed to load dynamic library (193)
My Constructor, where the problem occours:
21 ProjectDataBaseDataSourceObjectBox(){
22 getApplicationDocumentsDirectory().then((Directory dir){
23 store = Store(getObjectBoxModel() , directory: dir.path + '/objectbox' );
24 box = store!.box<Project>();
25 });
26
27 }
Flutter Doctor found no issues.
I build_run my models again (overwrote objectbox.g.dart file), didn't help.
My versions are:
objectbox: 0.14.0
objectbox_flutter_libs: any
path_provider: ^2.0.1
I first thought it was a problem with the path_provider, I did set a MockMethodCallHandler to return a mocked directory path if the path_provider tries to getApplicationDocumentsDirectory.
final directory = await Directory.systemTemp.createTemp();
const MethodChannel('plugins.flutter.io/path_provider').setMockMethodCallHandler((MethodCall call) async {
if(call.method == 'getApplicationDocumentsDirectory'){
return directory.path;
}
return null;
});
But that didn't help either.
The important part of the error is:
Failed to load dynamic library (193)
From the paths in your question, I assume you're running on Windows. In that case, you need to install the dynamic library so that the compiled test executable can find it. Respecting how Windows loads DLLs this can be either the same directory as where the .exe is, or a system directory. Following Dart CLI apps or Flutter desktop apps installation instructions should help:
Install objectbox-c system-wide (use "Git bash" on Windows):
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh)
Copy the downloaded lib/objectbox.dll to C:\Windows\System32\ (requires admin privileges).

How to open a file using its absolute path?

How to open a file using its absolute path?
await File('c:/Users/rozerro/AndroidStudioProjects/rxdart_streams/assets/sonet.txt')
.openRead()
.transform(utf8.decoder)
.transform(LineSplitter())
.toList();
as well as
c:\\Users\\rozerro\\AndroidStudioProjects\\rxdart_streams\\assets\\sonet.txt
The error
E/flutter ( 4943): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)]
Unhandled Exception: FileSystemException: Cannot open file, path =
'c:/Users/rozerro/AndroidStudioProjects/rxdart_streams/assets/sonet.txt'
(OS Error: No such file or directory, errno = 2)
You cannot do that. You cannot access a file on your local PC from an app deployed on your smartphone.
Since your text file is in a folder named assets, I assume it is some kind of file you want to have deployed with your app. In flutter that is called an "asset" and you can read up on how to achieve that in the documentation about assets and images.
In short, your pubspec.yaml file needs to have an entry for it:
flutter:
assets:
- assets/sonet.txt
And then you can access it in your source with this method:
final textFromFile = await rootBundle.loadString('assets/sonet.txt');