Access to pubspec.yaml attributes (version) from Dart app - dart

Is there any way to access some of the attributes listed in a pubspec.yaml file in that files Dart application?
In particular, the version and description attributes may be quite useful to see in a version info dialog, or even a '--version' when using a console app. I haven't been able to find a way to access in the API. I'm not sure if Mirrors would have anything appropriate, but if a web app is compiled to JS, then I don't see the description anywhere in the output JS.
Thanks.
EDIT
feature request: https://code.google.com/p/dart/issues/detail?id=18769

FOR FLUTTER ONLY
Please use this new package package_info_plus from flutter community.
import 'package:package_info_plus/package_info_plus.dart';
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appName = packageInfo.appName;
String packageName = packageInfo.packageName;
String version = packageInfo.version;
String buildNumber = packageInfo.buildNumber;
BELOW SOLUTION IS DEPRICATED.
I know the OP wants to read YAML but for flutter dev's you guys can read the version and other info of the application using package_info.
This is the sample to fetch details from Android/iOS application.
import 'package:package_info/package_info.dart';
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appName = packageInfo.appName;
String packageName = packageInfo.packageName;
String version = packageInfo.version;
String buildNumber = packageInfo.buildNumber;

you can install the "dart_config" package and use this code to parse a pubspec.yaml file:
import 'package:dart_config/default_server.dart';
import 'dart:async';
void main() {
Future<Map> conf = loadConfig("../pubspec.yaml");
conf.then((Map config) {
print(config['name']);
print(config['description']);
print(config['version']);
print(config['author']);
print(config['homepage']);
print(config['dependencies']);
});
}
The output looks similar to this:
test_cli
A sample command-line application
0.0.1
Robert Hartung
URL
{dart_config: any}
EDIT
You can do it with the Yaml package itself:
*NOTE: this will not work on Flutter Web
import 'package:yaml/yaml.dart';
import 'dart:io'; // *** NOTE *** This will not work on Flutter Web
void main() {
File f = new File("../pubspec.yaml");
f.readAsString().then((String text) {
Map yaml = loadYaml(text);
print(yaml['name']);
print(yaml['description']);
print(yaml['version']);
print(yaml['author']);
print(yaml['homepage']);
print(yaml['dependencies']);
});
}
Regards Robert

None of the above answers worked for me, but here's a working solution for a Flutter app:
In your pubspec.yaml add the "pubspec.yaml" to assets:
assets:
- assets/
- pubspec.yaml
If you have a widget where you need to show the app version like this:
...
Container(
child: Text('Version: 1.0.0+1'),
),
...
Wrap your widget with a FutureBuilder like this:
import 'package:flutter/services.dart';
import 'package:yaml/yaml.dart';
...
FutureBuilder(
future: rootBundle.loadString("pubspec.yaml"),
builder: (context, snapshot) {
String version = "Unknown";
if (snapshot.hasData) {
var yaml = loadYaml(snapshot.data);
version = yaml["version"];
}
return Container(
child: Text(
'Version: $version'
),
);
}),
...
The services rootBundle property contains the resources that were packaged with the application when it was built.
If you want to show the version without the build number, you can split the string like so:
'Version: ${version.split("+")[0]}'
UPDATE: As mentioned by #wildsurfer, this approach has a potential security risk in web development because the pubspec.yaml is shared with the browser!

So assuming that this is for a dart cli application then the #Robert suggestion won't work.
dart_config isn't available for dart 2.x and your pubspec.yaml isn't going to be relative to your cwd except when you are in your development environment
So you need to get the pubspec.yaml relative to the libraries executable path.
This example uses the 'paths' package but it isn't required.
This can be obtained by:
import 'package:path/path.dart';
String pathToYaml = join(dirname(Platform.script.toFilePath()), '../pubspec.yaml');
You can now read the yaml:
import 'package:path/path.dart';
import 'package:yaml/yaml.dart';
String pathToYaml = join(dirname(Platform.script.toFilePath()), '../pubspec.yaml');
File f = new File(pathToYaml);
String yamlText = f.readAsStringSync();
Map yaml = loadYaml(yamlText);
print(yaml['name']);
print(yaml['description']);
print(yaml['version']);
print(yaml['author']);
print(yaml['homepage']);
print(yaml['dependencies']);
});

For Flutter only (Web, Android and IOS)... since October 2020
If you want your app working on Web, Android and IOS use "Package info_plus" instead.

