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

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>

Related

How to include dlls in flutter build windows

I am working on a flutter project that runs fine in development.
However I do not know how to get the build to include dll's referenced using FFI.
I can't find clear instructions on how to do it.
I tried following the steps to build an msix here, which worked but didn't seem to include the dll (it fails in the same way as the regular build)
what is the procedure to have the build process consider the dll's?
other dll's show up in the build directory from 3rd party packages so there must be a way right?
That's really hard to discover by your own, but indeed you can bind those libraries to you MSIX. In my case I just made a package for label printers using Dart FFI and DLL`s provided by manufacturer and this is how I did it.
You need to add these DLL's to your assets setting on pubspec.yaml from your package. This is my case:
[...]
flutter:
[...]
assets:
- assets/WinPort.dll
- assets/Winppla.dll
- assets/Winpplb.dll
- assets/Winpplz.dll
With this setting you will embed your DLL files in your final MSIX, but this was the easy part. Now you have make sure to load the proper load these files in code.
Based on my own tests, I still dealing with two ways to develop and test code, the first one is when I am running a project in my machine via flutter run I must set the target for current.path, when I get it done and start building for deploy I change this to resolvedExecutable.parent.path. Where is what you need to do.
Loading you DLL in development environment (flutter run):
final String _packageAssetsDirPath = normalize(join(Directory.current.path,'assets'));
On production environment (running from .exe or MSIX installed):
final String _assetsPackageDir = normalize(
join('data', 'flutter_assets', 'packages', 'YOUR_PACKAGE_NAME', 'assets'));
final String _exeDirPath = File(Platform.resolvedExecutable).parent.path;
final String _packageAssetsDirPath =
normalize(join(_exeDirPath, _assetsPackageDir));
After with this var called _packageAssetsDirPath will be easy to load your DLL's, now you invoke a DynamicLibrary constructor:
// Path for DLL file
final String _libDllSourceFullPath =
normalize(join(_packageAssetsDirPath, 'Winppla.dll'));
// Target for copy, place DLL in same place the .exe you are running
final String _libDllDestFullPath =
normalize(join(_packageAssetsDirPath, 'YOUROWN.dll'));
// Try to copy for running exe path
File(_libDllSourceFullPath).copySync(_libDllDestFullPath);
// With this copy, would be simple to load, and if it fails, try in full path
// LOAD DLL
try {
String _packageAssetsDirPath =
normalize(join(Directory.current.path, 'assets'));
String _printerLibraryPath =
normalize(join(_packageAssetsDirPath, 'Winppla.dll'));
DynamicLibrary _library = DynamicLibrary.open(_printerLibraryPath);
return _library;
} catch (e) {
try {
DynamicLibrary _library = DynamicLibrary.open('Winppla.dll');
return _library;
} catch (e) {
// Avoing errors creating a fake DLL, but you could deal with an exception
return DynamicLibrary.process();
}
}
At this point you can load a DLL and use it, you can check my package full code at https://github.com/saviobatista/argox_printer check for lib/src/ppla.dart at function _setupDll() and you will see that loading.
I built a simpler option inspired in the solution of Sávio Batista
(You must have your .dll in your assets folder)
if (kReleaseMode) {
// I'm on release mode, absolute linking
final String local_lib = join('data', 'flutter_assets', 'assets', 'libturbojpeg.dll');
String pathToLib = join(Directory(Platform.resolvedExecutable).parent.path, local_lib);
DynamicLibrary lib = DynamicLibrary.open(pathToLib);
} else {
// I'm on debug mode, local linking
var path = Directory.current.path;
DynamicLibrary lib = DynamicLibrary.open('$path/assets/libturbojpeg.dll');
}
Just replace libturbojpeg.dll for your .dll

How do I pass environment configuration like URLs to Flutter application using Jenkins?

I have a Jenkins job that builds my Flutter application based on different parameters like flavor (for different environments), backend URLs, etc. Right now in the build phase, I have a bash script that creates a new dart file based on my parameters and then Flutter uses that file as its environment configuration.
Future<void> main() async {
final config = {
'param1': Platform.environment['param1'],
};
final filename = 'lib/env.dart';
await File(filename)
.writeAsString('final environment = ${json.encode(config)};');
}
Is there a better way to send parameters like URLs from Jenkins to the Flutter application during the build process?
Maybe you must try pass your URLs with environments variables from jenkins to flask app.
In jenkins for example you can create a build stage like this
stage('Build') {
agent {
label 'master'
}
environment {
YOUR_URL = "https://example.com/api/v1"
}
steps {
script {
/* Some actions */
}
}
}
So, in your flask app only just missing get the env variables with any method like os.environ method like this example
import os
YOUR_URL = os.environ.get("YOUR_URL")

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 access methods in a Flutter for desktop (macOS) app

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.

How to get build and version number of Flutter Web app

The keyword here is Web. I can get the build and version number of a Flutter app by using the package_info plugin, but if it is running on the web I can't. How do I get the package info for a Flutter Web app?
I'll include an answer below as a temporary fix, but I am looking a solution that gets the version info from pubspec.yaml.
As a temporary workaround you can create a separate file with the version info in it:
web_version_info.dart
class WebVersionInfo {
static const String name = '1.0.0';
static const int build = 1;
}
You can use that for all platforms or in your code you can use kIsWeb to just use it for the web:
Future<String> _getAppVersion() async {
if (kIsWeb) {
return WebVersionInfo.name;
} else {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version;
}
}
Of course, this is not a great solution because now you need to remember to update the version and build information in both pubspec.yaml and in WebVersionInfo every time you update the app.
If you use beta channel you can use package_info_plus plugin that appears to be a drop-in-replacement for package_info. So all you need to change is pubspec.yaml and your import. (I only use version so there could be differences that I haven't noticed)
Change pubspec and your import
pubspec.yaml: package_info_plus: '>=0.6.3 <2.0.0'
import: import 'package:package_info_plus/package_info_plus.dart'
Reference:
Github issue 46609
For those using Linux and in order to improve Suragch's answer, I suggest automating the build process using bash scripts. For that, we need two scripts: one to increase the version build number and another to call the flutter build command itself, forwarding the parameters. That way, if you prefer to just increment the version build number manually, you can just call the update script and then 'flutter build' later, but if you want to do everything in one step, you can call the builder script.
You will only need to edit the '.app_version' file as the version changes.
The '.build_seq', '.version_number' files are always rewritten, and the '.app_version' file is created only if it is not found.
The scripts:
updversion.sh:
#!/bin/bash
if [ -f ".app_version" ]; then
VER=`cat .app_version`
else
VER="1.0.0"
echo $VER > .app_version
fi
if [ -f ".build_seq" ]; then
BLD=`cat .build_seq`
else
BLD='0'
fi
((BLD++))
echo $BLD > .build_seq
echo "Ver: $VER ($BLD)" > .current_version
echo "
// Auto-generated by updversion.sh. Do not edit.
class WebVersionInfo {
static const String name = '$VER';
static const int build = $BLD;
}
" > lib/version_info.dart
exit 0
buildweb.sh:
#!/bin/bash
./updversion.sh
flutter build web $*
exit $?