Introduction
flutter_oss_licenses is a tool to generate detail and better OSS license list using pubspec.yaml/lock files.
Unlike the package name, it still runs with pure Dart environment :)
Installing
Adding the package name to dev_dependencies; not to dependencies because the package does nothing on runtime.
dev_dependencies:
flutter_oss_licenses: ^1.1.4
Generate oss_licenses.dart
Before executing the command, you must update your pubspec.lock using pub get (or pub upgrade if you want).
flutter pub get
And then, the following command generates oss_licenses.dart on the project's lib/ directory:
flutter pub run flutter_oss_licenses:generate.dart
The file structure
The generated file contains a simple Map<String, dynamic> that maps each project name to its corresponding license text, that is normally provided by LICENSE file on the project:
/// This code was generated by flutter_oss_licenses
/// https://pub.dev/packages/flutter_oss_licenses
final ossLicenses = <String, dynamic>{
"hello_world_dummy": {
"name": "hello_world_dummy",
"description": "Sample hello world dummy description.",
"homepage": "https://github.com/espresso3389",
"authors": ["Takashi Kawasaki <[email protected]>"],
"version": "1.0.0",
"license": "## Hello, world\nDummy copyright here!",
"isMarkdown": true,
"isSdk": false,
"isDirectDependency": false
},
...
}
And, you can use the map on your project code in your way. The package does not do anything on the list.
Flutter usage sample
Using on the generated oss_licenses.dart file, you can create your own license page like the following one:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'oss_licenses.dart';
class OssLicensesPage extends StatelessWidget {
static Future<List<String>> loadLicenses() async {
// merging non-dart based dependency list using LicenseRegistry.
final ossKeys = ossLicenses.keys.toList();
final lm = <String, List<String>>{};
await for (var l in LicenseRegistry.licenses) {
for (var p in l.packages) {
if (!ossKeys.contains(p)) {
final lp = lm.putIfAbsent(p, () => []);
lp.addAll(l.paragraphs.map((p) => p.text));
ossKeys.add(p);
}
}
}
for (var key in lm.keys) {
ossLicenses[key] = {
'license': lm[key].join('\n')
};
}
return ossKeys..sort();
}
static final _licenses = loadLicenses();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Open Source Licenses'),
),
body: FutureBuilder<List<String>>(
future: _licenses,
builder: (context, snapshot) {
return ListView.separated(
padding: const EdgeInsets.all(0),
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) {
final key = snapshot.data[index];
final licenseJson = ossLicenses[key];
final version = licenseJson['version'];
final desc = licenseJson['description'];
return ListTile(
title: Text('$key ${version ?? ''}'),
subtitle: desc != null ? Text(desc) : null,
trailing: Icon(Icons.chevron_right),
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => MiscOssLicenseSingle(name: key, json: licenseJson)))
);
},
separatorBuilder: (context, index) => const Divider()
);
}
)
);
}
}
class MiscOssLicenseSingle extends StatelessWidget {
final String name;
final Map<String, dynamic> json;
String get version => json['version'];
String get description => json['description'];
String get licenseText => json['license'];
String get homepage => json['homepage'];
MiscOssLicenseSingle({this.name, this.json});
String _bodyText() {
return licenseText.split('\n').map((line) {
if (line.startsWith('//')) line = line.substring(2);
line = line.trim();
return line;
}).join('\n');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('$name ${version ?? ''}')),
body: Container(
color: Theme.of(context).canvasColor,
child: ListView(children: <Widget>[
if (description != null)
Padding(
padding: const EdgeInsets.only(top: 12.0, left: 12.0, right: 12.0),
child: Text(
description,
style: Theme.of(context).textTheme.bodyText2.copyWith(fontWeight: FontWeight.bold))
),
if (homepage != null)
Padding(
padding: const EdgeInsets.only(top: 12.0, left: 12.0, right: 12.0),
child: InkWell(
child: Text(homepage, style: const TextStyle(color: Colors.blue, decoration: TextDecoration.underline)),
onTap: () => launch(homepage),
)
),
if (description != null || homepage != null)
const Divider(),
Padding(
padding: const EdgeInsets.only(top: 12.0, left: 12.0, right: 12.0),
child: Text(
_bodyText(),
style: Theme.of(context).textTheme.bodyText2
),
),
])
),
);
}
}
Command line options
The following command line generates JSON file instead of dart file:
flutter pub run flutter_oss_licenses:generate.dart -o licenses.json --json
The following table lists the acceptable options:
| Option | Abbr. | Description |
|---|---|---|
--output OUTPUT_FILE_PATH |
-o |
Specify output file path. If the file extension is .json, --json option is implied anyway. The default output file path depends on the --json flag:with --json: PROJECT_ROOT/assets/oss_licenses.jsonwithout --json: PROJECT_ROOT/lib/oss_licenses.dart |
--project-root PROJECT_ROOT |
-p |
Explicitly specify project root directory that contains pubspec.lock. |
--json |
-j |
Generate JSON file rather than dart file. |
--help |
-h |
Show the help. |
Environment variables
The bin/generated.dart uses one or two environment variable(s) depending on your use case:
PUB_CACHEis used to determine package directory.FLUTTER_ROOTis for Flutter projects only. If not set, Flutter SDK dependencies are simply ignored and not listed.
They are normally set by dart run or flutter pub run.
Reporting issues
Report any bugs on the project's issues.