Introduction

flutter_oss_licenses is a tool for generating detail and better OSS license list using pubspec.lock.

Installing

Adding the package name to dev_dependencies; not to dependencies because the package does nothing on runtime.

dev_dependencies:
  flutter_oss_licenses: ^0.6.3

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

Based 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 ossl = ossLicenses[key];
              final version = ossl['version'];
              final desc = ossl['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: ossl)))
            );
            },
            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

Either running generate.dart using pub run or directly, it accepts two or less options. The first option is output dart file name. The default is lib/oss_licenses.dart. And the another is project root, which is by default detected automatically.

$ generate.dart [OUTPUT_FILENAME [PROJECT_ROOT]]

The bin/generated.dart uses two environment variables; one is FLUTTER_ROOT and PUB_CACHE. They are normally set by flutter pub run but if you directly execute the script, you must set them manually.

Reporting issues

Report any bugs on the project's issues.

URLs