How to Incorporate Automated Version Information into A Dart Command Line App
To update version information in your code without having to package a resource file to be parsed during run time, you can have the information hard coded into an automatically generated dart source file which gets compiled into your binary. The following example hard codes the version, name, and description information into the Map object "meta" in a meta.dart file. The meta.dart file is recreated and overwritten every time the test suite is run in development. To verify the source code has the correct version information, the app's code verifies the version and other meta information against the attributes in the pubspec.yaml file (but only when run as interpreted code in development). If there is a difference from pubspec.yaml, it throws an exception. Once compiled into a binary, it will skip that check as it won't find the pubspec.yaml file, so no error is thrown from the binary. Even if a pubspec.yaml file happens to be around and is found, it only throws an exception and does not create a "meta.dart" source file.
1. Create a MetaUpdate class and save it as "meta_update.dart":
import 'dart:io';
import 'package:yaml/yaml.dart';
import 'meta.dart';
class MetaUpdate {
String pathToYaml = "";
String metaDartFileContents = "";
MetaUpdate(this.pathToYaml);
void writeMetaDartFile(String metaDartFilePath) {
File metaDartFile = File(metaDartFilePath);
String metaDartFileContents = """
/// DO NOT EDIT THIS FILE EXCEPT TO ENTER INITIAL VERSION AND OTHER META INFO
/// THIS FILE IS AUTOMATICALLY OVER WRITTEN BY MetaUpdate
Map<String, String> meta = <String, String>{
"name": "${getPubSpec('name')}",
"description":
// ignore: lines_longer_than_80_chars
"${getPubSpec('description')}",
"version":"${getPubSpec('version')}",
};
""";
metaDartFile.writeAsStringSync(metaDartFileContents);
}
String getPubSpec(String pubSpecParam) {
File f = File(pathToYaml);
String yamlText = f.readAsStringSync();
// ignore: always_specify_types
Map yaml = loadYaml(yamlText);
return yaml[pubSpecParam];
}
void verifyLatestVersionFromPubSpec() {
try {
File f = File(pathToYaml);
//exit if no pubspec found so no warning in production
if (!f.existsSync()) return;
//compare meta.dart with pubspec meta and give warning if difference
if (meta.keys
.where((dynamic e) => (meta[e] != getPubSpec(e)))
.isNotEmpty) {
throw Exception(
"""Version number and other meta attributes in code are different from pubspec.yaml. Please check pubspec.yaml and then run test so that MetaUpdate can update meta information in code, then recompile""");
}
} on Exception {
rethrow;
}
}
}
2. Create a "meta.dart" file:
/// DO NOT EDIT THIS FILE EXCEPT TO ENTER INITIAL VERSION AND OTHER META INFO
/// THIS FILE IS AUTOMATICALLY OVER WRITTEN BY MetaUpdate
Map<String, String> meta = <String, String>{
"name": "Acme Transmogrifier",
"description":
"The best dart application ever.",
"version":"2021.09.001",
};
When you initially create the meta.dart file, copy your specific info from pubspec.yaml. This will later be overwritten each time your MetaUpdate.writeMetaDartFile() is run, changing the contents whenever the info in pubspec.yaml is changed.
3. Implement The Version Update In Your Test Code
Add the following in the first line of Main() in your test code (not the source of the main program, we don't want it to be compiled into the binary), changing the path to meta.dart as appropriate:
MetaUpdate("pubspec.yaml").writeMetaDartFile("lib/src/meta.dart");
4. Add A Meta Check To Your Main Code
Put this in your app's code so it is one of the first methods executed when your app is run - it will generate an exception if there is a difference between the attributes shown in the meta.dart and pubspec.yaml:
MetaUpdate("pubspec.yaml").verifyLatestVersionFromPubSpec();
5. Using
Make sure the Name, Version, and Description information in pubspec.yaml contains the latest information you want reflected in your code.
Import "meta.dart" and insert meta['name'], meta['version'], etc. where you need them to be shown (e.g., in --help or --version messages to be printed to the console).
As long as you run your tests before compiling your code the meta information will be accurately reflected in your code.

You can access pubspec.yaml properties with the official pubspec_parse package from the Dart team.
dart pub add pubspec_parse
import 'dart:io';
import 'package:pubspec_parse/pubspec_parse.dart';
final pubspec = File('pubspec.yaml').readAsStringSync();
final parsed = Pubspec.parse(pubspec);
You can then access typed properties on the parsed object.
You can find supported properties here: https://pub.dev/documentation/pubspec_parse/latest/pubspec_parse/Pubspec-class.html.

Related

How to read and write a text file in Flutter

How do you read text from a file and write text to a file?
I've been learning about how to read and write text to and from a file. I found another question about reading from assets, but that is not the same. I will add my answer below from what I learned from the documentation.
Setup
Add the following plugin in pubspec.yaml:
dependencies:
path_provider: ^1.6.27
Update the version number to whatever is current.
And import it in your code.
import 'package:path_provider/path_provider.dart';
You also have to import dart:io to use the File class.
import 'dart:io';
Writing to a text file
_write(String text) async {
final Directory directory = await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/my_file.txt');
await file.writeAsString(text);
}
Reading from a text file
Future<String> _read() async {
String text;
try {
final Directory directory = await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/my_file.txt');
text = await file.readAsString();
} catch (e) {
print("Couldn't read file");
}
return text;
}
Notes
You can also get the path string with join(directory.path, 'my_file.txt') but you need to import 'package:path/path.dart'.
Flutter's Official Documentation of Reading and Writing Files
This works for iOS, Android, Linux and MacOS but not for web.
As additional info to #Suragch's answer, if you want to find the file you created, you can do as the images show:
And then inside that data folder, go again to a folder named data and search for your package, and then go to:
If you happen to create new files, in order to be able to see them, just right click and click Synchronize.
An another way to pull the file from the device is by using adb pull command. You can find the file path by debugging the code and then use adb pull command. adb is located in Android SDK -> platform-tools directory.
./adb pull /storage/emulated/0/Android/data/com.innovate.storage.storage_sample/files/sample.txt ~/Downloads
#Suragch 's answer is right. Except the version of path_provider that you want to use now is:
path_provider: ^2.0.9

How to add a File Picker plugin in Flutter?

I am creating a Flutter project in which, I have a piece of data (JSON) that I want to Import from and Export to a location the user wants to. In order to achieve this, I require a File Picker plugin in Flutter. Now, I searched the Dart Packages repository for "file picker" but didn't find one.
Is there a way to get a File Picker that looks like this:
or even this...
The first screenshot is preferable for me as it allows file selection from different sources (like Drive).
Also, since I want to Export the data, I might want a Folder Picker too. ;)
But, if there is any other alternative to Folder Picker. I'd be happy to know...
I've created a file_picker plugin some time ago in order to make it possible to pick (both on iOS and Android) absolute paths and then loaded it with Flutter.
You can check it here: https://pub.dev/packages/file_picker
I used file_picker library to pick files. you can use this for pick images as well.
Future getPdfAndUpload(int position) async {
File file = await FilePicker.getFile(
type: FileType.custom,
allowedExtensions: ['pdf','docx'], //here you can add any of extention what you need to pick
);
if(file != null) {
setState(() {
file1 = file; //file1 is a global variable which i created
});
}
}
here file_picker flutter library.
I'm in the exact same boat as you, haha!
I noticed documents_picker 0.0.2. It allows the user to pick multiple files, and it seems to fit the need!
check it out: https://pub.dartlang.org/packages/documents_picker#-readme-tab-
Here's a better document picker. It looks like the native document picker from the Storage Access Framework, which is what you have in your picture.
flutter_document_picker
Just found the FileSelector plugin from flutter.dev. Compatible with MacOS, Windows and Web.
From its pub.dev page:
Open a single file
final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']);
final file = await openFile(acceptedTypeGroups: [typeGroup]);
Open multiple files at once
final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']);
final files = await openFiles(acceptedTypeGroups: [typeGroup]);
Saving a file
final path = await getSavePath();
final name = "hello_file_selector.txt";
final data = Uint8List.fromList("Hello World!".codeUnits);
final mimeType = "text/plain";
final file = XFile.fromData(data, name: name, mimeType: mimeType);
await file.saveTo(path);
MacOS: Provide file read or/and write privileges
On target MacOS please provide sufficient rights using Xcode:
In case you don't provide file read or/and write permissions, the call to
final XFile? file =
await openFile(acceptedTypeGroups: <XTypeGroup>[typeGroup]);
neither shows anything not returns.

