Commit cd89e867 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

About box API (#4677)

This API is the front-end part of the work on showing licenses.

Future patches will:

* Provide an API for registering what licenses should be shown here,
  which will be used by this feature to shown licenses but could also be
  used by custom code for showing licenses (e.g. for people not using
  the Material widgets).

* Actually populate this license API from all the licenses we currently
  use in the engine, in the framework, and from any pub packages that
  are used (directly or indirectly) by the application.
parent ef49e28e
......@@ -52,7 +52,7 @@ class TypographyDemo extends StatelessWidget {
new TextStyleItem(name: 'Title', style: textTheme.title, text: 'Medium 20sp'),
new TextStyleItem(name: 'Subheading', style: textTheme.subhead, text: 'Regular 16sp'),
new TextStyleItem(name: 'Body 2', style: textTheme.body2, text: 'Medium 14sp'),
new TextStyleItem(name: 'Body 1', style: textTheme.body1, text: 'Reguluar 14sp'),
new TextStyleItem(name: 'Body 1', style: textTheme.body1, text: 'Regular 14sp'),
new TextStyleItem(name: 'Caption', style: textTheme.caption, text: 'Regular 12sp'),
new TextStyleItem(name: 'Button', style: textTheme.button, text: 'MEDIUM (ALL CAPS) 14sp')
];
......
......@@ -94,6 +94,18 @@ class GalleryDrawer extends StatelessWidget {
]
)
),
new AboutDrawerItem(
applicationVersion: '2016 Q2 Preview',
applicationIcon: new AssetImage('packages/flutter_gallery_assets/appbar_background.jpg'),
applicationLegalese: '© 2016 The Chromium Authors',
aboutBoxChildren: <Widget>[
new Padding(
padding: const EdgeInsets.only(top: 24.0, bottom: 8.0),
child: new Center(child: new CircularProgressIndicator())
),
new Text('Awaiting fix for issue 4512.', textAlign: TextAlign.center),
]
),
]
)
);
......
......@@ -11,6 +11,7 @@
/// for an introduction to Material Design.
library material;
export 'src/material/about.dart';
export 'src/material/app.dart';
export 'src/material/app_bar.dart';
export 'src/material/bottom_sheet.dart';
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'app_bar.dart';
import 'debug.dart';
import 'dialog.dart';
import 'drawer_item.dart';
import 'flat_button.dart';
import 'icon.dart';
import 'page.dart';
import 'scaffold.dart';
import 'theme.dart';
/// A [DrawerItem] to show an about box.
///
/// Place this in a [Drawer], specifying your preferred application name,
/// version, icon, and copyright in the appropriate fields.
///
/// The about box will include a button that shows licenses for software used by
/// the application.
///
/// If your application does not have a [Drawer], you should provide an
/// affordance to call [showAboutDialog] or (at least) [showLicensePage].
// TODO(ianh): Mention the API for registering more licenses once it exists.
class AboutDrawerItem extends StatelessWidget {
/// Creates a drawer item for showing an about box.
///
/// The arguments are all optional. The application name, if omitted, will be
/// derived from the nearest [Title] widget. The version, icon, and legalese
/// values default to the empty string.
AboutDrawerItem({
Key key,
this.icon: const Icon(null),
this.child,
this.applicationName,
this.applicationVersion,
this.applicationIcon,
this.applicationLegalese,
this.aboutBoxChildren
}) : super(key: key);
/// The icon to show for this drawer item.
///
/// By default no icon is shown.
///
/// This is not necessarily the same as the image shown in the dialog box
/// itself; which is controlled by the [applicationIcon] property.
final Widget icon;
/// The label to show on this drawer item.
///
/// Defaults to a text widget that says "About Foo" where "Foo" is the
/// application name specified by [applicationName].
final Widget child;
/// The name of the application.
///
/// This string is used in the default label for this drawer item (see
/// [child]) and as the caption of the [AboutDialog] that is shown.
///
/// Defaults to the value of [Title.title], if a [Title] widget can be found.
/// Otherwise, defaults to "this Flutter application".
// TODO(ianh): once https://github.com/flutter/flutter/issues/3648 is fixed:
// /// Otherwise, defaults to [Platform.resolvedExecutable].
final String applicationName;
/// The version of this build of the application.
///
/// This string is shown under the application name in the [AboutDialog].
///
/// Defaults to the empty string.
final String applicationVersion;
/// The icon to show next to the application name in the [AboutDialog].
///
/// By default no icon is shown.
///
/// This is not necessarily the same as the icon shown on the drawer item
/// itself, which is controlled by the [icon] property.
final ImageProvider applicationIcon;
/// A string to show in small print in the [AboutDialog].
///
/// Typically this is a copyright notice.
///
/// Defaults to the empty string.
final String applicationLegalese;
/// Widgets to add to the [AboutDialog] after the name, version, and legalese.
///
/// This could include a link to a Web site, some descriptive text, credits,
/// or other information to show in the about box.
///
/// Defaults to nothing.
final List<Widget> aboutBoxChildren;
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
return new DrawerItem(
icon: icon,
child: child ?? new Text('About ${applicationName ?? _defaultApplicationName(context)}'),
onPressed: () {
showAboutDialog(
context: context,
applicationName: applicationName,
applicationVersion: applicationVersion,
applicationIcon: applicationIcon,
applicationLegalese: applicationLegalese,
children: aboutBoxChildren
);
}
);
}
}
/// Displays an [AboutDialog], which describes the application and provides a
/// button to show licenses for software used by the application.
///
/// The arguments correspond to the properties on [AboutDialog].
///
/// If the application has a [Drawer], consider using [AboutDrawerItem] instead
/// of calling this directly.
///
/// If you do not need an about box in your application, you should at least
/// provide an affordance to call [showLicensePage].
void showAboutDialog({
@required BuildContext context,
String applicationName,
String applicationVersion,
ImageProvider applicationIcon,
String applicationLegalese,
List<Widget> children
}) {
showDialog/*<Null>*/(
context: context,
child: new AboutDialog(
applicationName: applicationName,
applicationVersion: applicationVersion,
applicationIcon: applicationIcon,
applicationLegalese: applicationLegalese,
children: children
)
);
}
/// Displays a [LicensePage], which shows licenses for software used by the
/// application.
///
/// The arguments correspond to the properties on [LicensePage].
///
/// If the application has a [Drawer], consider using [AboutDrawerItem] instead
/// of calling this directly.
///
/// The [AboutDialog] shown by [showAboutDialog] includes a button that calls
/// [showLicensePage].
// TODO(ianh): Mention the API for registering more licenses once it exists.
void showLicensePage({
@required BuildContext context,
String applicationName,
String applicationVersion,
ImageProvider applicationIcon,
String applicationLegalese
}) {
Navigator.openTransaction(context, (NavigatorTransaction transaction) {
// TODO(ianh): remove pop once https://github.com/flutter/flutter/issues/4667 is fixed
transaction.pop();
transaction.push(new MaterialPageRoute<Null>(
builder: (BuildContext context) => new LicensePage(
applicationName: applicationName,
applicationVersion: applicationVersion,
applicationLegalese: applicationLegalese
)
));
});
}
/// An about box. This is a dialog box with the application's icon, name,
/// version number, and copyright, plus a button to show licenses for software
/// used by the application.
///
/// To show an [AboutDialog], use [showAboutDialog].
class AboutDialog extends StatelessWidget {
/// Creates an about box.
///
/// The arguments are all optional. The application name, if omitted, will be
/// derived from the nearest [Title] widget. The version, icon, and legalese
/// values default to the empty string.
AboutDialog({
Key key,
this.applicationName,
this.applicationVersion,
this.applicationIcon,
this.applicationLegalese,
this.children
}) : super(key: key);
/// The name of the application.
///
/// Defaults to the value of [Title.title], if a [Title] widget can be found.
/// Otherwise, defaults to "this Flutter application".
// TODO(ianh): once https://github.com/flutter/flutter/issues/3648 is fixed:
// /// Otherwise, defaults to [Platform.resolvedExecutable].
final String applicationName;
/// The version of this build of the application.
///
/// This string is shown under the application name.
///
/// Defaults to the empty string.
final String applicationVersion;
/// The icon to show next to the application name.
///
/// By default no icon is shown.
final ImageProvider applicationIcon;
/// A string to show in small print.
///
/// Typically this is a copyright notice.
///
/// Defaults to the empty string.
final String applicationLegalese;
/// Widgets to add to the dialog box after the name, version, and legalese.
///
/// This could include a link to a Web site, some descriptive text, credits,
/// or other information to show in the about box.
///
/// Defaults to nothing.
final List<Widget> children;
@override
Widget build(BuildContext context) {
final String name = applicationName ?? _defaultApplicationName(context);
final String version = applicationVersion ?? _defaultApplicationVersion(context);
final ImageProvider icon = applicationIcon ?? _defaultApplicationIcon(context);
List<Widget> body = <Widget>[];
if (icon != null) {
body.add(new Image(
image: icon,
width: 48.0,
height: 48.0
));
}
body.add(new Flexible(
child: new Padding(
padding: new EdgeInsets.symmetric(horizontal: 24.0),
child: new BlockBody(
children: <Widget>[
new Text(name, style: Theme.of(context).textTheme.headline),
new Text(version, style: Theme.of(context).textTheme.body1),
new Container(height: 18.0),
new Text(applicationLegalese ?? '', style: Theme.of(context).textTheme.caption)
]
)
)
));
body = <Widget>[
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: body
),
];
if (children != null)
body.addAll(children);
return new Dialog(
content: new Block(
children: body
),
actions: <Widget>[
new FlatButton(
child: new Text('VIEW LICENSES'),
onPressed: () {
showLicensePage(
context: context,
applicationName: applicationName,
applicationVersion: applicationVersion,
applicationIcon: applicationIcon,
applicationLegalese: applicationLegalese
);
}
),
new FlatButton(
child: new Text('CLOSE'),
onPressed: () {
Navigator.pop(context);
}
),
]
);
}
}
/// A page that shows licenses for software used by the application.
///
/// To show a [LicensePage], use [showLicensePage].
// TODO(ianh): Mention the API for registering more licenses once it exists.
class LicensePage extends StatelessWidget {
/// Creates a page that shows licenses for software used by the application.
///
/// The arguments are all optional. The application name, if omitted, will be
/// derived from the nearest [Title] widget. The version and legalese values
/// default to the empty string.
// TODO(ianh): Mention the API for registering more licenses once it exists.
const LicensePage({
Key key,
this.applicationName,
this.applicationVersion,
this.applicationLegalese
}) : super(key: key);
/// The name of the application.
///
/// Defaults to the value of [Title.title], if a [Title] widget can be found.
/// Otherwise, defaults to "this Flutter application".
// TODO(ianh): once https://github.com/flutter/flutter/issues/3648 is fixed:
// /// Otherwise, defaults to [Platform.resolvedExecutable].
final String applicationName;
/// The version of this build of the application.
///
/// This string is shown under the application name.
///
/// Defaults to the empty string.
final String applicationVersion;
/// A string to show in small print.
///
/// Typically this is a copyright notice.
///
/// Defaults to the empty string.
final String applicationLegalese;
@override
Widget build(BuildContext context) {
final String name = applicationName ?? _defaultApplicationName(context);
final String version = applicationVersion ?? _defaultApplicationVersion(context);
return new Scaffold(
appBar: new AppBar(
title: new Text('Licenses')
),
body: new Block(
padding: new EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
children: <Widget>[
new Text(name, style: Theme.of(context).textTheme.headline, textAlign: TextAlign.center),
new Text(version, style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
new Container(height: 18.0),
new Text(applicationLegalese ?? '', style: Theme.of(context).textTheme.caption, textAlign: TextAlign.center),
new Container(height: 18.0),
new Text('Powered by Flutter', style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
new Container(height: 24.0),
// TODO(ianh): Fill in the licenses from the API for registering more licenses once it exists.
new Text('<licenses will be automatically included here>', style: Theme.of(context).textTheme.caption)
]
)
);
}
}
String _defaultApplicationName(BuildContext context) {
Title ancestorTitle = context.ancestorWidgetOfExactType(Title);
return ancestorTitle?.title ?? 'this Flutter application';
// TODO(ianh): once https://github.com/flutter/flutter/issues/3648 is fixed,
// replace the string in the previous line with:
// Platform.resolvedExecutable.split(Platform.pathSeparator).last
// (then fix the dartdocs in the classes above)
}
String _defaultApplicationVersion(BuildContext context) {
// TODO(ianh): Get this from the embedder somehow.
return '';
}
ImageProvider _defaultApplicationIcon(BuildContext context) {
// TODO(ianh): Get this from the embedder somehow.
return null;
}
......@@ -18,6 +18,7 @@ import 'theme.dart';
/// dialog.
///
/// See also:
///
/// * [showDialog]
/// * <https://www.google.com/design/spec/components/dialogs.html>
class Dialog extends StatelessWidget {
......@@ -35,6 +36,8 @@ class Dialog extends StatelessWidget {
/// The (optional) title of the dialog is displayed in a large font at the top
/// of the dialog.
///
/// Typically a [Text] widget.
final Widget title;
/// Padding around the title.
......@@ -45,6 +48,10 @@ class Dialog extends StatelessWidget {
/// The (optional) content of the dialog is displayed in the center of the
/// dialog in a lighter font.
///
/// Typically, this is a [Block] containing the contents of the dialog. Using
/// a [Block] ensures that the contents can scroll if they are too big to fit
/// on the display.
final Widget content;
/// Padding around the content.
......@@ -54,6 +61,10 @@ class Dialog extends StatelessWidget {
/// The (optional) set of actions that are displayed at the bottom of the
/// dialog.
///
/// Typically this is a list of [FlatButton] widgets.
///
/// These widgets will be wrapped in a [ButtonBar].
final List<Widget> actions;
Color _getColor(BuildContext context) {
......
......@@ -31,7 +31,7 @@ class DrawerItem extends StatelessWidget {
/// Requires one of its ancestors to be a [Material] widget.
const DrawerItem({
Key key,
this.icon,
this.icon: const Icon(null),
this.child,
this.onPressed,
this.selected: false
......
......@@ -22,6 +22,7 @@ import 'theme.dart';
/// its body.
///
/// See also:
///
/// * [AppBar]
/// * [Scaffold]
/// * <https://www.google.com/design/spec/patterns/scrolling-techniques.html>
......
......@@ -87,7 +87,7 @@ class TextTheme {
/// Used for the default text style for [Material].
final TextStyle body1;
/// Used for auxillary text associted with images.
/// Used for auxillary text associated with images.
final TextStyle caption;
/// Used for text on [RaisedButton] and [FlatButton].
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment