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 <espresso3389@gmail.com>"],
    "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.json
without --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_CACHE is used to determine package directory.
  • FLUTTER_ROOT is 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.

URLs

Libraries