static final object changes identity

I have a browser application written in Dart. I noticed a strange error appearing where my StageXL ResourceManager was missing the resources that it previously had. After debugging the program for a while I ended up with this situation:
In global.dart:
class Global {
static final ResourceManager resourceManager = new ResourceManager();
}
In main function:
var resources = Global.resourceManager;
resources.addBitmapData("Player", "images/player_base.png");
await resources.load();
print("in main: ${identityHashCode(Global.resourceManager)} = "
" ${Global.resourceManager.resources}, isolate: ${identityHashCode(
Isolate.current)}");
In another function where I need to access the resource afterwards:
print("elsewhere: ${identityHashCode(Global.resourceManager)} = "
" ${Global.resourceManager.resources}, isolate: ${identityHashCode(
Isolate.current)}");
Expected output (identityHashCodes match and so do the object contents):
in main: 12345678 = [ResourceManagerResource [kind=BitmapData, name=Player,
url = images/player_base.png]], isolate: 09876543
elsewhere: 12345678 = [ResourceManagerResource [kind=BitmapData,
name=Player, url = images/player_base.png]], isolate: 09876543
Actual output (note the identityHashCode mismatch):
in main: 516570559 = [ResourceManagerResource
[kind=BitmapData, name=Player, url = images/player_base.png]],
isolate: 843028171
elsewhere: 419835243 = [], isolate: 843028171
I thought this may have something to do with running in a different isolate (not familiar with them) but as you can see, the current isolates identityHashCodes match.
That is surprising. My best guess is that you are importing the same library twice, using different URIs. The fact that one of your files is a "main" file supports this, since its a common mistake to specify the main file on the command line as a file and have it import a package library using a relative reference.
Is your "main" file in a package lib directory, and does it import the resource file using a relative path? If so, try changing that import to a package:packageName/thepath URI instead and see if it changes anything.
(My personal recommendation is to never have a Dart library URL that contains lib, whether in an import/export or on the command line. Always use a package: URI in that case instead.)

