Revert "migrate some material files to nullsafety (#67078)" (#67183)

This reverts commit 8143992262.
This commit is contained in:
Michael Goderbauer 2020-10-03 04:54:45 -07:00 committed by GitHub
parent cbf1e135c4
commit a0a65fc604
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1244 additions and 1176 deletions

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:developer' show Timeline, Flow; import 'dart:developer' show Timeline, Flow;
import 'dart:io' show Platform; import 'dart:io' show Platform;
@ -121,7 +123,7 @@ class AboutListTile extends StatelessWidget {
/// derived from the nearest [Title] widget. The version, icon, and legalese /// derived from the nearest [Title] widget. The version, icon, and legalese
/// values default to the empty string. /// values default to the empty string.
const AboutListTile({ const AboutListTile({
Key? key, Key key,
this.icon, this.icon,
this.child, this.child,
this.applicationName, this.applicationName,
@ -138,13 +140,13 @@ class AboutListTile extends StatelessWidget {
/// ///
/// This is not necessarily the same as the image shown in the dialog box /// This is not necessarily the same as the image shown in the dialog box
/// itself; which is controlled by the [applicationIcon] property. /// itself; which is controlled by the [applicationIcon] property.
final Widget? icon; final Widget icon;
/// The label to show on this drawer item. /// The label to show on this drawer item.
/// ///
/// Defaults to a text widget that says "About Foo" where "Foo" is the /// Defaults to a text widget that says "About Foo" where "Foo" is the
/// application name specified by [applicationName]. /// application name specified by [applicationName].
final Widget? child; final Widget child;
/// The name of the application. /// The name of the application.
/// ///
@ -153,14 +155,14 @@ class AboutListTile extends StatelessWidget {
/// ///
/// Defaults to the value of [Title.title], if a [Title] widget can be found. /// Defaults to the value of [Title.title], if a [Title] widget can be found.
/// Otherwise, defaults to [Platform.resolvedExecutable]. /// Otherwise, defaults to [Platform.resolvedExecutable].
final String? applicationName; final String applicationName;
/// The version of this build of the application. /// The version of this build of the application.
/// ///
/// This string is shown under the application name in the [AboutDialog]. /// This string is shown under the application name in the [AboutDialog].
/// ///
/// Defaults to the empty string. /// Defaults to the empty string.
final String? applicationVersion; final String applicationVersion;
/// The icon to show next to the application name in the [AboutDialog]. /// The icon to show next to the application name in the [AboutDialog].
/// ///
@ -171,14 +173,14 @@ class AboutListTile extends StatelessWidget {
/// ///
/// This is not necessarily the same as the icon shown on the drawer item /// This is not necessarily the same as the icon shown on the drawer item
/// itself, which is controlled by the [icon] property. /// itself, which is controlled by the [icon] property.
final Widget? applicationIcon; final Widget applicationIcon;
/// A string to show in small print in the [AboutDialog]. /// A string to show in small print in the [AboutDialog].
/// ///
/// Typically this is a copyright notice. /// Typically this is a copyright notice.
/// ///
/// Defaults to the empty string. /// Defaults to the empty string.
final String? applicationLegalese; final String applicationLegalese;
/// Widgets to add to the [AboutDialog] after the name, version, and legalese. /// Widgets to add to the [AboutDialog] after the name, version, and legalese.
/// ///
@ -186,14 +188,14 @@ class AboutListTile extends StatelessWidget {
/// or other information to show in the about box. /// or other information to show in the about box.
/// ///
/// Defaults to nothing. /// Defaults to nothing.
final List<Widget>? aboutBoxChildren; final List<Widget> aboutBoxChildren;
/// Whether this list tile is part of a vertically dense list. /// Whether this list tile is part of a vertically dense list.
/// ///
/// If this property is null, then its value is based on [ListTileTheme.dense]. /// If this property is null, then its value is based on [ListTileTheme.dense].
/// ///
/// Dense list tiles default to a smaller height. /// Dense list tiles default to a smaller height.
final bool? dense; final bool dense;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -201,7 +203,7 @@ class AboutListTile extends StatelessWidget {
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
return ListTile( return ListTile(
leading: icon, leading: icon,
title: child ?? Text(MaterialLocalizations.of(context)!.aboutListTileTitle( title: child ?? Text(MaterialLocalizations.of(context).aboutListTileTitle(
applicationName ?? _defaultApplicationName(context), applicationName ?? _defaultApplicationName(context),
)), )),
dense: dense, dense: dense,
@ -236,14 +238,14 @@ class AboutListTile extends StatelessWidget {
/// The [context], [useRootNavigator] and [routeSettings] arguments are passed to /// The [context], [useRootNavigator] and [routeSettings] arguments are passed to
/// [showDialog], the documentation for which discusses how it is used. /// [showDialog], the documentation for which discusses how it is used.
void showAboutDialog({ void showAboutDialog({
required BuildContext context, @required BuildContext context,
String? applicationName, String applicationName,
String? applicationVersion, String applicationVersion,
Widget? applicationIcon, Widget applicationIcon,
String? applicationLegalese, String applicationLegalese,
List<Widget>? children, List<Widget> children,
bool useRootNavigator = true, bool useRootNavigator = true,
RouteSettings? routeSettings, RouteSettings routeSettings,
}) { }) {
assert(context != null); assert(context != null);
assert(useRootNavigator != null); assert(useRootNavigator != null);
@ -283,16 +285,16 @@ void showAboutDialog({
/// The licenses shown on the [LicensePage] are those returned by the /// The licenses shown on the [LicensePage] are those returned by the
/// [LicenseRegistry] API, which can be used to add more licenses to the list. /// [LicenseRegistry] API, which can be used to add more licenses to the list.
void showLicensePage({ void showLicensePage({
required BuildContext context, @required BuildContext context,
String? applicationName, String applicationName,
String? applicationVersion, String applicationVersion,
Widget? applicationIcon, Widget applicationIcon,
String? applicationLegalese, String applicationLegalese,
bool useRootNavigator = false, bool useRootNavigator = false,
}) { }) {
assert(context != null); assert(context != null);
assert(useRootNavigator != null); assert(useRootNavigator != null);
Navigator.of(context, rootNavigator: useRootNavigator)!.push(MaterialPageRoute<void>( Navigator.of(context, rootNavigator: useRootNavigator).push(MaterialPageRoute<void>(
builder: (BuildContext context) => LicensePage( builder: (BuildContext context) => LicensePage(
applicationName: applicationName, applicationName: applicationName,
applicationVersion: applicationVersion, applicationVersion: applicationVersion,
@ -328,7 +330,7 @@ class AboutDialog extends StatelessWidget {
/// derived from the nearest [Title] widget. The version, icon, and legalese /// derived from the nearest [Title] widget. The version, icon, and legalese
/// values default to the empty string. /// values default to the empty string.
const AboutDialog({ const AboutDialog({
Key? key, Key key,
this.applicationName, this.applicationName,
this.applicationVersion, this.applicationVersion,
this.applicationIcon, this.applicationIcon,
@ -340,14 +342,14 @@ class AboutDialog extends StatelessWidget {
/// ///
/// Defaults to the value of [Title.title], if a [Title] widget can be found. /// Defaults to the value of [Title.title], if a [Title] widget can be found.
/// Otherwise, defaults to [Platform.resolvedExecutable]. /// Otherwise, defaults to [Platform.resolvedExecutable].
final String? applicationName; final String applicationName;
/// The version of this build of the application. /// The version of this build of the application.
/// ///
/// This string is shown under the application name. /// This string is shown under the application name.
/// ///
/// Defaults to the empty string. /// Defaults to the empty string.
final String? applicationVersion; final String applicationVersion;
/// The icon to show next to the application name. /// The icon to show next to the application name.
/// ///
@ -355,14 +357,14 @@ class AboutDialog extends StatelessWidget {
/// ///
/// Typically this will be an [ImageIcon] widget. It should honor the /// Typically this will be an [ImageIcon] widget. It should honor the
/// [IconTheme]'s [IconThemeData.size]. /// [IconTheme]'s [IconThemeData.size].
final Widget? applicationIcon; final Widget applicationIcon;
/// A string to show in small print. /// A string to show in small print.
/// ///
/// Typically this is a copyright notice. /// Typically this is a copyright notice.
/// ///
/// Defaults to the empty string. /// Defaults to the empty string.
final String? applicationLegalese; final String applicationLegalese;
/// Widgets to add to the dialog box after the name, version, and legalese. /// Widgets to add to the dialog box after the name, version, and legalese.
/// ///
@ -370,30 +372,30 @@ class AboutDialog extends StatelessWidget {
/// or other information to show in the about box. /// or other information to show in the about box.
/// ///
/// Defaults to nothing. /// Defaults to nothing.
final List<Widget>? children; final List<Widget> children;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final String name = applicationName ?? _defaultApplicationName(context); final String name = applicationName ?? _defaultApplicationName(context);
final String version = applicationVersion ?? _defaultApplicationVersion(context); final String version = applicationVersion ?? _defaultApplicationVersion(context);
final Widget? icon = applicationIcon ?? _defaultApplicationIcon(context); final Widget icon = applicationIcon ?? _defaultApplicationIcon(context);
return AlertDialog( return AlertDialog(
content: ListBody( content: ListBody(
children: <Widget>[ children: <Widget>[
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
if (icon != null) IconTheme(data: Theme.of(context)!.iconTheme, child: icon), if (icon != null) IconTheme(data: Theme.of(context).iconTheme, child: icon),
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0), padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: ListBody( child: ListBody(
children: <Widget>[ children: <Widget>[
Text(name, style: Theme.of(context)!.textTheme.headline5), Text(name, style: Theme.of(context).textTheme.headline5),
Text(version, style: Theme.of(context)!.textTheme.bodyText2), Text(version, style: Theme.of(context).textTheme.bodyText2),
const SizedBox(height: _textVerticalSeparation), const SizedBox(height: _textVerticalSeparation),
Text(applicationLegalese ?? '', style: Theme.of(context)!.textTheme.caption), Text(applicationLegalese ?? '', style: Theme.of(context).textTheme.caption),
], ],
), ),
), ),
@ -405,7 +407,7 @@ class AboutDialog extends StatelessWidget {
), ),
actions: <Widget>[ actions: <Widget>[
TextButton( TextButton(
child: Text(MaterialLocalizations.of(context)!.viewLicensesButtonLabel), child: Text(MaterialLocalizations.of(context).viewLicensesButtonLabel),
onPressed: () { onPressed: () {
showLicensePage( showLicensePage(
context: context, context: context,
@ -417,7 +419,7 @@ class AboutDialog extends StatelessWidget {
}, },
), ),
TextButton( TextButton(
child: Text(MaterialLocalizations.of(context)!.closeButtonLabel), child: Text(MaterialLocalizations.of(context).closeButtonLabel),
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
}, },
@ -447,7 +449,7 @@ class LicensePage extends StatefulWidget {
/// The licenses shown on the [LicensePage] are those returned by the /// The licenses shown on the [LicensePage] are those returned by the
/// [LicenseRegistry] API, which can be used to add more licenses to the list. /// [LicenseRegistry] API, which can be used to add more licenses to the list.
const LicensePage({ const LicensePage({
Key? key, Key key,
this.applicationName, this.applicationName,
this.applicationVersion, this.applicationVersion,
this.applicationIcon, this.applicationIcon,
@ -458,14 +460,14 @@ class LicensePage extends StatefulWidget {
/// ///
/// Defaults to the value of [Title.title], if a [Title] widget can be found. /// Defaults to the value of [Title.title], if a [Title] widget can be found.
/// Otherwise, defaults to [Platform.resolvedExecutable]. /// Otherwise, defaults to [Platform.resolvedExecutable].
final String? applicationName; final String applicationName;
/// The version of this build of the application. /// The version of this build of the application.
/// ///
/// This string is shown under the application name. /// This string is shown under the application name.
/// ///
/// Defaults to the empty string. /// Defaults to the empty string.
final String? applicationVersion; final String applicationVersion;
/// The icon to show below the application name. /// The icon to show below the application name.
/// ///
@ -473,33 +475,33 @@ class LicensePage extends StatefulWidget {
/// ///
/// Typically this will be an [ImageIcon] widget. It should honor the /// Typically this will be an [ImageIcon] widget. It should honor the
/// [IconTheme]'s [IconThemeData.size]. /// [IconTheme]'s [IconThemeData.size].
final Widget? applicationIcon; final Widget applicationIcon;
/// A string to show in small print. /// A string to show in small print.
/// ///
/// Typically this is a copyright notice. /// Typically this is a copyright notice.
/// ///
/// Defaults to the empty string. /// Defaults to the empty string.
final String? applicationLegalese; final String applicationLegalese;
@override @override
_LicensePageState createState() => _LicensePageState(); _LicensePageState createState() => _LicensePageState();
} }
class _LicensePageState extends State<LicensePage> { class _LicensePageState extends State<LicensePage> {
final ValueNotifier<int?> selectedId = ValueNotifier<int?>(null); final ValueNotifier<int> selectedId = ValueNotifier<int>(null);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _MasterDetailFlow( return _MasterDetailFlow(
detailPageFABlessGutterWidth: _getGutterSize(context), detailPageFABlessGutterWidth: _getGutterSize(context),
title: Text(MaterialLocalizations.of(context)!.licensesPageTitle), title: Text(MaterialLocalizations.of(context).licensesPageTitle),
detailPageBuilder: _packageLicensePage, detailPageBuilder: _packageLicensePage,
masterViewBuilder: _packagesView, masterViewBuilder: _packagesView,
); );
} }
Widget _packageLicensePage(BuildContext _, Object? args, ScrollController? scrollController) { Widget _packageLicensePage(BuildContext _, Object args, ScrollController scrollController) {
assert(args is _DetailArguments); assert(args is _DetailArguments);
final _DetailArguments detailArguments = args as _DetailArguments; final _DetailArguments detailArguments = args as _DetailArguments;
return _PackageLicensePage( return _PackageLicensePage(
@ -526,9 +528,9 @@ class _LicensePageState extends State<LicensePage> {
class _AboutProgram extends StatelessWidget { class _AboutProgram extends StatelessWidget {
const _AboutProgram({ const _AboutProgram({
Key? key, Key key,
required this.name, @required this.name,
required this.version, @required this.version,
this.icon, this.icon,
this.legalese, this.legalese,
}) : assert(name != null), }) : assert(name != null),
@ -537,8 +539,8 @@ class _AboutProgram extends StatelessWidget {
final String name; final String name;
final String version; final String version;
final Widget? icon; final Widget icon;
final String? legalese; final String legalese;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -551,26 +553,26 @@ class _AboutProgram extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Text( Text(
name, name,
style: Theme.of(context)!.textTheme.headline5, style: Theme.of(context).textTheme.headline5,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
if (icon != null) if (icon != null)
IconTheme(data: Theme.of(context)!.iconTheme, child: icon!), IconTheme(data: Theme.of(context).iconTheme, child: icon),
Text( Text(
version, version,
style: Theme.of(context)!.textTheme.bodyText2, style: Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: _textVerticalSeparation), const SizedBox(height: _textVerticalSeparation),
Text( Text(
legalese ?? '', legalese ?? '',
style: Theme.of(context)!.textTheme.caption, style: Theme.of(context).textTheme.caption,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: _textVerticalSeparation), const SizedBox(height: _textVerticalSeparation),
Text( Text(
'Powered by Flutter', 'Powered by Flutter',
style: Theme.of(context)!.textTheme.bodyText2, style: Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@ -581,17 +583,17 @@ class _AboutProgram extends StatelessWidget {
class _PackagesView extends StatefulWidget { class _PackagesView extends StatefulWidget {
const _PackagesView({ const _PackagesView({
Key? key, Key key,
required this.about, @required this.about,
required this.isLateral, @required this.isLateral,
required this.selectedId, @required this.selectedId,
}) : assert(about != null), }) : assert(about != null),
assert(isLateral != null), assert(isLateral != null),
super(key: key); super(key: key);
final Widget about; final Widget about;
final bool isLateral; final bool isLateral;
final ValueNotifier<int?> selectedId; final ValueNotifier<int> selectedId;
@override @override
_PackagesViewState createState() => _PackagesViewState(); _PackagesViewState createState() => _PackagesViewState();
@ -615,17 +617,17 @@ class _PackagesViewState extends State<_PackagesView> {
builder: (BuildContext context, BoxConstraints constraints) { builder: (BuildContext context, BoxConstraints constraints) {
switch (snapshot.connectionState) { switch (snapshot.connectionState) {
case ConnectionState.done: case ConnectionState.done:
_initDefaultDetailPage(snapshot.data!, context); _initDefaultDetailPage(snapshot.data, context);
return ValueListenableBuilder<int?>( return ValueListenableBuilder<int>(
valueListenable: widget.selectedId, valueListenable: widget.selectedId,
builder: (BuildContext context, int? selectedId, Widget? _) { builder: (BuildContext context, int selectedId, Widget _) {
return Center( return Center(
child: Material( child: Material(
color: Theme.of(context)!.cardColor, color: Theme.of(context).cardColor,
elevation: 4.0, elevation: 4.0,
child: Container( child: Container(
constraints: BoxConstraints.loose(const Size.fromWidth(600.0)), constraints: BoxConstraints.loose(const Size.fromWidth(600.0)),
child: _packagesList(context, selectedId, snapshot.data!, widget.isLateral), child: _packagesList(context, selectedId, snapshot.data, widget.isLateral),
), ),
), ),
); );
@ -633,7 +635,7 @@ class _PackagesViewState extends State<_PackagesView> {
); );
default: default:
return Material( return Material(
color: Theme.of(context)!.cardColor, color: Theme.of(context).cardColor,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ children: <Widget>[
@ -654,8 +656,8 @@ class _PackagesViewState extends State<_PackagesView> {
return; return;
} }
final String packageName = data.packages[widget.selectedId.value ?? 0]; final String packageName = data.packages[widget.selectedId.value ?? 0];
final List<int> bindings = data.packageLicenseBindings[packageName]!; final List<int> bindings = data.packageLicenseBindings[packageName];
_MasterDetailFlow.of(context)!.setInitialDetailPage( _MasterDetailFlow.of(context).setInitialDetailPage(
_DetailArguments( _DetailArguments(
packageName, packageName,
bindings.map((int i) => data.licenses[i]).toList(growable: false), bindings.map((int i) => data.licenses[i]).toList(growable: false),
@ -665,7 +667,7 @@ class _PackagesViewState extends State<_PackagesView> {
Widget _packagesList( Widget _packagesList(
final BuildContext context, final BuildContext context,
final int? selectedId, final int selectedId,
final _LicenseData data, final _LicenseData data,
final bool drawSelection, final bool drawSelection,
) { ) {
@ -678,7 +680,7 @@ class _PackagesViewState extends State<_PackagesView> {
.map<Widget>((MapEntry<int, String> entry) { .map<Widget>((MapEntry<int, String> entry) {
final String packageName = entry.value; final String packageName = entry.value;
final int index = entry.key; final int index = entry.key;
final List<int> bindings = data.packageLicenseBindings[packageName]!; final List<int> bindings = data.packageLicenseBindings[packageName];
return _PackageListTile( return _PackageListTile(
packageName: packageName, packageName: packageName,
index: index, index: index,
@ -686,7 +688,7 @@ class _PackagesViewState extends State<_PackagesView> {
numberLicenses: bindings.length, numberLicenses: bindings.length,
onTap: () { onTap: () {
widget.selectedId.value = index; widget.selectedId.value = index;
_MasterDetailFlow.of(context)!.openDetailPage(_DetailArguments( _MasterDetailFlow.of(context).openDetailPage(_DetailArguments(
packageName, packageName,
bindings.map((int i) => data.licenses[i]).toList(growable: false), bindings.map((int i) => data.licenses[i]).toList(growable: false),
)); ));
@ -700,27 +702,27 @@ class _PackagesViewState extends State<_PackagesView> {
class _PackageListTile extends StatelessWidget { class _PackageListTile extends StatelessWidget {
const _PackageListTile({ const _PackageListTile({
Key? key, Key key,
required this.packageName, this.packageName,
this.index, this.index,
required this.isSelected, this.isSelected,
required this.numberLicenses, this.numberLicenses,
this.onTap, this.onTap,
}) : super(key:key); }) : super(key:key);
final String packageName; final String packageName;
final int? index; final int index;
final bool isSelected; final bool isSelected;
final int numberLicenses; final int numberLicenses;
final GestureTapCallback? onTap; final GestureTapCallback onTap;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Ink( return Ink(
color: isSelected ? Theme.of(context)!.highlightColor : Theme.of(context)!.cardColor, color: isSelected ? Theme.of(context).highlightColor : Theme.of(context).cardColor,
child: ListTile( child: ListTile(
title: Text(packageName), title: Text(packageName),
subtitle: Text(MaterialLocalizations.of(context)!.licensesPackageDetailText(numberLicenses)), subtitle: Text(MaterialLocalizations.of(context).licensesPackageDetailText(numberLicenses)),
selected: isSelected, selected: isSelected,
onTap: onTap, onTap: onTap,
), ),
@ -738,7 +740,7 @@ class _LicenseData {
// Special treatment for the first package since it should be the package // Special treatment for the first package since it should be the package
// for delivered application. // for delivered application.
String? firstPackage; String firstPackage;
void addLicense(LicenseEntry entry) { void addLicense(LicenseEntry entry) {
// Before the license can be added, we must first record the packages to // Before the license can be added, we must first record the packages to
@ -748,7 +750,7 @@ class _LicenseData {
// Bind this license to the package using the next index value. This // Bind this license to the package using the next index value. This
// creates a contract that this license must be inserted at this same // creates a contract that this license must be inserted at this same
// index value. // index value.
packageLicenseBindings[package]!.add(licenses.length); packageLicenseBindings[package].add(licenses.length);
} }
licenses.add(entry); // Completion of the contract above. licenses.add(entry); // Completion of the contract above.
} }
@ -766,7 +768,7 @@ class _LicenseData {
/// Sort the packages using some comparison method, or by the default manner, /// Sort the packages using some comparison method, or by the default manner,
/// which is to put the application package first, followed by every other /// which is to put the application package first, followed by every other
/// package in case-insensitive alphabetical order. /// package in case-insensitive alphabetical order.
void sortPackages([int Function(String a, String b)? compare]) { void sortPackages([int compare(String a, String b)]) {
packages.sort(compare ?? (String a, String b) { packages.sort(compare ?? (String a, String b) {
// Based on how LicenseRegistry currently behaves, the first package // Based on how LicenseRegistry currently behaves, the first package
// returned is the end user application license. This should be // returned is the end user application license. This should be
@ -804,15 +806,15 @@ class _DetailArguments {
class _PackageLicensePage extends StatefulWidget { class _PackageLicensePage extends StatefulWidget {
const _PackageLicensePage({ const _PackageLicensePage({
Key? key, Key key,
required this.packageName, this.packageName,
required this.licenseEntries, this.licenseEntries,
required this.scrollController, this.scrollController,
}) : super(key: key); }) : super(key: key);
final String packageName; final String packageName;
final List<LicenseEntry> licenseEntries; final List<LicenseEntry> licenseEntries;
final ScrollController? scrollController; final ScrollController scrollController;
@override @override
_PackageLicensePageState createState() => _PackageLicensePageState(); _PackageLicensePageState createState() => _PackageLicensePageState();
@ -845,7 +847,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> {
return true; return true;
}()); }());
final List<LicenseParagraph> paragraphs = final List<LicenseParagraph> paragraphs =
await SchedulerBinding.instance!.scheduleTask<List<LicenseParagraph>>( await SchedulerBinding.instance.scheduleTask<List<LicenseParagraph>>(
license.paragraphs.toList, license.paragraphs.toList,
Priority.animation, Priority.animation,
debugLabel: 'License', debugLabel: 'License',
@ -890,8 +892,8 @@ class _PackageLicensePageState extends State<_PackageLicensePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final ThemeData? theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final String title = widget.packageName; final String title = widget.packageName;
final String subtitle = localizations.licensesPackageDetailText(widget.licenseEntries.length); final String subtitle = localizations.licensesPackageDetailText(widget.licenseEntries.length);
final double pad = _getGutterSize(context); final double pad = _getGutterSize(context);
@ -914,7 +916,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> {
title: _PackageLicensePageTitle( title: _PackageLicensePageTitle(
title, title,
subtitle, subtitle,
theme!.appBarTheme.textTheme ?? theme.primaryTextTheme, theme.appBarTheme.textTheme ?? theme.primaryTextTheme,
), ),
), ),
body: Center( body: Center(
@ -941,7 +943,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> {
SliverAppBar( SliverAppBar(
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
pinned: true, pinned: true,
backgroundColor: theme!.cardColor, backgroundColor: theme.cardColor,
title: _PackageLicensePageTitle(title, subtitle, theme.textTheme), title: _PackageLicensePageTitle(title, subtitle, theme.textTheme),
), ),
SliverPadding( SliverPadding(
@ -972,7 +974,7 @@ class _PackageLicensePageTitle extends StatelessWidget {
this.title, this.title,
this.subtitle, this.subtitle,
this.theme, { this.theme, {
Key? key, Key key,
}) : super(key: key); }) : super(key: key);
final String title; final String title;
@ -999,7 +1001,7 @@ String _defaultApplicationName(BuildContext context) {
// someone really wants their application title to change dynamically, they // someone really wants their application title to change dynamically, they
// can provide an explicit applicationName to the widgets defined in this // can provide an explicit applicationName to the widgets defined in this
// file, instead of relying on the default. // file, instead of relying on the default.
final Title? ancestorTitle = context.findAncestorWidgetOfExactType<Title>(); final Title ancestorTitle = context.findAncestorWidgetOfExactType<Title>();
return ancestorTitle?.title ?? Platform.resolvedExecutable.split(Platform.pathSeparator).last; return ancestorTitle?.title ?? Platform.resolvedExecutable.split(Platform.pathSeparator).last;
} }
@ -1008,7 +1010,7 @@ String _defaultApplicationVersion(BuildContext context) {
return ''; return '';
} }
Widget? _defaultApplicationIcon(BuildContext context) { Widget _defaultApplicationIcon(BuildContext context) {
// TODO(ianh): Get this from the embedder somehow. // TODO(ianh): Get this from the embedder somehow.
return null; return null;
} }
@ -1018,7 +1020,7 @@ const double _wideGutterSize = 24.0;
const double _narrowGutterSize = 12.0; const double _narrowGutterSize = 12.0;
double _getGutterSize(BuildContext context) => double _getGutterSize(BuildContext context) =>
MediaQuery.of(context)!.size.width >= _materialGutterThreshold ? _wideGutterSize : _narrowGutterSize; MediaQuery.of(context).size.width >= _materialGutterThreshold ? _wideGutterSize : _narrowGutterSize;
/// Signature for the builder callback used by [_MasterDetailFlow]. /// Signature for the builder callback used by [_MasterDetailFlow].
typedef _MasterViewBuilder = Widget Function(BuildContext context, bool isLateralUI); typedef _MasterViewBuilder = Widget Function(BuildContext context, bool isLateralUI);
@ -1027,7 +1029,7 @@ typedef _MasterViewBuilder = Widget Function(BuildContext context, bool isLatera
/// ///
/// scrollController is provided when the page destination is the draggable /// scrollController is provided when the page destination is the draggable
/// sheet in the lateral UI. Otherwise, it is null. /// sheet in the lateral UI. Otherwise, it is null.
typedef _DetailPageBuilder = Widget Function(BuildContext context, Object? arguments, ScrollController? scrollController); typedef _DetailPageBuilder = Widget Function(BuildContext context, Object arguments, ScrollController scrollController);
/// Signature for the builder callback used by [_MasterDetailFlow.actionBuilder]. /// Signature for the builder callback used by [_MasterDetailFlow.actionBuilder].
/// ///
@ -1076,9 +1078,9 @@ class _MasterDetailFlow extends StatefulWidget {
/// Creates a master detail navigation flow which is either nested or /// Creates a master detail navigation flow which is either nested or
/// lateral depending on screen width. /// lateral depending on screen width.
const _MasterDetailFlow({ const _MasterDetailFlow({
Key? key, Key key,
required this.detailPageBuilder, @required this.detailPageBuilder,
required this.masterViewBuilder, @required this.masterViewBuilder,
this.actionBuilder, this.actionBuilder,
this.automaticallyImplyLeading = true, this.automaticallyImplyLeading = true,
this.breakpoint, this.breakpoint,
@ -1111,7 +1113,7 @@ class _MasterDetailFlow extends StatefulWidget {
/// This builder is usually a wrapper around the [masterViewBuilder] builder to provide the /// This builder is usually a wrapper around the [masterViewBuilder] builder to provide the
/// extra UI required to make a page. However, this builder is optional, and the master page /// extra UI required to make a page. However, this builder is optional, and the master page
/// can be built using the master view builder and the configuration for the lateral UI's app bar. /// can be built using the master view builder and the configuration for the lateral UI's app bar.
final _MasterViewBuilder? masterPageBuilder; final _MasterViewBuilder masterPageBuilder;
/// Builder for the detail page. /// Builder for the detail page.
/// ///
@ -1122,29 +1124,29 @@ class _MasterDetailFlow extends StatefulWidget {
final _DetailPageBuilder detailPageBuilder; final _DetailPageBuilder detailPageBuilder;
/// Override the width of the master view in the lateral UI. /// Override the width of the master view in the lateral UI.
final double? masterViewWidth; final double masterViewWidth;
/// Override the width of the floating action button gutter in the lateral UI. /// Override the width of the floating action button gutter in the lateral UI.
final double? detailPageFABGutterWidth; final double detailPageFABGutterWidth;
/// Override the width of the gutter when there is no floating action button. /// Override the width of the gutter when there is no floating action button.
final double? detailPageFABlessGutterWidth; final double detailPageFABlessGutterWidth;
/// Add a floating action button to the lateral UI. If no [masterPageBuilder] is supplied, this /// Add a floating action button to the lateral UI. If no [masterPageBuilder] is supplied, this
/// floating action button is also used on the nested master page. /// floating action button is also used on the nested master page.
/// ///
/// See [Scaffold.floatingActionButton]. /// See [Scaffold.floatingActionButton].
final FloatingActionButton? floatingActionButton; final FloatingActionButton floatingActionButton;
/// The title for the lateral UI [AppBar]. /// The title for the lateral UI [AppBar].
/// ///
/// See [AppBar.title]. /// See [AppBar.title].
final Widget? title; final Widget title;
/// A widget to display before the title for the lateral UI [AppBar]. /// A widget to display before the title for the lateral UI [AppBar].
/// ///
/// See [AppBar.leading]. /// See [AppBar.leading].
final Widget? leading; final Widget leading;
/// Override the framework from determining whether to show a leading widget or not. /// Override the framework from determining whether to show a leading widget or not.
/// ///
@ -1155,10 +1157,10 @@ class _MasterDetailFlow extends StatefulWidget {
/// app bar or not. /// app bar or not.
/// ///
/// See [AppBar.centerTitle]. /// See [AppBar.centerTitle].
final bool? centerTitle; final bool centerTitle;
/// See [AppBar.flexibleSpace]. /// See [AppBar.flexibleSpace].
final Widget? flexibleSpace; final Widget flexibleSpace;
/// Build actions for the lateral UI, and potentially the master page in the nested UI. /// Build actions for the lateral UI, and potentially the master page in the nested UI.
/// ///
@ -1169,25 +1171,25 @@ class _MasterDetailFlow extends StatefulWidget {
/// by [_MasterDetailFlow], then [_ActionLevel.composite] indicates the /// by [_MasterDetailFlow], then [_ActionLevel.composite] indicates the
/// actions are for the /// actions are for the
/// nested master page. /// nested master page.
final _ActionBuilder? actionBuilder; final _ActionBuilder actionBuilder;
/// Determine where the floating action button will go. /// Determine where the floating action button will go.
/// ///
/// If null, [FloatingActionButtonLocation.endTop] is used. /// If null, [FloatingActionButtonLocation.endTop] is used.
/// ///
/// Also see [Scaffold.floatingActionButtonLocation]. /// Also see [Scaffold.floatingActionButtonLocation].
final FloatingActionButtonLocation? floatingActionButtonLocation; final FloatingActionButtonLocation floatingActionButtonLocation;
/// Determine where the floating action button will go on the master page. /// Determine where the floating action button will go on the master page.
/// ///
/// See [Scaffold.floatingActionButtonLocation]. /// See [Scaffold.floatingActionButtonLocation].
final FloatingActionButtonLocation? floatingActionButtonMasterPageLocation; final FloatingActionButtonLocation floatingActionButtonMasterPageLocation;
/// Forces display mode and style. /// Forces display mode and style.
final _LayoutMode displayMode; final _LayoutMode displayMode;
/// Width at which layout changes from nested to lateral. /// Width at which layout changes from nested to lateral.
final double? breakpoint; final double breakpoint;
@override @override
_MasterDetailFlowState createState() => _MasterDetailFlowState(); _MasterDetailFlowState createState() => _MasterDetailFlowState();
@ -1200,11 +1202,11 @@ class _MasterDetailFlow extends StatefulWidget {
/// ```dart /// ```dart
/// _MasterDetailFlow.of(context).openDetailPage(arguments); /// _MasterDetailFlow.of(context).openDetailPage(arguments);
/// ``` /// ```
static _MasterDetailFlowProxy? of( static _MasterDetailFlowProxy of(
BuildContext context, { BuildContext context, {
bool nullOk = false, bool nullOk = false,
}) { }) {
_PageOpener? pageOpener = context.findAncestorStateOfType<_MasterDetailScaffoldState>(); _PageOpener pageOpener = context.findAncestorStateOfType<_MasterDetailScaffoldState>();
pageOpener ??= context.findAncestorStateOfType<_MasterDetailFlowState>(); pageOpener ??= context.findAncestorStateOfType<_MasterDetailFlowState>();
assert(() { assert(() {
if (pageOpener == null && !nullOk) { if (pageOpener == null && !nullOk) {
@ -1251,10 +1253,10 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp
_Focus focus = _Focus.master; _Focus focus = _Focus.master;
/// Cache of arguments passed when opening a detail page. Used when rebuilding. /// Cache of arguments passed when opening a detail page. Used when rebuilding.
Object? _cachedDetailArguments; Object _cachedDetailArguments;
/// Record of the layout that was built. /// Record of the layout that was built.
_LayoutMode? _builtLayout; _LayoutMode _builtLayout;
/// Key to access navigator in the nested layout. /// Key to access navigator in the nested layout.
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
@ -1263,7 +1265,7 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp
void openDetailPage(Object arguments) { void openDetailPage(Object arguments) {
_cachedDetailArguments = arguments; _cachedDetailArguments = arguments;
if (_builtLayout == _LayoutMode.nested) { if (_builtLayout == _LayoutMode.nested) {
_navigatorKey.currentState!.pushNamed(_navDetail, arguments: arguments); _navigatorKey.currentState.pushNamed(_navDetail, arguments: arguments);
} else { } else {
focus = _Focus.detail; focus = _Focus.detail;
} }
@ -1301,7 +1303,7 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp
return WillPopScope( return WillPopScope(
// Push pop check into nested navigator. // Push pop check into nested navigator.
onWillPop: () async => !(await _navigatorKey.currentState!.maybePop()), onWillPop: () async => !(await _navigatorKey.currentState.maybePop()),
child: Navigator( child: Navigator(
key: _navigatorKey, key: _navigatorKey,
initialRoute: 'initial', initialRoute: 'initial',
@ -1341,11 +1343,11 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp
return MaterialPageRoute<dynamic>( return MaterialPageRoute<dynamic>(
builder: (BuildContext c) => BlockSemantics( builder: (BuildContext c) => BlockSemantics(
child: widget.masterPageBuilder != null child: widget.masterPageBuilder != null
? widget.masterPageBuilder!(c, false) ? widget.masterPageBuilder(c, false)
: _MasterPage( : _MasterPage(
leading: widget.leading ?? leading: widget.leading ??
(widget.automaticallyImplyLeading && Navigator.of(context)!.canPop() (widget.automaticallyImplyLeading && Navigator.of(context).canPop()
? BackButton(onPressed: () => Navigator.of(context)!.pop()) ? BackButton(onPressed: () => Navigator.of(context).pop())
: null), : null),
title: widget.title, title: widget.title,
centerTitle: widget.centerTitle, centerTitle: widget.centerTitle,
@ -1360,13 +1362,13 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp
); );
} }
MaterialPageRoute<void> _detailPageRoute(Object? arguments) { MaterialPageRoute<void> _detailPageRoute(Object arguments) {
return MaterialPageRoute<dynamic>(builder: (BuildContext context) { return MaterialPageRoute<dynamic>(builder: (BuildContext context) {
return WillPopScope( return WillPopScope(
onWillPop: () async { onWillPop: () async {
// No need for setState() as rebuild happens on navigation pop. // No need for setState() as rebuild happens on navigation pop.
focus = _Focus.master; focus = _Focus.master;
Navigator.of(context)!.pop(); Navigator.of(context).pop();
return false; return false;
}, },
child: BlockSemantics(child: widget.detailPageBuilder(context, arguments, null)), child: BlockSemantics(child: widget.detailPageBuilder(context, arguments, null)),
@ -1380,7 +1382,7 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp
actionBuilder: widget.actionBuilder ?? (_, __) => const<Widget>[], actionBuilder: widget.actionBuilder ?? (_, __) => const<Widget>[],
automaticallyImplyLeading: widget.automaticallyImplyLeading, automaticallyImplyLeading: widget.automaticallyImplyLeading,
centerTitle: widget.centerTitle, centerTitle: widget.centerTitle,
detailPageBuilder: (BuildContext context, Object? args, ScrollController? scrollController) => detailPageBuilder: (BuildContext context, Object args, ScrollController scrollController) =>
widget.detailPageBuilder(context, args ?? _cachedDetailArguments, scrollController), widget.detailPageBuilder(context, args ?? _cachedDetailArguments, scrollController),
floatingActionButton: widget.floatingActionButton, floatingActionButton: widget.floatingActionButton,
detailPageFABlessGutterWidth: widget.detailPageFABlessGutterWidth, detailPageFABlessGutterWidth: widget.detailPageFABlessGutterWidth,
@ -1397,7 +1399,7 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp
class _MasterPage extends StatelessWidget { class _MasterPage extends StatelessWidget {
const _MasterPage({ const _MasterPage({
Key? key, Key key,
this.leading, this.leading,
this.title, this.title,
this.actionBuilder, this.actionBuilder,
@ -1406,18 +1408,18 @@ class _MasterPage extends StatelessWidget {
this.floatingActionButton, this.floatingActionButton,
this.floatingActionButtonLocation, this.floatingActionButtonLocation,
this.masterViewBuilder, this.masterViewBuilder,
required this.automaticallyImplyLeading, this.automaticallyImplyLeading,
}) : super(key: key); }) : super(key: key);
final _MasterViewBuilder? masterViewBuilder; final _MasterViewBuilder masterViewBuilder;
final Widget? title; final Widget title;
final Widget? leading; final Widget leading;
final bool automaticallyImplyLeading; final bool automaticallyImplyLeading;
final bool? centerTitle; final bool centerTitle;
final Widget? flexibleSpace; final Widget flexibleSpace;
final _ActionBuilder? actionBuilder; final _ActionBuilder actionBuilder;
final FloatingActionButton? floatingActionButton; final FloatingActionButton floatingActionButton;
final FloatingActionButtonLocation? floatingActionButtonLocation; final FloatingActionButtonLocation floatingActionButtonLocation;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -1427,12 +1429,12 @@ class _MasterPage extends StatelessWidget {
leading: leading, leading: leading,
actions: actionBuilder == null actions: actionBuilder == null
? const <Widget>[] ? const <Widget>[]
: actionBuilder!(context, _ActionLevel.composite), : actionBuilder(context, _ActionLevel.composite),
centerTitle: centerTitle, centerTitle: centerTitle,
flexibleSpace: flexibleSpace, flexibleSpace: flexibleSpace,
automaticallyImplyLeading: automaticallyImplyLeading, automaticallyImplyLeading: automaticallyImplyLeading,
), ),
body: masterViewBuilder!(context, false), body: masterViewBuilder(context, false),
floatingActionButton: floatingActionButton, floatingActionButton: floatingActionButton,
floatingActionButtonLocation: floatingActionButtonLocation, floatingActionButtonLocation: floatingActionButtonLocation,
); );
@ -1447,16 +1449,16 @@ const double _kDetailPageFABGutterWidth = 84.0;
class _MasterDetailScaffold extends StatefulWidget { class _MasterDetailScaffold extends StatefulWidget {
const _MasterDetailScaffold({ const _MasterDetailScaffold({
Key? key, Key key,
required this.detailPageBuilder, @required this.detailPageBuilder,
required this.masterViewBuilder, @required this.masterViewBuilder,
this.actionBuilder, this.actionBuilder,
this.floatingActionButton, this.floatingActionButton,
this.floatingActionButtonLocation, this.floatingActionButtonLocation,
this.initialArguments, this.initialArguments,
this.leading, this.leading,
this.title, this.title,
required this.automaticallyImplyLeading, this.automaticallyImplyLeading,
this.centerTitle, this.centerTitle,
this.detailPageFABlessGutterWidth, this.detailPageFABlessGutterWidth,
this.detailPageFABGutterWidth, this.detailPageFABGutterWidth,
@ -1473,17 +1475,17 @@ class _MasterDetailScaffold extends StatefulWidget {
/// that uses the [ScrollController] provided. In fact, it is strongly recommended the entire /// that uses the [ScrollController] provided. In fact, it is strongly recommended the entire
/// lateral page is scrollable. /// lateral page is scrollable.
final _DetailPageBuilder detailPageBuilder; final _DetailPageBuilder detailPageBuilder;
final _ActionBuilder? actionBuilder; final _ActionBuilder actionBuilder;
final FloatingActionButton? floatingActionButton; final FloatingActionButton floatingActionButton;
final FloatingActionButtonLocation? floatingActionButtonLocation; final FloatingActionButtonLocation floatingActionButtonLocation;
final Object? initialArguments; final Object initialArguments;
final Widget? leading; final Widget leading;
final Widget? title; final Widget title;
final bool automaticallyImplyLeading; final bool automaticallyImplyLeading;
final bool? centerTitle; final bool centerTitle;
final double? detailPageFABlessGutterWidth; final double detailPageFABlessGutterWidth;
final double? detailPageFABGutterWidth; final double detailPageFABGutterWidth;
final double? masterViewWidth; final double masterViewWidth;
@override @override
_MasterDetailScaffoldState createState() => _MasterDetailScaffoldState(); _MasterDetailScaffoldState createState() => _MasterDetailScaffoldState();
@ -1491,12 +1493,12 @@ class _MasterDetailScaffold extends StatefulWidget {
class _MasterDetailScaffoldState extends State<_MasterDetailScaffold> class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
implements _PageOpener { implements _PageOpener {
late FloatingActionButtonLocation floatingActionButtonLocation; FloatingActionButtonLocation floatingActionButtonLocation;
late double detailPageFABGutterWidth; double detailPageFABGutterWidth;
late double detailPageFABlessGutterWidth; double detailPageFABlessGutterWidth;
late double masterViewWidth; double masterViewWidth;
final ValueNotifier<Object?> _detailArguments = ValueNotifier<Object?>(null); final ValueNotifier<Object> _detailArguments = ValueNotifier<Object>(null);
@override @override
void initState() { void initState() {
@ -1509,16 +1511,16 @@ class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
@override @override
void openDetailPage(Object arguments) { void openDetailPage(Object arguments) {
SchedulerBinding.instance! SchedulerBinding.instance
.addPostFrameCallback((_) => _detailArguments.value = arguments); .addPostFrameCallback((_) => _detailArguments.value = arguments);
_MasterDetailFlow.of(context)!.openDetailPage(arguments); _MasterDetailFlow.of(context).openDetailPage(arguments);
} }
@override @override
void setInitialDetailPage(Object arguments) { void setInitialDetailPage(Object arguments) {
SchedulerBinding.instance! SchedulerBinding.instance
.addPostFrameCallback((_) => _detailArguments.value = arguments); .addPostFrameCallback((_) => _detailArguments.value = arguments);
_MasterDetailFlow.of(context)!.setInitialDetailPage(arguments); _MasterDetailFlow.of(context).setInitialDetailPage(arguments);
} }
@override @override
@ -1529,7 +1531,7 @@ class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
floatingActionButtonLocation: floatingActionButtonLocation, floatingActionButtonLocation: floatingActionButtonLocation,
appBar: AppBar( appBar: AppBar(
title: widget.title, title: widget.title,
actions: widget.actionBuilder!(context, _ActionLevel.top), actions: widget.actionBuilder(context, _ActionLevel.top),
leading: widget.leading, leading: widget.leading,
automaticallyImplyLeading: widget.automaticallyImplyLeading, automaticallyImplyLeading: widget.automaticallyImplyLeading,
centerTitle: widget.centerTitle, centerTitle: widget.centerTitle,
@ -1542,10 +1544,10 @@ class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
constraints: constraints:
BoxConstraints.tightFor(width: masterViewWidth), BoxConstraints.tightFor(width: masterViewWidth),
child: IconTheme( child: IconTheme(
data: Theme.of(context)!.primaryIconTheme, data: Theme.of(context).primaryIconTheme,
child: ButtonBar( child: ButtonBar(
children: children:
widget.actionBuilder!(context, _ActionLevel.view), widget.actionBuilder(context, _ActionLevel.view),
), ),
), ),
) )
@ -1565,9 +1567,9 @@ class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
? detailPageFABlessGutterWidth ? detailPageFABlessGutterWidth
: detailPageFABGutterWidth, : detailPageFABGutterWidth,
), ),
child: ValueListenableBuilder<Object?>( child: ValueListenableBuilder<Object>(
valueListenable: _detailArguments, valueListenable: _detailArguments,
builder: (BuildContext context, Object? value, Widget? child) { builder: (BuildContext context, Object value, Widget child) {
return AnimatedSwitcher( return AnimatedSwitcher(
transitionBuilder: transitionBuilder:
(Widget child, Animation<double> animation) => (Widget child, Animation<double> animation) =>
@ -1576,7 +1578,7 @@ class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
null, null, animation, null, child), null, null, animation, null, child),
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
child: Container( child: Container(
key: ValueKey<Object?>(value ?? widget.initialArguments), key: ValueKey<Object>(value ?? widget.initialArguments),
constraints: const BoxConstraints.expand(), constraints: const BoxConstraints.expand(),
child: _DetailView( child: _DetailView(
builder: widget.detailPageBuilder, builder: widget.detailPageBuilder,
@ -1599,7 +1601,7 @@ class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
? Scaffold( ? Scaffold(
appBar: AppBar( appBar: AppBar(
title: widget.title, title: widget.title,
actions: widget.actionBuilder!(context, _ActionLevel.top), actions: widget.actionBuilder(context, _ActionLevel.top),
leading: widget.leading, leading: widget.leading,
automaticallyImplyLeading: widget.automaticallyImplyLeading, automaticallyImplyLeading: widget.automaticallyImplyLeading,
centerTitle: widget.centerTitle, centerTitle: widget.centerTitle,
@ -1613,23 +1615,23 @@ class _MasterDetailScaffoldState extends State<_MasterDetailScaffold>
class _DetailView extends StatelessWidget { class _DetailView extends StatelessWidget {
const _DetailView({ const _DetailView({
Key? key, Key key,
required _DetailPageBuilder builder, @required _DetailPageBuilder builder,
Object? arguments, Object arguments,
}) : assert(builder != null), }) : assert(builder != null),
_builder = builder, _builder = builder,
_arguments = arguments, _arguments = arguments,
super(key: key); super(key: key);
final _DetailPageBuilder _builder; final _DetailPageBuilder _builder;
final Object? _arguments; final Object _arguments;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (_arguments == null) { if (_arguments == null) {
return Container(); return Container();
} }
final double screenHeight = MediaQuery.of(context)!.size.height; final double screenHeight = MediaQuery.of(context).size.height;
final double minHeight = (screenHeight - kToolbarHeight) / screenHeight; final double minHeight = (screenHeight - kToolbarHeight) / screenHeight;
return DraggableScrollableSheet( return DraggableScrollableSheet(
@ -1641,7 +1643,7 @@ class _DetailView extends StatelessWidget {
return MouseRegion( return MouseRegion(
// TODO(TonicArtos): Remove MouseRegion workaround for pointer hover events passing through DraggableScrollableSheet once https://github.com/flutter/flutter/issues/59741 is resolved. // TODO(TonicArtos): Remove MouseRegion workaround for pointer hover events passing through DraggableScrollableSheet once https://github.com/flutter/flutter/issues/59741 is resolved.
child: Card( child: Card(
color: Theme.of(context)!.cardColor, color: Theme.of(context).cardColor,
elevation: _kCardElevation, elevation: _kCardElevation,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
margin: const EdgeInsets.fromLTRB( margin: const EdgeInsets.fromLTRB(

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -164,7 +166,7 @@ class MaterialApp extends StatefulWidget {
/// ///
/// The boolean arguments, [routes], and [navigatorObservers], must not be null. /// The boolean arguments, [routes], and [navigatorObservers], must not be null.
const MaterialApp({ const MaterialApp({
Key? key, Key key,
this.navigatorKey, this.navigatorKey,
this.home, this.home,
this.routes = const <String, WidgetBuilder>{}, this.routes = const <String, WidgetBuilder>{},
@ -213,10 +215,10 @@ class MaterialApp extends StatefulWidget {
/// Creates a [MaterialApp] that uses the [Router] instead of a [Navigator]. /// Creates a [MaterialApp] that uses the [Router] instead of a [Navigator].
const MaterialApp.router({ const MaterialApp.router({
Key? key, Key key,
this.routeInformationProvider, this.routeInformationProvider,
required this.routeInformationParser, @required this.routeInformationParser,
required this.routerDelegate, @required this.routerDelegate,
this.backButtonDispatcher, this.backButtonDispatcher,
this.builder, this.builder,
this.title = '', this.title = '',
@ -261,10 +263,10 @@ class MaterialApp extends StatefulWidget {
super(key: key); super(key: key);
/// {@macro flutter.widgets.widgetsApp.navigatorKey} /// {@macro flutter.widgets.widgetsApp.navigatorKey}
final GlobalKey<NavigatorState>? navigatorKey; final GlobalKey<NavigatorState> navigatorKey;
/// {@macro flutter.widgets.widgetsApp.home} /// {@macro flutter.widgets.widgetsApp.home}
final Widget? home; final Widget home;
/// The application's top-level routing table. /// The application's top-level routing table.
/// ///
@ -274,41 +276,41 @@ class MaterialApp extends StatefulWidget {
/// an appropriate transition, including [Hero] animations, to the new route. /// an appropriate transition, including [Hero] animations, to the new route.
/// ///
/// {@macro flutter.widgets.widgetsApp.routes} /// {@macro flutter.widgets.widgetsApp.routes}
final Map<String, WidgetBuilder>? routes; final Map<String, WidgetBuilder> routes;
/// {@macro flutter.widgets.widgetsApp.initialRoute} /// {@macro flutter.widgets.widgetsApp.initialRoute}
final String? initialRoute; final String initialRoute;
/// {@macro flutter.widgets.widgetsApp.onGenerateRoute} /// {@macro flutter.widgets.widgetsApp.onGenerateRoute}
final RouteFactory? onGenerateRoute; final RouteFactory onGenerateRoute;
/// {@macro flutter.widgets.widgetsApp.onGenerateInitialRoutes} /// {@macro flutter.widgets.widgetsApp.onGenerateInitialRoutes}
final InitialRouteListFactory? onGenerateInitialRoutes; final InitialRouteListFactory onGenerateInitialRoutes;
/// {@macro flutter.widgets.widgetsApp.onUnknownRoute} /// {@macro flutter.widgets.widgetsApp.onUnknownRoute}
final RouteFactory? onUnknownRoute; final RouteFactory onUnknownRoute;
/// {@macro flutter.widgets.widgetsApp.navigatorObservers} /// {@macro flutter.widgets.widgetsApp.navigatorObservers}
final List<NavigatorObserver>? navigatorObservers; final List<NavigatorObserver> navigatorObservers;
/// {@macro flutter.widgets.widgetsApp.routeInformationProvider} /// {@macro flutter.widgets.widgetsApp.routeInformationProvider}
final RouteInformationProvider? routeInformationProvider; final RouteInformationProvider routeInformationProvider;
/// {@macro flutter.widgets.widgetsApp.routeInformationParser} /// {@macro flutter.widgets.widgetsApp.routeInformationParser}
final RouteInformationParser<Object>? routeInformationParser; final RouteInformationParser<Object> routeInformationParser;
/// {@macro flutter.widgets.widgetsApp.routerDelegate} /// {@macro flutter.widgets.widgetsApp.routerDelegate}
final RouterDelegate<Object>? routerDelegate; final RouterDelegate<Object> routerDelegate;
/// {@macro flutter.widgets.widgetsApp.backButtonDispatcher} /// {@macro flutter.widgets.widgetsApp.backButtonDispatcher}
final BackButtonDispatcher? backButtonDispatcher; final BackButtonDispatcher backButtonDispatcher;
/// {@macro flutter.widgets.widgetsApp.builder} /// {@macro flutter.widgets.widgetsApp.builder}
/// ///
/// Material specific features such as [showDialog] and [showMenu], and widgets /// Material specific features such as [showDialog] and [showMenu], and widgets
/// such as [Tooltip], [PopupMenuButton], also require a [Navigator] to properly /// such as [Tooltip], [PopupMenuButton], also require a [Navigator] to properly
/// function. /// function.
final TransitionBuilder? builder; final TransitionBuilder builder;
/// {@macro flutter.widgets.widgetsApp.title} /// {@macro flutter.widgets.widgetsApp.title}
/// ///
@ -318,7 +320,7 @@ class MaterialApp extends StatefulWidget {
/// {@macro flutter.widgets.widgetsApp.onGenerateTitle} /// {@macro flutter.widgets.widgetsApp.onGenerateTitle}
/// ///
/// This value is passed unmodified to [WidgetsApp.onGenerateTitle]. /// This value is passed unmodified to [WidgetsApp.onGenerateTitle].
final GenerateAppTitle? onGenerateTitle; final GenerateAppTitle onGenerateTitle;
/// Default visual properties, like colors fonts and shapes, for this app's /// Default visual properties, like colors fonts and shapes, for this app's
/// material widgets. /// material widgets.
@ -337,7 +339,7 @@ class MaterialApp extends StatefulWidget {
/// and [darkTheme] in [MaterialApp]. /// and [darkTheme] in [MaterialApp].
/// * [ThemeData.brightness], which indicates the [Brightness] of a theme's /// * [ThemeData.brightness], which indicates the [Brightness] of a theme's
/// colors. /// colors.
final ThemeData? theme; final ThemeData theme;
/// The [ThemeData] to use when a 'dark mode' is requested by the system. /// The [ThemeData] to use when a 'dark mode' is requested by the system.
/// ///
@ -359,7 +361,7 @@ class MaterialApp extends StatefulWidget {
/// and [darkTheme] in [MaterialApp]. /// and [darkTheme] in [MaterialApp].
/// * [ThemeData.brightness], which is typically set to the value of /// * [ThemeData.brightness], which is typically set to the value of
/// [MediaQueryData.platformBrightness]. /// [MediaQueryData.platformBrightness].
final ThemeData? darkTheme; final ThemeData darkTheme;
/// The [ThemeData] to use when 'high contrast' is requested by the system. /// The [ThemeData] to use when 'high contrast' is requested by the system.
/// ///
@ -372,7 +374,7 @@ class MaterialApp extends StatefulWidget {
/// ///
/// * [MediaQueryData.highContrast], which indicates the platform's /// * [MediaQueryData.highContrast], which indicates the platform's
/// desire to increase contrast. /// desire to increase contrast.
final ThemeData? highContrastTheme; final ThemeData highContrastTheme;
/// The [ThemeData] to use when a 'dark mode' and 'high contrast' is requested /// The [ThemeData] to use when a 'dark mode' and 'high contrast' is requested
/// by the system. /// by the system.
@ -388,7 +390,7 @@ class MaterialApp extends StatefulWidget {
/// ///
/// * [MediaQueryData.highContrast], which indicates the platform's /// * [MediaQueryData.highContrast], which indicates the platform's
/// desire to increase contrast. /// desire to increase contrast.
final ThemeData? highContrastDarkTheme; final ThemeData highContrastDarkTheme;
/// Determines which theme will be used by the application if both [theme] /// Determines which theme will be used by the application if both [theme]
/// and [darkTheme] are provided. /// and [darkTheme] are provided.
@ -414,13 +416,13 @@ class MaterialApp extends StatefulWidget {
/// * [darkTheme], which is used when a dark mode is selected. /// * [darkTheme], which is used when a dark mode is selected.
/// * [ThemeData.brightness], which indicates to various parts of the /// * [ThemeData.brightness], which indicates to various parts of the
/// system what kind of theme is being used. /// system what kind of theme is being used.
final ThemeMode? themeMode; final ThemeMode themeMode;
/// {@macro flutter.widgets.widgetsApp.color} /// {@macro flutter.widgets.widgetsApp.color}
final Color? color; final Color color;
/// {@macro flutter.widgets.widgetsApp.locale} /// {@macro flutter.widgets.widgetsApp.locale}
final Locale? locale; final Locale locale;
/// {@macro flutter.widgets.widgetsApp.localizationsDelegates} /// {@macro flutter.widgets.widgetsApp.localizationsDelegates}
/// ///
@ -513,17 +515,17 @@ class MaterialApp extends StatefulWidget {
/// which provides material localizations for many languages. /// which provides material localizations for many languages.
/// * The Flutter Internationalization Tutorial, /// * The Flutter Internationalization Tutorial,
/// <https://flutter.dev/tutorials/internationalization/>. /// <https://flutter.dev/tutorials/internationalization/>.
final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates; final Iterable<LocalizationsDelegate<dynamic>> localizationsDelegates;
/// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback} /// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback}
/// ///
/// This callback is passed along to the [WidgetsApp] built by this widget. /// This callback is passed along to the [WidgetsApp] built by this widget.
final LocaleListResolutionCallback? localeListResolutionCallback; final LocaleListResolutionCallback localeListResolutionCallback;
/// {@macro flutter.widgets.widgetsApp.localeResolutionCallback} /// {@macro flutter.widgets.widgetsApp.localeResolutionCallback}
/// ///
/// This callback is passed along to the [WidgetsApp] built by this widget. /// This callback is passed along to the [WidgetsApp] built by this widget.
final LocaleResolutionCallback? localeResolutionCallback; final LocaleResolutionCallback localeResolutionCallback;
/// {@macro flutter.widgets.widgetsApp.supportedLocales} /// {@macro flutter.widgets.widgetsApp.supportedLocales}
/// ///
@ -585,7 +587,7 @@ class MaterialApp extends StatefulWidget {
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
/// {@macro flutter.widgets.widgetsApp.shortcuts.seeAlso} /// {@macro flutter.widgets.widgetsApp.shortcuts.seeAlso}
final Map<LogicalKeySet, Intent>? shortcuts; final Map<LogicalKeySet, Intent> shortcuts;
/// {@macro flutter.widgets.widgetsApp.actions} /// {@macro flutter.widgets.widgetsApp.actions}
/// {@tool snippet} /// {@tool snippet}
@ -618,10 +620,10 @@ class MaterialApp extends StatefulWidget {
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
/// {@macro flutter.widgets.widgetsApp.actions.seeAlso} /// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
final Map<Type, Action<Intent>>? actions; final Map<Type, Action<Intent>> actions;
/// {@macro flutter.widgets.widgetsApp.restorationScopeId} /// {@macro flutter.widgets.widgetsApp.restorationScopeId}
final String? restorationScopeId; final String restorationScopeId;
/// Turns on a [GridPaper] overlay that paints a baseline grid /// Turns on a [GridPaper] overlay that paints a baseline grid
/// Material apps. /// Material apps.
@ -641,7 +643,7 @@ class MaterialApp extends StatefulWidget {
/// Used by the [MaterialApp]. /// Used by the [MaterialApp].
static HeroController createMaterialHeroController() { static HeroController createMaterialHeroController() {
return HeroController( return HeroController(
createRectTween: (Rect? begin, Rect? end) { createRectTween: (Rect begin, Rect end) {
return MaterialRectArcTween(begin: begin, end: end); return MaterialRectArcTween(begin: begin, end: end);
}, },
); );
@ -651,7 +653,7 @@ class MaterialApp extends StatefulWidget {
class _MaterialScrollBehavior extends ScrollBehavior { class _MaterialScrollBehavior extends ScrollBehavior {
@override @override
TargetPlatform getPlatform(BuildContext context) { TargetPlatform getPlatform(BuildContext context) {
return Theme.of(context)!.platform; return Theme.of(context).platform;
} }
@override @override
@ -669,14 +671,15 @@ class _MaterialScrollBehavior extends ScrollBehavior {
return GlowingOverscrollIndicator( return GlowingOverscrollIndicator(
child: child, child: child,
axisDirection: axisDirection, axisDirection: axisDirection,
color: Theme.of(context)!.accentColor, color: Theme.of(context).accentColor,
); );
} }
return null;
} }
} }
class _MaterialAppState extends State<MaterialApp> { class _MaterialAppState extends State<MaterialApp> {
late HeroController _heroController; HeroController _heroController;
bool get _usesRouter => widget.routerDelegate != null; bool get _usesRouter => widget.routerDelegate != null;
@ -693,7 +696,7 @@ class _MaterialAppState extends State<MaterialApp> {
// _MaterialLocalizationsDelegate. // _MaterialLocalizationsDelegate.
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* { Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
if (widget.localizationsDelegates != null) if (widget.localizationsDelegates != null)
yield* widget.localizationsDelegates!; yield* widget.localizationsDelegates;
yield DefaultMaterialLocalizations.delegate; yield DefaultMaterialLocalizations.delegate;
yield DefaultCupertinoLocalizations.delegate; yield DefaultCupertinoLocalizations.delegate;
} }
@ -706,14 +709,14 @@ class _MaterialAppState extends State<MaterialApp> {
); );
} }
Widget _materialBuilder(BuildContext context, Widget? child) { Widget _materialBuilder(BuildContext context, Widget child) {
// Resolve which theme to use based on brightness and high contrast. // Resolve which theme to use based on brightness and high contrast.
final ThemeMode mode = widget.themeMode ?? ThemeMode.system; final ThemeMode mode = widget.themeMode ?? ThemeMode.system;
final Brightness platformBrightness = MediaQuery.platformBrightnessOf(context); final Brightness platformBrightness = MediaQuery.platformBrightnessOf(context);
final bool useDarkTheme = mode == ThemeMode.dark final bool useDarkTheme = mode == ThemeMode.dark
|| (mode == ThemeMode.system && platformBrightness == ui.Brightness.dark); || (mode == ThemeMode.system && platformBrightness == ui.Brightness.dark);
final bool highContrast = MediaQuery.highContrastOf(context); final bool highContrast = MediaQuery.highContrastOf(context);
ThemeData? theme; ThemeData theme;
if (useDarkTheme && highContrast && widget.highContrastDarkTheme != null) { if (useDarkTheme && highContrast && widget.highContrastDarkTheme != null) {
theme = widget.highContrastDarkTheme; theme = widget.highContrastDarkTheme;
@ -741,10 +744,10 @@ class _MaterialAppState extends State<MaterialApp> {
// surround widget.builder with yet another builder so that // surround widget.builder with yet another builder so that
// a context separates them and Theme.of() correctly // a context separates them and Theme.of() correctly
// resolves to the theme we passed to AnimatedTheme. // resolves to the theme we passed to AnimatedTheme.
return widget.builder!(context, child); return widget.builder(context, child);
}, },
) )
: child!, : child,
); );
} }
@ -761,8 +764,8 @@ class _MaterialAppState extends State<MaterialApp> {
return WidgetsApp.router( return WidgetsApp.router(
key: GlobalObjectKey(this), key: GlobalObjectKey(this),
routeInformationProvider: widget.routeInformationProvider, routeInformationProvider: widget.routeInformationProvider,
routeInformationParser: widget.routeInformationParser!, routeInformationParser: widget.routeInformationParser,
routerDelegate: widget.routerDelegate!, routerDelegate: widget.routerDelegate,
backButtonDispatcher: widget.backButtonDispatcher, backButtonDispatcher: widget.backButtonDispatcher,
builder: _materialBuilder, builder: _materialBuilder,
title: widget.title, title: widget.title,
@ -789,12 +792,12 @@ class _MaterialAppState extends State<MaterialApp> {
return WidgetsApp( return WidgetsApp(
key: GlobalObjectKey(this), key: GlobalObjectKey(this),
navigatorKey: widget.navigatorKey, navigatorKey: widget.navigatorKey,
navigatorObservers: widget.navigatorObservers!, navigatorObservers: widget.navigatorObservers,
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) { pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) {
return MaterialPageRoute<T>(settings: settings, builder: builder); return MaterialPageRoute<T>(settings: settings, builder: builder);
}, },
home: widget.home, home: widget.home,
routes: widget.routes!, routes: widget.routes,
initialRoute: widget.initialRoute, initialRoute: widget.initialRoute,
onGenerateRoute: widget.onGenerateRoute, onGenerateRoute: widget.onGenerateRoute,
onGenerateInitialRoutes: widget.onGenerateInitialRoutes, onGenerateInitialRoutes: widget.onGenerateInitialRoutes,

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -190,7 +192,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// ///
/// Typically used in the [Scaffold.appBar] property. /// Typically used in the [Scaffold.appBar] property.
AppBar({ AppBar({
Key? key, Key key,
this.leading, this.leading,
this.automaticallyImplyLeading = true, this.automaticallyImplyLeading = true,
this.title, this.title,
@ -219,7 +221,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
assert(titleSpacing != null), assert(titleSpacing != null),
assert(toolbarOpacity != null), assert(toolbarOpacity != null),
assert(bottomOpacity != null), assert(bottomOpacity != null),
preferredSize = Size.fromHeight(toolbarHeight ?? kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), preferredSize = Size.fromHeight(toolbarHeight ?? kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
super(key: key); super(key: key);
/// A widget to display before the [title]. /// A widget to display before the [title].
@ -267,7 +269,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// ///
/// * [Scaffold.appBar], in which an [AppBar] is usually placed. /// * [Scaffold.appBar], in which an [AppBar] is usually placed.
/// * [Scaffold.drawer], in which the [Drawer] is usually placed. /// * [Scaffold.drawer], in which the [Drawer] is usually placed.
final Widget? leading; final Widget leading;
/// Controls whether we should try to imply the leading widget if null. /// Controls whether we should try to imply the leading widget if null.
/// ///
@ -306,7 +308,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// ), /// ),
/// ) /// )
/// ``` /// ```
final Widget? title; final Widget title;
/// Widgets to display in a row after the [title] widget. /// Widgets to display in a row after the [title] widget.
/// ///
@ -317,7 +319,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// The [actions] become the trailing component of the [NavigationToolbar] built /// The [actions] become the trailing component of the [NavigationToolbar] built
/// by this widget. The height of each action is constrained to be no bigger /// by this widget. The height of each action is constrained to be no bigger
/// than the [toolbarHeight]. /// than the [toolbarHeight].
final List<Widget>? actions; final List<Widget> actions;
/// This widget is stacked behind the toolbar and the tab bar. It's height will /// This widget is stacked behind the toolbar and the tab bar. It's height will
/// be the same as the app bar's overall height. /// be the same as the app bar's overall height.
@ -327,7 +329,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// changes the [AppBar]'s height when scrolled. /// changes the [AppBar]'s height when scrolled.
/// ///
/// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details. /// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details.
final Widget? flexibleSpace; final Widget flexibleSpace;
/// This widget appears across the bottom of the app bar. /// This widget appears across the bottom of the app bar.
/// ///
@ -337,7 +339,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// See also: /// See also:
/// ///
/// * [PreferredSize], which can be used to give an arbitrary widget a preferred size. /// * [PreferredSize], which can be used to give an arbitrary widget a preferred size.
final PreferredSizeWidget? bottom; final PreferredSizeWidget bottom;
/// The z-coordinate at which to place this app bar relative to its parent. /// The z-coordinate at which to place this app bar relative to its parent.
/// ///
@ -348,20 +350,20 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// If this property is null, then [AppBarTheme.elevation] of /// If this property is null, then [AppBarTheme.elevation] of
/// [ThemeData.appBarTheme] is used. If that is also null, the default value /// [ThemeData.appBarTheme] is used. If that is also null, the default value
/// is 4. /// is 4.
final double? elevation; final double elevation;
/// The color to paint the shadow below the app bar. /// The color to paint the shadow below the app bar.
/// ///
/// If this property is null, then [AppBarTheme.shadowColor] of /// If this property is null, then [AppBarTheme.shadowColor] of
/// [ThemeData.appBarTheme] is used. If that is also null, the default value /// [ThemeData.appBarTheme] is used. If that is also null, the default value
/// is fully opaque black. /// is fully opaque black.
final Color? shadowColor; final Color shadowColor;
/// The material's shape as well its shadow. /// The material's shape as well its shadow.
/// ///
/// A shadow is only displayed if the [elevation] is greater than /// A shadow is only displayed if the [elevation] is greater than
/// zero. /// zero.
final ShapeBorder? shape; final ShapeBorder shape;
/// The color to use for the app bar's material. Typically this should be set /// The color to use for the app bar's material. Typically this should be set
/// along with [brightness], [iconTheme], [textTheme]. /// along with [brightness], [iconTheme], [textTheme].
@ -369,7 +371,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// If this property is null, then [AppBarTheme.color] of /// If this property is null, then [AppBarTheme.color] of
/// [ThemeData.appBarTheme] is used. If that is also null, then /// [ThemeData.appBarTheme] is used. If that is also null, then
/// [ThemeData.primaryColor] is used. /// [ThemeData.primaryColor] is used.
final Color? backgroundColor; final Color backgroundColor;
/// The brightness of the app bar's material. Typically this is set along /// The brightness of the app bar's material. Typically this is set along
/// with [backgroundColor], [iconTheme], [textTheme]. /// with [backgroundColor], [iconTheme], [textTheme].
@ -377,7 +379,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// If this property is null, then [AppBarTheme.brightness] of /// If this property is null, then [AppBarTheme.brightness] of
/// [ThemeData.appBarTheme] is used. If that is also null, then /// [ThemeData.appBarTheme] is used. If that is also null, then
/// [ThemeData.primaryColorBrightness] is used. /// [ThemeData.primaryColorBrightness] is used.
final Brightness? brightness; final Brightness brightness;
/// The color, opacity, and size to use for app bar icons. Typically this /// The color, opacity, and size to use for app bar icons. Typically this
/// is set along with [backgroundColor], [brightness], [textTheme]. /// is set along with [backgroundColor], [brightness], [textTheme].
@ -385,7 +387,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// If this property is null, then [AppBarTheme.iconTheme] of /// If this property is null, then [AppBarTheme.iconTheme] of
/// [ThemeData.appBarTheme] is used. If that is also null, then /// [ThemeData.appBarTheme] is used. If that is also null, then
/// [ThemeData.primaryIconTheme] is used. /// [ThemeData.primaryIconTheme] is used.
final IconThemeData? iconTheme; final IconThemeData iconTheme;
/// The color, opacity, and size to use for the icons that appear in the app /// The color, opacity, and size to use for the icons that appear in the app
/// bar's [actions]. This should only be used when the [actions] should be /// bar's [actions]. This should only be used when the [actions] should be
@ -395,7 +397,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// If this property is null, then [AppBarTheme.actionsIconTheme] of /// If this property is null, then [AppBarTheme.actionsIconTheme] of
/// [ThemeData.appBarTheme] is used. If that is also null, then this falls /// [ThemeData.appBarTheme] is used. If that is also null, then this falls
/// back to [iconTheme]. /// back to [iconTheme].
final IconThemeData? actionsIconTheme; final IconThemeData actionsIconTheme;
/// The typographic styles to use for text in the app bar. Typically this is /// The typographic styles to use for text in the app bar. Typically this is
/// set along with [brightness] [backgroundColor], [iconTheme]. /// set along with [brightness] [backgroundColor], [iconTheme].
@ -403,7 +405,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// If this property is null, then [AppBarTheme.textTheme] of /// If this property is null, then [AppBarTheme.textTheme] of
/// [ThemeData.appBarTheme] is used. If that is also null, then /// [ThemeData.appBarTheme] is used. If that is also null, then
/// [ThemeData.primaryTextTheme] is used. /// [ThemeData.primaryTextTheme] is used.
final TextTheme? textTheme; final TextTheme textTheme;
/// Whether this app bar is being displayed at the top of the screen. /// Whether this app bar is being displayed at the top of the screen.
/// ///
@ -417,7 +419,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// If this property is null, then [AppBarTheme.centerTitle] of /// If this property is null, then [AppBarTheme.centerTitle] of
/// [ThemeData.appBarTheme] is used. If that is also null, then value is /// [ThemeData.appBarTheme] is used. If that is also null, then value is
/// adapted to the current [TargetPlatform]. /// adapted to the current [TargetPlatform].
final bool? centerTitle; final bool centerTitle;
/// Whether the title should be wrapped with header [Semantics]. /// Whether the title should be wrapped with header [Semantics].
/// ///
@ -459,18 +461,18 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// Defines the height of the toolbar component of an [AppBar]. /// Defines the height of the toolbar component of an [AppBar].
/// ///
/// By default, the value of `toolbarHeight` is [kToolbarHeight]. /// By default, the value of `toolbarHeight` is [kToolbarHeight].
final double? toolbarHeight; final double toolbarHeight;
/// Defines the width of [leading] widget. /// Defines the width of [leading] widget.
/// ///
/// By default, the value of `leadingWidth` is 56.0. /// By default, the value of `leadingWidth` is 56.0.
final double? leadingWidth; final double leadingWidth;
bool _getEffectiveCenterTitle(ThemeData theme) { bool _getEffectiveCenterTitle(ThemeData theme) {
if (centerTitle != null) if (centerTitle != null)
return centerTitle!; return centerTitle;
if (theme.appBarTheme.centerTitle != null) if (theme.appBarTheme.centerTitle != null)
return theme.appBarTheme.centerTitle!; return theme.appBarTheme.centerTitle;
assert(theme.platform != null); assert(theme.platform != null);
switch (theme.platform) { switch (theme.platform) {
case TargetPlatform.android: case TargetPlatform.android:
@ -480,8 +482,9 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
return false; return false;
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS: case TargetPlatform.macOS:
return actions == null || actions!.length < 2; return actions == null || actions.length < 2;
} }
return null;
} }
@override @override
@ -493,21 +496,21 @@ class _AppBarState extends State<AppBar> {
static const Color _defaultShadowColor = Color(0xFF000000); static const Color _defaultShadowColor = Color(0xFF000000);
void _handleDrawerButton() { void _handleDrawerButton() {
Scaffold.of(context)!.openDrawer(); Scaffold.of(context).openDrawer();
} }
void _handleDrawerButtonEnd() { void _handleDrawerButtonEnd() {
Scaffold.of(context)!.openEndDrawer(); Scaffold.of(context).openEndDrawer();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(!widget.primary || debugCheckHasMediaQuery(context)); assert(!widget.primary || debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final ThemeData? theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final AppBarTheme appBarTheme = AppBarTheme.of(context); final AppBarTheme appBarTheme = AppBarTheme.of(context);
final ScaffoldState? scaffold = Scaffold.of(context, nullOk: true); final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
final ModalRoute<dynamic>? parentRoute = ModalRoute.of(context); final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);
final bool hasDrawer = scaffold?.hasDrawer ?? false; final bool hasDrawer = scaffold?.hasDrawer ?? false;
final bool hasEndDrawer = scaffold?.hasEndDrawer ?? false; final bool hasEndDrawer = scaffold?.hasEndDrawer ?? false;
@ -518,23 +521,23 @@ class _AppBarState extends State<AppBar> {
IconThemeData overallIconTheme = widget.iconTheme IconThemeData overallIconTheme = widget.iconTheme
?? appBarTheme.iconTheme ?? appBarTheme.iconTheme
?? theme!.primaryIconTheme; ?? theme.primaryIconTheme;
IconThemeData actionsIconTheme = widget.actionsIconTheme IconThemeData actionsIconTheme = widget.actionsIconTheme
?? appBarTheme.actionsIconTheme ?? appBarTheme.actionsIconTheme
?? overallIconTheme; ?? overallIconTheme;
TextStyle? centerStyle = widget.textTheme?.headline6 TextStyle centerStyle = widget.textTheme?.headline6
?? appBarTheme.textTheme?.headline6 ?? appBarTheme.textTheme?.headline6
?? theme!.primaryTextTheme.headline6; ?? theme.primaryTextTheme.headline6;
TextStyle? sideStyle = widget.textTheme?.bodyText2 TextStyle sideStyle = widget.textTheme?.bodyText2
?? appBarTheme.textTheme?.bodyText2 ?? appBarTheme.textTheme?.bodyText2
?? theme!.primaryTextTheme.bodyText2; ?? theme.primaryTextTheme.bodyText2;
if (widget.toolbarOpacity != 1.0) { if (widget.toolbarOpacity != 1.0) {
final double opacity = const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.toolbarOpacity); final double opacity = const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.toolbarOpacity);
if (centerStyle?.color != null) if (centerStyle?.color != null)
centerStyle = centerStyle!.copyWith(color: centerStyle.color!.withOpacity(opacity)); centerStyle = centerStyle.copyWith(color: centerStyle.color.withOpacity(opacity));
if (sideStyle?.color != null) if (sideStyle?.color != null)
sideStyle = sideStyle!.copyWith(color: sideStyle.color!.withOpacity(opacity)); sideStyle = sideStyle.copyWith(color: sideStyle.color.withOpacity(opacity));
overallIconTheme = overallIconTheme.copyWith( overallIconTheme = overallIconTheme.copyWith(
opacity: opacity * (overallIconTheme.opacity ?? 1.0) opacity: opacity * (overallIconTheme.opacity ?? 1.0)
); );
@ -543,13 +546,13 @@ class _AppBarState extends State<AppBar> {
); );
} }
Widget? leading = widget.leading; Widget leading = widget.leading;
if (leading == null && widget.automaticallyImplyLeading) { if (leading == null && widget.automaticallyImplyLeading) {
if (hasDrawer) { if (hasDrawer) {
leading = IconButton( leading = IconButton(
icon: const Icon(Icons.menu), icon: const Icon(Icons.menu),
onPressed: _handleDrawerButton, onPressed: _handleDrawerButton,
tooltip: MaterialLocalizations.of(context)!.openAppDrawerTooltip, tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
); );
} else { } else {
if (!hasEndDrawer && canPop) if (!hasEndDrawer && canPop)
@ -563,10 +566,10 @@ class _AppBarState extends State<AppBar> {
); );
} }
Widget? title = widget.title; Widget title = widget.title;
if (title != null) { if (title != null) {
bool? namesRoute; bool namesRoute;
switch (theme!.platform) { switch (theme.platform) {
case TargetPlatform.android: case TargetPlatform.android:
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.linux: case TargetPlatform.linux:
@ -599,7 +602,7 @@ class _AppBarState extends State<AppBar> {
// sizes. To opt out, wrap the [title] widget in a [MediaQuery] widget // sizes. To opt out, wrap the [title] widget in a [MediaQuery] widget
// with [MediaQueryData.textScaleFactor] set to // with [MediaQueryData.textScaleFactor] set to
// `MediaQuery.textScaleFactorOf(context)`. // `MediaQuery.textScaleFactorOf(context)`.
final MediaQueryData mediaQueryData = MediaQuery.of(context)!; final MediaQueryData mediaQueryData = MediaQuery.of(context);
title = MediaQuery( title = MediaQuery(
data: mediaQueryData.copyWith( data: mediaQueryData.copyWith(
textScaleFactor: math.min( textScaleFactor: math.min(
@ -611,18 +614,18 @@ class _AppBarState extends State<AppBar> {
); );
} }
Widget? actions; Widget actions;
if (widget.actions != null && widget.actions!.isNotEmpty) { if (widget.actions != null && widget.actions.isNotEmpty) {
actions = Row( actions = Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: widget.actions!, children: widget.actions,
); );
} else if (hasEndDrawer) { } else if (hasEndDrawer) {
actions = IconButton( actions = IconButton(
icon: const Icon(Icons.menu), icon: const Icon(Icons.menu),
onPressed: _handleDrawerButtonEnd, onPressed: _handleDrawerButtonEnd,
tooltip: MaterialLocalizations.of(context)!.openAppDrawerTooltip, tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
); );
} }
@ -638,7 +641,7 @@ class _AppBarState extends State<AppBar> {
leading: leading, leading: leading,
middle: title, middle: title,
trailing: actions, trailing: actions,
centerMiddle: widget._getEffectiveCenterTitle(theme!), centerMiddle: widget._getEffectiveCenterTitle(theme),
middleSpacing: widget.titleSpacing, middleSpacing: widget.titleSpacing,
); );
@ -667,7 +670,7 @@ class _AppBarState extends State<AppBar> {
), ),
), ),
if (widget.bottomOpacity == 1.0) if (widget.bottomOpacity == 1.0)
widget.bottom! widget.bottom
else else
Opacity( Opacity(
opacity: const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.bottomOpacity), opacity: const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.bottomOpacity),
@ -746,7 +749,7 @@ class _AppBarState extends State<AppBar> {
} }
class _FloatingAppBar extends StatefulWidget { class _FloatingAppBar extends StatefulWidget {
const _FloatingAppBar({ Key? key, required this.child }) : super(key: key); const _FloatingAppBar({ Key key, this.child }) : super(key: key);
final Widget child; final Widget child;
@ -757,26 +760,26 @@ class _FloatingAppBar extends StatefulWidget {
// A wrapper for the widget created by _SliverAppBarDelegate that starts and // A wrapper for the widget created by _SliverAppBarDelegate that starts and
// stops the floating app bar's snap-into-view or snap-out-of-view animation. // stops the floating app bar's snap-into-view or snap-out-of-view animation.
class _FloatingAppBarState extends State<_FloatingAppBar> { class _FloatingAppBarState extends State<_FloatingAppBar> {
ScrollPosition? _position; ScrollPosition _position;
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
if (_position != null) if (_position != null)
_position!.isScrollingNotifier.removeListener(_isScrollingListener); _position.isScrollingNotifier.removeListener(_isScrollingListener);
_position = Scrollable.of(context)?.position; _position = Scrollable.of(context)?.position;
if (_position != null) if (_position != null)
_position!.isScrollingNotifier.addListener(_isScrollingListener); _position.isScrollingNotifier.addListener(_isScrollingListener);
} }
@override @override
void dispose() { void dispose() {
if (_position != null) if (_position != null)
_position!.isScrollingNotifier.removeListener(_isScrollingListener); _position.isScrollingNotifier.removeListener(_isScrollingListener);
super.dispose(); super.dispose();
} }
RenderSliverFloatingPersistentHeader? _headerRenderer() { RenderSliverFloatingPersistentHeader _headerRenderer() {
return context.findAncestorRenderObjectOfType<RenderSliverFloatingPersistentHeader>(); return context.findAncestorRenderObjectOfType<RenderSliverFloatingPersistentHeader>();
} }
@ -786,11 +789,11 @@ class _FloatingAppBarState extends State<_FloatingAppBar> {
// When a scroll stops, then maybe snap the appbar into view. // When a scroll stops, then maybe snap the appbar into view.
// Similarly, when a scroll starts, then maybe stop the snap animation. // Similarly, when a scroll starts, then maybe stop the snap animation.
final RenderSliverFloatingPersistentHeader? header = _headerRenderer(); final RenderSliverFloatingPersistentHeader header = _headerRenderer();
if (_position!.isScrollingNotifier.value) if (_position.isScrollingNotifier.value)
header?.maybeStopSnapAnimation(_position!.userScrollDirection); header?.maybeStopSnapAnimation(_position.userScrollDirection);
else else
header?.maybeStartSnapAnimation(_position!.userScrollDirection); header?.maybeStartSnapAnimation(_position.userScrollDirection);
} }
@override @override
@ -799,69 +802,69 @@ class _FloatingAppBarState extends State<_FloatingAppBar> {
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate({ _SliverAppBarDelegate({
required this.leading, @required this.leading,
required this.automaticallyImplyLeading, @required this.automaticallyImplyLeading,
required this.title, @required this.title,
required this.actions, @required this.actions,
required this.flexibleSpace, @required this.flexibleSpace,
required this.bottom, @required this.bottom,
required this.elevation, @required this.elevation,
required this.shadowColor, @required this.shadowColor,
required this.forceElevated, @required this.forceElevated,
required this.backgroundColor, @required this.backgroundColor,
required this.brightness, @required this.brightness,
required this.iconTheme, @required this.iconTheme,
required this.actionsIconTheme, @required this.actionsIconTheme,
required this.textTheme, @required this.textTheme,
required this.primary, @required this.primary,
required this.centerTitle, @required this.centerTitle,
required this.excludeHeaderSemantics, @required this.excludeHeaderSemantics,
required this.titleSpacing, @required this.titleSpacing,
required this.expandedHeight, @required this.expandedHeight,
required this.collapsedHeight, @required this.collapsedHeight,
required this.topPadding, @required this.topPadding,
required this.floating, @required this.floating,
required this.pinned, @required this.pinned,
required this.vsync, @required this.vsync,
required this.snapConfiguration, @required this.snapConfiguration,
required this.stretchConfiguration, @required this.stretchConfiguration,
required this.showOnScreenConfiguration, @required this.showOnScreenConfiguration,
required this.shape, @required this.shape,
required this.toolbarHeight, @required this.toolbarHeight,
required this.leadingWidth, @required this.leadingWidth,
}) : assert(primary || topPadding == 0.0), }) : assert(primary || topPadding == 0.0),
assert( assert(
!floating || (snapConfiguration == null && showOnScreenConfiguration == null) || vsync != null, !floating || (snapConfiguration == null && showOnScreenConfiguration == null) || vsync != null,
'vsync cannot be null when snapConfiguration or showOnScreenConfiguration is not null, and floating is true', 'vsync cannot be null when snapConfiguration or showOnScreenConfiguration is not null, and floating is true',
), ),
_bottomHeight = bottom?.preferredSize.height ?? 0.0; _bottomHeight = bottom?.preferredSize?.height ?? 0.0;
final Widget? leading; final Widget leading;
final bool automaticallyImplyLeading; final bool automaticallyImplyLeading;
final Widget? title; final Widget title;
final List<Widget>? actions; final List<Widget> actions;
final Widget? flexibleSpace; final Widget flexibleSpace;
final PreferredSizeWidget? bottom; final PreferredSizeWidget bottom;
final double? elevation; final double elevation;
final Color? shadowColor; final Color shadowColor;
final bool forceElevated; final bool forceElevated;
final Color? backgroundColor; final Color backgroundColor;
final Brightness? brightness; final Brightness brightness;
final IconThemeData? iconTheme; final IconThemeData iconTheme;
final IconThemeData? actionsIconTheme; final IconThemeData actionsIconTheme;
final TextTheme? textTheme; final TextTheme textTheme;
final bool primary; final bool primary;
final bool? centerTitle; final bool centerTitle;
final bool excludeHeaderSemantics; final bool excludeHeaderSemantics;
final double titleSpacing; final double titleSpacing;
final double? expandedHeight; final double expandedHeight;
final double collapsedHeight; final double collapsedHeight;
final double topPadding; final double topPadding;
final bool floating; final bool floating;
final bool pinned; final bool pinned;
final ShapeBorder? shape; final ShapeBorder shape;
final double? toolbarHeight; final double toolbarHeight;
final double? leadingWidth; final double leadingWidth;
final double _bottomHeight; final double _bottomHeight;
@ -875,13 +878,13 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final TickerProvider vsync; final TickerProvider vsync;
@override @override
final FloatingHeaderSnapConfiguration? snapConfiguration; final FloatingHeaderSnapConfiguration snapConfiguration;
@override @override
final OverScrollHeaderStretchConfiguration? stretchConfiguration; final OverScrollHeaderStretchConfiguration stretchConfiguration;
@override @override
final PersistentHeaderShowOnScreenConfiguration? showOnScreenConfiguration; final PersistentHeaderShowOnScreenConfiguration showOnScreenConfiguration;
@override @override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
@ -891,7 +894,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final bool isPinnedWithOpacityFade = pinned && floating && bottom != null && extraToolbarHeight == 0.0; final bool isPinnedWithOpacityFade = pinned && floating && bottom != null && extraToolbarHeight == 0.0;
final double toolbarOpacity = !pinned || isPinnedWithOpacityFade final double toolbarOpacity = !pinned || isPinnedWithOpacityFade
? (visibleToolbarHeight / (toolbarHeight ?? kToolbarHeight)).clamp(0.0, 1.0) ? (visibleToolbarHeight / (toolbarHeight ?? kToolbarHeight)).clamp(0.0, 1.0) as double
: 1.0; : 1.0;
final Widget appBar = FlexibleSpaceBar.createSettings( final Widget appBar = FlexibleSpaceBar.createSettings(
@ -921,7 +924,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
titleSpacing: titleSpacing, titleSpacing: titleSpacing,
shape: shape, shape: shape,
toolbarOpacity: toolbarOpacity, toolbarOpacity: toolbarOpacity,
bottomOpacity: pinned ? 1.0 : ((visibleMainHeight / _bottomHeight).clamp(0.0, 1.0)), bottomOpacity: pinned ? 1.0 : ((visibleMainHeight / _bottomHeight).clamp(0.0, 1.0) as double),
toolbarHeight: toolbarHeight, toolbarHeight: toolbarHeight,
leadingWidth: leadingWidth, leadingWidth: leadingWidth,
), ),
@ -1051,7 +1054,7 @@ class SliverAppBar extends StatefulWidget {
/// The arguments [forceElevated], [primary], [floating], [pinned], [snap] /// The arguments [forceElevated], [primary], [floating], [pinned], [snap]
/// and [automaticallyImplyLeading] must not be null. /// and [automaticallyImplyLeading] must not be null.
const SliverAppBar({ const SliverAppBar({
Key? key, Key key,
this.leading, this.leading,
this.automaticallyImplyLeading = true, this.automaticallyImplyLeading = true,
this.title, this.title,
@ -1103,7 +1106,7 @@ class SliverAppBar extends StatefulWidget {
/// [IconButton] that opens the drawer. If there's no [Drawer] and the parent /// [IconButton] that opens the drawer. If there's no [Drawer] and the parent
/// [Navigator] can go back, the [AppBar] will use a [BackButton] that calls /// [Navigator] can go back, the [AppBar] will use a [BackButton] that calls
/// [Navigator.maybePop]. /// [Navigator.maybePop].
final Widget? leading; final Widget leading;
/// Controls whether we should try to imply the leading widget if null. /// Controls whether we should try to imply the leading widget if null.
/// ///
@ -1116,7 +1119,7 @@ class SliverAppBar extends StatefulWidget {
/// ///
/// Typically a [Text] widget containing a description of the current contents /// Typically a [Text] widget containing a description of the current contents
/// of the app. /// of the app.
final Widget? title; final Widget title;
/// Widgets to display after the [title] widget. /// Widgets to display after the [title] widget.
/// ///
@ -1149,7 +1152,7 @@ class SliverAppBar extends StatefulWidget {
/// ) /// )
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
final List<Widget>? actions; final List<Widget> actions;
/// This widget is stacked behind the toolbar and the tab bar. It's height will /// This widget is stacked behind the toolbar and the tab bar. It's height will
/// be the same as the app bar's overall height. /// be the same as the app bar's overall height.
@ -1158,7 +1161,7 @@ class SliverAppBar extends StatefulWidget {
/// must be large enough to accommodate the [SliverAppBar.flexibleSpace] widget. /// must be large enough to accommodate the [SliverAppBar.flexibleSpace] widget.
/// ///
/// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details. /// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details.
final Widget? flexibleSpace; final Widget flexibleSpace;
/// This widget appears across the bottom of the app bar. /// This widget appears across the bottom of the app bar.
/// ///
@ -1168,7 +1171,7 @@ class SliverAppBar extends StatefulWidget {
/// See also: /// See also:
/// ///
/// * [PreferredSize], which can be used to give an arbitrary widget a preferred size. /// * [PreferredSize], which can be used to give an arbitrary widget a preferred size.
final PreferredSizeWidget? bottom; final PreferredSizeWidget bottom;
/// The z-coordinate at which to place this app bar when it is above other /// The z-coordinate at which to place this app bar when it is above other
/// content. This controls the size of the shadow below the app bar. /// content. This controls the size of the shadow below the app bar.
@ -1181,7 +1184,7 @@ class SliverAppBar extends StatefulWidget {
/// no content underneath it. For example, if the app bar is [pinned] but no /// no content underneath it. For example, if the app bar is [pinned] but no
/// content is scrolled under it, or if it scrolls with the content, then no /// content is scrolled under it, or if it scrolls with the content, then no
/// shadow is drawn, regardless of the value of [elevation]. /// shadow is drawn, regardless of the value of [elevation].
final double? elevation; final double elevation;
/// The color to paint the shadow below the app bar. Typically this should be set /// The color to paint the shadow below the app bar. Typically this should be set
/// along with [elevation]. /// along with [elevation].
@ -1189,7 +1192,7 @@ class SliverAppBar extends StatefulWidget {
/// If this property is null, then [AppBarTheme.shadowColor] of /// If this property is null, then [AppBarTheme.shadowColor] of
/// [ThemeData.appBarTheme] is used, if that is also null, the default value /// [ThemeData.appBarTheme] is used, if that is also null, the default value
/// is fully opaque black. /// is fully opaque black.
final Color? shadowColor; final Color shadowColor;
/// Whether to show the shadow appropriate for the [elevation] even if the /// Whether to show the shadow appropriate for the [elevation] even if the
/// content is not scrolled under the [AppBar]. /// content is not scrolled under the [AppBar].
@ -1208,7 +1211,7 @@ class SliverAppBar extends StatefulWidget {
/// If this property is null, then [AppBarTheme.color] of /// If this property is null, then [AppBarTheme.color] of
/// [ThemeData.appBarTheme] is used. If that is also null, then /// [ThemeData.appBarTheme] is used. If that is also null, then
/// [ThemeData.primaryColor] is used. /// [ThemeData.primaryColor] is used.
final Color? backgroundColor; final Color backgroundColor;
/// The brightness of the app bar's material. Typically this is set along /// The brightness of the app bar's material. Typically this is set along
/// with [backgroundColor], [iconTheme], [textTheme]. /// with [backgroundColor], [iconTheme], [textTheme].
@ -1216,7 +1219,7 @@ class SliverAppBar extends StatefulWidget {
/// If this property is null, then [AppBarTheme.brightness] of /// If this property is null, then [AppBarTheme.brightness] of
/// [ThemeData.appBarTheme] is used. If that is also null, then /// [ThemeData.appBarTheme] is used. If that is also null, then
/// [ThemeData.primaryColorBrightness] is used. /// [ThemeData.primaryColorBrightness] is used.
final Brightness? brightness; final Brightness brightness;
/// The color, opacity, and size to use for app bar icons. Typically this /// The color, opacity, and size to use for app bar icons. Typically this
/// is set along with [backgroundColor], [brightness], [textTheme]. /// is set along with [backgroundColor], [brightness], [textTheme].
@ -1224,7 +1227,7 @@ class SliverAppBar extends StatefulWidget {
/// If this property is null, then [AppBarTheme.iconTheme] of /// If this property is null, then [AppBarTheme.iconTheme] of
/// [ThemeData.appBarTheme] is used, if that is also null, then /// [ThemeData.appBarTheme] is used, if that is also null, then
/// [ThemeData.primaryIconTheme] is used. /// [ThemeData.primaryIconTheme] is used.
final IconThemeData? iconTheme; final IconThemeData iconTheme;
/// The color, opacity, and size to use for trailing app bar icons. This /// The color, opacity, and size to use for trailing app bar icons. This
/// should only be used when the trailing icons should be themed differently /// should only be used when the trailing icons should be themed differently
@ -1233,7 +1236,7 @@ class SliverAppBar extends StatefulWidget {
/// If this property is null, then [AppBarTheme.actionsIconTheme] of /// If this property is null, then [AppBarTheme.actionsIconTheme] of
/// [ThemeData.appBarTheme] is used, if that is also null, then this falls /// [ThemeData.appBarTheme] is used, if that is also null, then this falls
/// back to [iconTheme]. /// back to [iconTheme].
final IconThemeData? actionsIconTheme; final IconThemeData actionsIconTheme;
/// The typographic styles to use for text in the app bar. Typically this is /// The typographic styles to use for text in the app bar. Typically this is
/// set along with [brightness] [backgroundColor], [iconTheme]. /// set along with [brightness] [backgroundColor], [iconTheme].
@ -1241,7 +1244,7 @@ class SliverAppBar extends StatefulWidget {
/// If this property is null, then [AppBarTheme.textTheme] of /// If this property is null, then [AppBarTheme.textTheme] of
/// [ThemeData.appBarTheme] is used, if that is also null, then /// [ThemeData.appBarTheme] is used, if that is also null, then
/// [ThemeData.primaryTextTheme] is used. /// [ThemeData.primaryTextTheme] is used.
final TextTheme? textTheme; final TextTheme textTheme;
/// Whether this app bar is being displayed at the top of the screen. /// Whether this app bar is being displayed at the top of the screen.
/// ///
@ -1252,7 +1255,7 @@ class SliverAppBar extends StatefulWidget {
/// Whether the title should be centered. /// Whether the title should be centered.
/// ///
/// Defaults to being adapted to the current [TargetPlatform]. /// Defaults to being adapted to the current [TargetPlatform].
final bool? centerTitle; final bool centerTitle;
/// Whether the title should be wrapped with header [Semantics]. /// Whether the title should be wrapped with header [Semantics].
/// ///
@ -1276,7 +1279,7 @@ class SliverAppBar extends StatefulWidget {
/// If [pinned] and [floating] are true, with [bottom] set, the default /// If [pinned] and [floating] are true, with [bottom] set, the default
/// collapsed height is only the height of [PreferredSizeWidget.preferredSize] /// collapsed height is only the height of [PreferredSizeWidget.preferredSize]
/// with the [MediaQuery] top padding. /// with the [MediaQuery] top padding.
final double? collapsedHeight; final double collapsedHeight;
/// The size of the app bar when it is fully expanded. /// The size of the app bar when it is fully expanded.
/// ///
@ -1286,7 +1289,7 @@ class SliverAppBar extends StatefulWidget {
/// ///
/// This does not include the status bar height (which will be automatically /// This does not include the status bar height (which will be automatically
/// included if [primary] is true). /// included if [primary] is true).
final double? expandedHeight; final double expandedHeight;
/// Whether the app bar should become visible as soon as the user scrolls /// Whether the app bar should become visible as soon as the user scrolls
/// towards the app bar. /// towards the app bar.
@ -1338,7 +1341,7 @@ class SliverAppBar extends StatefulWidget {
/// The material's shape as well as its shadow. /// The material's shape as well as its shadow.
/// ///
/// A shadow is only displayed if the [elevation] is greater than zero. /// A shadow is only displayed if the [elevation] is greater than zero.
final ShapeBorder? shape; final ShapeBorder shape;
/// If [snap] and [floating] are true then the floating app bar will "snap" /// If [snap] and [floating] are true then the floating app bar will "snap"
/// into view. /// into view.
@ -1385,7 +1388,7 @@ class SliverAppBar extends StatefulWidget {
/// The callback function to be executed when a user over-scrolls to the /// The callback function to be executed when a user over-scrolls to the
/// offset specified by [stretchTriggerOffset]. /// offset specified by [stretchTriggerOffset].
final AsyncCallback? onStretchTrigger; final AsyncCallback onStretchTrigger;
/// Defines the height of the toolbar component of an [AppBar]. /// Defines the height of the toolbar component of an [AppBar].
/// ///
@ -1395,7 +1398,7 @@ class SliverAppBar extends StatefulWidget {
/// Defines the width of [leading] widget. /// Defines the width of [leading] widget.
/// ///
/// By default, the value of `leadingWidth` is 56.0. /// By default, the value of `leadingWidth` is 56.0.
final double? leadingWidth; final double leadingWidth;
@override @override
_SliverAppBarState createState() => _SliverAppBarState(); _SliverAppBarState createState() => _SliverAppBarState();
@ -1404,9 +1407,9 @@ class SliverAppBar extends StatefulWidget {
// This class is only Stateful because it owns the TickerProvider used // This class is only Stateful because it owns the TickerProvider used
// by the floating appbar snap animation (via FloatingHeaderSnapConfiguration). // by the floating appbar snap animation (via FloatingHeaderSnapConfiguration).
class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMixin { class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMixin {
FloatingHeaderSnapConfiguration? _snapConfiguration; FloatingHeaderSnapConfiguration _snapConfiguration;
OverScrollHeaderStretchConfiguration? _stretchConfiguration; OverScrollHeaderStretchConfiguration _stretchConfiguration;
PersistentHeaderShowOnScreenConfiguration? _showOnScreenConfiguration; PersistentHeaderShowOnScreenConfiguration _showOnScreenConfiguration;
void _updateSnapConfiguration() { void _updateSnapConfiguration() {
if (widget.snap && widget.floating) { if (widget.snap && widget.floating) {
@ -1453,8 +1456,8 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(!widget.primary || debugCheckHasMediaQuery(context)); assert(!widget.primary || debugCheckHasMediaQuery(context));
final double bottomHeight = widget.bottom?.preferredSize.height ?? 0.0; final double bottomHeight = widget.bottom?.preferredSize?.height ?? 0.0;
final double topPadding = widget.primary ? MediaQuery.of(context)!.padding.top : 0.0; final double topPadding = widget.primary ? MediaQuery.of(context).padding.top : 0.0;
final double collapsedHeight = (widget.pinned && widget.floating && widget.bottom != null) final double collapsedHeight = (widget.pinned && widget.floating && widget.bottom != null)
? (widget.collapsedHeight ?? 0.0) + bottomHeight + topPadding ? (widget.collapsedHeight ?? 0.0) + bottomHeight + topPadding
: (widget.collapsedHeight ?? widget.toolbarHeight) + bottomHeight + topPadding; : (widget.collapsedHeight ?? widget.toolbarHeight) + bottomHeight + topPadding;
@ -1506,7 +1509,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
// center it within its (NavigationToolbar) parent, and allow the // center it within its (NavigationToolbar) parent, and allow the
// parent to constrain the title's actual height. // parent to constrain the title's actual height.
class _AppBarTitleBox extends SingleChildRenderObjectWidget { class _AppBarTitleBox extends SingleChildRenderObjectWidget {
const _AppBarTitleBox({ Key? key, required Widget child }) : assert(child != null), super(key: key, child: child); const _AppBarTitleBox({ Key key, @required Widget child }) : assert(child != null), super(key: key, child: child);
@override @override
_RenderAppBarTitleBox createRenderObject(BuildContext context) { _RenderAppBarTitleBox createRenderObject(BuildContext context) {
@ -1523,15 +1526,16 @@ class _AppBarTitleBox extends SingleChildRenderObjectWidget {
class _RenderAppBarTitleBox extends RenderAligningShiftedBox { class _RenderAppBarTitleBox extends RenderAligningShiftedBox {
_RenderAppBarTitleBox({ _RenderAppBarTitleBox({
RenderBox? child, RenderBox child,
TextDirection? textDirection, TextDirection textDirection,
}) : super(child: child, alignment: Alignment.center, textDirection: textDirection); }) : super(child: child, alignment: Alignment.center, textDirection: textDirection);
@override @override
void performLayout() { void performLayout() {
final BoxConstraints constraints = this.constraints;
final BoxConstraints innerConstraints = constraints.copyWith(maxHeight: double.infinity); final BoxConstraints innerConstraints = constraints.copyWith(maxHeight: double.infinity);
child!.layout(innerConstraints, parentUsesSize: true); child.layout(innerConstraints, parentUsesSize: true);
size = constraints.constrain(child!.size); size = constraints.constrain(child.size);
alignChild(); alignChild();
} }
} }

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' show lerpDouble; import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -44,54 +46,54 @@ class AppBarTheme with Diagnosticable {
/// Default value for [AppBar.brightness]. /// Default value for [AppBar.brightness].
/// ///
/// If null, [AppBar] uses [ThemeData.primaryColorBrightness]. /// If null, [AppBar] uses [ThemeData.primaryColorBrightness].
final Brightness? brightness; final Brightness brightness;
/// Default value for [AppBar.backgroundColor]. /// Default value for [AppBar.backgroundColor].
/// ///
/// If null, [AppBar] uses [ThemeData.primaryColor]. /// If null, [AppBar] uses [ThemeData.primaryColor].
final Color? color; final Color color;
/// Default value for [AppBar.elevation]. /// Default value for [AppBar.elevation].
/// ///
/// If null, [AppBar] uses a default value of 4.0. /// If null, [AppBar] uses a default value of 4.0.
final double? elevation; final double elevation;
/// Default value for [AppBar.shadowColor]. /// Default value for [AppBar.shadowColor].
/// ///
/// If null, [AppBar] uses a default value of fully opaque black. /// If null, [AppBar] uses a default value of fully opaque black.
final Color? shadowColor; final Color shadowColor;
/// Default value for [AppBar.iconTheme]. /// Default value for [AppBar.iconTheme].
/// ///
/// If null, [AppBar] uses [ThemeData.primaryIconTheme]. /// If null, [AppBar] uses [ThemeData.primaryIconTheme].
final IconThemeData? iconTheme; final IconThemeData iconTheme;
/// Default value for [AppBar.actionsIconTheme]. /// Default value for [AppBar.actionsIconTheme].
/// ///
/// If null, [AppBar] uses [ThemeData.primaryIconTheme]. /// If null, [AppBar] uses [ThemeData.primaryIconTheme].
final IconThemeData? actionsIconTheme; final IconThemeData actionsIconTheme;
/// Default value for [AppBar.textTheme]. /// Default value for [AppBar.textTheme].
/// ///
/// If null, [AppBar] uses [ThemeData.primaryTextTheme]. /// If null, [AppBar] uses [ThemeData.primaryTextTheme].
final TextTheme? textTheme; final TextTheme textTheme;
/// Default value for [AppBar.centerTitle]. /// Default value for [AppBar.centerTitle].
/// ///
/// If null, the value is adapted to current [TargetPlatform]. /// If null, the value is adapted to current [TargetPlatform].
final bool? centerTitle; final bool centerTitle;
/// Creates a copy of this object with the given fields replaced with the /// Creates a copy of this object with the given fields replaced with the
/// new values. /// new values.
AppBarTheme copyWith({ AppBarTheme copyWith({
IconThemeData? actionsIconTheme, IconThemeData actionsIconTheme,
Brightness? brightness, Brightness brightness,
Color? color, Color color,
double? elevation, double elevation,
Color? shadowColor, Color shadowColor,
IconThemeData? iconTheme, IconThemeData iconTheme,
TextTheme? textTheme, TextTheme textTheme,
bool? centerTitle, bool centerTitle,
}) { }) {
return AppBarTheme( return AppBarTheme(
brightness: brightness ?? this.brightness, brightness: brightness ?? this.brightness,
@ -107,7 +109,7 @@ class AppBarTheme with Diagnosticable {
/// The [ThemeData.appBarTheme] property of the ambient [Theme]. /// The [ThemeData.appBarTheme] property of the ambient [Theme].
static AppBarTheme of(BuildContext context) { static AppBarTheme of(BuildContext context) {
return Theme.of(context)!.appBarTheme; return Theme.of(context).appBarTheme;
} }
/// Linearly interpolate between two AppBar themes. /// Linearly interpolate between two AppBar themes.
@ -115,7 +117,7 @@ class AppBarTheme with Diagnosticable {
/// The argument `t` must not be null. /// The argument `t` must not be null.
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
static AppBarTheme lerp(AppBarTheme? a, AppBarTheme? b, double t) { static AppBarTheme lerp(AppBarTheme a, AppBarTheme b, double t) {
assert(t != null); assert(t != null);
return AppBarTheme( return AppBarTheme(
brightness: t < 0.5 ? a?.brightness : b?.brightness, brightness: t < 0.5 ? a?.brightness : b?.brightness,

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'debug.dart'; import 'debug.dart';
@ -25,7 +27,7 @@ import 'theme.dart';
class BackButtonIcon extends StatelessWidget { class BackButtonIcon extends StatelessWidget {
/// Creates an icon that shows the appropriate "back" image for /// Creates an icon that shows the appropriate "back" image for
/// the current platform (as obtained from the [Theme]). /// the current platform (as obtained from the [Theme]).
const BackButtonIcon({ Key? key }) : super(key: key); const BackButtonIcon({ Key key }) : super(key: key);
/// Returns the appropriate "back" icon for the given `platform`. /// Returns the appropriate "back" icon for the given `platform`.
static IconData _getIconData(TargetPlatform platform) { static IconData _getIconData(TargetPlatform platform) {
@ -39,10 +41,12 @@ class BackButtonIcon extends StatelessWidget {
case TargetPlatform.macOS: case TargetPlatform.macOS:
return Icons.arrow_back_ios; return Icons.arrow_back_ios;
} }
assert(false);
return null;
} }
@override @override
Widget build(BuildContext context) => Icon(_getIconData(Theme.of(context)!.platform)); Widget build(BuildContext context) => Icon(_getIconData(Theme.of(context).platform));
} }
/// A material design back button. /// A material design back button.
@ -74,13 +78,13 @@ class BackButtonIcon extends StatelessWidget {
class BackButton extends StatelessWidget { class BackButton extends StatelessWidget {
/// Creates an [IconButton] with the appropriate "back" icon for the current /// Creates an [IconButton] with the appropriate "back" icon for the current
/// target platform. /// target platform.
const BackButton({ Key? key, this.color, this.onPressed }) : super(key: key); const BackButton({ Key key, this.color, this.onPressed }) : super(key: key);
/// The color to use for the icon. /// The color to use for the icon.
/// ///
/// Defaults to the [IconThemeData.color] specified in the ambient [IconTheme], /// Defaults to the [IconThemeData.color] specified in the ambient [IconTheme],
/// which usually matches the ambient [Theme]'s [ThemeData.iconTheme]. /// which usually matches the ambient [Theme]'s [ThemeData.iconTheme].
final Color? color; final Color color;
/// An override callback to perform instead of the default behavior which is /// An override callback to perform instead of the default behavior which is
/// to pop the [Navigator]. /// to pop the [Navigator].
@ -90,7 +94,7 @@ class BackButton extends StatelessWidget {
/// situations. /// situations.
/// ///
/// Defaults to null. /// Defaults to null.
final VoidCallback? onPressed; final VoidCallback onPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -98,10 +102,10 @@ class BackButton extends StatelessWidget {
return IconButton( return IconButton(
icon: const BackButtonIcon(), icon: const BackButtonIcon(),
color: color, color: color,
tooltip: MaterialLocalizations.of(context)!.backButtonTooltip, tooltip: MaterialLocalizations.of(context).backButtonTooltip,
onPressed: () { onPressed: () {
if (onPressed != null) { if (onPressed != null) {
onPressed!(); onPressed();
} else { } else {
Navigator.maybePop(context); Navigator.maybePop(context);
} }
@ -128,13 +132,13 @@ class BackButton extends StatelessWidget {
/// * [IconButton], to create other material design icon buttons. /// * [IconButton], to create other material design icon buttons.
class CloseButton extends StatelessWidget { class CloseButton extends StatelessWidget {
/// Creates a Material Design close button. /// Creates a Material Design close button.
const CloseButton({ Key? key, this.color, this.onPressed }) : super(key: key); const CloseButton({ Key key, this.color, this.onPressed }) : super(key: key);
/// The color to use for the icon. /// The color to use for the icon.
/// ///
/// Defaults to the [IconThemeData.color] specified in the ambient [IconTheme], /// Defaults to the [IconThemeData.color] specified in the ambient [IconTheme],
/// which usually matches the ambient [Theme]'s [ThemeData.iconTheme]. /// which usually matches the ambient [Theme]'s [ThemeData.iconTheme].
final Color? color; final Color color;
/// An override callback to perform instead of the default behavior which is /// An override callback to perform instead of the default behavior which is
/// to pop the [Navigator]. /// to pop the [Navigator].
@ -144,7 +148,7 @@ class CloseButton extends StatelessWidget {
/// situations. /// situations.
/// ///
/// Defaults to null. /// Defaults to null.
final VoidCallback? onPressed; final VoidCallback onPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -152,10 +156,10 @@ class CloseButton extends StatelessWidget {
return IconButton( return IconButton(
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
color: color, color: color,
tooltip: MaterialLocalizations.of(context)!.closeButtonTooltip, tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
onPressed: () { onPressed: () {
if (onPressed != null) { if (onPressed != null) {
onPressed!(); onPressed();
} else { } else {
Navigator.maybePop(context); Navigator.maybePop(context);
} }

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'banner_theme.dart'; import 'banner_theme.dart';
@ -33,10 +35,10 @@ class MaterialBanner extends StatelessWidget {
/// The [actions], [content], and [forceActionsBelow] must be non-null. /// The [actions], [content], and [forceActionsBelow] must be non-null.
/// The [actions.length] must be greater than 0. /// The [actions.length] must be greater than 0.
const MaterialBanner({ const MaterialBanner({
Key? key, Key key,
required this.content, @required this.content,
this.contentTextStyle, this.contentTextStyle,
required this.actions, @required this.actions,
this.leading, this.leading,
this.backgroundColor, this.backgroundColor,
this.padding, this.padding,
@ -56,7 +58,7 @@ class MaterialBanner extends StatelessWidget {
/// ///
/// If `null`, [MaterialBannerThemeData.contentTextStyle] is used. If that is /// If `null`, [MaterialBannerThemeData.contentTextStyle] is used. If that is
/// also `null`, [TextTheme.bodyText2] of [ThemeData.textTheme] is used. /// also `null`, [TextTheme.bodyText2] of [ThemeData.textTheme] is used.
final TextStyle? contentTextStyle; final TextStyle contentTextStyle;
/// The set of actions that are displayed at the bottom or trailing side of /// The set of actions that are displayed at the bottom or trailing side of
/// the [MaterialBanner]. /// the [MaterialBanner].
@ -67,13 +69,13 @@ class MaterialBanner extends StatelessWidget {
/// The (optional) leading widget of the [MaterialBanner]. /// The (optional) leading widget of the [MaterialBanner].
/// ///
/// Typically an [Icon] widget. /// Typically an [Icon] widget.
final Widget? leading; final Widget leading;
/// The color of the surface of this [MaterialBanner]. /// The color of the surface of this [MaterialBanner].
/// ///
/// If `null`, [MaterialBannerThemeData.backgroundColor] is used. If that is /// If `null`, [MaterialBannerThemeData.backgroundColor] is used. If that is
/// also `null`, [ColorScheme.surface] of [ThemeData.colorScheme] is used. /// also `null`, [ColorScheme.surface] of [ThemeData.colorScheme] is used.
final Color? backgroundColor; final Color backgroundColor;
/// The amount of space by which to inset the [content]. /// The amount of space by which to inset the [content].
/// ///
@ -82,12 +84,12 @@ class MaterialBanner extends StatelessWidget {
/// ///
/// If the [actions] are trailing the [content], this defaults to /// If the [actions] are trailing the [content], this defaults to
/// `EdgeInsetsDirectional.only(start: 16.0, top: 2.0)`. /// `EdgeInsetsDirectional.only(start: 16.0, top: 2.0)`.
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry padding;
/// The amount of space by which to inset the [leading] widget. /// The amount of space by which to inset the [leading] widget.
/// ///
/// This defaults to `EdgeInsetsDirectional.only(end: 16.0)`. /// This defaults to `EdgeInsetsDirectional.only(end: 16.0)`.
final EdgeInsetsGeometry? leadingPadding; final EdgeInsetsGeometry leadingPadding;
/// An override to force the [actions] to be below the [content] regardless of /// An override to force the [actions] to be below the [content] regardless of
/// how many there are. /// how many there are.
@ -102,7 +104,7 @@ class MaterialBanner extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(actions.isNotEmpty); assert(actions.isNotEmpty);
final ThemeData? theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final MaterialBannerThemeData bannerTheme = MaterialBannerTheme.of(context); final MaterialBannerThemeData bannerTheme = MaterialBannerTheme.of(context);
final bool isSingleRow = actions.length == 1 && !forceActionsBelow; final bool isSingleRow = actions.length == 1 && !forceActionsBelow;
@ -125,10 +127,10 @@ class MaterialBanner extends StatelessWidget {
final Color backgroundColor = this.backgroundColor final Color backgroundColor = this.backgroundColor
?? bannerTheme.backgroundColor ?? bannerTheme.backgroundColor
?? theme!.colorScheme.surface; ?? theme.colorScheme.surface;
final TextStyle? textStyle = contentTextStyle final TextStyle textStyle = contentTextStyle
?? bannerTheme.contentTextStyle ?? bannerTheme.contentTextStyle
?? theme!.textTheme.bodyText2; ?? theme.textTheme.bodyText2;
return Container( return Container(
color: backgroundColor, color: backgroundColor,

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -36,25 +38,25 @@ class MaterialBannerThemeData with Diagnosticable {
}); });
/// The background color of a [MaterialBanner]. /// The background color of a [MaterialBanner].
final Color? backgroundColor; final Color backgroundColor;
/// Used to configure the [DefaultTextStyle] for the [MaterialBanner.content] /// Used to configure the [DefaultTextStyle] for the [MaterialBanner.content]
/// widget. /// widget.
final TextStyle? contentTextStyle; final TextStyle contentTextStyle;
/// The amount of space by which to inset [MaterialBanner.content]. /// The amount of space by which to inset [MaterialBanner.content].
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry padding;
/// The amount of space by which to inset [MaterialBanner.leading]. /// The amount of space by which to inset [MaterialBanner.leading].
final EdgeInsetsGeometry? leadingPadding; final EdgeInsetsGeometry leadingPadding;
/// Creates a copy of this object with the given fields replaced with the /// Creates a copy of this object with the given fields replaced with the
/// new values. /// new values.
MaterialBannerThemeData copyWith({ MaterialBannerThemeData copyWith({
Color? backgroundColor, Color backgroundColor,
TextStyle? contentTextStyle, TextStyle contentTextStyle,
EdgeInsetsGeometry? padding, EdgeInsetsGeometry padding,
EdgeInsetsGeometry? leadingPadding, EdgeInsetsGeometry leadingPadding,
}) { }) {
return MaterialBannerThemeData( return MaterialBannerThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor, backgroundColor: backgroundColor ?? this.backgroundColor,
@ -69,7 +71,7 @@ class MaterialBannerThemeData with Diagnosticable {
/// The argument `t` must not be null. /// The argument `t` must not be null.
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
static MaterialBannerThemeData lerp(MaterialBannerThemeData? a, MaterialBannerThemeData? b, double t) { static MaterialBannerThemeData lerp(MaterialBannerThemeData a, MaterialBannerThemeData b, double t) {
assert(t != null); assert(t != null);
return MaterialBannerThemeData( return MaterialBannerThemeData(
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
@ -121,13 +123,13 @@ class MaterialBannerTheme extends InheritedTheme {
/// Creates a banner theme that controls the configurations for /// Creates a banner theme that controls the configurations for
/// [MaterialBanner]s in its widget subtree. /// [MaterialBanner]s in its widget subtree.
const MaterialBannerTheme({ const MaterialBannerTheme({
Key? key, Key key,
this.data, this.data,
required Widget child, Widget child,
}) : super(key: key, child: child); }) : super(key: key, child: child);
/// The properties for descendant [MaterialBanner] widgets. /// The properties for descendant [MaterialBanner] widgets.
final MaterialBannerThemeData? data; final MaterialBannerThemeData data;
/// The closest instance of this class's [data] value that encloses the given /// The closest instance of this class's [data] value that encloses the given
/// context. /// context.
@ -141,13 +143,13 @@ class MaterialBannerTheme extends InheritedTheme {
/// MaterialBannerThemeData theme = MaterialBannerTheme.of(context); /// MaterialBannerThemeData theme = MaterialBannerTheme.of(context);
/// ``` /// ```
static MaterialBannerThemeData of(BuildContext context) { static MaterialBannerThemeData of(BuildContext context) {
final MaterialBannerTheme? bannerTheme = context.dependOnInheritedWidgetOfExactType<MaterialBannerTheme>(); final MaterialBannerTheme bannerTheme = context.dependOnInheritedWidgetOfExactType<MaterialBannerTheme>();
return bannerTheme?.data ?? Theme.of(context)!.bannerTheme; return bannerTheme?.data ?? Theme.of(context).bannerTheme;
} }
@override @override
Widget wrap(BuildContext context, Widget child) { Widget wrap(BuildContext context, Widget child) {
final MaterialBannerTheme? ancestorTheme = context.findAncestorWidgetOfExactType<MaterialBannerTheme>(); final MaterialBannerTheme ancestorTheme = context.findAncestorWidgetOfExactType<MaterialBannerTheme>();
return identical(this, ancestorTheme) ? child : MaterialBannerTheme(data: data, child: child); return identical(this, ancestorTheme) ? child : MaterialBannerTheme(data: data, child: child);
} }

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -48,7 +50,7 @@ class BottomAppBar extends StatefulWidget {
/// If the corresponding [BottomAppBarTheme] property is null, then the default /// If the corresponding [BottomAppBarTheme] property is null, then the default
/// specified in the property's documentation will be used. /// specified in the property's documentation will be used.
const BottomAppBar({ const BottomAppBar({
Key? key, Key key,
this.color, this.color,
this.elevation, this.elevation,
this.shape, this.shape,
@ -66,14 +68,14 @@ class BottomAppBar extends StatefulWidget {
/// ///
/// Typically this the child will be a [Row], with the first child /// Typically this the child will be a [Row], with the first child
/// being an [IconButton] with the [Icons.menu] icon. /// being an [IconButton] with the [Icons.menu] icon.
final Widget? child; final Widget child;
/// The bottom app bar's background color. /// The bottom app bar's background color.
/// ///
/// If this property is null then [BottomAppBarTheme.color] of /// If this property is null then [BottomAppBarTheme.color] of
/// [ThemeData.bottomAppBarTheme] is used. If that's null then /// [ThemeData.bottomAppBarTheme] is used. If that's null then
/// [ThemeData.bottomAppBarColor] is used. /// [ThemeData.bottomAppBarColor] is used.
final Color? color; final Color color;
/// The z-coordinate at which to place this bottom app bar relative to its /// The z-coordinate at which to place this bottom app bar relative to its
/// parent. /// parent.
@ -84,14 +86,14 @@ class BottomAppBar extends StatefulWidget {
/// If this property is null then [BottomAppBarTheme.elevation] of /// If this property is null then [BottomAppBarTheme.elevation] of
/// [ThemeData.bottomAppBarTheme] is used. If that's null, the default value /// [ThemeData.bottomAppBarTheme] is used. If that's null, the default value
/// is 8. /// is 8.
final double? elevation; final double elevation;
/// The notch that is made for the floating action button. /// The notch that is made for the floating action button.
/// ///
/// If this property is null then [BottomAppBarTheme.shape] of /// If this property is null then [BottomAppBarTheme.shape] of
/// [ThemeData.bottomAppBarTheme] is used. If that's null then the shape will /// [ThemeData.bottomAppBarTheme] is used. If that's null then the shape will
/// be rectangular with no notch. /// be rectangular with no notch.
final NotchedShape? shape; final NotchedShape shape;
/// {@macro flutter.widgets.Clip} /// {@macro flutter.widgets.Clip}
/// ///
@ -109,19 +111,19 @@ class BottomAppBar extends StatefulWidget {
} }
class _BottomAppBarState extends State<BottomAppBar> { class _BottomAppBarState extends State<BottomAppBar> {
late ValueListenable<ScaffoldGeometry> geometryListenable; ValueListenable<ScaffoldGeometry> geometryListenable;
static const double _defaultElevation = 8.0; static const double _defaultElevation = 8.0;
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
geometryListenable = Scaffold.geometryOf(context)!; geometryListenable = Scaffold.geometryOf(context);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final BottomAppBarTheme babTheme = BottomAppBarTheme.of(context); final BottomAppBarTheme babTheme = BottomAppBarTheme.of(context);
final NotchedShape? notchedShape = widget.shape ?? babTheme.shape; final NotchedShape notchedShape = widget.shape ?? babTheme.shape;
final CustomClipper<Path> clipper = notchedShape != null final CustomClipper<Path> clipper = notchedShape != null
? _BottomAppBarClipper( ? _BottomAppBarClipper(
geometry: geometryListenable, geometry: geometryListenable,
@ -130,7 +132,7 @@ class _BottomAppBarState extends State<BottomAppBar> {
) )
: const ShapeBorderClipper(shape: RoundedRectangleBorder()); : const ShapeBorderClipper(shape: RoundedRectangleBorder());
final double elevation = widget.elevation ?? babTheme.elevation ?? _defaultElevation; final double elevation = widget.elevation ?? babTheme.elevation ?? _defaultElevation;
final Color color = widget.color ?? babTheme.color ?? Theme.of(context)!.bottomAppBarColor; final Color color = widget.color ?? babTheme.color ?? Theme.of(context).bottomAppBarColor;
final Color effectiveColor = ElevationOverlay.applyOverlay(context, color, elevation); final Color effectiveColor = ElevationOverlay.applyOverlay(context, color, elevation);
return PhysicalShape( return PhysicalShape(
clipper: clipper, clipper: clipper,
@ -141,7 +143,7 @@ class _BottomAppBarState extends State<BottomAppBar> {
type: MaterialType.transparency, type: MaterialType.transparency,
child: widget.child == null child: widget.child == null
? null ? null
: SafeArea(child: widget.child!), : SafeArea(child: widget.child),
), ),
); );
} }
@ -149,9 +151,9 @@ class _BottomAppBarState extends State<BottomAppBar> {
class _BottomAppBarClipper extends CustomClipper<Path> { class _BottomAppBarClipper extends CustomClipper<Path> {
const _BottomAppBarClipper({ const _BottomAppBarClipper({
required this.geometry, @required this.geometry,
required this.shape, @required this.shape,
required this.notchMargin, @required this.notchMargin,
}) : assert(geometry != null), }) : assert(geometry != null),
assert(shape != null), assert(shape != null),
assert(notchMargin != null), assert(notchMargin != null),
@ -166,9 +168,9 @@ class _BottomAppBarClipper extends CustomClipper<Path> {
// button is the floating action button's bounding rectangle in the // button is the floating action button's bounding rectangle in the
// coordinate system whose origin is at the appBar's top left corner, // coordinate system whose origin is at the appBar's top left corner,
// or null if there is no floating action button. // or null if there is no floating action button.
final Rect? button = geometry.value.floatingActionButtonArea?.translate( final Rect button = geometry.value.floatingActionButtonArea?.translate(
0.0, 0.0,
geometry.value.bottomNavigationBarTop! * -1.0, geometry.value.bottomNavigationBarTop * -1.0,
); );
return shape.getOuterPath(Offset.zero & size, button?.inflate(notchMargin)); return shape.getOuterPath(Offset.zero & size, button?.inflate(notchMargin));
} }

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' show lerpDouble; import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -37,20 +39,20 @@ class BottomAppBarTheme with Diagnosticable {
/// Default value for [BottomAppBar.color]. /// Default value for [BottomAppBar.color].
/// ///
/// If null, [BottomAppBar] uses [ThemeData.bottomAppBarColor]. /// If null, [BottomAppBar] uses [ThemeData.bottomAppBarColor].
final Color? color; final Color color;
/// Default value for [BottomAppBar.elevation]. /// Default value for [BottomAppBar.elevation].
final double? elevation; final double elevation;
/// Default value for [BottomAppBar.shape]. /// Default value for [BottomAppBar.shape].
final NotchedShape? shape; final NotchedShape shape;
/// Creates a copy of this object but with the given fields replaced with the /// Creates a copy of this object but with the given fields replaced with the
/// new values. /// new values.
BottomAppBarTheme copyWith({ BottomAppBarTheme copyWith({
Color? color, Color color,
double? elevation, double elevation,
NotchedShape? shape, NotchedShape shape,
}) { }) {
return BottomAppBarTheme( return BottomAppBarTheme(
color: color ?? this.color, color: color ?? this.color,
@ -61,7 +63,7 @@ class BottomAppBarTheme with Diagnosticable {
/// The [ThemeData.bottomAppBarTheme] property of the ambient [Theme]. /// The [ThemeData.bottomAppBarTheme] property of the ambient [Theme].
static BottomAppBarTheme of(BuildContext context) { static BottomAppBarTheme of(BuildContext context) {
return Theme.of(context)!.bottomAppBarTheme; return Theme.of(context).bottomAppBarTheme;
} }
/// Linearly interpolate between two BAB themes. /// Linearly interpolate between two BAB themes.
@ -69,7 +71,7 @@ class BottomAppBarTheme with Diagnosticable {
/// The argument `t` must not be null. /// The argument `t` must not be null.
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
static BottomAppBarTheme lerp(BottomAppBarTheme? a, BottomAppBarTheme? b, double t) { static BottomAppBarTheme lerp(BottomAppBarTheme a, BottomAppBarTheme b, double t) {
assert(t != null); assert(t != null);
return BottomAppBarTheme( return BottomAppBarTheme(
color: Color.lerp(a?.color, b?.color, t), color: Color.lerp(a?.color, b?.color, t),

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:collection' show Queue; import 'dart:collection' show Queue;
import 'dart:math' as math; import 'dart:math' as math;
@ -170,16 +172,16 @@ class BottomNavigationBar extends StatefulWidget {
/// [BottomNavigationBarType.fixed] and `false` if [type] is /// [BottomNavigationBarType.fixed] and `false` if [type] is
/// [BottomNavigationBarType.shifting]. /// [BottomNavigationBarType.shifting].
BottomNavigationBar({ BottomNavigationBar({
Key? key, Key key,
required this.items, @required this.items,
this.onTap, this.onTap,
this.currentIndex = 0, this.currentIndex = 0,
this.elevation, this.elevation,
this.type, this.type,
Color? fixedColor, Color fixedColor,
this.backgroundColor, this.backgroundColor,
this.iconSize = 24.0, this.iconSize = 24.0,
Color? selectedItemColor, Color selectedItemColor,
this.unselectedItemColor, this.unselectedItemColor,
this.selectedIconTheme, this.selectedIconTheme,
this.unselectedIconTheme, this.unselectedIconTheme,
@ -219,7 +221,7 @@ class BottomNavigationBar extends StatefulWidget {
/// The stateful widget that creates the bottom navigation bar needs to keep /// The stateful widget that creates the bottom navigation bar needs to keep
/// track of the index of the selected [BottomNavigationBarItem] and call /// track of the index of the selected [BottomNavigationBarItem] and call
/// `setState` to rebuild the bottom navigation bar with the new [currentIndex]. /// `setState` to rebuild the bottom navigation bar with the new [currentIndex].
final ValueChanged<int>? onTap; final ValueChanged<int> onTap;
/// The index into [items] for the current active [BottomNavigationBarItem]. /// The index into [items] for the current active [BottomNavigationBarItem].
final int currentIndex; final int currentIndex;
@ -229,26 +231,26 @@ class BottomNavigationBar extends StatefulWidget {
/// If null, defaults to `8.0`. /// If null, defaults to `8.0`.
/// ///
/// {@macro flutter.material.material.elevation} /// {@macro flutter.material.material.elevation}
final double? elevation; final double elevation;
/// Defines the layout and behavior of a [BottomNavigationBar]. /// Defines the layout and behavior of a [BottomNavigationBar].
/// ///
/// See documentation for [BottomNavigationBarType] for information on the /// See documentation for [BottomNavigationBarType] for information on the
/// meaning of different types. /// meaning of different types.
final BottomNavigationBarType? type; final BottomNavigationBarType type;
/// The value of [selectedItemColor]. /// The value of [selectedItemColor].
/// ///
/// This getter only exists for backwards compatibility, the /// This getter only exists for backwards compatibility, the
/// [selectedItemColor] property is preferred. /// [selectedItemColor] property is preferred.
Color? get fixedColor => selectedItemColor; Color get fixedColor => selectedItemColor;
/// The color of the [BottomNavigationBar] itself. /// The color of the [BottomNavigationBar] itself.
/// ///
/// If [type] is [BottomNavigationBarType.shifting] and the /// If [type] is [BottomNavigationBarType.shifting] and the
/// [items] have [BottomNavigationBarItem.backgroundColor] set, the [items]' /// [items] have [BottomNavigationBarItem.backgroundColor] set, the [items]'
/// backgroundColor will splash and overwrite this color. /// backgroundColor will splash and overwrite this color.
final Color? backgroundColor; final Color backgroundColor;
/// The size of all of the [BottomNavigationBarItem] icons. /// The size of all of the [BottomNavigationBarItem] icons.
/// ///
@ -259,13 +261,13 @@ class BottomNavigationBar extends StatefulWidget {
/// [BottomNavigationBarItem.title]. /// [BottomNavigationBarItem.title].
/// ///
/// If null then the [ThemeData.primaryColor] is used. /// If null then the [ThemeData.primaryColor] is used.
final Color? selectedItemColor; final Color selectedItemColor;
/// The color of the unselected [BottomNavigationBarItem.icon] and /// The color of the unselected [BottomNavigationBarItem.icon] and
/// [BottomNavigationBarItem.title]s. /// [BottomNavigationBarItem.title]s.
/// ///
/// If null then the [TextTheme.caption]'s color is used. /// If null then the [TextTheme.caption]'s color is used.
final Color? unselectedItemColor; final Color unselectedItemColor;
/// The size, opacity, and color of the icon in the currently selected /// The size, opacity, and color of the icon in the currently selected
/// [BottomNavigationBarItem.icon]. /// [BottomNavigationBarItem.icon].
@ -276,7 +278,7 @@ class BottomNavigationBar extends StatefulWidget {
/// It this field is provided, it must contain non-null [IconThemeData.size] /// It this field is provided, it must contain non-null [IconThemeData.size]
/// and [IconThemeData.color] properties. Also, if this field is supplied, /// and [IconThemeData.color] properties. Also, if this field is supplied,
/// [unselectedIconTheme] must be provided. /// [unselectedIconTheme] must be provided.
final IconThemeData? selectedIconTheme; final IconThemeData selectedIconTheme;
/// The size, opacity, and color of the icon in the currently unselected /// The size, opacity, and color of the icon in the currently unselected
/// [BottomNavigationBarItem.icon]s. /// [BottomNavigationBarItem.icon]s.
@ -287,15 +289,15 @@ class BottomNavigationBar extends StatefulWidget {
/// It this field is provided, it must contain non-null [IconThemeData.size] /// It this field is provided, it must contain non-null [IconThemeData.size]
/// and [IconThemeData.color] properties. Also, if this field is supplied, /// and [IconThemeData.color] properties. Also, if this field is supplied,
/// [unselectedIconTheme] must be provided. /// [unselectedIconTheme] must be provided.
final IconThemeData? unselectedIconTheme; final IconThemeData unselectedIconTheme;
/// The [TextStyle] of the [BottomNavigationBarItem] labels when they are /// The [TextStyle] of the [BottomNavigationBarItem] labels when they are
/// selected. /// selected.
final TextStyle? selectedLabelStyle; final TextStyle selectedLabelStyle;
/// The [TextStyle] of the [BottomNavigationBarItem] labels when they are not /// The [TextStyle] of the [BottomNavigationBarItem] labels when they are not
/// selected. /// selected.
final TextStyle? unselectedLabelStyle; final TextStyle unselectedLabelStyle;
/// The font size of the [BottomNavigationBarItem] labels when they are selected. /// The font size of the [BottomNavigationBarItem] labels when they are selected.
/// ///
@ -315,7 +317,7 @@ class BottomNavigationBar extends StatefulWidget {
final double unselectedFontSize; final double unselectedFontSize;
/// Whether the labels are shown for the selected [BottomNavigationBarItem]. /// Whether the labels are shown for the selected [BottomNavigationBarItem].
final bool? showUnselectedLabels; final bool showUnselectedLabels;
/// Whether the labels are shown for the unselected [BottomNavigationBarItem]s. /// Whether the labels are shown for the unselected [BottomNavigationBarItem]s.
final bool showSelectedLabels; final bool showSelectedLabels;
@ -324,7 +326,7 @@ class BottomNavigationBar extends StatefulWidget {
/// tiles. /// tiles.
/// ///
/// If this property is null, [SystemMouseCursors.click] will be used. /// If this property is null, [SystemMouseCursors.click] will be used.
final MouseCursor? mouseCursor; final MouseCursor mouseCursor;
@override @override
_BottomNavigationBarState createState() => _BottomNavigationBarState(); _BottomNavigationBarState createState() => _BottomNavigationBarState();
@ -342,14 +344,14 @@ class _BottomNavigationTile extends StatelessWidget {
this.colorTween, this.colorTween,
this.flex, this.flex,
this.selected = false, this.selected = false,
required this.selectedLabelStyle, @required this.selectedLabelStyle,
required this.unselectedLabelStyle, @required this.unselectedLabelStyle,
required this.selectedIconTheme, @required this.selectedIconTheme,
required this.unselectedIconTheme, @required this.unselectedIconTheme,
this.showSelectedLabels, this.showSelectedLabels,
this.showUnselectedLabels, this.showUnselectedLabels,
this.indexLabel, this.indexLabel,
required this.mouseCursor, @required this.mouseCursor,
}) : assert(type != null), }) : assert(type != null),
assert(item != null), assert(item != null),
assert(animation != null), assert(animation != null),
@ -362,17 +364,17 @@ class _BottomNavigationTile extends StatelessWidget {
final BottomNavigationBarItem item; final BottomNavigationBarItem item;
final Animation<double> animation; final Animation<double> animation;
final double iconSize; final double iconSize;
final VoidCallback? onTap; final VoidCallback onTap;
final ColorTween? colorTween; final ColorTween colorTween;
final double? flex; final double flex;
final bool selected; final bool selected;
final IconThemeData? selectedIconTheme; final IconThemeData selectedIconTheme;
final IconThemeData? unselectedIconTheme; final IconThemeData unselectedIconTheme;
final TextStyle selectedLabelStyle; final TextStyle selectedLabelStyle;
final TextStyle unselectedLabelStyle; final TextStyle unselectedLabelStyle;
final String? indexLabel; final String indexLabel;
final bool? showSelectedLabels; final bool showSelectedLabels;
final bool? showUnselectedLabels; final bool showUnselectedLabels;
final MouseCursor mouseCursor; final MouseCursor mouseCursor;
@override @override
@ -385,13 +387,13 @@ class _BottomNavigationTile extends StatelessWidget {
final BottomNavigationBarThemeData bottomTheme = BottomNavigationBarTheme.of(context); final BottomNavigationBarThemeData bottomTheme = BottomNavigationBarTheme.of(context);
final double selectedFontSize = selectedLabelStyle.fontSize!; final double selectedFontSize = selectedLabelStyle.fontSize;
final double selectedIconSize = selectedIconTheme?.size final double selectedIconSize = selectedIconTheme?.size
?? bottomTheme.selectedIconTheme?.size ?? bottomTheme?.selectedIconTheme?.size
?? iconSize; ?? iconSize;
final double unselectedIconSize = unselectedIconTheme?.size final double unselectedIconSize = unselectedIconTheme?.size
?? bottomTheme.unselectedIconTheme?.size ?? bottomTheme?.unselectedIconTheme?.size
?? iconSize; ?? iconSize;
// The amount that the selected icon is bigger than the unselected icons, // The amount that the selected icon is bigger than the unselected icons,
@ -420,7 +422,7 @@ class _BottomNavigationTile extends StatelessWidget {
// ======= // =======
double bottomPadding; double bottomPadding;
double topPadding; double topPadding;
if (showSelectedLabels! && !showUnselectedLabels!) { if (showSelectedLabels && !showUnselectedLabels) {
bottomPadding = Tween<double>( bottomPadding = Tween<double>(
begin: selectedIconDiff / 2.0, begin: selectedIconDiff / 2.0,
end: selectedFontSize / 2.0 - unselectedIconDiff / 2.0, end: selectedFontSize / 2.0 - unselectedIconDiff / 2.0,
@ -429,7 +431,7 @@ class _BottomNavigationTile extends StatelessWidget {
begin: selectedFontSize + selectedIconDiff / 2.0, begin: selectedFontSize + selectedIconDiff / 2.0,
end: selectedFontSize / 2.0 - unselectedIconDiff / 2.0, end: selectedFontSize / 2.0 - unselectedIconDiff / 2.0,
).evaluate(animation); ).evaluate(animation);
} else if (!showSelectedLabels! && !showUnselectedLabels!) { } else if (!showSelectedLabels && !showUnselectedLabels) {
bottomPadding = Tween<double>( bottomPadding = Tween<double>(
begin: selectedIconDiff / 2.0, begin: selectedIconDiff / 2.0,
end: unselectedIconDiff / 2.0, end: unselectedIconDiff / 2.0,
@ -454,7 +456,7 @@ class _BottomNavigationTile extends StatelessWidget {
size = 1; size = 1;
break; break;
case BottomNavigationBarType.shifting: case BottomNavigationBarType.shifting:
size = (flex! * 1000.0).round(); size = (flex * 1000.0).round();
break; break;
} }
@ -469,7 +471,7 @@ class _BottomNavigationTile extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
_TileIcon( _TileIcon(
colorTween: colorTween!, colorTween: colorTween,
animation: animation, animation: animation,
iconSize: iconSize, iconSize: iconSize,
selected: selected, selected: selected,
@ -478,13 +480,13 @@ class _BottomNavigationTile extends StatelessWidget {
unselectedIconTheme: unselectedIconTheme ?? bottomTheme.unselectedIconTheme, unselectedIconTheme: unselectedIconTheme ?? bottomTheme.unselectedIconTheme,
), ),
_Label( _Label(
colorTween: colorTween!, colorTween: colorTween,
animation: animation, animation: animation,
item: item, item: item,
selectedLabelStyle: selectedLabelStyle, selectedLabelStyle: selectedLabelStyle ?? bottomTheme.selectedLabelStyle,
unselectedLabelStyle: unselectedLabelStyle, unselectedLabelStyle: unselectedLabelStyle ?? bottomTheme.unselectedLabelStyle,
showSelectedLabels: showSelectedLabels ?? bottomTheme.showUnselectedLabels!, showSelectedLabels: showSelectedLabels ?? bottomTheme.showUnselectedLabels,
showUnselectedLabels: showUnselectedLabels ?? bottomTheme.showUnselectedLabels!, showUnselectedLabels: showUnselectedLabels ?? bottomTheme.showUnselectedLabels,
), ),
], ],
), ),
@ -493,7 +495,7 @@ class _BottomNavigationTile extends StatelessWidget {
if (item.label != null) { if (item.label != null) {
result = Tooltip( result = Tooltip(
message: item.label!, message: item.label,
preferBelow: false, preferBelow: false,
verticalOffset: selectedIconSize + selectedFontSize, verticalOffset: selectedIconSize + selectedFontSize,
child: result, child: result,
@ -523,14 +525,14 @@ class _BottomNavigationTile extends StatelessWidget {
class _TileIcon extends StatelessWidget { class _TileIcon extends StatelessWidget {
const _TileIcon({ const _TileIcon({
Key? key, Key key,
required this.colorTween, @required this.colorTween,
required this.animation, @required this.animation,
required this.iconSize, @required this.iconSize,
required this.selected, @required this.selected,
required this.item, @required this.item,
required this.selectedIconTheme, @required this.selectedIconTheme,
required this.unselectedIconTheme, @required this.unselectedIconTheme,
}) : assert(selected != null), }) : assert(selected != null),
assert(item != null), assert(item != null),
super(key: key); super(key: key);
@ -540,12 +542,12 @@ class _TileIcon extends StatelessWidget {
final double iconSize; final double iconSize;
final bool selected; final bool selected;
final BottomNavigationBarItem item; final BottomNavigationBarItem item;
final IconThemeData? selectedIconTheme; final IconThemeData selectedIconTheme;
final IconThemeData? unselectedIconTheme; final IconThemeData unselectedIconTheme;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final Color? iconColor = colorTween.evaluate(animation); final Color iconColor = colorTween.evaluate(animation);
final IconThemeData defaultIconTheme = IconThemeData( final IconThemeData defaultIconTheme = IconThemeData(
color: iconColor, color: iconColor,
size: iconSize, size: iconSize,
@ -571,14 +573,14 @@ class _TileIcon extends StatelessWidget {
class _Label extends StatelessWidget { class _Label extends StatelessWidget {
const _Label({ const _Label({
Key? key, Key key,
required this.colorTween, @required this.colorTween,
required this.animation, @required this.animation,
required this.item, @required this.item,
required this.selectedLabelStyle, @required this.selectedLabelStyle,
required this.unselectedLabelStyle, @required this.unselectedLabelStyle,
required this.showSelectedLabels, @required this.showSelectedLabels,
required this.showUnselectedLabels, @required this.showUnselectedLabels,
}) : assert(colorTween != null), }) : assert(colorTween != null),
assert(animation != null), assert(animation != null),
assert(item != null), assert(item != null),
@ -598,14 +600,14 @@ class _Label extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double? selectedFontSize = selectedLabelStyle.fontSize; final double selectedFontSize = selectedLabelStyle.fontSize;
final double? unselectedFontSize = unselectedLabelStyle.fontSize; final double unselectedFontSize = unselectedLabelStyle.fontSize;
final TextStyle customStyle = TextStyle.lerp( final TextStyle customStyle = TextStyle.lerp(
unselectedLabelStyle, unselectedLabelStyle,
selectedLabelStyle, selectedLabelStyle,
animation.value, animation.value,
)!; );
Widget text = DefaultTextStyle.merge( Widget text = DefaultTextStyle.merge(
style: customStyle.copyWith( style: customStyle.copyWith(
fontSize: selectedFontSize, fontSize: selectedFontSize,
@ -618,13 +620,13 @@ class _Label extends StatelessWidget {
transform: Matrix4.diagonal3( transform: Matrix4.diagonal3(
Vector3.all( Vector3.all(
Tween<double>( Tween<double>(
begin: unselectedFontSize! / selectedFontSize!, begin: unselectedFontSize / selectedFontSize,
end: 1.0, end: 1.0,
).evaluate(animation), ).evaluate(animation),
), ),
), ),
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: item.title ?? Text(item.label!), child: item.title ?? Text(item.label),
), ),
); );
@ -660,7 +662,7 @@ class _Label extends StatelessWidget {
if (item.label != null) { if (item.label != null) {
// Do not grow text in bottom navigation bar when we can show a tooltip // Do not grow text in bottom navigation bar when we can show a tooltip
// instead. // instead.
final MediaQueryData mediaQueryData = MediaQuery.of(context)!; final MediaQueryData mediaQueryData = MediaQuery.of(context);
text = MediaQuery( text = MediaQuery(
data: mediaQueryData.copyWith( data: mediaQueryData.copyWith(
textScaleFactor: math.min(1.0, mediaQueryData.textScaleFactor), textScaleFactor: math.min(1.0, mediaQueryData.textScaleFactor),
@ -675,14 +677,14 @@ class _Label extends StatelessWidget {
class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerProviderStateMixin { class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerProviderStateMixin {
List<AnimationController> _controllers = <AnimationController>[]; List<AnimationController> _controllers = <AnimationController>[];
late List<CurvedAnimation> _animations; List<CurvedAnimation> _animations;
// A queue of color splashes currently being animated. // A queue of color splashes currently being animated.
final Queue<_Circle> _circles = Queue<_Circle>(); final Queue<_Circle> _circles = Queue<_Circle>();
// Last splash circle's color, and the final color of the control after // Last splash circle's color, and the final color of the control after
// animation is complete. // animation is complete.
Color? _backgroundColor; Color _backgroundColor;
static final Animatable<double> _flexTween = Tween<double>(begin: 1.0, end: 1.5); static final Animatable<double> _flexTween = Tween<double>(begin: 1.0, end: 1.5);
@ -733,6 +735,8 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
case BottomNavigationBarType.fixed: case BottomNavigationBarType.fixed:
return true; return true;
} }
assert(false);
return false;
} }
@override @override
@ -765,7 +769,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
_Circle( _Circle(
state: this, state: this,
index: index, index: index,
color: widget.items[index].backgroundColor!, color: widget.items[index].backgroundColor,
vsync: this, vsync: this,
)..controller.addStatusListener( )..controller.addStatusListener(
(AnimationStatus status) { (AnimationStatus status) {
@ -816,17 +820,17 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
// If the given [TextStyle] has a non-null `fontSize`, it should be used. // If the given [TextStyle] has a non-null `fontSize`, it should be used.
// Otherwise, the [selectedFontSize] parameter should be used. // Otherwise, the [selectedFontSize] parameter should be used.
static TextStyle _effectiveTextStyle(TextStyle? textStyle, double fontSize) { static TextStyle _effectiveTextStyle(TextStyle textStyle, double fontSize) {
textStyle ??= const TextStyle(); textStyle ??= const TextStyle();
// Prefer the font size on textStyle if present. // Prefer the font size on textStyle if present.
return textStyle.fontSize == null ? textStyle.copyWith(fontSize: fontSize) : textStyle; return textStyle.fontSize == null ? textStyle.copyWith(fontSize: fontSize) : textStyle;
} }
List<Widget> _createTiles() { List<Widget> _createTiles() {
final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; final MaterialLocalizations localizations = MaterialLocalizations.of(context);
assert(localizations != null); assert(localizations != null);
final ThemeData themeData = Theme.of(context)!; final ThemeData themeData = Theme.of(context);
final BottomNavigationBarThemeData bottomTheme = BottomNavigationBarTheme.of(context); final BottomNavigationBarThemeData bottomTheme = BottomNavigationBarTheme.of(context);
final TextStyle effectiveSelectedLabelStyle = final TextStyle effectiveSelectedLabelStyle =
@ -856,7 +860,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
colorTween = ColorTween( colorTween = ColorTween(
begin: widget.unselectedItemColor begin: widget.unselectedItemColor
?? bottomTheme.unselectedItemColor ?? bottomTheme.unselectedItemColor
?? themeData.textTheme.caption!.color, ?? themeData.textTheme.caption.color,
end: widget.selectedItemColor end: widget.selectedItemColor
?? bottomTheme.selectedItemColor ?? bottomTheme.selectedItemColor
?? widget.fixedColor ?? widget.fixedColor
@ -889,12 +893,12 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
unselectedLabelStyle: effectiveUnselectedLabelStyle, unselectedLabelStyle: effectiveUnselectedLabelStyle,
onTap: () { onTap: () {
if (widget.onTap != null) if (widget.onTap != null)
widget.onTap!(i); widget.onTap(i);
}, },
colorTween: colorTween, colorTween: colorTween,
flex: _evaluateFlex(_animations[i]), flex: _evaluateFlex(_animations[i]),
selected: i == widget.currentIndex, selected: i == widget.currentIndex,
showSelectedLabels: widget.showSelectedLabels, showSelectedLabels: widget.showSelectedLabels ?? bottomTheme.showSelectedLabels,
showUnselectedLabels: widget.showUnselectedLabels ?? bottomTheme.showUnselectedLabels ?? _defaultShowUnselected, showUnselectedLabels: widget.showUnselectedLabels ?? bottomTheme.showUnselectedLabels ?? _defaultShowUnselected,
indexLabel: localizations.tabLabel(tabIndex: i + 1, tabCount: widget.items.length), indexLabel: localizations.tabLabel(tabIndex: i + 1, tabCount: widget.items.length),
mouseCursor: effectiveMouseCursor, mouseCursor: effectiveMouseCursor,
@ -923,8 +927,8 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
final BottomNavigationBarThemeData bottomTheme = BottomNavigationBarTheme.of(context); final BottomNavigationBarThemeData bottomTheme = BottomNavigationBarTheme.of(context);
// Labels apply up to _bottomMargin padding. Remainder is media padding. // Labels apply up to _bottomMargin padding. Remainder is media padding.
final double additionalBottomPadding = math.max(MediaQuery.of(context)!.padding.bottom - widget.selectedFontSize / 2.0, 0.0); final double additionalBottomPadding = math.max(MediaQuery.of(context).padding.bottom - widget.selectedFontSize / 2.0, 0.0);
Color? backgroundColor; Color backgroundColor;
switch (_effectiveType) { switch (_effectiveType) {
case BottomNavigationBarType.fixed: case BottomNavigationBarType.fixed:
backgroundColor = widget.backgroundColor ?? bottomTheme.backgroundColor; backgroundColor = widget.backgroundColor ?? bottomTheme.backgroundColor;
@ -943,7 +947,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
child: CustomPaint( child: CustomPaint(
painter: _RadialPainter( painter: _RadialPainter(
circles: _circles.toList(), circles: _circles.toList(),
textDirection: Directionality.of(context)!, textDirection: Directionality.of(context),
), ),
child: Material( // Splashes. child: Material( // Splashes.
type: MaterialType.transparency, type: MaterialType.transparency,
@ -966,10 +970,10 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
// Describes an animating color splash circle. // Describes an animating color splash circle.
class _Circle { class _Circle {
_Circle({ _Circle({
required this.state, @required this.state,
required this.index, @required this.index,
required this.color, @required this.color,
required TickerProvider vsync, @required TickerProvider vsync,
}) : assert(state != null), }) : assert(state != null),
assert(index != null), assert(index != null),
assert(color != null) { assert(color != null) {
@ -987,8 +991,8 @@ class _Circle {
final _BottomNavigationBarState state; final _BottomNavigationBarState state;
final int index; final int index;
final Color color; final Color color;
late AnimationController controller; AnimationController controller;
late CurvedAnimation animation; CurvedAnimation animation;
double get horizontalLeadingOffset { double get horizontalLeadingOffset {
double weightSum(Iterable<Animation<double>> animations) { double weightSum(Iterable<Animation<double>> animations) {
@ -1013,8 +1017,8 @@ class _Circle {
// Paints the animating color splash circles. // Paints the animating color splash circles.
class _RadialPainter extends CustomPainter { class _RadialPainter extends CustomPainter {
_RadialPainter({ _RadialPainter({
required this.circles, @required this.circles,
required this.textDirection, @required this.textDirection,
}) : assert(circles != null), }) : assert(circles != null),
assert(textDirection != null); assert(textDirection != null);

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' show lerpDouble; import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -48,78 +50,78 @@ class BottomNavigationBarThemeData with Diagnosticable {
/// The color of the [BottomNavigationBar] itself. /// The color of the [BottomNavigationBar] itself.
/// ///
/// See [BottomNavigationBar.backgroundColor]. /// See [BottomNavigationBar.backgroundColor].
final Color? backgroundColor; final Color backgroundColor;
/// The z-coordinate of the [BottomNavigationBar]. /// The z-coordinate of the [BottomNavigationBar].
/// ///
/// See [BottomNavigationBar.elevation]. /// See [BottomNavigationBar.elevation].
final double? elevation; final double elevation;
/// The size, opacity, and color of the icon in the currently selected /// The size, opacity, and color of the icon in the currently selected
/// [BottomNavigationBarItem.icon]. /// [BottomNavigationBarItem.icon].
/// ///
/// See [BottomNavigationBar.selectedIconTheme]. /// See [BottomNavigationBar.selectedIconTheme].
final IconThemeData? selectedIconTheme; final IconThemeData selectedIconTheme;
/// The size, opacity, and color of the icon in the currently unselected /// The size, opacity, and color of the icon in the currently unselected
/// [BottomNavigationBarItem.icon]s. /// [BottomNavigationBarItem.icon]s.
/// ///
/// See [BottomNavigationBar.unselectedIconTheme]. /// See [BottomNavigationBar.unselectedIconTheme].
final IconThemeData? unselectedIconTheme; final IconThemeData unselectedIconTheme;
/// The color of the selected [BottomNavigationBarItem.icon] and /// The color of the selected [BottomNavigationBarItem.icon] and
/// [BottomNavigationBarItem.title]. /// [BottomNavigationBarItem.title].
/// ///
/// See [BottomNavigationBar.selectedItemColor]. /// See [BottomNavigationBar.selectedItemColor].
final Color? selectedItemColor; final Color selectedItemColor;
/// The color of the unselected [BottomNavigationBarItem.icon] and /// The color of the unselected [BottomNavigationBarItem.icon] and
/// [BottomNavigationBarItem.title]s. /// [BottomNavigationBarItem.title]s.
/// ///
/// See [BottomNavigationBar.unselectedItemColor]. /// See [BottomNavigationBar.unselectedItemColor].
final Color? unselectedItemColor; final Color unselectedItemColor;
/// The [TextStyle] of the [BottomNavigationBarItem] labels when they are /// The [TextStyle] of the [BottomNavigationBarItem] labels when they are
/// selected. /// selected.
/// ///
/// See [BottomNavigationBar.selectedLabelStyle]. /// See [BottomNavigationBar.selectedLabelStyle].
final TextStyle? selectedLabelStyle; final TextStyle selectedLabelStyle;
/// The [TextStyle] of the [BottomNavigationBarItem] labels when they are not /// The [TextStyle] of the [BottomNavigationBarItem] labels when they are not
/// selected. /// selected.
/// ///
/// See [BottomNavigationBar.unselectedLabelStyle]. /// See [BottomNavigationBar.unselectedLabelStyle].
final TextStyle? unselectedLabelStyle; final TextStyle unselectedLabelStyle;
/// Whether the labels are shown for the unselected [BottomNavigationBarItem]s. /// Whether the labels are shown for the unselected [BottomNavigationBarItem]s.
/// ///
/// See [BottomNavigationBar.showSelectedLabels]. /// See [BottomNavigationBar.showSelectedLabels].
final bool? showSelectedLabels; final bool showSelectedLabels;
/// Whether the labels are shown for the selected [BottomNavigationBarItem]. /// Whether the labels are shown for the selected [BottomNavigationBarItem].
/// ///
/// See [BottomNavigationBar.showUnselectedLabels]. /// See [BottomNavigationBar.showUnselectedLabels].
final bool? showUnselectedLabels; final bool showUnselectedLabels;
/// Defines the layout and behavior of a [BottomNavigationBar]. /// Defines the layout and behavior of a [BottomNavigationBar].
/// ///
/// See [BottomNavigationBar.type]. /// See [BottomNavigationBar.type].
final BottomNavigationBarType? type; final BottomNavigationBarType type;
/// Creates a copy of this object but with the given fields replaced with the /// Creates a copy of this object but with the given fields replaced with the
/// new values. /// new values.
BottomNavigationBarThemeData copyWith({ BottomNavigationBarThemeData copyWith({
Color? backgroundColor, Color backgroundColor,
double? elevation, double elevation,
IconThemeData? selectedIconTheme, IconThemeData selectedIconTheme,
IconThemeData? unselectedIconTheme, IconThemeData unselectedIconTheme,
Color? selectedItemColor, Color selectedItemColor,
Color? unselectedItemColor, Color unselectedItemColor,
TextStyle? selectedLabelStyle, TextStyle selectedLabelStyle,
TextStyle? unselectedLabelStyle, TextStyle unselectedLabelStyle,
bool? showSelectedLabels, bool showSelectedLabels,
bool? showUnselectedLabels, bool showUnselectedLabels,
BottomNavigationBarType? type, BottomNavigationBarType type,
}) { }) {
return BottomNavigationBarThemeData( return BottomNavigationBarThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor, backgroundColor: backgroundColor ?? this.backgroundColor,
@ -141,7 +143,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
/// The argument `t` must not be null. /// The argument `t` must not be null.
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
static BottomNavigationBarThemeData lerp(BottomNavigationBarThemeData? a, BottomNavigationBarThemeData? b, double t) { static BottomNavigationBarThemeData lerp(BottomNavigationBarThemeData a, BottomNavigationBarThemeData b, double t) {
assert(t != null); assert(t != null);
return BottomNavigationBarThemeData( return BottomNavigationBarThemeData(
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
@ -233,9 +235,9 @@ class BottomNavigationBarTheme extends InheritedWidget {
/// ///
/// The [data] must not be null. /// The [data] must not be null.
const BottomNavigationBarTheme({ const BottomNavigationBarTheme({
Key? key, Key key,
required this.data, @required this.data,
required Widget child, Widget child,
}) : assert(data != null), super(key: key, child: child); }) : assert(data != null), super(key: key, child: child);
/// The properties used for all descendant [BottomNavigationBar] widgets. /// The properties used for all descendant [BottomNavigationBar] widgets.
@ -252,8 +254,8 @@ class BottomNavigationBarTheme extends InheritedWidget {
/// BottomNavigationBarThemeData theme = BottomNavigationBarTheme.of(context); /// BottomNavigationBarThemeData theme = BottomNavigationBarTheme.of(context);
/// ``` /// ```
static BottomNavigationBarThemeData of(BuildContext context) { static BottomNavigationBarThemeData of(BuildContext context) {
final BottomNavigationBarTheme? bottomNavTheme = context.dependOnInheritedWidgetOfExactType<BottomNavigationBarTheme>(); final BottomNavigationBarTheme bottomNavTheme = context.dependOnInheritedWidgetOfExactType<BottomNavigationBarTheme>();
return bottomNavTheme?.data ?? Theme.of(context)!.bottomNavigationBarTheme; return bottomNavTheme?.data ?? Theme.of(context).bottomNavigationBarTheme;
} }
@override @override

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' show lerpDouble; import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -33,7 +35,7 @@ typedef BottomSheetDragStartHandler = void Function(DragStartDetails details);
/// Used by [BottomSheet.onDragEnd]. /// Used by [BottomSheet.onDragEnd].
typedef BottomSheetDragEndHandler = void Function( typedef BottomSheetDragEndHandler = void Function(
DragEndDetails details, { DragEndDetails details, {
required bool isClosing, bool isClosing,
}); });
/// A material design bottom sheet. /// A material design bottom sheet.
@ -70,7 +72,7 @@ class BottomSheet extends StatefulWidget {
/// [ScaffoldState.showBottomSheet], for persistent bottom sheets, or by /// [ScaffoldState.showBottomSheet], for persistent bottom sheets, or by
/// [showModalBottomSheet], for modal bottom sheets. /// [showModalBottomSheet], for modal bottom sheets.
const BottomSheet({ const BottomSheet({
Key? key, Key key,
this.animationController, this.animationController,
this.enableDrag = true, this.enableDrag = true,
this.onDragStart, this.onDragStart,
@ -79,8 +81,8 @@ class BottomSheet extends StatefulWidget {
this.elevation, this.elevation,
this.shape, this.shape,
this.clipBehavior, this.clipBehavior,
required this.onClosing, @required this.onClosing,
required this.builder, @required this.builder,
}) : assert(enableDrag != null), }) : assert(enableDrag != null),
assert(onClosing != null), assert(onClosing != null),
assert(builder != null), assert(builder != null),
@ -92,7 +94,7 @@ class BottomSheet extends StatefulWidget {
/// ///
/// The BottomSheet widget will manipulate the position of this animation, it /// The BottomSheet widget will manipulate the position of this animation, it
/// is not just a passive observer. /// is not just a passive observer.
final AnimationController? animationController; final AnimationController animationController;
/// Called when the bottom sheet begins to close. /// Called when the bottom sheet begins to close.
/// ///
@ -118,7 +120,7 @@ class BottomSheet extends StatefulWidget {
/// ///
/// Would typically be used to change the bottom sheet animation curve so /// Would typically be used to change the bottom sheet animation curve so
/// that it tracks the user's finger accurately. /// that it tracks the user's finger accurately.
final BottomSheetDragStartHandler? onDragStart; final BottomSheetDragStartHandler onDragStart;
/// Called when the user stops dragging the bottom sheet, if [enableDrag] /// Called when the user stops dragging the bottom sheet, if [enableDrag]
/// is true. /// is true.
@ -126,28 +128,28 @@ class BottomSheet extends StatefulWidget {
/// Would typically be used to reset the bottom sheet animation curve, so /// Would typically be used to reset the bottom sheet animation curve, so
/// that it animates non-linearly. Called before [onClosing] if the bottom /// that it animates non-linearly. Called before [onClosing] if the bottom
/// sheet is closing. /// sheet is closing.
final BottomSheetDragEndHandler? onDragEnd; final BottomSheetDragEndHandler onDragEnd;
/// The bottom sheet's background color. /// The bottom sheet's background color.
/// ///
/// Defines the bottom sheet's [Material.color]. /// Defines the bottom sheet's [Material.color].
/// ///
/// Defaults to null and falls back to [Material]'s default. /// Defaults to null and falls back to [Material]'s default.
final Color? backgroundColor; final Color backgroundColor;
/// The z-coordinate at which to place this material relative to its parent. /// The z-coordinate at which to place this material relative to its parent.
/// ///
/// This controls the size of the shadow below the material. /// This controls the size of the shadow below the material.
/// ///
/// Defaults to 0. The value is non-negative. /// Defaults to 0. The value is non-negative.
final double? elevation; final double elevation;
/// The shape of the bottom sheet. /// The shape of the bottom sheet.
/// ///
/// Defines the bottom sheet's [Material.shape]. /// Defines the bottom sheet's [Material.shape].
/// ///
/// Defaults to null and falls back to [Material]'s default. /// Defaults to null and falls back to [Material]'s default.
final ShapeBorder? shape; final ShapeBorder shape;
/// {@macro flutter.widgets.Clip} /// {@macro flutter.widgets.Clip}
/// ///
@ -161,7 +163,7 @@ class BottomSheet extends StatefulWidget {
/// If this property is null then [BottomSheetThemeData.clipBehavior] of /// If this property is null then [BottomSheetThemeData.clipBehavior] of
/// [ThemeData.bottomSheetTheme] is used. If that's null then the behavior /// [ThemeData.bottomSheetTheme] is used. If that's null then the behavior
/// will be [Clip.none]. /// will be [Clip.none].
final Clip? clipBehavior; final Clip clipBehavior;
@override @override
_BottomSheetState createState() => _BottomSheetState(); _BottomSheetState createState() => _BottomSheetState();
@ -187,15 +189,15 @@ class _BottomSheetState extends State<BottomSheet> {
final GlobalKey _childKey = GlobalKey(debugLabel: 'BottomSheet child'); final GlobalKey _childKey = GlobalKey(debugLabel: 'BottomSheet child');
double get _childHeight { double get _childHeight {
final RenderBox renderBox = _childKey.currentContext!.findRenderObject() as RenderBox; final RenderBox renderBox = _childKey.currentContext.findRenderObject() as RenderBox;
return renderBox.size.height; return renderBox.size.height;
} }
bool get _dismissUnderway => widget.animationController!.status == AnimationStatus.reverse; bool get _dismissUnderway => widget.animationController.status == AnimationStatus.reverse;
void _handleDragStart(DragStartDetails details) { void _handleDragStart(DragStartDetails details) {
if (widget.onDragStart != null) { if (widget.onDragStart != null) {
widget.onDragStart!(details); widget.onDragStart(details);
} }
} }
@ -203,7 +205,7 @@ class _BottomSheetState extends State<BottomSheet> {
assert(widget.enableDrag); assert(widget.enableDrag);
if (_dismissUnderway) if (_dismissUnderway)
return; return;
widget.animationController!.value -= details.primaryDelta! / _childHeight; widget.animationController.value -= details.primaryDelta / (_childHeight ?? details.primaryDelta);
} }
void _handleDragEnd(DragEndDetails details) { void _handleDragEnd(DragEndDetails details) {
@ -213,22 +215,22 @@ class _BottomSheetState extends State<BottomSheet> {
bool isClosing = false; bool isClosing = false;
if (details.velocity.pixelsPerSecond.dy > _minFlingVelocity) { if (details.velocity.pixelsPerSecond.dy > _minFlingVelocity) {
final double flingVelocity = -details.velocity.pixelsPerSecond.dy / _childHeight; final double flingVelocity = -details.velocity.pixelsPerSecond.dy / _childHeight;
if (widget.animationController!.value > 0.0) { if (widget.animationController.value > 0.0) {
widget.animationController!.fling(velocity: flingVelocity); widget.animationController.fling(velocity: flingVelocity);
} }
if (flingVelocity < 0.0) { if (flingVelocity < 0.0) {
isClosing = true; isClosing = true;
} }
} else if (widget.animationController!.value < _closeProgressThreshold) { } else if (widget.animationController.value < _closeProgressThreshold) {
if (widget.animationController!.value > 0.0) if (widget.animationController.value > 0.0)
widget.animationController!.fling(velocity: -1.0); widget.animationController.fling(velocity: -1.0);
isClosing = true; isClosing = true;
} else { } else {
widget.animationController!.forward(); widget.animationController.forward();
} }
if (widget.onDragEnd != null) { if (widget.onDragEnd != null) {
widget.onDragEnd!( widget.onDragEnd(
details, details,
isClosing: isClosing, isClosing: isClosing,
); );
@ -248,10 +250,10 @@ class _BottomSheetState extends State<BottomSheet> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final BottomSheetThemeData bottomSheetTheme = Theme.of(context)!.bottomSheetTheme; final BottomSheetThemeData bottomSheetTheme = Theme.of(context).bottomSheetTheme;
final Color? color = widget.backgroundColor ?? bottomSheetTheme.backgroundColor; final Color color = widget.backgroundColor ?? bottomSheetTheme.backgroundColor;
final double elevation = widget.elevation ?? bottomSheetTheme.elevation ?? 0; final double elevation = widget.elevation ?? bottomSheetTheme.elevation ?? 0;
final ShapeBorder? shape = widget.shape ?? bottomSheetTheme.shape; final ShapeBorder shape = widget.shape ?? bottomSheetTheme.shape;
final Clip clipBehavior = widget.clipBehavior ?? bottomSheetTheme.clipBehavior ?? Clip.none; final Clip clipBehavior = widget.clipBehavior ?? bottomSheetTheme.clipBehavior ?? Clip.none;
final Widget bottomSheet = Material( final Widget bottomSheet = Material(
@ -312,7 +314,7 @@ class _ModalBottomSheetLayout extends SingleChildLayoutDelegate {
class _ModalBottomSheet<T> extends StatefulWidget { class _ModalBottomSheet<T> extends StatefulWidget {
const _ModalBottomSheet({ const _ModalBottomSheet({
Key? key, Key key,
this.route, this.route,
this.backgroundColor, this.backgroundColor,
this.elevation, this.elevation,
@ -324,12 +326,12 @@ class _ModalBottomSheet<T> extends StatefulWidget {
assert(enableDrag != null), assert(enableDrag != null),
super(key: key); super(key: key);
final _ModalBottomSheetRoute<T>? route; final _ModalBottomSheetRoute<T> route;
final bool isScrollControlled; final bool isScrollControlled;
final Color? backgroundColor; final Color backgroundColor;
final double? elevation; final double elevation;
final ShapeBorder? shape; final ShapeBorder shape;
final Clip? clipBehavior; final Clip clipBehavior;
final bool enableDrag; final bool enableDrag;
@override @override
@ -340,7 +342,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
ParametricCurve<double> animationCurve = _modalBottomSheetCurve; ParametricCurve<double> animationCurve = _modalBottomSheetCurve;
String _getRouteLabel(MaterialLocalizations localizations) { String _getRouteLabel(MaterialLocalizations localizations) {
switch (Theme.of(context)!.platform) { switch (Theme.of(context).platform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS: case TargetPlatform.macOS:
return ''; return '';
@ -350,6 +352,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
case TargetPlatform.windows: case TargetPlatform.windows:
return localizations.dialogLabel; return localizations.dialogLabel;
} }
return null;
} }
void handleDragStart(DragStartDetails details) { void handleDragStart(DragStartDetails details) {
@ -357,10 +360,10 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
animationCurve = Curves.linear; animationCurve = Curves.linear;
} }
void handleDragEnd(DragEndDetails details, {bool? isClosing}) { void handleDragEnd(DragEndDetails details, {bool isClosing}) {
// Allow the bottom sheet to animate smoothly from its current position. // Allow the bottom sheet to animate smoothly from its current position.
animationCurve = _BottomSheetSuspendedCurve( animationCurve = _BottomSheetSuspendedCurve(
widget.route!.animation!.value, widget.route.animation.value,
curve: _modalBottomSheetCurve, curve: _modalBottomSheetCurve,
); );
} }
@ -369,20 +372,20 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final MediaQueryData? mediaQuery = MediaQuery.of(context); final MediaQueryData mediaQuery = MediaQuery.of(context);
final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final String routeLabel = _getRouteLabel(localizations); final String routeLabel = _getRouteLabel(localizations);
return AnimatedBuilder( return AnimatedBuilder(
animation: widget.route!.animation!, animation: widget.route.animation,
child: BottomSheet( child: BottomSheet(
animationController: widget.route!._animationController, animationController: widget.route._animationController,
onClosing: () { onClosing: () {
if (widget.route!.isCurrent) { if (widget.route.isCurrent) {
Navigator.pop(context); Navigator.pop(context);
} }
}, },
builder: widget.route!.builder!, builder: widget.route.builder,
backgroundColor: widget.backgroundColor, backgroundColor: widget.backgroundColor,
elevation: widget.elevation, elevation: widget.elevation,
shape: widget.shape, shape: widget.shape,
@ -391,11 +394,11 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
onDragStart: handleDragStart, onDragStart: handleDragStart,
onDragEnd: handleDragEnd, onDragEnd: handleDragEnd,
), ),
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget child) {
// Disable the initial animation when accessible navigation is on so // Disable the initial animation when accessible navigation is on so
// that the semantics are added to the tree at the correct time. // that the semantics are added to the tree at the correct time.
final double animationValue = animationCurve.transform( final double animationValue = animationCurve.transform(
mediaQuery!.accessibleNavigation ? 1.0 : widget.route!.animation!.value mediaQuery.accessibleNavigation ? 1.0 : widget.route.animation.value
); );
return Semantics( return Semantics(
scopesRoute: true, scopesRoute: true,
@ -426,21 +429,21 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
this.modalBarrierColor, this.modalBarrierColor,
this.isDismissible = true, this.isDismissible = true,
this.enableDrag = true, this.enableDrag = true,
required this.isScrollControlled, @required this.isScrollControlled,
RouteSettings? settings, RouteSettings settings,
}) : assert(isScrollControlled != null), }) : assert(isScrollControlled != null),
assert(isDismissible != null), assert(isDismissible != null),
assert(enableDrag != null), assert(enableDrag != null),
super(settings: settings); super(settings: settings);
final WidgetBuilder? builder; final WidgetBuilder builder;
final ThemeData? theme; final ThemeData theme;
final bool isScrollControlled; final bool isScrollControlled;
final Color? backgroundColor; final Color backgroundColor;
final double? elevation; final double elevation;
final ShapeBorder? shape; final ShapeBorder shape;
final Clip? clipBehavior; final Clip clipBehavior;
final Color? modalBarrierColor; final Color modalBarrierColor;
final bool isDismissible; final bool isDismissible;
final bool enableDrag; final bool enableDrag;
@ -454,23 +457,23 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
bool get barrierDismissible => isDismissible; bool get barrierDismissible => isDismissible;
@override @override
final String? barrierLabel; final String barrierLabel;
@override @override
Color get barrierColor => modalBarrierColor ?? Colors.black54; Color get barrierColor => modalBarrierColor ?? Colors.black54;
AnimationController? _animationController; AnimationController _animationController;
@override @override
AnimationController createAnimationController() { AnimationController createAnimationController() {
assert(_animationController == null); assert(_animationController == null);
_animationController = BottomSheet.createAnimationController(navigator!.overlay!); _animationController = BottomSheet.createAnimationController(navigator.overlay);
return _animationController!; return _animationController;
} }
@override @override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
final BottomSheetThemeData sheetTheme = theme?.bottomSheetTheme ?? Theme.of(context)!.bottomSheetTheme; final BottomSheetThemeData sheetTheme = theme?.bottomSheetTheme ?? Theme.of(context).bottomSheetTheme;
// By definition, the bottom sheet is aligned to the bottom of the page // By definition, the bottom sheet is aligned to the bottom of the page
// and isn't exposed to the top padding of the MediaQuery. // and isn't exposed to the top padding of the MediaQuery.
Widget bottomSheet = MediaQuery.removePadding( Widget bottomSheet = MediaQuery.removePadding(
@ -478,8 +481,8 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
removeTop: true, removeTop: true,
child: _ModalBottomSheet<T>( child: _ModalBottomSheet<T>(
route: this, route: this,
backgroundColor: backgroundColor ?? sheetTheme.modalBackgroundColor ?? sheetTheme.backgroundColor, backgroundColor: backgroundColor ?? sheetTheme?.modalBackgroundColor ?? sheetTheme?.backgroundColor,
elevation: elevation ?? sheetTheme.modalElevation ?? sheetTheme.elevation, elevation: elevation ?? sheetTheme?.modalElevation ?? sheetTheme?.elevation,
shape: shape, shape: shape,
clipBehavior: clipBehavior, clipBehavior: clipBehavior,
isScrollControlled: isScrollControlled, isScrollControlled: isScrollControlled,
@ -487,7 +490,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
), ),
); );
if (theme != null) if (theme != null)
bottomSheet = Theme(data: theme!, child: bottomSheet); bottomSheet = Theme(data: theme, child: bottomSheet);
return bottomSheet; return bottomSheet;
} }
} }
@ -541,7 +544,7 @@ class _BottomSheetSuspendedCurve extends ParametricCurve<double> {
final double curveProgress = (t - startingPoint) / (1 - startingPoint); final double curveProgress = (t - startingPoint) / (1 - startingPoint);
final double transformed = curve.transform(curveProgress); final double transformed = curve.transform(curveProgress);
return lerpDouble(startingPoint, 1, transformed)!; return lerpDouble(startingPoint, 1, transformed);
} }
@override @override
@ -645,18 +648,18 @@ class _BottomSheetSuspendedCurve extends ParametricCurve<double> {
/// that grows and then becomes scrollable once it reaches its maximum size. /// that grows and then becomes scrollable once it reaches its maximum size.
/// * <https://material.io/design/components/sheets-bottom.html#modal-bottom-sheet> /// * <https://material.io/design/components/sheets-bottom.html#modal-bottom-sheet>
Future<T> showModalBottomSheet<T>({ Future<T> showModalBottomSheet<T>({
required BuildContext context, @required BuildContext context,
required WidgetBuilder builder, @required WidgetBuilder builder,
Color? backgroundColor, Color backgroundColor,
double? elevation, double elevation,
ShapeBorder? shape, ShapeBorder shape,
Clip? clipBehavior, Clip clipBehavior,
Color? barrierColor, Color barrierColor,
bool isScrollControlled = false, bool isScrollControlled = false,
bool useRootNavigator = false, bool useRootNavigator = false,
bool isDismissible = true, bool isDismissible = true,
bool enableDrag = true, bool enableDrag = true,
RouteSettings? routeSettings, RouteSettings routeSettings,
}) { }) {
assert(context != null); assert(context != null);
assert(builder != null); assert(builder != null);
@ -667,11 +670,11 @@ Future<T> showModalBottomSheet<T>({
assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
return Navigator.of(context, rootNavigator: useRootNavigator)!.push(_ModalBottomSheetRoute<T>( return Navigator.of(context, rootNavigator: useRootNavigator).push(_ModalBottomSheetRoute<T>(
builder: builder, builder: builder,
theme: Theme.of(context, shadowThemeOnly: true), theme: Theme.of(context, shadowThemeOnly: true),
isScrollControlled: isScrollControlled, isScrollControlled: isScrollControlled,
barrierLabel: MaterialLocalizations.of(context)!.modalBarrierDismissLabel, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
@ -723,18 +726,18 @@ Future<T> showModalBottomSheet<T>({
/// * [Scaffold.of], for information about how to obtain the [BuildContext]. /// * [Scaffold.of], for information about how to obtain the [BuildContext].
/// * <https://material.io/design/components/sheets-bottom.html#standard-bottom-sheet> /// * <https://material.io/design/components/sheets-bottom.html#standard-bottom-sheet>
PersistentBottomSheetController<T> showBottomSheet<T>({ PersistentBottomSheetController<T> showBottomSheet<T>({
required BuildContext context, @required BuildContext context,
required WidgetBuilder builder, @required WidgetBuilder builder,
Color? backgroundColor, Color backgroundColor,
double? elevation, double elevation,
ShapeBorder? shape, ShapeBorder shape,
Clip? clipBehavior, Clip clipBehavior,
}) { }) {
assert(context != null); assert(context != null);
assert(builder != null); assert(builder != null);
assert(debugCheckHasScaffold(context)); assert(debugCheckHasScaffold(context));
return Scaffold.of(context)!.showBottomSheet<T>( return Scaffold.of(context).showBottomSheet<T>(
builder, builder,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -49,8 +51,8 @@ class RawMaterialButton extends StatefulWidget {
/// [elevation], [focusElevation], [hoverElevation], [highlightElevation], and /// [elevation], [focusElevation], [hoverElevation], [highlightElevation], and
/// [disabledElevation] must be non-negative. /// [disabledElevation] must be non-negative.
const RawMaterialButton({ const RawMaterialButton({
Key? key, Key key,
required this.onPressed, @required this.onPressed,
this.onLongPress, this.onLongPress,
this.onHighlightChanged, this.onHighlightChanged,
this.mouseCursor, this.mouseCursor,
@ -73,7 +75,7 @@ class RawMaterialButton extends StatefulWidget {
this.clipBehavior = Clip.none, this.clipBehavior = Clip.none,
this.focusNode, this.focusNode,
this.autofocus = false, this.autofocus = false,
MaterialTapTargetSize? materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
this.child, this.child,
this.enableFeedback = true, this.enableFeedback = true,
}) : materialTapTargetSize = materialTapTargetSize ?? MaterialTapTargetSize.padded, }) : materialTapTargetSize = materialTapTargetSize ?? MaterialTapTargetSize.padded,
@ -97,7 +99,7 @@ class RawMaterialButton extends StatefulWidget {
/// See also: /// See also:
/// ///
/// * [enabled], which is true if the button is enabled. /// * [enabled], which is true if the button is enabled.
final VoidCallback? onPressed; final VoidCallback onPressed;
/// Called when the button is long-pressed. /// Called when the button is long-pressed.
/// ///
@ -106,7 +108,7 @@ class RawMaterialButton extends StatefulWidget {
/// See also: /// See also:
/// ///
/// * [enabled], which is true if the button is enabled. /// * [enabled], which is true if the button is enabled.
final VoidCallback? onLongPress; final VoidCallback onLongPress;
/// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged] /// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged]
/// callback. /// callback.
@ -114,7 +116,7 @@ class RawMaterialButton extends StatefulWidget {
/// If [onPressed] changes from null to non-null while a gesture is ongoing, /// If [onPressed] changes from null to non-null while a gesture is ongoing,
/// this can fire during the build phase (in which case calling /// this can fire during the build phase (in which case calling
/// [State.setState] is not allowed). /// [State.setState] is not allowed).
final ValueChanged<bool>? onHighlightChanged; final ValueChanged<bool> onHighlightChanged;
/// {@template flutter.material.button.mouseCursor} /// {@template flutter.material.button.mouseCursor}
/// The cursor for a mouse pointer when it enters or is hovering over the /// The cursor for a mouse pointer when it enters or is hovering over the
@ -130,7 +132,7 @@ class RawMaterialButton extends StatefulWidget {
/// ///
/// If this property is null, [MaterialStateMouseCursor.clickable] will be used. /// If this property is null, [MaterialStateMouseCursor.clickable] will be used.
/// {@endtemplate} /// {@endtemplate}
final MouseCursor? mouseCursor; final MouseCursor mouseCursor;
/// Defines the default text style, with [Material.textStyle], for the /// Defines the default text style, with [Material.textStyle], for the
/// button's [child]. /// button's [child].
@ -142,22 +144,22 @@ class RawMaterialButton extends StatefulWidget {
/// * [MaterialState.hovered]. /// * [MaterialState.hovered].
/// * [MaterialState.focused]. /// * [MaterialState.focused].
/// * [MaterialState.disabled]. /// * [MaterialState.disabled].
final TextStyle? textStyle; final TextStyle textStyle;
/// The color of the button's [Material]. /// The color of the button's [Material].
final Color? fillColor; final Color fillColor;
/// The color for the button's [Material] when it has the input focus. /// The color for the button's [Material] when it has the input focus.
final Color? focusColor; final Color focusColor;
/// The color for the button's [Material] when a pointer is hovering over it. /// The color for the button's [Material] when a pointer is hovering over it.
final Color? hoverColor; final Color hoverColor;
/// The highlight color for the button's [InkWell]. /// The highlight color for the button's [InkWell].
final Color? highlightColor; final Color highlightColor;
/// The splash color for the button's [InkWell]. /// The splash color for the button's [InkWell].
final Color? splashColor; final Color splashColor;
/// The elevation for the button's [Material] when the button /// The elevation for the button's [Material] when the button
/// is [enabled] but not pressed. /// is [enabled] but not pressed.
@ -273,7 +275,7 @@ class RawMaterialButton extends StatefulWidget {
final Duration animationDuration; final Duration animationDuration;
/// Typically the button's label. /// Typically the button's label.
final Widget? child; final Widget child;
/// Whether the button is enabled or disabled. /// Whether the button is enabled or disabled.
/// ///
@ -291,7 +293,7 @@ class RawMaterialButton extends StatefulWidget {
final MaterialTapTargetSize materialTapTargetSize; final MaterialTapTargetSize materialTapTargetSize;
/// {@macro flutter.widgets.Focus.focusNode} /// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode; final FocusNode focusNode;
/// {@macro flutter.widgets.Focus.autofocus} /// {@macro flutter.widgets.Focus.autofocus}
final bool autofocus; final bool autofocus;
@ -332,7 +334,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
setState(() { setState(() {
_updateState(MaterialState.pressed, value); _updateState(MaterialState.pressed, value);
if (widget.onHighlightChanged != null) { if (widget.onHighlightChanged != null) {
widget.onHighlightChanged!(value); widget.onHighlightChanged(value);
} }
}); });
} }
@ -393,11 +395,11 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final Color? effectiveTextColor = MaterialStateProperty.resolveAs<Color>(widget.textStyle?.color, _states); final Color effectiveTextColor = MaterialStateProperty.resolveAs<Color>(widget.textStyle?.color, _states);
final ShapeBorder? effectiveShape = MaterialStateProperty.resolveAs<ShapeBorder>(widget.shape, _states); final ShapeBorder effectiveShape = MaterialStateProperty.resolveAs<ShapeBorder>(widget.shape, _states);
final Offset densityAdjustment = widget.visualDensity.baseSizeAdjustment; final Offset densityAdjustment = widget.visualDensity.baseSizeAdjustment;
final BoxConstraints effectiveConstraints = widget.visualDensity.effectiveConstraints(widget.constraints); final BoxConstraints effectiveConstraints = widget.visualDensity.effectiveConstraints(widget.constraints);
final MouseCursor? effectiveMouseCursor = MaterialStateProperty.resolveAs<MouseCursor>( final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs<MouseCursor>(
widget.mouseCursor ?? MaterialStateMouseCursor.clickable, widget.mouseCursor ?? MaterialStateMouseCursor.clickable,
_states, _states,
); );
@ -485,9 +487,9 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
/// "tap target", but not its material or its ink splashes. /// "tap target", but not its material or its ink splashes.
class _InputPadding extends SingleChildRenderObjectWidget { class _InputPadding extends SingleChildRenderObjectWidget {
const _InputPadding({ const _InputPadding({
Key? key, Key key,
Widget? child, Widget child,
required this.minSize, this.minSize,
}) : super(key: key, child: child); }) : super(key: key, child: child);
final Size minSize; final Size minSize;
@ -504,7 +506,7 @@ class _InputPadding extends SingleChildRenderObjectWidget {
} }
class _RenderInputPadding extends RenderShiftedBox { class _RenderInputPadding extends RenderShiftedBox {
_RenderInputPadding(this._minSize, [RenderBox? child]) : super(child); _RenderInputPadding(this._minSize, [RenderBox child]) : super(child);
Size get minSize => _minSize; Size get minSize => _minSize;
Size _minSize; Size _minSize;
@ -518,57 +520,58 @@ class _RenderInputPadding extends RenderShiftedBox {
@override @override
double computeMinIntrinsicWidth(double height) { double computeMinIntrinsicWidth(double height) {
if (child != null) if (child != null)
return math.max(child!.getMinIntrinsicWidth(height), minSize.width); return math.max(child.getMinIntrinsicWidth(height), minSize.width);
return 0.0; return 0.0;
} }
@override @override
double computeMinIntrinsicHeight(double width) { double computeMinIntrinsicHeight(double width) {
if (child != null) if (child != null)
return math.max(child!.getMinIntrinsicHeight(width), minSize.height); return math.max(child.getMinIntrinsicHeight(width), minSize.height);
return 0.0; return 0.0;
} }
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) {
if (child != null) if (child != null)
return math.max(child!.getMaxIntrinsicWidth(height), minSize.width); return math.max(child.getMaxIntrinsicWidth(height), minSize.width);
return 0.0; return 0.0;
} }
@override @override
double computeMaxIntrinsicHeight(double width) { double computeMaxIntrinsicHeight(double width) {
if (child != null) if (child != null)
return math.max(child!.getMaxIntrinsicHeight(width), minSize.height); return math.max(child.getMaxIntrinsicHeight(width), minSize.height);
return 0.0; return 0.0;
} }
@override @override
void performLayout() { void performLayout() {
final BoxConstraints constraints = this.constraints;
if (child != null) { if (child != null) {
child!.layout(constraints, parentUsesSize: true); child.layout(constraints, parentUsesSize: true);
final double height = math.max(child!.size.width, minSize.width); final double height = math.max(child.size.width, minSize.width);
final double width = math.max(child!.size.height, minSize.height); final double width = math.max(child.size.height, minSize.height);
size = constraints.constrain(Size(height, width)); size = constraints.constrain(Size(height, width));
final BoxParentData childParentData = child!.parentData as BoxParentData; final BoxParentData childParentData = child.parentData as BoxParentData;
childParentData.offset = Alignment.center.alongOffset(size - child!.size as Offset); childParentData.offset = Alignment.center.alongOffset(size - child.size as Offset);
} else { } else {
size = Size.zero; size = Size.zero;
} }
} }
@override @override
bool hitTest(BoxHitTestResult result, { required Offset position }) { bool hitTest(BoxHitTestResult result, { Offset position }) {
if (super.hitTest(result, position: position)) { if (super.hitTest(result, position: position)) {
return true; return true;
} }
final Offset center = child!.size.center(Offset.zero); final Offset center = child.size.center(Offset.zero);
return result.addWithRawTransform( return result.addWithRawTransform(
transform: MatrixUtils.forceToPoint(center), transform: MatrixUtils.forceToPoint(center),
position: center, position: center,
hitTest: (BoxHitTestResult result, Offset? position) { hitTest: (BoxHitTestResult result, Offset position) {
assert(position == center); assert(position == center);
return child!.hitTest(result, position: center); return child.hitTest(result, position: center);
}, },
); );
} }

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
@ -54,7 +56,7 @@ class ButtonBar extends StatelessWidget {
/// Both [buttonMinWidth] and [buttonHeight] must be non-negative if they /// Both [buttonMinWidth] and [buttonHeight] must be non-negative if they
/// are not null. /// are not null.
const ButtonBar({ const ButtonBar({
Key? key, Key key,
this.alignment, this.alignment,
this.mainAxisSize, this.mainAxisSize,
this.buttonTextTheme, this.buttonTextTheme,
@ -75,13 +77,13 @@ class ButtonBar extends StatelessWidget {
/// ///
/// If null then it will use [ButtonBarThemeData.alignment]. If that is null, /// If null then it will use [ButtonBarThemeData.alignment]. If that is null,
/// it will default to [MainAxisAlignment.end]. /// it will default to [MainAxisAlignment.end].
final MainAxisAlignment? alignment; final MainAxisAlignment alignment;
/// How much horizontal space is available. See [Row.mainAxisSize]. /// How much horizontal space is available. See [Row.mainAxisSize].
/// ///
/// If null then it will use the surrounding [ButtonBarThemeData.mainAxisSize]. /// If null then it will use the surrounding [ButtonBarThemeData.mainAxisSize].
/// If that is null, it will default to [MainAxisSize.max]. /// If that is null, it will default to [MainAxisSize.max].
final MainAxisSize? mainAxisSize; final MainAxisSize mainAxisSize;
/// Overrides the surrounding [ButtonBarThemeData.buttonTextTheme] to define a /// Overrides the surrounding [ButtonBarThemeData.buttonTextTheme] to define a
/// button's base colors, size, internal padding and shape. /// button's base colors, size, internal padding and shape.
@ -89,21 +91,21 @@ class ButtonBar extends StatelessWidget {
/// If null then it will use the surrounding /// If null then it will use the surrounding
/// [ButtonBarThemeData.buttonTextTheme]. If that is null, it will default to /// [ButtonBarThemeData.buttonTextTheme]. If that is null, it will default to
/// [ButtonTextTheme.primary]. /// [ButtonTextTheme.primary].
final ButtonTextTheme? buttonTextTheme; final ButtonTextTheme buttonTextTheme;
/// Overrides the surrounding [ButtonThemeData.minWidth] to define a button's /// Overrides the surrounding [ButtonThemeData.minWidth] to define a button's
/// minimum width. /// minimum width.
/// ///
/// If null then it will use the surrounding [ButtonBarThemeData.buttonMinWidth]. /// If null then it will use the surrounding [ButtonBarThemeData.buttonMinWidth].
/// If that is null, it will default to 64.0 logical pixels. /// If that is null, it will default to 64.0 logical pixels.
final double? buttonMinWidth; final double buttonMinWidth;
/// Overrides the surrounding [ButtonThemeData.height] to define a button's /// Overrides the surrounding [ButtonThemeData.height] to define a button's
/// minimum height. /// minimum height.
/// ///
/// If null then it will use the surrounding [ButtonBarThemeData.buttonHeight]. /// If null then it will use the surrounding [ButtonBarThemeData.buttonHeight].
/// If that is null, it will default to 36.0 logical pixels. /// If that is null, it will default to 36.0 logical pixels.
final double? buttonHeight; final double buttonHeight;
/// Overrides the surrounding [ButtonThemeData.padding] to define the padding /// Overrides the surrounding [ButtonThemeData.padding] to define the padding
/// for a button's child (typically the button's label). /// for a button's child (typically the button's label).
@ -111,14 +113,14 @@ class ButtonBar extends StatelessWidget {
/// If null then it will use the surrounding [ButtonBarThemeData.buttonPadding]. /// If null then it will use the surrounding [ButtonBarThemeData.buttonPadding].
/// If that is null, it will default to 8.0 logical pixels on the left /// If that is null, it will default to 8.0 logical pixels on the left
/// and right. /// and right.
final EdgeInsetsGeometry? buttonPadding; final EdgeInsetsGeometry buttonPadding;
/// Overrides the surrounding [ButtonThemeData.alignedDropdown] to define whether /// Overrides the surrounding [ButtonThemeData.alignedDropdown] to define whether
/// a [DropdownButton] menu's width will match the button's width. /// a [DropdownButton] menu's width will match the button's width.
/// ///
/// If null then it will use the surrounding [ButtonBarThemeData.buttonAlignedDropdown]. /// If null then it will use the surrounding [ButtonBarThemeData.buttonAlignedDropdown].
/// If that is null, it will default to false. /// If that is null, it will default to false.
final bool? buttonAlignedDropdown; final bool buttonAlignedDropdown;
/// Defines whether a [ButtonBar] should size itself with a minimum size /// Defines whether a [ButtonBar] should size itself with a minimum size
/// constraint or with padding. /// constraint or with padding.
@ -127,7 +129,7 @@ class ButtonBar extends StatelessWidget {
/// ///
/// If null then it will use the surrounding [ButtonBarThemeData.layoutBehavior]. /// If null then it will use the surrounding [ButtonBarThemeData.layoutBehavior].
/// If that is null, it will default [ButtonBarLayoutBehavior.padded]. /// If that is null, it will default [ButtonBarLayoutBehavior.padded].
final ButtonBarLayoutBehavior? layoutBehavior; final ButtonBarLayoutBehavior layoutBehavior;
/// Defines the vertical direction of a [ButtonBar]'s children if it /// Defines the vertical direction of a [ButtonBar]'s children if it
/// overflows. /// overflows.
@ -143,7 +145,7 @@ class ButtonBar extends StatelessWidget {
/// If null then it will use the surrounding /// If null then it will use the surrounding
/// [ButtonBarThemeData.overflowDirection]. If that is null, it will /// [ButtonBarThemeData.overflowDirection]. If that is null, it will
/// default to [VerticalDirection.down]. /// default to [VerticalDirection.down].
final VerticalDirection? overflowDirection; final VerticalDirection overflowDirection;
/// The spacing between buttons when the button bar overflows. /// The spacing between buttons when the button bar overflows.
/// ///
@ -159,7 +161,7 @@ class ButtonBar extends StatelessWidget {
/// ///
/// If null then no spacing will be added in between buttons in /// If null then no spacing will be added in between buttons in
/// an overflow state. /// an overflow state.
final double? overflowButtonSpacing; final double overflowButtonSpacing;
/// The buttons to arrange horizontally. /// The buttons to arrange horizontally.
/// ///
@ -214,6 +216,8 @@ class ButtonBar extends StatelessWidget {
child: child, child: child,
); );
} }
assert(false);
return null;
} }
} }
@ -235,14 +239,14 @@ class _ButtonBarRow extends Flex {
/// Creates a button bar that attempts to display in a row, but displays in /// Creates a button bar that attempts to display in a row, but displays in
/// a column if there is insufficient horizontal space. /// a column if there is insufficient horizontal space.
_ButtonBarRow({ _ButtonBarRow({
required List<Widget> children, List<Widget> children,
Axis direction = Axis.horizontal, Axis direction = Axis.horizontal,
MainAxisSize mainAxisSize = MainAxisSize.max, MainAxisSize mainAxisSize = MainAxisSize.max,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection? textDirection, TextDirection textDirection,
VerticalDirection overflowDirection = VerticalDirection.down, VerticalDirection overflowDirection = VerticalDirection.down,
TextBaseline? textBaseline, TextBaseline textBaseline,
this.overflowButtonSpacing, this.overflowButtonSpacing,
}) : super( }) : super(
children: children, children: children,
@ -255,7 +259,7 @@ class _ButtonBarRow extends Flex {
textBaseline: textBaseline, textBaseline: textBaseline,
); );
final double? overflowButtonSpacing; final double overflowButtonSpacing;
@override @override
_RenderButtonBarRow createRenderObject(BuildContext context) { _RenderButtonBarRow createRenderObject(BuildContext context) {
@ -264,7 +268,7 @@ class _ButtonBarRow extends Flex {
mainAxisAlignment: mainAxisAlignment, mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize, mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment, crossAxisAlignment: crossAxisAlignment,
textDirection: getEffectiveTextDirection(context)!, textDirection: getEffectiveTextDirection(context),
verticalDirection: verticalDirection, verticalDirection: verticalDirection,
textBaseline: textBaseline, textBaseline: textBaseline,
overflowButtonSpacing: overflowButtonSpacing, overflowButtonSpacing: overflowButtonSpacing,
@ -303,14 +307,14 @@ class _RenderButtonBarRow extends RenderFlex {
/// Creates a button bar that attempts to display in a row, but displays in /// Creates a button bar that attempts to display in a row, but displays in
/// a column if there is insufficient horizontal space. /// a column if there is insufficient horizontal space.
_RenderButtonBarRow({ _RenderButtonBarRow({
List<RenderBox>? children, List<RenderBox> children,
Axis direction = Axis.horizontal, Axis direction = Axis.horizontal,
MainAxisSize mainAxisSize = MainAxisSize.max, MainAxisSize mainAxisSize = MainAxisSize.max,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
required TextDirection textDirection, @required TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down, VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline? textBaseline, TextBaseline textBaseline,
this.overflowButtonSpacing, this.overflowButtonSpacing,
}) : assert(textDirection != null), }) : assert(textDirection != null),
assert(overflowButtonSpacing == null || overflowButtonSpacing >= 0), assert(overflowButtonSpacing == null || overflowButtonSpacing >= 0),
@ -326,13 +330,13 @@ class _RenderButtonBarRow extends RenderFlex {
); );
bool _hasCheckedLayoutWidth = false; bool _hasCheckedLayoutWidth = false;
double? overflowButtonSpacing; double overflowButtonSpacing;
@override @override
BoxConstraints get constraints { BoxConstraints get constraints {
if (_hasCheckedLayoutWidth) if (_hasCheckedLayoutWidth)
return super.constraints; return super.constraints;
return super.constraints.copyWith(maxWidth: double.infinity); return super.constraints?.copyWith(maxWidth: double.infinity);
} }
@override @override
@ -355,7 +359,7 @@ class _RenderButtonBarRow extends RenderFlex {
super.performLayout(); super.performLayout();
} else { } else {
final BoxConstraints childConstraints = constraints.copyWith(minWidth: 0.0); final BoxConstraints childConstraints = constraints.copyWith(minWidth: 0.0);
RenderBox? child; RenderBox child;
double currentHeight = 0.0; double currentHeight = 0.0;
switch (verticalDirection) { switch (verticalDirection) {
case VerticalDirection.down: case VerticalDirection.down:
@ -377,7 +381,7 @@ class _RenderButtonBarRow extends RenderFlex {
// alignment for a row. For [MainAxisAlignment.spaceAround], // alignment for a row. For [MainAxisAlignment.spaceAround],
// [MainAxisAlignment.spaceBetween] and [MainAxisAlignment.spaceEvenly] // [MainAxisAlignment.spaceBetween] and [MainAxisAlignment.spaceEvenly]
// cases, use [MainAxisAlignment.start]. // cases, use [MainAxisAlignment.start].
switch (textDirection!) { switch (textDirection) {
case TextDirection.ltr: case TextDirection.ltr:
switch (mainAxisAlignment) { switch (mainAxisAlignment) {
case MainAxisAlignment.center: case MainAxisAlignment.center:
@ -418,7 +422,7 @@ class _RenderButtonBarRow extends RenderFlex {
} }
if (overflowButtonSpacing != null && child != null) if (overflowButtonSpacing != null && child != null)
currentHeight += overflowButtonSpacing!; currentHeight += overflowButtonSpacing;
} }
size = constraints.constrain(Size(constraints.maxWidth, currentHeight)); size = constraints.constrain(Size(constraints.maxWidth, currentHeight));
} }

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' show lerpDouble; import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -44,10 +46,10 @@ class ButtonBarThemeData with Diagnosticable {
assert(buttonHeight == null || buttonHeight >= 0.0); assert(buttonHeight == null || buttonHeight >= 0.0);
/// How the children should be placed along the horizontal axis. /// How the children should be placed along the horizontal axis.
final MainAxisAlignment? alignment; final MainAxisAlignment alignment;
/// How much horizontal space is available. See [Row.mainAxisSize]. /// How much horizontal space is available. See [Row.mainAxisSize].
final MainAxisSize? mainAxisSize; final MainAxisSize mainAxisSize;
/// Defines a [ButtonBar] button's base colors, and the defaults for /// Defines a [ButtonBar] button's base colors, and the defaults for
/// the button's minimum size, internal padding, and shape. /// the button's minimum size, internal padding, and shape.
@ -57,7 +59,7 @@ class ButtonBarThemeData with Diagnosticable {
/// ///
/// Despite the name, this property is not a [TextTheme], its value is not a /// Despite the name, this property is not a [TextTheme], its value is not a
/// collection of [TextStyle]s. /// collection of [TextStyle]s.
final ButtonTextTheme? buttonTextTheme; final ButtonTextTheme buttonTextTheme;
/// The minimum width for [ButtonBar] buttons. /// The minimum width for [ButtonBar] buttons.
/// ///
@ -66,19 +68,19 @@ class ButtonBarThemeData with Diagnosticable {
/// ///
/// The actual horizontal space allocated for a button's child is /// The actual horizontal space allocated for a button's child is
/// at least this value less the theme's horizontal [ButtonThemeData.padding]. /// at least this value less the theme's horizontal [ButtonThemeData.padding].
final double? buttonMinWidth; final double buttonMinWidth;
/// The minimum height for [ButtonBar] buttons. /// The minimum height for [ButtonBar] buttons.
/// ///
/// This will override the surrounding [ButtonThemeData.height] setting /// This will override the surrounding [ButtonThemeData.height] setting
/// for buttons contained in the [ButtonBar]. /// for buttons contained in the [ButtonBar].
final double? buttonHeight; final double buttonHeight;
/// Padding for a [ButtonBar] button's child (typically the button's label). /// Padding for a [ButtonBar] button's child (typically the button's label).
/// ///
/// This will override the surrounding [ButtonThemeData.padding] setting /// This will override the surrounding [ButtonThemeData.padding] setting
/// for buttons contained in the [ButtonBar]. /// for buttons contained in the [ButtonBar].
final EdgeInsetsGeometry? buttonPadding; final EdgeInsetsGeometry buttonPadding;
/// If true, then a [DropdownButton] menu's width will match the [ButtonBar] /// If true, then a [DropdownButton] menu's width will match the [ButtonBar]
/// button's width. /// button's width.
@ -93,11 +95,11 @@ class ButtonBarThemeData with Diagnosticable {
/// ///
/// This property only affects [DropdownButton] contained in a [ButtonBar] /// This property only affects [DropdownButton] contained in a [ButtonBar]
/// and its menu. /// and its menu.
final bool? buttonAlignedDropdown; final bool buttonAlignedDropdown;
/// Defines whether a [ButtonBar] should size itself with a minimum size /// Defines whether a [ButtonBar] should size itself with a minimum size
/// constraint or with padding. /// constraint or with padding.
final ButtonBarLayoutBehavior? layoutBehavior; final ButtonBarLayoutBehavior layoutBehavior;
/// Defines the vertical direction of a [ButtonBar]'s children if it /// Defines the vertical direction of a [ButtonBar]'s children if it
/// overflows. /// overflows.
@ -109,20 +111,20 @@ class ButtonBarThemeData with Diagnosticable {
/// the first action will be at the bottom of the column if this /// the first action will be at the bottom of the column if this
/// property is set to [VerticalDirection.up], since it "starts" at the /// property is set to [VerticalDirection.up], since it "starts" at the
/// bottom and "ends" at the top. /// bottom and "ends" at the top.
final VerticalDirection? overflowDirection; final VerticalDirection overflowDirection;
/// Creates a copy of this object but with the given fields replaced with the /// Creates a copy of this object but with the given fields replaced with the
/// new values. /// new values.
ButtonBarThemeData copyWith({ ButtonBarThemeData copyWith({
MainAxisAlignment? alignment, MainAxisAlignment alignment,
MainAxisSize? mainAxisSize, MainAxisSize mainAxisSize,
ButtonTextTheme? buttonTextTheme, ButtonTextTheme buttonTextTheme,
double? buttonMinWidth, double buttonMinWidth,
double? buttonHeight, double buttonHeight,
EdgeInsetsGeometry? buttonPadding, EdgeInsetsGeometry buttonPadding,
bool? buttonAlignedDropdown, bool buttonAlignedDropdown,
ButtonBarLayoutBehavior? layoutBehavior, ButtonBarLayoutBehavior layoutBehavior,
VerticalDirection? overflowDirection, VerticalDirection overflowDirection,
}) { }) {
return ButtonBarThemeData( return ButtonBarThemeData(
alignment: alignment ?? this.alignment, alignment: alignment ?? this.alignment,
@ -142,20 +144,20 @@ class ButtonBarThemeData with Diagnosticable {
/// If both arguments are null, then null is returned. /// If both arguments are null, then null is returned.
/// ///
/// {@macro dart.ui.shadow.lerp} /// {@macro dart.ui.shadow.lerp}
static ButtonBarThemeData? lerp(ButtonBarThemeData? a, ButtonBarThemeData? b, double t) { static ButtonBarThemeData lerp(ButtonBarThemeData a, ButtonBarThemeData b, double t) {
assert(t != null); assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
return ButtonBarThemeData( return ButtonBarThemeData(
alignment: t < 0.5 ? a?.alignment : b?.alignment, alignment: t < 0.5 ? a.alignment : b.alignment,
mainAxisSize: t < 0.5 ? a?.mainAxisSize : b?.mainAxisSize, mainAxisSize: t < 0.5 ? a.mainAxisSize : b.mainAxisSize,
buttonTextTheme: t < 0.5 ? a?.buttonTextTheme : b?.buttonTextTheme, buttonTextTheme: t < 0.5 ? a.buttonTextTheme : b.buttonTextTheme,
buttonMinWidth: lerpDouble(a?.buttonMinWidth, b?.buttonMinWidth, t), buttonMinWidth: lerpDouble(a?.buttonMinWidth, b?.buttonMinWidth, t),
buttonHeight: lerpDouble(a?.buttonHeight, b?.buttonHeight, t), buttonHeight: lerpDouble(a?.buttonHeight, b?.buttonHeight, t),
buttonPadding: EdgeInsetsGeometry.lerp(a?.buttonPadding, b?.buttonPadding, t), buttonPadding: EdgeInsetsGeometry.lerp(a?.buttonPadding, b?.buttonPadding, t),
buttonAlignedDropdown: t < 0.5 ? a?.buttonAlignedDropdown : b?.buttonAlignedDropdown, buttonAlignedDropdown: t < 0.5 ? a.buttonAlignedDropdown : b.buttonAlignedDropdown,
layoutBehavior: t < 0.5 ? a?.layoutBehavior : b?.layoutBehavior, layoutBehavior: t < 0.5 ? a.layoutBehavior : b.layoutBehavior,
overflowDirection: t < 0.5 ? a?.overflowDirection : b?.overflowDirection, overflowDirection: t < 0.5 ? a.overflowDirection : b.overflowDirection,
); );
} }
@ -233,9 +235,9 @@ class ButtonBarTheme extends InheritedWidget {
/// ///
/// The [data] must not be null. /// The [data] must not be null.
const ButtonBarTheme({ const ButtonBarTheme({
Key? key, Key key,
required this.data, @required this.data,
required Widget child, Widget child,
}) : assert(data != null), super(key: key, child: child); }) : assert(data != null), super(key: key, child: child);
/// The properties used for all descendant [ButtonBar] widgets. /// The properties used for all descendant [ButtonBar] widgets.
@ -251,8 +253,8 @@ class ButtonBarTheme extends InheritedWidget {
/// ButtonBarThemeData theme = ButtonBarTheme.of(context); /// ButtonBarThemeData theme = ButtonBarTheme.of(context);
/// ``` /// ```
static ButtonBarThemeData of(BuildContext context) { static ButtonBarThemeData of(BuildContext context) {
final ButtonBarTheme? buttonBarTheme = context.dependOnInheritedWidgetOfExactType<ButtonBarTheme>(); final ButtonBarTheme buttonBarTheme = context.dependOnInheritedWidgetOfExactType<ButtonBarTheme>();
return buttonBarTheme?.data ?? Theme.of(context)!.buttonBarTheme; return buttonBarTheme?.data ?? Theme.of(context).buttonBarTheme;
} }
@override @override

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui' show lerpDouble; import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -120,10 +122,10 @@ class ButtonStyle with Diagnosticable {
/// ///
/// The color of the [textStyle] is typically not used directly, the /// The color of the [textStyle] is typically not used directly, the
/// [foregroundColor] is used instead. /// [foregroundColor] is used instead.
final MaterialStateProperty<TextStyle>? textStyle; final MaterialStateProperty<TextStyle> textStyle;
/// The button's background fill color. /// The button's background fill color.
final MaterialStateProperty<Color>? backgroundColor; final MaterialStateProperty<Color> backgroundColor;
/// The color for the button's [Text] and [Icon] widget descendants. /// The color for the button's [Text] and [Icon] widget descendants.
/// ///
@ -131,11 +133,11 @@ class ButtonStyle with Diagnosticable {
/// of the components that compute defaults from [ButtonStyle] values /// of the components that compute defaults from [ButtonStyle] values
/// compute a default [foregroundColor] and use that instead of the /// compute a default [foregroundColor] and use that instead of the
/// [textStyle]'s color. /// [textStyle]'s color.
final MaterialStateProperty<Color>? foregroundColor; final MaterialStateProperty<Color> foregroundColor;
/// The highlight color that's typically used to indicate that /// The highlight color that's typically used to indicate that
/// the button is focused, hovered, or pressed. /// the button is focused, hovered, or pressed.
final MaterialStateProperty<Color>? overlayColor; final MaterialStateProperty<Color> overlayColor;
/// The shadow color of the button's [Material]. /// The shadow color of the button's [Material].
/// ///
@ -143,35 +145,35 @@ class ButtonStyle with Diagnosticable {
/// dark themes, so by default the button classes add a /// dark themes, so by default the button classes add a
/// semi-transparent overlay to indicate elevation. See /// semi-transparent overlay to indicate elevation. See
/// [ThemeData.applyElevationOverlayColor]. /// [ThemeData.applyElevationOverlayColor].
final MaterialStateProperty<Color>? shadowColor; final MaterialStateProperty<Color> shadowColor;
/// The elevation of the button's [Material]. /// The elevation of the button's [Material].
final MaterialStateProperty<double>? elevation; final MaterialStateProperty<double> elevation;
/// The padding between the button's boundary and its child. /// The padding between the button's boundary and its child.
final MaterialStateProperty<EdgeInsetsGeometry>? padding; final MaterialStateProperty<EdgeInsetsGeometry> padding;
/// The minimum size of the button itself. /// The minimum size of the button itself.
/// ///
/// The size of the rectangle the button lies within may be larger /// The size of the rectangle the button lies within may be larger
/// per [tapTargetSize]. /// per [tapTargetSize].
final MaterialStateProperty<Size>? minimumSize; final MaterialStateProperty<Size> minimumSize;
/// The color and weight of the button's outline. /// The color and weight of the button's outline.
/// ///
/// This value is combined with [shape] to create a shape decorated /// This value is combined with [shape] to create a shape decorated
/// with an outline. /// with an outline.
final MaterialStateProperty<BorderSide>? side; final MaterialStateProperty<BorderSide> side;
/// The shape of the button's underlying [Material]. /// The shape of the button's underlying [Material].
/// ///
/// This shape is combined with [side] to create a shape decorated /// This shape is combined with [side] to create a shape decorated
/// with an outline. /// with an outline.
final MaterialStateProperty<OutlinedBorder>? shape; final MaterialStateProperty<OutlinedBorder> shape;
/// The cursor for a mouse pointer when it enters or is hovering over /// The cursor for a mouse pointer when it enters or is hovering over
/// this button's [InkWell]. /// this button's [InkWell].
final MaterialStateProperty<MouseCursor>? mouseCursor; final MaterialStateProperty<MouseCursor> mouseCursor;
/// Defines how compact the button's layout will be. /// Defines how compact the button's layout will be.
/// ///
@ -181,7 +183,7 @@ class ButtonStyle with Diagnosticable {
/// ///
/// * [ThemeData.visualDensity], which specifies the [visualDensity] for all widgets /// * [ThemeData.visualDensity], which specifies the [visualDensity] for all widgets
/// within a [Theme]. /// within a [Theme].
final VisualDensity? visualDensity; final VisualDensity visualDensity;
/// Configures the minimum size of the area within which the button may be pressed. /// Configures the minimum size of the area within which the button may be pressed.
/// ///
@ -189,12 +191,12 @@ class ButtonStyle with Diagnosticable {
/// a transparent margin that responds to taps. /// a transparent margin that responds to taps.
/// ///
/// Always defaults to [ThemeData.materialTapTargetSize]. /// Always defaults to [ThemeData.materialTapTargetSize].
final MaterialTapTargetSize? tapTargetSize; final MaterialTapTargetSize tapTargetSize;
/// Defines the duration of animated changes for [shape] and [elevation]. /// Defines the duration of animated changes for [shape] and [elevation].
/// ///
/// Typically the component default value is [kThemeChangeDuration]. /// Typically the component default value is [kThemeChangeDuration].
final Duration? animationDuration; final Duration animationDuration;
/// Whether detected gestures should provide acoustic and/or haptic feedback. /// Whether detected gestures should provide acoustic and/or haptic feedback.
/// ///
@ -206,26 +208,26 @@ class ButtonStyle with Diagnosticable {
/// See also: /// See also:
/// ///
/// * [Feedback] for providing platform-specific feedback to certain actions. /// * [Feedback] for providing platform-specific feedback to certain actions.
final bool? enableFeedback; final bool enableFeedback;
/// Returns a copy of this ButtonStyle with the given fields replaced with /// Returns a copy of this ButtonStyle with the given fields replaced with
/// the new values. /// the new values.
ButtonStyle copyWith({ ButtonStyle copyWith({
MaterialStateProperty<TextStyle>? textStyle, MaterialStateProperty<TextStyle> textStyle,
MaterialStateProperty<Color>? backgroundColor, MaterialStateProperty<Color> backgroundColor,
MaterialStateProperty<Color>? foregroundColor, MaterialStateProperty<Color> foregroundColor,
MaterialStateProperty<Color>? overlayColor, MaterialStateProperty<Color> overlayColor,
MaterialStateProperty<Color>? shadowColor, MaterialStateProperty<Color> shadowColor,
MaterialStateProperty<double>? elevation, MaterialStateProperty<double> elevation,
MaterialStateProperty<EdgeInsetsGeometry>? padding, MaterialStateProperty<EdgeInsetsGeometry> padding,
MaterialStateProperty<Size>? minimumSize, MaterialStateProperty<Size> minimumSize,
MaterialStateProperty<BorderSide>? side, MaterialStateProperty<BorderSide> side,
MaterialStateProperty<OutlinedBorder>? shape, MaterialStateProperty<OutlinedBorder> shape,
MaterialStateProperty<MouseCursor>? mouseCursor, MaterialStateProperty<MouseCursor> mouseCursor,
VisualDensity? visualDensity, VisualDensity visualDensity,
MaterialTapTargetSize? tapTargetSize, MaterialTapTargetSize tapTargetSize,
Duration? animationDuration, Duration animationDuration,
bool? enableFeedback, bool enableFeedback,
}) { }) {
return ButtonStyle( return ButtonStyle(
textStyle: textStyle ?? this.textStyle, textStyle: textStyle ?? this.textStyle,
@ -251,7 +253,7 @@ class ButtonStyle with Diagnosticable {
/// ///
/// In other words, [style] is used to fill in unspecified (null) fields /// In other words, [style] is used to fill in unspecified (null) fields
/// this ButtonStyle. /// this ButtonStyle.
ButtonStyle merge(ButtonStyle? style) { ButtonStyle merge(ButtonStyle style) {
if (style == null) if (style == null)
return this; return this;
return copyWith( return copyWith(
@ -339,7 +341,7 @@ class ButtonStyle with Diagnosticable {
} }
/// Linearly interpolate between two [ButtonStyle]s. /// Linearly interpolate between two [ButtonStyle]s.
static ButtonStyle? lerp(ButtonStyle? a, ButtonStyle? b, double t) { static ButtonStyle lerp(ButtonStyle a, ButtonStyle b, double t) {
assert (t != null); assert (t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
@ -354,15 +356,15 @@ class ButtonStyle with Diagnosticable {
minimumSize: _lerpProperties<Size>(a?.minimumSize, b?.minimumSize, t, Size.lerp), minimumSize: _lerpProperties<Size>(a?.minimumSize, b?.minimumSize, t, Size.lerp),
side: _lerpSides(a?.side, b?.side, t), side: _lerpSides(a?.side, b?.side, t),
shape: _lerpShapes(a?.shape, b?.shape, t), shape: _lerpShapes(a?.shape, b?.shape, t),
mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor, mouseCursor: t < 0.5 ? a.mouseCursor : b.mouseCursor,
visualDensity: t < 0.5 ? a?.visualDensity : b?.visualDensity, visualDensity: t < 0.5 ? a.visualDensity : b.visualDensity,
tapTargetSize: t < 0.5 ? a?.tapTargetSize : b?.tapTargetSize, tapTargetSize: t < 0.5 ? a.tapTargetSize : b.tapTargetSize,
animationDuration: t < 0.5 ? a?.animationDuration : b?.animationDuration, animationDuration: t < 0.5 ? a.animationDuration : b.animationDuration,
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback, enableFeedback: t < 0.5 ? a.enableFeedback : b.enableFeedback,
); );
} }
static MaterialStateProperty<T>? _lerpProperties<T>(MaterialStateProperty<T>? a, MaterialStateProperty<T>? b, double t, T? Function(T?, T?, double) lerpFunction ) { static MaterialStateProperty<T> _lerpProperties<T>(MaterialStateProperty<T> a, MaterialStateProperty<T> b, double t, T Function(T, T, double) lerpFunction ) {
// Avoid creating a _LerpProperties object for a common case. // Avoid creating a _LerpProperties object for a common case.
if (a == null && b == null) if (a == null && b == null)
return null; return null;
@ -370,14 +372,14 @@ class ButtonStyle with Diagnosticable {
} }
// Special case because BorderSide.lerp() doesn't support null arguments // Special case because BorderSide.lerp() doesn't support null arguments
static MaterialStateProperty<BorderSide>? _lerpSides(MaterialStateProperty<BorderSide>? a, MaterialStateProperty<BorderSide>? b, double t) { static MaterialStateProperty<BorderSide> _lerpSides(MaterialStateProperty<BorderSide> a, MaterialStateProperty<BorderSide> b, double t) {
if (a == null && b == null) if (a == null && b == null)
return null; return null;
return _LerpSides(a, b, t); return _LerpSides(a, b, t);
} }
// TODO(hansmuller): OutlinedBorder needs a lerp method - https://github.com/flutter/flutter/issues/60555. // TODO(hansmuller): OutlinedBorder needs a lerp method - https://github.com/flutter/flutter/issues/60555.
static MaterialStateProperty<OutlinedBorder>? _lerpShapes(MaterialStateProperty<OutlinedBorder>? a, MaterialStateProperty<OutlinedBorder>? b, double t) { static MaterialStateProperty<OutlinedBorder> _lerpShapes(MaterialStateProperty<OutlinedBorder> a, MaterialStateProperty<OutlinedBorder> b, double t) {
if (a == null && b == null) if (a == null && b == null)
return null; return null;
return _LerpShapes(a, b, t); return _LerpShapes(a, b, t);
@ -387,15 +389,15 @@ class ButtonStyle with Diagnosticable {
class _LerpProperties<T> implements MaterialStateProperty<T> { class _LerpProperties<T> implements MaterialStateProperty<T> {
const _LerpProperties(this.a, this.b, this.t, this.lerpFunction); const _LerpProperties(this.a, this.b, this.t, this.lerpFunction);
final MaterialStateProperty<T>? a; final MaterialStateProperty<T> a;
final MaterialStateProperty<T>? b; final MaterialStateProperty<T> b;
final double t; final double t;
final T? Function(T?, T?, double) lerpFunction; final T Function(T, T, double) lerpFunction;
@override @override
T? resolve(Set<MaterialState> states) { T resolve(Set<MaterialState> states) {
final T? resolvedA = a?.resolve(states); final T resolvedA = a?.resolve(states);
final T? resolvedB = b?.resolve(states); final T resolvedB = b?.resolve(states);
return lerpFunction(resolvedA, resolvedB, t); return lerpFunction(resolvedA, resolvedB, t);
} }
} }
@ -403,18 +405,18 @@ class _LerpProperties<T> implements MaterialStateProperty<T> {
class _LerpSides implements MaterialStateProperty<BorderSide> { class _LerpSides implements MaterialStateProperty<BorderSide> {
const _LerpSides(this.a, this.b, this.t); const _LerpSides(this.a, this.b, this.t);
final MaterialStateProperty<BorderSide>? a; final MaterialStateProperty<BorderSide> a;
final MaterialStateProperty<BorderSide>? b; final MaterialStateProperty<BorderSide> b;
final double t; final double t;
@override @override
BorderSide? resolve(Set<MaterialState> states) { BorderSide resolve(Set<MaterialState> states) {
final BorderSide? resolvedA = a?.resolve(states); final BorderSide resolvedA = a?.resolve(states);
final BorderSide? resolvedB = b?.resolve(states); final BorderSide resolvedB = b?.resolve(states);
if (resolvedA == null && resolvedB == null) if (resolvedA == null && resolvedB == null)
return null; return null;
if (resolvedA == null) if (resolvedA == null)
return BorderSide.lerp(BorderSide(width: 0, color: resolvedB!.color.withAlpha(0)), resolvedB, t); return BorderSide.lerp(BorderSide(width: 0, color: resolvedB.color.withAlpha(0)), resolvedB, t);
if (resolvedB == null) if (resolvedB == null)
return BorderSide.lerp(BorderSide(width: 0, color: resolvedA.color.withAlpha(0)), resolvedA, t); return BorderSide.lerp(BorderSide(width: 0, color: resolvedA.color.withAlpha(0)), resolvedA, t);
return BorderSide.lerp(resolvedA, resolvedB, t); return BorderSide.lerp(resolvedA, resolvedB, t);
@ -424,14 +426,14 @@ class _LerpSides implements MaterialStateProperty<BorderSide> {
class _LerpShapes implements MaterialStateProperty<OutlinedBorder> { class _LerpShapes implements MaterialStateProperty<OutlinedBorder> {
const _LerpShapes(this.a, this.b, this.t); const _LerpShapes(this.a, this.b, this.t);
final MaterialStateProperty<OutlinedBorder>? a; final MaterialStateProperty<OutlinedBorder> a;
final MaterialStateProperty<OutlinedBorder>? b; final MaterialStateProperty<OutlinedBorder> b;
final double t; final double t;
@override @override
OutlinedBorder? resolve(Set<MaterialState> states) { OutlinedBorder resolve(Set<MaterialState> states) {
final OutlinedBorder? resolvedA = a?.resolve(states); final OutlinedBorder resolvedA = a?.resolve(states);
final OutlinedBorder? resolvedB = b?.resolve(states); final OutlinedBorder resolvedB = b?.resolve(states);
return ShapeBorder.lerp(resolvedA, resolvedB, t) as OutlinedBorder?; return ShapeBorder.lerp(resolvedA, resolvedB, t) as OutlinedBorder;
} }
} }

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -30,14 +32,14 @@ import 'theme_data.dart';
abstract class ButtonStyleButton extends StatefulWidget { abstract class ButtonStyleButton extends StatefulWidget {
/// Create a [ButtonStyleButton]. /// Create a [ButtonStyleButton].
const ButtonStyleButton({ const ButtonStyleButton({
Key? key, Key key,
required this.onPressed, @required this.onPressed,
required this.onLongPress, @required this.onLongPress,
required this.style, @required this.style,
required this.focusNode, @required this.focusNode,
required this.autofocus, @required this.autofocus,
required this.clipBehavior, @required this.clipBehavior,
required this.child, @required this.child,
}) : assert(autofocus != null), }) : assert(autofocus != null),
assert(clipBehavior != null), assert(clipBehavior != null),
super(key: key); super(key: key);
@ -49,7 +51,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
/// See also: /// See also:
/// ///
/// * [enabled], which is true if the button is enabled. /// * [enabled], which is true if the button is enabled.
final VoidCallback? onPressed; final VoidCallback onPressed;
/// Called when the button is long-pressed. /// Called when the button is long-pressed.
/// ///
@ -58,7 +60,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
/// See also: /// See also:
/// ///
/// * [enabled], which is true if the button is enabled. /// * [enabled], which is true if the button is enabled.
final VoidCallback? onLongPress; final VoidCallback onLongPress;
/// Customizes this button's appearance. /// Customizes this button's appearance.
/// ///
@ -68,7 +70,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
/// [MaterialStateProperty]s in [themeStyleOf] and [defaultStyleOf]. /// [MaterialStateProperty]s in [themeStyleOf] and [defaultStyleOf].
/// ///
/// Null by default. /// Null by default.
final ButtonStyle? style; final ButtonStyle style;
/// {@macro flutter.widgets.Clip} /// {@macro flutter.widgets.Clip}
/// ///
@ -76,13 +78,13 @@ abstract class ButtonStyleButton extends StatefulWidget {
final Clip clipBehavior; final Clip clipBehavior;
/// {@macro flutter.widgets.Focus.focusNode} /// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode; final FocusNode focusNode;
/// {@macro flutter.widgets.Focus.autofocus} /// {@macro flutter.widgets.Focus.autofocus}
final bool autofocus; final bool autofocus;
/// Typically the button's label. /// Typically the button's label.
final Widget? child; final Widget child;
/// Returns a non-null [ButtonStyle] that's based primarily on the [Theme]'s /// Returns a non-null [ButtonStyle] that's based primarily on the [Theme]'s
/// [ThemeData.textTheme] and [ThemeData.colorScheme]. /// [ThemeData.textTheme] and [ThemeData.colorScheme].
@ -115,7 +117,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
/// ///
/// * [defaultStyleOf], Returns the default [ButtonStyle] for this button. /// * [defaultStyleOf], Returns the default [ButtonStyle] for this button.
@protected @protected
ButtonStyle? themeStyleOf(BuildContext context); ButtonStyle themeStyleOf(BuildContext context);
/// Whether the button is enabled or disabled. /// Whether the button is enabled or disabled.
/// ///
@ -137,7 +139,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
/// Returns null if [value] is null, otherwise `MaterialStateProperty.all<T>(value)`. /// Returns null if [value] is null, otherwise `MaterialStateProperty.all<T>(value)`.
/// ///
/// A convenience method for subclasses. /// A convenience method for subclasses.
static MaterialStateProperty<T>? allOrNull<T>(T? value) => value == null ? null : MaterialStateProperty.all<T>(value); static MaterialStateProperty<T> allOrNull<T>(T value) => value == null ? null : MaterialStateProperty.all<T>(value);
/// Returns an interpolated value based on the [textScaleFactor] parameter: /// Returns an interpolated value based on the [textScaleFactor] parameter:
/// ///
@ -163,9 +165,9 @@ abstract class ButtonStyleButton extends StatefulWidget {
} else if (textScaleFactor >= 3) { } else if (textScaleFactor >= 3) {
return geometry3x; return geometry3x;
} else if (textScaleFactor <= 2) { } else if (textScaleFactor <= 2) {
return EdgeInsetsGeometry.lerp(geometry1x, geometry2x, textScaleFactor - 1)!; return EdgeInsetsGeometry.lerp(geometry1x, geometry2x, textScaleFactor - 1);
} }
return EdgeInsetsGeometry.lerp(geometry2x, geometry3x, textScaleFactor - 2)!; return EdgeInsetsGeometry.lerp(geometry2x, geometry3x, textScaleFactor - 2);
} }
} }
@ -178,9 +180,9 @@ abstract class ButtonStyleButton extends StatefulWidget {
/// * [ElevatedButton], a filled button whose material elevates when pressed. /// * [ElevatedButton], a filled button whose material elevates when pressed.
/// * [OutlinedButton], similar to [TextButton], but with an outline. /// * [OutlinedButton], similar to [TextButton], but with an outline.
class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStateMixin { class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStateMixin {
AnimationController? _controller; AnimationController _controller;
double? _elevation; double _elevation;
Color? _backgroundColor; Color _backgroundColor;
final Set<MaterialState> _states = <MaterialState>{}; final Set<MaterialState> _states = <MaterialState>{};
bool get _hovered => _states.contains(MaterialState.hovered); bool get _hovered => _states.contains(MaterialState.hovered);
@ -243,54 +245,54 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ButtonStyle? widgetStyle = widget.style; final ButtonStyle widgetStyle = widget.style;
final ButtonStyle? themeStyle = widget.themeStyleOf(context); final ButtonStyle themeStyle = widget.themeStyleOf(context);
final ButtonStyle defaultStyle = widget.defaultStyleOf(context); final ButtonStyle defaultStyle = widget.defaultStyleOf(context);
assert(defaultStyle != null); assert(defaultStyle != null);
T? effectiveValue<T>(T? Function(ButtonStyle? style) getProperty) { T effectiveValue<T>(T Function(ButtonStyle style) getProperty) {
final T? widgetValue = getProperty(widgetStyle); final T widgetValue = getProperty(widgetStyle);
final T? themeValue = getProperty(themeStyle); final T themeValue = getProperty(themeStyle);
final T? defaultValue = getProperty(defaultStyle); final T defaultValue = getProperty(defaultStyle);
return widgetValue ?? themeValue ?? defaultValue; return widgetValue ?? themeValue ?? defaultValue;
} }
T? resolve<T>(MaterialStateProperty<T>? Function(ButtonStyle? style) getProperty) { T resolve<T>(MaterialStateProperty<T> Function(ButtonStyle style) getProperty) {
return effectiveValue( return effectiveValue(
(ButtonStyle? style) => getProperty(style)?.resolve(_states), (ButtonStyle style) => getProperty(style)?.resolve(_states),
); );
} }
final double? resolvedElevation = resolve<double>((ButtonStyle? style) => style?.elevation); final double resolvedElevation = resolve<double>((ButtonStyle style) => style?.elevation);
final TextStyle? resolvedTextStyle = resolve<TextStyle>((ButtonStyle? style) => style?.textStyle); final TextStyle resolvedTextStyle = resolve<TextStyle>((ButtonStyle style) => style?.textStyle);
Color? resolvedBackgroundColor = resolve<Color>((ButtonStyle? style) => style?.backgroundColor); Color resolvedBackgroundColor = resolve<Color>((ButtonStyle style) => style?.backgroundColor);
final Color? resolvedForegroundColor = resolve<Color>((ButtonStyle? style) => style?.foregroundColor); final Color resolvedForegroundColor = resolve<Color>((ButtonStyle style) => style?.foregroundColor);
final Color? resolvedShadowColor = resolve<Color>((ButtonStyle? style) => style?.shadowColor); final Color resolvedShadowColor = resolve<Color>((ButtonStyle style) => style?.shadowColor);
final EdgeInsetsGeometry? resolvedPadding = resolve<EdgeInsetsGeometry>((ButtonStyle? style) => style?.padding); final EdgeInsetsGeometry resolvedPadding = resolve<EdgeInsetsGeometry>((ButtonStyle style) => style?.padding);
final Size? resolvedMinimumSize = resolve<Size>((ButtonStyle? style) => style?.minimumSize); final Size resolvedMinimumSize = resolve<Size>((ButtonStyle style) => style?.minimumSize);
final BorderSide? resolvedSide = resolve<BorderSide>((ButtonStyle? style) => style?.side); final BorderSide resolvedSide = resolve<BorderSide>((ButtonStyle style) => style?.side);
final OutlinedBorder? resolvedShape = resolve<OutlinedBorder>((ButtonStyle? style) => style?.shape); final OutlinedBorder resolvedShape = resolve<OutlinedBorder>((ButtonStyle style) => style?.shape);
final MaterialStateMouseCursor resolvedMouseCursor = _MouseCursor( final MaterialStateMouseCursor resolvedMouseCursor = _MouseCursor(
(Set<MaterialState> states) => effectiveValue((ButtonStyle? style) => style?.mouseCursor?.resolve(states)), (Set<MaterialState> states) => effectiveValue((ButtonStyle style) => style?.mouseCursor?.resolve(states)),
); );
final MaterialStateProperty<Color> overlayColor = MaterialStateProperty.resolveWith<Color>( final MaterialStateProperty<Color> overlayColor = MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) => effectiveValue((ButtonStyle? style) => style?.overlayColor?.resolve(states)), (Set<MaterialState> states) => effectiveValue((ButtonStyle style) => style?.overlayColor?.resolve(states)),
); );
final VisualDensity? resolvedVisualDensity = effectiveValue((ButtonStyle? style) => style?.visualDensity); final VisualDensity resolvedVisualDensity = effectiveValue((ButtonStyle style) => style?.visualDensity);
final MaterialTapTargetSize? resolvedTapTargetSize = effectiveValue((ButtonStyle? style) => style?.tapTargetSize); final MaterialTapTargetSize resolvedTapTargetSize = effectiveValue((ButtonStyle style) => style?.tapTargetSize);
final Duration? resolvedAnimationDuration = effectiveValue((ButtonStyle? style) => style?.animationDuration); final Duration resolvedAnimationDuration = effectiveValue((ButtonStyle style) => style?.animationDuration);
final bool? resolvedEnableFeedback = effectiveValue((ButtonStyle? style) => style?.enableFeedback); final bool resolvedEnableFeedback = effectiveValue((ButtonStyle style) => style?.enableFeedback);
final Offset densityAdjustment = resolvedVisualDensity!.baseSizeAdjustment; final Offset densityAdjustment = resolvedVisualDensity.baseSizeAdjustment;
final BoxConstraints effectiveConstraints = resolvedVisualDensity.effectiveConstraints( final BoxConstraints effectiveConstraints = resolvedVisualDensity.effectiveConstraints(
BoxConstraints( BoxConstraints(
minWidth: resolvedMinimumSize!.width, minWidth: resolvedMinimumSize.width,
minHeight: resolvedMinimumSize.height, minHeight: resolvedMinimumSize.height,
), ),
); );
final EdgeInsetsGeometry padding = resolvedPadding!.add( final EdgeInsetsGeometry padding = resolvedPadding.add(
EdgeInsets.only( EdgeInsets.only(
left: densityAdjustment.dx, left: densityAdjustment.dx,
top: densityAdjustment.dy, top: densityAdjustment.dy,
@ -303,12 +305,12 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
// elevation is changing, change the elevation first. Material implicitly // elevation is changing, change the elevation first. Material implicitly
// animates its elevation but not its color. SKIA renders non-zero // animates its elevation but not its color. SKIA renders non-zero
// elevations as a shadow colored fill behind the Material's background. // elevations as a shadow colored fill behind the Material's background.
if (resolvedAnimationDuration! > Duration.zero if (resolvedAnimationDuration > Duration.zero
&& _elevation != null && _elevation != null
&& _backgroundColor != null && _backgroundColor != null
&& _elevation != resolvedElevation && _elevation != resolvedElevation
&& _backgroundColor!.value != resolvedBackgroundColor!.value && _backgroundColor.value != resolvedBackgroundColor.value
&& _backgroundColor!.opacity == 1 && _backgroundColor.opacity == 1
&& resolvedBackgroundColor.opacity < 1 && resolvedBackgroundColor.opacity < 1
&& resolvedElevation == 0) { && resolvedElevation == 0) {
if (_controller?.duration != resolvedAnimationDuration) { if (_controller?.duration != resolvedAnimationDuration) {
@ -324,8 +326,8 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
}); });
} }
resolvedBackgroundColor = _backgroundColor; // Defer changing the background color. resolvedBackgroundColor = _backgroundColor; // Defer changing the background color.
_controller!.value = 0; _controller.value = 0;
_controller!.forward(); _controller.forward();
} }
_elevation = resolvedElevation; _elevation = resolvedElevation;
_backgroundColor = resolvedBackgroundColor; _backgroundColor = resolvedBackgroundColor;
@ -333,9 +335,9 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
final Widget result = ConstrainedBox( final Widget result = ConstrainedBox(
constraints: effectiveConstraints, constraints: effectiveConstraints,
child: Material( child: Material(
elevation: resolvedElevation!, elevation: resolvedElevation,
textStyle: resolvedTextStyle?.copyWith(color: resolvedForegroundColor), textStyle: resolvedTextStyle?.copyWith(color: resolvedForegroundColor),
shape: resolvedShape!.copyWith(side: resolvedSide), shape: resolvedShape.copyWith(side: resolvedSide),
color: resolvedBackgroundColor, color: resolvedBackgroundColor,
shadowColor: resolvedShadowColor, shadowColor: resolvedShadowColor,
type: resolvedBackgroundColor == null ? MaterialType.transparency : MaterialType.button, type: resolvedBackgroundColor == null ? MaterialType.transparency : MaterialType.button,
@ -372,7 +374,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
); );
Size minSize; Size minSize;
switch (resolvedTapTargetSize!) { switch (resolvedTapTargetSize) {
case MaterialTapTargetSize.padded: case MaterialTapTargetSize.padded:
minSize = Size( minSize = Size(
kMinInteractiveDimension + densityAdjustment.dx, kMinInteractiveDimension + densityAdjustment.dx,
@ -404,7 +406,7 @@ class _MouseCursor extends MaterialStateMouseCursor {
final MaterialPropertyResolver<MouseCursor> resolveCallback; final MaterialPropertyResolver<MouseCursor> resolveCallback;
@override @override
MouseCursor? resolve(Set<MaterialState> states) => resolveCallback(states); MouseCursor resolve(Set<MaterialState> states) => resolveCallback(states);
@override @override
String get debugDescription => 'ButtonStyleButton_MouseCursor'; String get debugDescription => 'ButtonStyleButton_MouseCursor';
@ -417,9 +419,9 @@ class _MouseCursor extends MaterialStateMouseCursor {
/// "tap target", but not its material or its ink splashes. /// "tap target", but not its material or its ink splashes.
class _InputPadding extends SingleChildRenderObjectWidget { class _InputPadding extends SingleChildRenderObjectWidget {
const _InputPadding({ const _InputPadding({
Key? key, Key key,
Widget? child, Widget child,
required this.minSize, this.minSize,
}) : super(key: key, child: child); }) : super(key: key, child: child);
final Size minSize; final Size minSize;
@ -436,7 +438,7 @@ class _InputPadding extends SingleChildRenderObjectWidget {
} }
class _RenderInputPadding extends RenderShiftedBox { class _RenderInputPadding extends RenderShiftedBox {
_RenderInputPadding(this._minSize, [RenderBox? child]) : super(child); _RenderInputPadding(this._minSize, [RenderBox child]) : super(child);
Size get minSize => _minSize; Size get minSize => _minSize;
Size _minSize; Size _minSize;
@ -450,28 +452,28 @@ class _RenderInputPadding extends RenderShiftedBox {
@override @override
double computeMinIntrinsicWidth(double height) { double computeMinIntrinsicWidth(double height) {
if (child != null) if (child != null)
return math.max(child!.getMinIntrinsicWidth(height), minSize.width); return math.max(child.getMinIntrinsicWidth(height), minSize.width);
return 0.0; return 0.0;
} }
@override @override
double computeMinIntrinsicHeight(double width) { double computeMinIntrinsicHeight(double width) {
if (child != null) if (child != null)
return math.max(child!.getMinIntrinsicHeight(width), minSize.height); return math.max(child.getMinIntrinsicHeight(width), minSize.height);
return 0.0; return 0.0;
} }
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) {
if (child != null) if (child != null)
return math.max(child!.getMaxIntrinsicWidth(height), minSize.width); return math.max(child.getMaxIntrinsicWidth(height), minSize.width);
return 0.0; return 0.0;
} }
@override @override
double computeMaxIntrinsicHeight(double width) { double computeMaxIntrinsicHeight(double width) {
if (child != null) if (child != null)
return math.max(child!.getMaxIntrinsicHeight(width), minSize.height); return math.max(child.getMaxIntrinsicHeight(width), minSize.height);
return 0.0; return 0.0;
} }
@ -479,29 +481,29 @@ class _RenderInputPadding extends RenderShiftedBox {
void performLayout() { void performLayout() {
final BoxConstraints constraints = this.constraints; final BoxConstraints constraints = this.constraints;
if (child != null) { if (child != null) {
child!.layout(constraints, parentUsesSize: true); child.layout(constraints, parentUsesSize: true);
final double height = math.max(child!.size.width, minSize.width); final double height = math.max(child.size.width, minSize.width);
final double width = math.max(child!.size.height, minSize.height); final double width = math.max(child.size.height, minSize.height);
size = constraints.constrain(Size(height, width)); size = constraints.constrain(Size(height, width));
final BoxParentData childParentData = child!.parentData as BoxParentData; final BoxParentData childParentData = child.parentData as BoxParentData;
childParentData.offset = Alignment.center.alongOffset(size - child!.size as Offset); childParentData.offset = Alignment.center.alongOffset(size - child.size as Offset);
} else { } else {
size = Size.zero; size = Size.zero;
} }
} }
@override @override
bool hitTest(BoxHitTestResult result, { required Offset position }) { bool hitTest(BoxHitTestResult result, { Offset position }) {
if (super.hitTest(result, position: position)) { if (super.hitTest(result, position: position)) {
return true; return true;
} }
final Offset center = child!.size.center(Offset.zero); final Offset center = child.size.center(Offset.zero);
return result.addWithRawTransform( return result.addWithRawTransform(
transform: MatrixUtils.forceToPoint(center), transform: MatrixUtils.forceToPoint(center),
position: center, position: center,
hitTest: (BoxHitTestResult result, Offset? position) { hitTest: (BoxHitTestResult result, Offset position) {
assert(position == center); assert(position == center);
return child!.hitTest(result, position: center); return child.hitTest(result, position: center);
}, },
); );
} }

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -79,23 +81,23 @@ class ButtonTheme extends InheritedTheme {
/// The [textTheme], [minWidth], [height], and [colorScheme] arguments /// The [textTheme], [minWidth], [height], and [colorScheme] arguments
/// must not be null. /// must not be null.
ButtonTheme({ ButtonTheme({
Key? key, Key key,
ButtonTextTheme textTheme = ButtonTextTheme.normal, ButtonTextTheme textTheme = ButtonTextTheme.normal,
ButtonBarLayoutBehavior layoutBehavior = ButtonBarLayoutBehavior.padded, ButtonBarLayoutBehavior layoutBehavior = ButtonBarLayoutBehavior.padded,
double minWidth = 88.0, double minWidth = 88.0,
double height = 36.0, double height = 36.0,
EdgeInsetsGeometry? padding, EdgeInsetsGeometry padding,
ShapeBorder? shape, ShapeBorder shape,
bool alignedDropdown = false, bool alignedDropdown = false,
Color? buttonColor, Color buttonColor,
Color? disabledColor, Color disabledColor,
Color? focusColor, Color focusColor,
Color? hoverColor, Color hoverColor,
Color? highlightColor, Color highlightColor,
Color? splashColor, Color splashColor,
ColorScheme? colorScheme, ColorScheme colorScheme,
MaterialTapTargetSize? materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
required Widget child, Widget child,
}) : assert(textTheme != null), }) : assert(textTheme != null),
assert(minWidth != null && minWidth >= 0.0), assert(minWidth != null && minWidth >= 0.0),
assert(height != null && height >= 0.0), assert(height != null && height >= 0.0),
@ -124,9 +126,9 @@ class ButtonTheme extends InheritedTheme {
/// ///
/// The [data] argument must not be null. /// The [data] argument must not be null.
const ButtonTheme.fromButtonThemeData({ const ButtonTheme.fromButtonThemeData({
Key? key, Key key,
required this.data, @required this.data,
required Widget child, Widget child,
}) : assert(data != null), }) : assert(data != null),
super(key: key, child: child); super(key: key, child: child);
@ -175,21 +177,21 @@ class ButtonTheme extends InheritedTheme {
'This feature was deprecated after v1.9.1.' 'This feature was deprecated after v1.9.1.'
) )
ButtonTheme.bar({ ButtonTheme.bar({
Key? key, Key key,
ButtonTextTheme textTheme = ButtonTextTheme.accent, ButtonTextTheme textTheme = ButtonTextTheme.accent,
double minWidth = 64.0, double minWidth = 64.0,
double height = 36.0, double height = 36.0,
EdgeInsetsGeometry padding = const EdgeInsets.symmetric(horizontal: 8.0), EdgeInsetsGeometry padding = const EdgeInsets.symmetric(horizontal: 8.0),
ShapeBorder? shape, ShapeBorder shape,
bool alignedDropdown = false, bool alignedDropdown = false,
Color? buttonColor, Color buttonColor,
Color? disabledColor, Color disabledColor,
Color? focusColor, Color focusColor,
Color? hoverColor, Color hoverColor,
Color? highlightColor, Color highlightColor,
Color? splashColor, Color splashColor,
ColorScheme? colorScheme, ColorScheme colorScheme,
required Widget child, Widget child,
ButtonBarLayoutBehavior layoutBehavior = ButtonBarLayoutBehavior.padded, ButtonBarLayoutBehavior layoutBehavior = ButtonBarLayoutBehavior.padded,
}) : assert(textTheme != null), }) : assert(textTheme != null),
assert(minWidth != null && minWidth >= 0.0), assert(minWidth != null && minWidth >= 0.0),
@ -224,10 +226,10 @@ class ButtonTheme extends InheritedTheme {
/// ButtonThemeData theme = ButtonTheme.of(context); /// ButtonThemeData theme = ButtonTheme.of(context);
/// ``` /// ```
static ButtonThemeData of(BuildContext context) { static ButtonThemeData of(BuildContext context) {
final ButtonTheme? inheritedButtonTheme = context.dependOnInheritedWidgetOfExactType<ButtonTheme>(); final ButtonTheme inheritedButtonTheme = context.dependOnInheritedWidgetOfExactType<ButtonTheme>();
ButtonThemeData? buttonTheme = inheritedButtonTheme?.data; ButtonThemeData buttonTheme = inheritedButtonTheme?.data;
if (buttonTheme?.colorScheme == null) { // if buttonTheme or buttonTheme.colorScheme is null if (buttonTheme?.colorScheme == null) { // if buttonTheme or buttonTheme.colorScheme is null
final ThemeData theme = Theme.of(context)!; final ThemeData theme = Theme.of(context);
buttonTheme ??= theme.buttonTheme; buttonTheme ??= theme.buttonTheme;
if (buttonTheme.colorScheme == null) { if (buttonTheme.colorScheme == null) {
buttonTheme = buttonTheme.copyWith( buttonTheme = buttonTheme.copyWith(
@ -236,12 +238,12 @@ class ButtonTheme extends InheritedTheme {
assert(buttonTheme.colorScheme != null); assert(buttonTheme.colorScheme != null);
} }
} }
return buttonTheme!; return buttonTheme;
} }
@override @override
Widget wrap(BuildContext context, Widget child) { Widget wrap(BuildContext context, Widget child) {
final ButtonTheme? ancestorTheme = context.findAncestorWidgetOfExactType<ButtonTheme>(); final ButtonTheme ancestorTheme = context.findAncestorWidgetOfExactType<ButtonTheme>();
return identical(this, ancestorTheme) ? child : ButtonTheme.fromButtonThemeData(data: data, child: child); return identical(this, ancestorTheme) ? child : ButtonTheme.fromButtonThemeData(data: data, child: child);
} }
@ -287,18 +289,18 @@ class ButtonThemeData with Diagnosticable {
this.textTheme = ButtonTextTheme.normal, this.textTheme = ButtonTextTheme.normal,
this.minWidth = 88.0, this.minWidth = 88.0,
this.height = 36.0, this.height = 36.0,
EdgeInsetsGeometry? padding, EdgeInsetsGeometry padding,
ShapeBorder? shape, ShapeBorder shape,
this.layoutBehavior = ButtonBarLayoutBehavior.padded, this.layoutBehavior = ButtonBarLayoutBehavior.padded,
this.alignedDropdown = false, this.alignedDropdown = false,
Color? buttonColor, Color buttonColor,
Color? disabledColor, Color disabledColor,
Color? focusColor, Color focusColor,
Color? hoverColor, Color hoverColor,
Color? highlightColor, Color highlightColor,
Color? splashColor, Color splashColor,
this.colorScheme, this.colorScheme,
MaterialTapTargetSize? materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
}) : assert(textTheme != null), }) : assert(textTheme != null),
assert(minWidth != null && minWidth >= 0.0), assert(minWidth != null && minWidth >= 0.0),
assert(height != null && height >= 0.0), assert(height != null && height >= 0.0),
@ -367,7 +369,7 @@ class ButtonThemeData with Diagnosticable {
/// and [FlatButton]. /// and [FlatButton].
EdgeInsetsGeometry get padding { EdgeInsetsGeometry get padding {
if (_padding != null) if (_padding != null)
return _padding!; return _padding;
switch (textTheme) { switch (textTheme) {
case ButtonTextTheme.normal: case ButtonTextTheme.normal:
case ButtonTextTheme.accent: case ButtonTextTheme.accent:
@ -375,8 +377,10 @@ class ButtonThemeData with Diagnosticable {
case ButtonTextTheme.primary: case ButtonTextTheme.primary:
return const EdgeInsets.symmetric(horizontal: 24.0); return const EdgeInsets.symmetric(horizontal: 24.0);
} }
assert(false);
return EdgeInsets.zero;
} }
final EdgeInsetsGeometry? _padding; final EdgeInsetsGeometry _padding;
/// The shape of a button's material. /// The shape of a button's material.
/// ///
@ -394,7 +398,7 @@ class ButtonThemeData with Diagnosticable {
/// and [FlatButton]. /// and [FlatButton].
ShapeBorder get shape { ShapeBorder get shape {
if (_shape != null) if (_shape != null)
return _shape!; return _shape;
switch (textTheme) { switch (textTheme) {
case ButtonTextTheme.normal: case ButtonTextTheme.normal:
case ButtonTextTheme.accent: case ButtonTextTheme.accent:
@ -406,8 +410,9 @@ class ButtonThemeData with Diagnosticable {
borderRadius: BorderRadius.all(Radius.circular(4.0)), borderRadius: BorderRadius.all(Radius.circular(4.0)),
); );
} }
return const RoundedRectangleBorder();
} }
final ShapeBorder? _shape; final ShapeBorder _shape;
/// If true, then a [DropdownButton] menu's width will match the button's /// If true, then a [DropdownButton] menu's width will match the button's
/// width. /// width.
@ -432,7 +437,7 @@ class ButtonThemeData with Diagnosticable {
/// ///
/// * [getFillColor], which is used by [RaisedButton] to compute its /// * [getFillColor], which is used by [RaisedButton] to compute its
/// background fill color. /// background fill color.
final Color? _buttonColor; final Color _buttonColor;
/// The background fill color for disabled [RaisedButton]s. /// The background fill color for disabled [RaisedButton]s.
/// ///
@ -442,7 +447,7 @@ class ButtonThemeData with Diagnosticable {
/// ///
/// * [getDisabledFillColor], which is used by [RaisedButton] to compute its /// * [getDisabledFillColor], which is used by [RaisedButton] to compute its
/// background fill color. /// background fill color.
final Color? _disabledColor; final Color _disabledColor;
/// The fill color of the button when it has the input focus. /// The fill color of the button when it has the input focus.
/// ///
@ -455,7 +460,7 @@ class ButtonThemeData with Diagnosticable {
/// ///
/// * [getFocusColor], which is used by [RaisedButton], [OutlineButton] /// * [getFocusColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton]. /// and [FlatButton].
final Color? _focusColor; final Color _focusColor;
/// The fill color of the button when a pointer is hovering over it. /// The fill color of the button when a pointer is hovering over it.
/// ///
@ -468,7 +473,7 @@ class ButtonThemeData with Diagnosticable {
/// ///
/// * [getHoverColor], which is used by [RaisedButton], [OutlineButton] /// * [getHoverColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton]. /// and [FlatButton].
final Color? _hoverColor; final Color _hoverColor;
/// The color of the overlay that appears when a button is pressed. /// The color of the overlay that appears when a button is pressed.
/// ///
@ -478,7 +483,7 @@ class ButtonThemeData with Diagnosticable {
/// ///
/// * [getHighlightColor], which is used by [RaisedButton], [OutlineButton] /// * [getHighlightColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton]. /// and [FlatButton].
final Color? _highlightColor; final Color _highlightColor;
/// The color of the ink "splash" overlay that appears when a button is tapped. /// The color of the ink "splash" overlay that appears when a button is tapped.
/// ///
@ -488,7 +493,7 @@ class ButtonThemeData with Diagnosticable {
/// ///
/// * [getSplashColor], which is used by [RaisedButton], [OutlineButton] /// * [getSplashColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton]. /// and [FlatButton].
final Color? _splashColor; final Color _splashColor;
/// A set of thirteen colors that can be used to derive the button theme's /// A set of thirteen colors that can be used to derive the button theme's
/// colors. /// colors.
@ -500,7 +505,7 @@ class ButtonThemeData with Diagnosticable {
/// The colors for new button classes can be defined exclusively in terms /// The colors for new button classes can be defined exclusively in terms
/// of [colorScheme]. When it's possible, the existing buttons will /// of [colorScheme]. When it's possible, the existing buttons will
/// (continue to) gradually migrate to it. /// (continue to) gradually migrate to it.
final ColorScheme? colorScheme; final ColorScheme colorScheme;
// The minimum size of a button's tap target. // The minimum size of a button's tap target.
// //
@ -510,14 +515,14 @@ class ButtonThemeData with Diagnosticable {
// //
// * [getMaterialTargetTapSize], which is used by [RaisedButton], // * [getMaterialTargetTapSize], which is used by [RaisedButton],
// [OutlineButton] and [FlatButton]. // [OutlineButton] and [FlatButton].
final MaterialTapTargetSize? _materialTapTargetSize; final MaterialTapTargetSize _materialTapTargetSize;
/// The [button]'s overall brightness. /// The [button]'s overall brightness.
/// ///
/// Returns the button's [MaterialButton.colorBrightness] if it is non-null, /// Returns the button's [MaterialButton.colorBrightness] if it is non-null,
/// otherwise the color scheme's [ColorScheme.brightness] is returned. /// otherwise the color scheme's [ColorScheme.brightness] is returned.
Brightness getBrightness(MaterialButton button) { Brightness getBrightness(MaterialButton button) {
return button.colorBrightness ?? colorScheme!.brightness; return button.colorBrightness ?? colorScheme.brightness;
} }
/// Defines the [button]'s base colors, and the defaults for the button's /// Defines the [button]'s base colors, and the defaults for the button's
@ -540,10 +545,10 @@ class ButtonThemeData with Diagnosticable {
/// used as the `disabledTextColor`. It will be resolved in the [MaterialState.disabled] state. /// used as the `disabledTextColor`. It will be resolved in the [MaterialState.disabled] state.
Color getDisabledTextColor(MaterialButton button) { Color getDisabledTextColor(MaterialButton button) {
if (button.textColor is MaterialStateProperty<Color>) if (button.textColor is MaterialStateProperty<Color>)
return button.textColor!; return button.textColor;
if (button.disabledTextColor != null) if (button.disabledTextColor != null)
return button.disabledTextColor!; return button.disabledTextColor;
return colorScheme!.onSurface.withOpacity(0.38); return colorScheme.onSurface.withOpacity(0.38);
} }
/// The [button]'s background color when [MaterialButton.onPressed] is null /// The [button]'s background color when [MaterialButton.onPressed] is null
@ -558,10 +563,10 @@ class ButtonThemeData with Diagnosticable {
/// with its opacity set to 0.38. /// with its opacity set to 0.38.
Color getDisabledFillColor(MaterialButton button) { Color getDisabledFillColor(MaterialButton button) {
if (button.disabledColor != null) if (button.disabledColor != null)
return button.disabledColor!; return button.disabledColor;
if (_disabledColor != null) if (_disabledColor != null)
return _disabledColor!; return _disabledColor;
return colorScheme!.onSurface.withOpacity(0.38); return colorScheme.onSurface.withOpacity(0.38);
} }
/// The button's background fill color or null for buttons that don't have /// The button's background fill color or null for buttons that don't have
@ -589,8 +594,8 @@ class ButtonThemeData with Diagnosticable {
/// otherwise the color scheme's ColorScheme.primary color. If the button /// otherwise the color scheme's ColorScheme.primary color. If the button
/// is not enabled then the colorScheme's [ColorScheme.onSurface] color /// is not enabled then the colorScheme's [ColorScheme.onSurface] color
/// with opacity 0.12. /// with opacity 0.12.
Color? getFillColor(MaterialButton button) { Color getFillColor(MaterialButton button) {
final Color? fillColor = button.enabled ? button.color : button.disabledColor; final Color fillColor = button.enabled ? button.color : button.disabledColor;
if (fillColor != null) if (fillColor != null)
return fillColor; return fillColor;
@ -603,12 +608,15 @@ class ButtonThemeData with Diagnosticable {
switch (getTextTheme(button)) { switch (getTextTheme(button)) {
case ButtonTextTheme.normal: case ButtonTextTheme.normal:
case ButtonTextTheme.accent: case ButtonTextTheme.accent:
return button.enabled ? colorScheme!.primary : getDisabledFillColor(button); return button.enabled ? colorScheme.primary : getDisabledFillColor(button);
case ButtonTextTheme.primary: case ButtonTextTheme.primary:
return button.enabled return button.enabled
? _buttonColor ?? colorScheme!.primary ? _buttonColor ?? colorScheme.primary
: colorScheme!.onSurface.withOpacity(0.12); : colorScheme.onSurface.withOpacity(0.12);
} }
assert(false);
return null;
} }
/// The foreground color of the [button]'s text and icon. /// The foreground color of the [button]'s text and icon.
@ -633,26 +641,29 @@ class ButtonThemeData with Diagnosticable {
return getDisabledTextColor(button); return getDisabledTextColor(button);
if (button.textColor != null) if (button.textColor != null)
return button.textColor!; return button.textColor;
switch (getTextTheme(button)) { switch (getTextTheme(button)) {
case ButtonTextTheme.normal: case ButtonTextTheme.normal:
return getBrightness(button) == Brightness.dark ? Colors.white : Colors.black87; return getBrightness(button) == Brightness.dark ? Colors.white : Colors.black87;
case ButtonTextTheme.accent: case ButtonTextTheme.accent:
return colorScheme!.secondary; return colorScheme.secondary;
case ButtonTextTheme.primary: case ButtonTextTheme.primary:
final Color? fillColor = getFillColor(button); final Color fillColor = getFillColor(button);
final bool fillIsDark = fillColor != null final bool fillIsDark = fillColor != null
? ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark ? ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark
: getBrightness(button) == Brightness.dark; : getBrightness(button) == Brightness.dark;
if (fillIsDark) if (fillIsDark)
return Colors.white; return Colors.white;
if (button is FlatButton || button is OutlineButton) if (button is FlatButton || button is OutlineButton)
return colorScheme!.primary; return colorScheme.primary;
return Colors.black; return Colors.black;
} }
assert(false);
return null;
} }
/// The color of the ink "splash" overlay that appears when the (enabled) /// The color of the ink "splash" overlay that appears when the (enabled)
@ -670,16 +681,16 @@ class ButtonThemeData with Diagnosticable {
/// Otherwise, returns [getTextColor] with an opacity of 0.12. /// Otherwise, returns [getTextColor] with an opacity of 0.12.
Color getSplashColor(MaterialButton button) { Color getSplashColor(MaterialButton button) {
if (button.splashColor != null) if (button.splashColor != null)
return button.splashColor!; return button.splashColor;
if (_splashColor != null && (button is RaisedButton || button is OutlineButton)) if (_splashColor != null && (button is RaisedButton || button is OutlineButton))
return _splashColor!; return _splashColor;
if (_splashColor != null && button is FlatButton) { if (_splashColor != null && button is FlatButton) {
switch (getTextTheme(button)) { switch (getTextTheme(button)) {
case ButtonTextTheme.normal: case ButtonTextTheme.normal:
case ButtonTextTheme.accent: case ButtonTextTheme.accent:
return _splashColor!; return _splashColor;
case ButtonTextTheme.primary: case ButtonTextTheme.primary:
break; break;
} }
@ -725,7 +736,7 @@ class ButtonThemeData with Diagnosticable {
/// * [ButtonTextTheme.primary], returns [Colors.transparent]. /// * [ButtonTextTheme.primary], returns [Colors.transparent].
Color getHighlightColor(MaterialButton button) { Color getHighlightColor(MaterialButton button) {
if (button.highlightColor != null) if (button.highlightColor != null)
return button.highlightColor!; return button.highlightColor;
switch (getTextTheme(button)) { switch (getTextTheme(button)) {
case ButtonTextTheme.normal: case ButtonTextTheme.normal:
@ -734,6 +745,9 @@ class ButtonThemeData with Diagnosticable {
case ButtonTextTheme.primary: case ButtonTextTheme.primary:
return Colors.transparent; return Colors.transparent;
} }
assert(false);
return Colors.transparent;
} }
/// The [button]'s elevation when it is enabled and has not been pressed. /// The [button]'s elevation when it is enabled and has not been pressed.
@ -743,7 +757,7 @@ class ButtonThemeData with Diagnosticable {
/// If button is a [FlatButton] then elevation is 0.0, otherwise it is 2.0. /// If button is a [FlatButton] then elevation is 0.0, otherwise it is 2.0.
double getElevation(MaterialButton button) { double getElevation(MaterialButton button) {
if (button.elevation != null) if (button.elevation != null)
return button.elevation!; return button.elevation;
if (button is FlatButton) if (button is FlatButton)
return 0.0; return 0.0;
return 2.0; return 2.0;
@ -757,7 +771,7 @@ class ButtonThemeData with Diagnosticable {
/// elevation is 0.0, otherwise the highlight elevation is 4.0. /// elevation is 0.0, otherwise the highlight elevation is 4.0.
double getFocusElevation(MaterialButton button) { double getFocusElevation(MaterialButton button) {
if (button.focusElevation != null) if (button.focusElevation != null)
return button.focusElevation!; return button.focusElevation;
if (button is FlatButton) if (button is FlatButton)
return 0.0; return 0.0;
if (button is OutlineButton) if (button is OutlineButton)
@ -773,7 +787,7 @@ class ButtonThemeData with Diagnosticable {
/// elevation is 0.0, otherwise the highlight elevation is 4.0. /// elevation is 0.0, otherwise the highlight elevation is 4.0.
double getHoverElevation(MaterialButton button) { double getHoverElevation(MaterialButton button) {
if (button.hoverElevation != null) if (button.hoverElevation != null)
return button.hoverElevation!; return button.hoverElevation;
if (button is FlatButton) if (button is FlatButton)
return 0.0; return 0.0;
if (button is OutlineButton) if (button is OutlineButton)
@ -789,7 +803,7 @@ class ButtonThemeData with Diagnosticable {
/// elevation is 0.0, otherwise the highlight elevation is 8.0. /// elevation is 0.0, otherwise the highlight elevation is 8.0.
double getHighlightElevation(MaterialButton button) { double getHighlightElevation(MaterialButton button) {
if (button.highlightElevation != null) if (button.highlightElevation != null)
return button.highlightElevation!; return button.highlightElevation;
if (button is FlatButton) if (button is FlatButton)
return 0.0; return 0.0;
if (button is OutlineButton) if (button is OutlineButton)
@ -805,7 +819,7 @@ class ButtonThemeData with Diagnosticable {
/// Otherwise the disabled elevation is 0.0. /// Otherwise the disabled elevation is 0.0.
double getDisabledElevation(MaterialButton button) { double getDisabledElevation(MaterialButton button) {
if (button.disabledElevation != null) if (button.disabledElevation != null)
return button.disabledElevation!; return button.disabledElevation;
return 0.0; return 0.0;
} }
@ -824,13 +838,13 @@ class ButtonThemeData with Diagnosticable {
/// otherwise. /// otherwise.
EdgeInsetsGeometry getPadding(MaterialButton button) { EdgeInsetsGeometry getPadding(MaterialButton button) {
if (button.padding != null) if (button.padding != null)
return button.padding!; return button.padding;
if (button is MaterialButtonWithIconMixin) if (button is MaterialButtonWithIconMixin)
return const EdgeInsetsDirectional.only(start: 12.0, end: 16.0); return const EdgeInsetsDirectional.only(start: 12.0, end: 16.0);
if (_padding != null) if (_padding != null)
return _padding!; return _padding;
switch (getTextTheme(button)) { switch (getTextTheme(button)) {
case ButtonTextTheme.normal: case ButtonTextTheme.normal:
@ -839,6 +853,8 @@ class ButtonThemeData with Diagnosticable {
case ButtonTextTheme.primary: case ButtonTextTheme.primary:
return const EdgeInsets.symmetric(horizontal: 24.0); return const EdgeInsets.symmetric(horizontal: 24.0);
} }
assert(false);
return EdgeInsets.zero;
} }
/// The shape of the [button]'s [Material]. /// The shape of the [button]'s [Material].
@ -879,21 +895,21 @@ class ButtonThemeData with Diagnosticable {
/// Creates a copy of this button theme data object with the matching fields /// Creates a copy of this button theme data object with the matching fields
/// replaced with the non-null parameter values. /// replaced with the non-null parameter values.
ButtonThemeData copyWith({ ButtonThemeData copyWith({
ButtonTextTheme? textTheme, ButtonTextTheme textTheme,
ButtonBarLayoutBehavior? layoutBehavior, ButtonBarLayoutBehavior layoutBehavior,
double? minWidth, double minWidth,
double? height, double height,
EdgeInsetsGeometry? padding, EdgeInsetsGeometry padding,
ShapeBorder? shape, ShapeBorder shape,
bool? alignedDropdown, bool alignedDropdown,
Color? buttonColor, Color buttonColor,
Color? disabledColor, Color disabledColor,
Color? focusColor, Color focusColor,
Color? hoverColor, Color hoverColor,
Color? highlightColor, Color highlightColor,
Color? splashColor, Color splashColor,
ColorScheme? colorScheme, ColorScheme colorScheme,
MaterialTapTargetSize? materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
}) { }) {
return ButtonThemeData( return ButtonThemeData(
textTheme: textTheme ?? this.textTheme, textTheme: textTheme ?? this.textTheme,

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -63,8 +65,8 @@ class MaterialButton extends StatelessWidget {
/// [highlightElevation], and [disabledElevation] must be non-negative, if /// [highlightElevation], and [disabledElevation] must be non-negative, if
/// specified. /// specified.
const MaterialButton({ const MaterialButton({
Key? key, Key key,
required this.onPressed, @required this.onPressed,
this.onLongPress, this.onLongPress,
this.onHighlightChanged, this.onHighlightChanged,
this.mouseCursor, this.mouseCursor,
@ -111,7 +113,7 @@ class MaterialButton extends StatelessWidget {
/// See also: /// See also:
/// ///
/// * [enabled], which is true if the button is enabled. /// * [enabled], which is true if the button is enabled.
final VoidCallback? onPressed; final VoidCallback onPressed;
/// The callback that is called when the button is long-pressed. /// The callback that is called when the button is long-pressed.
/// ///
@ -120,7 +122,7 @@ class MaterialButton extends StatelessWidget {
/// See also: /// See also:
/// ///
/// * [enabled], which is true if the button is enabled. /// * [enabled], which is true if the button is enabled.
final VoidCallback? onLongPress; final VoidCallback onLongPress;
/// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged] /// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged]
/// callback. /// callback.
@ -128,16 +130,16 @@ class MaterialButton extends StatelessWidget {
/// If [onPressed] changes from null to non-null while a gesture is ongoing, /// If [onPressed] changes from null to non-null while a gesture is ongoing,
/// this can fire during the build phase (in which case calling /// this can fire during the build phase (in which case calling
/// [State.setState] is not allowed). /// [State.setState] is not allowed).
final ValueChanged<bool>? onHighlightChanged; final ValueChanged<bool> onHighlightChanged;
/// {@macro flutter.material.button.mouseCursor} /// {@macro flutter.material.button.mouseCursor}
final MouseCursor? mouseCursor; final MouseCursor mouseCursor;
/// Defines the button's base colors, and the defaults for the button's minimum /// Defines the button's base colors, and the defaults for the button's minimum
/// size, internal padding, and shape. /// size, internal padding, and shape.
/// ///
/// Defaults to `ButtonTheme.of(context).textTheme`. /// Defaults to `ButtonTheme.of(context).textTheme`.
final ButtonTextTheme? textTheme; final ButtonTextTheme textTheme;
/// The color to use for this button's text. /// The color to use for this button's text.
/// ///
@ -155,7 +157,7 @@ class MaterialButton extends StatelessWidget {
/// ///
/// * [disabledTextColor], the text color to use when the button has been /// * [disabledTextColor], the text color to use when the button has been
/// disabled. /// disabled.
final Color? textColor; final Color textColor;
/// The color to use for this button's text when the button is disabled. /// The color to use for this button's text when the button is disabled.
/// ///
@ -172,7 +174,7 @@ class MaterialButton extends StatelessWidget {
/// See also: /// See also:
/// ///
/// * [textColor] - The color to use for this button's text when the button is [enabled]. /// * [textColor] - The color to use for this button's text when the button is [enabled].
final Color? disabledTextColor; final Color disabledTextColor;
/// The button's fill color, displayed by its [Material], while it /// The button's fill color, displayed by its [Material], while it
/// is in its default (unpressed, [enabled]) state. /// is in its default (unpressed, [enabled]) state.
@ -182,7 +184,7 @@ class MaterialButton extends StatelessWidget {
/// See also: /// See also:
/// ///
/// * [disabledColor] - the fill color of the button when the button is disabled. /// * [disabledColor] - the fill color of the button when the button is disabled.
final Color? color; final Color color;
/// The fill color of the button when the button is disabled. /// The fill color of the button when the button is disabled.
/// ///
@ -192,7 +194,7 @@ class MaterialButton extends StatelessWidget {
/// See also: /// See also:
/// ///
/// * [color] - the fill color of the button when the button is [enabled]. /// * [color] - the fill color of the button when the button is [enabled].
final Color? disabledColor; final Color disabledColor;
/// The splash color of the button's [InkWell]. /// The splash color of the button's [InkWell].
/// ///
@ -205,20 +207,20 @@ class MaterialButton extends StatelessWidget {
/// ///
/// The appearance of the splash can be configured with the theme's splash /// The appearance of the splash can be configured with the theme's splash
/// factory, [ThemeData.splashFactory]. /// factory, [ThemeData.splashFactory].
final Color? splashColor; final Color splashColor;
/// The fill color of the button's [Material] when it has the input focus. /// The fill color of the button's [Material] when it has the input focus.
/// ///
/// The button changed focus color when the button has the input focus. It /// The button changed focus color when the button has the input focus. It
/// appears behind the button's child. /// appears behind the button's child.
final Color? focusColor; final Color focusColor;
/// The fill color of the button's [Material] when a pointer is hovering over /// The fill color of the button's [Material] when a pointer is hovering over
/// it. /// it.
/// ///
/// The button changes fill color when a pointer is hovering over the button. /// The button changes fill color when a pointer is hovering over the button.
/// It appears behind the button's child. /// It appears behind the button's child.
final Color? hoverColor; final Color hoverColor;
/// The highlight color of the button's [InkWell]. /// The highlight color of the button's [InkWell].
/// ///
@ -229,7 +231,7 @@ class MaterialButton extends StatelessWidget {
/// If [textTheme] is [ButtonTextTheme.primary], the default highlight color is /// If [textTheme] is [ButtonTextTheme.primary], the default highlight color is
/// transparent (in other words the highlight doesn't appear). Otherwise it's /// transparent (in other words the highlight doesn't appear). Otherwise it's
/// the current theme's highlight color, [ThemeData.highlightColor]. /// the current theme's highlight color, [ThemeData.highlightColor].
final Color? highlightColor; final Color highlightColor;
/// The z-coordinate at which to place this button relative to its parent. /// The z-coordinate at which to place this button relative to its parent.
/// ///
@ -246,7 +248,7 @@ class MaterialButton extends StatelessWidget {
/// button. /// button.
/// * [disabledElevation], the elevation when the button is disabled. /// * [disabledElevation], the elevation when the button is disabled.
/// * [highlightElevation], the elevation when the button is pressed. /// * [highlightElevation], the elevation when the button is pressed.
final double? elevation; final double elevation;
/// The elevation for the button's [Material] when the button /// The elevation for the button's [Material] when the button
/// is [enabled] and a pointer is hovering over it. /// is [enabled] and a pointer is hovering over it.
@ -259,7 +261,7 @@ class MaterialButton extends StatelessWidget {
/// * [focusElevation], the elevation when the button is focused. /// * [focusElevation], the elevation when the button is focused.
/// * [disabledElevation], the elevation when the button is disabled. /// * [disabledElevation], the elevation when the button is disabled.
/// * [highlightElevation], the elevation when the button is pressed. /// * [highlightElevation], the elevation when the button is pressed.
final double? hoverElevation; final double hoverElevation;
/// The elevation for the button's [Material] when the button /// The elevation for the button's [Material] when the button
/// is [enabled] and has the input focus. /// is [enabled] and has the input focus.
@ -273,7 +275,7 @@ class MaterialButton extends StatelessWidget {
/// button. /// button.
/// * [disabledElevation], the elevation when the button is disabled. /// * [disabledElevation], the elevation when the button is disabled.
/// * [highlightElevation], the elevation when the button is pressed. /// * [highlightElevation], the elevation when the button is pressed.
final double? focusElevation; final double focusElevation;
/// The elevation for the button's [Material] relative to its parent when the /// The elevation for the button's [Material] relative to its parent when the
/// button is [enabled] and pressed. /// button is [enabled] and pressed.
@ -291,7 +293,7 @@ class MaterialButton extends StatelessWidget {
/// * [hoverElevation], the elevation when a pointer is hovering over the /// * [hoverElevation], the elevation when a pointer is hovering over the
/// button. /// button.
/// * [disabledElevation], the elevation when the button is disabled. /// * [disabledElevation], the elevation when the button is disabled.
final double? highlightElevation; final double highlightElevation;
/// The elevation for the button's [Material] relative to its parent when the /// The elevation for the button's [Material] relative to its parent when the
/// button is not [enabled]. /// button is not [enabled].
@ -302,7 +304,7 @@ class MaterialButton extends StatelessWidget {
/// ///
/// * [elevation], the default elevation. /// * [elevation], the default elevation.
/// * [highlightElevation], the elevation when the button is pressed. /// * [highlightElevation], the elevation when the button is pressed.
final double? disabledElevation; final double disabledElevation;
/// The theme brightness to use for this button. /// The theme brightness to use for this button.
/// ///
@ -313,12 +315,12 @@ class MaterialButton extends StatelessWidget {
/// See also: /// See also:
/// ///
/// * [ButtonTextTheme], uses [Brightness] to determine text color. /// * [ButtonTextTheme], uses [Brightness] to determine text color.
final Brightness? colorBrightness; final Brightness colorBrightness;
/// The button's label. /// The button's label.
/// ///
/// Often a [Text] widget in all caps. /// Often a [Text] widget in all caps.
final Widget? child; final Widget child;
/// Whether the button is enabled or disabled. /// Whether the button is enabled or disabled.
/// ///
@ -330,7 +332,7 @@ class MaterialButton extends StatelessWidget {
/// ///
/// Defaults to the value from the current [ButtonTheme], /// Defaults to the value from the current [ButtonTheme],
/// [ButtonThemeData.padding]. /// [ButtonThemeData.padding].
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry padding;
/// Defines how compact the button's layout will be. /// Defines how compact the button's layout will be.
/// ///
@ -340,7 +342,7 @@ class MaterialButton extends StatelessWidget {
/// ///
/// * [ThemeData.visualDensity], which specifies the [visualDensity] for all /// * [ThemeData.visualDensity], which specifies the [visualDensity] for all
/// widgets within a [Theme]. /// widgets within a [Theme].
final VisualDensity? visualDensity; final VisualDensity visualDensity;
/// The shape of the button's [Material]. /// The shape of the button's [Material].
/// ///
@ -350,7 +352,7 @@ class MaterialButton extends StatelessWidget {
/// ///
/// Defaults to the value from the current [ButtonTheme], /// Defaults to the value from the current [ButtonTheme],
/// [ButtonThemeData.shape]. /// [ButtonThemeData.shape].
final ShapeBorder? shape; final ShapeBorder shape;
/// {@macro flutter.widgets.Clip} /// {@macro flutter.widgets.Clip}
/// ///
@ -358,7 +360,7 @@ class MaterialButton extends StatelessWidget {
final Clip clipBehavior; final Clip clipBehavior;
/// {@macro flutter.widgets.Focus.focusNode} /// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode; final FocusNode focusNode;
/// {@macro flutter.widgets.Focus.autofocus} /// {@macro flutter.widgets.Focus.autofocus}
final bool autofocus; final bool autofocus;
@ -366,7 +368,7 @@ class MaterialButton extends StatelessWidget {
/// Defines the duration of animated changes for [shape] and [elevation]. /// Defines the duration of animated changes for [shape] and [elevation].
/// ///
/// The default value is [kThemeChangeDuration]. /// The default value is [kThemeChangeDuration].
final Duration? animationDuration; final Duration animationDuration;
/// Configures the minimum size of the tap target. /// Configures the minimum size of the tap target.
/// ///
@ -375,17 +377,17 @@ class MaterialButton extends StatelessWidget {
/// See also: /// See also:
/// ///
/// * [MaterialTapTargetSize], for a description of how this affects tap targets. /// * [MaterialTapTargetSize], for a description of how this affects tap targets.
final MaterialTapTargetSize? materialTapTargetSize; final MaterialTapTargetSize materialTapTargetSize;
/// The smallest horizontal extent that the button will occupy. /// The smallest horizontal extent that the button will occupy.
/// ///
/// Defaults to the value from the current [ButtonTheme]. /// Defaults to the value from the current [ButtonTheme].
final double? minWidth; final double minWidth;
/// The vertical extent of the button. /// The vertical extent of the button.
/// ///
/// Defaults to the value from the current [ButtonTheme]. /// Defaults to the value from the current [ButtonTheme].
final double? height; final double height;
/// Whether detected gestures should provide acoustic and/or haptic feedback. /// Whether detected gestures should provide acoustic and/or haptic feedback.
/// ///
@ -399,7 +401,7 @@ class MaterialButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context)!; final ThemeData theme = Theme.of(context);
final ButtonThemeData buttonTheme = ButtonTheme.of(context); final ButtonThemeData buttonTheme = ButtonTheme.of(context);
return RawMaterialButton( return RawMaterialButton(
@ -409,9 +411,9 @@ class MaterialButton extends StatelessWidget {
onHighlightChanged: onHighlightChanged, onHighlightChanged: onHighlightChanged,
mouseCursor: mouseCursor, mouseCursor: mouseCursor,
fillColor: buttonTheme.getFillColor(this), fillColor: buttonTheme.getFillColor(this),
textStyle: theme.textTheme.button!.copyWith(color: buttonTheme.getTextColor(this)), textStyle: theme.textTheme.button.copyWith(color: buttonTheme.getTextColor(this)),
focusColor: focusColor ?? buttonTheme.getFocusColor(this), focusColor: focusColor ?? buttonTheme.getFocusColor(this) ?? theme.focusColor,
hoverColor: hoverColor ?? buttonTheme.getHoverColor(this), hoverColor: hoverColor ?? buttonTheme.getHoverColor(this) ?? theme.hoverColor,
highlightColor: highlightColor ?? theme.highlightColor, highlightColor: highlightColor ?? theme.highlightColor,
splashColor: splashColor ?? theme.splashColor, splashColor: splashColor ?? theme.splashColor,
elevation: buttonTheme.getElevation(this), elevation: buttonTheme.getElevation(this),

File diff suppressed because it is too large Load diff

View file

@ -30,7 +30,7 @@ void main() {
expect(themeData.showUnselectedLabels, null); expect(themeData.showUnselectedLabels, null);
expect(themeData.type, null); expect(themeData.type, null);
const BottomNavigationBarTheme theme = BottomNavigationBarTheme(data: BottomNavigationBarThemeData(), child: SizedBox()); const BottomNavigationBarTheme theme = BottomNavigationBarTheme(data: BottomNavigationBarThemeData());
expect(theme.data.backgroundColor, null); expect(theme.data.backgroundColor, null);
expect(theme.data.elevation, null); expect(theme.data.elevation, null);
expect(theme.data.selectedIconTheme, null); expect(theme.data.selectedIconTheme, null);