How to load resource files or package: URIs in Dart?

There is a way to get the path of the currently executing script and then find the resource files relative to this location, but there is no guarantee that the directory structure is the same when the application is published.
Does Dart provide a generic way to load resources (files, data, ...) in a way that works also with pub run or pub global run?
Asked another way, how do I dynamically load the contents of a package: URI, in a Dart app, at runtime?
Update
The resource package was published.
Update
This is being reworked. The Resource class will be moved to a package.
Original
There is a new Resource class in Dart (tested with Dart VM version: 1.12.0-edge.d4d89d6f12b44514336f1af748e466607bcd1453 (Fri Aug 7 19:38:14 2015))
resource_example/lib/resource/sample.txt
Sample text file.
resource_example/bin/main.dart
main() async {
var resource = const Resource('package:resource_example/resource/sample.txt');
print(await resource.readAsString());
// or for binary resources
// print(await resource.readAsBytes());
}
Executing
dart bin/main.dart
prints
Sample text file.
Executing
pub global activate -spath .
pub global run resource_example:main
also prints
Sample text file.
It's also possible to pass the content of a variable as a resource. This might for example be convenient in unit tests to pass mock resources.
const sampleText = "Sample const text";
main() async {
var uriEncoded = sampleText.replaceAll(' ', '%20');
var resource = new Resource('data:application/dart;charset=utf-8,$uriEncoded');
print(await resource.readAsString());
}
Arbitrary file or http uris are supported as well but this might not work when using pub run or pub global run when using relative paths.
main() async {
var uri = new Uri.file('lib/resource/sample.txt');
var resource = new Resource(uri.toString());
print(await resource.readAsString());
}
For more details see
- http://blog.sethladd.com/2015/08/dynamically-load-package-contents-with.html
- https://codereview.chromium.org/1276263002/

Using tranformer output files from pub run

I'm trying to figure out how I can use transformers for my Dart Server application. So far I've read the articles at https://www.dartlang.org/tools/pub/assets-and-transformers.html and https://www.dartlang.org/tools/pub/transformers/ and have used that information to write the following transformer.
import 'dart:async';
import 'package:barback/barback.dart';
class TestTransformer extends Transformer {
final BarbackSettings _settings;
TestTransformer.asPlugin(this._settings);
AssetId assetId;
Future<bool> isPrimary(AssetId id) {
assetId = id;
return new Future.value(id.toString().endsWith('.txt'));
}
apply(Transform transform) {
transform.primaryInput.readAsString().then((text) {
String output = text.toUpperCase;
print(output);
transform.addOutput(new Asset.fromString(assetId.addExtension('.upper'), output));
});
}
}
When I use pub run I see the transformer run, and the print line outputs as expected. What I'm missing is the *.txt.upper output file. I can't find such a file anywhere on my system. Where does this output end up, and how can I use it in my code? By use I mean being able to read the file with File.open() and, if the output is a Dart file, import it into another Dart file.
As far as I know are transformers for console/server applications not supported. When I run pub upgrade I also see after Precompiling dependencies that some transformers are loaded but this doesn't seem to process your source files.

Resources