flutter/examples/flutter_gallery/lib/gallery/options.dart
Ian Hickson 449f4a6673
License update (#45373)
* Update project.pbxproj files to say Flutter rather than Chromium

Also, the templates now have an empty organization so that we don't cause people to give their apps a Flutter copyright.

* Update the copyright notice checker to require a standard notice on all files

* Update copyrights on Dart files. (This was a mechanical commit.)

* Fix weird license headers on Dart files that deviate from our conventions; relicense Shrine.

Some were already marked "The Flutter Authors", not clear why. Their
dates have been normalized. Some were missing the blank line after the
license. Some were randomly different in trivial ways for no apparent
reason (e.g. missing the trailing period).

* Clean up the copyrights in non-Dart files. (Manual edits.)

Also, make sure templates don't have copyrights.

* Fix some more ORGANIZATIONNAMEs
2019-11-27 15:04:02 -08:00

489 lines
14 KiB
Dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'about.dart';
import 'scales.dart';
class GalleryOptions {
GalleryOptions({
this.themeMode,
this.textScaleFactor,
this.textDirection = TextDirection.ltr,
this.timeDilation = 1.0,
this.platform,
this.showOffscreenLayersCheckerboard = false,
this.showRasterCacheImagesCheckerboard = false,
this.showPerformanceOverlay = false,
});
final ThemeMode themeMode;
final GalleryTextScaleValue textScaleFactor;
final TextDirection textDirection;
final double timeDilation;
final TargetPlatform platform;
final bool showPerformanceOverlay;
final bool showRasterCacheImagesCheckerboard;
final bool showOffscreenLayersCheckerboard;
GalleryOptions copyWith({
ThemeMode themeMode,
GalleryTextScaleValue textScaleFactor,
TextDirection textDirection,
double timeDilation,
TargetPlatform platform,
bool showPerformanceOverlay,
bool showRasterCacheImagesCheckerboard,
bool showOffscreenLayersCheckerboard,
}) {
return GalleryOptions(
themeMode: themeMode ?? this.themeMode,
textScaleFactor: textScaleFactor ?? this.textScaleFactor,
textDirection: textDirection ?? this.textDirection,
timeDilation: timeDilation ?? this.timeDilation,
platform: platform ?? this.platform,
showPerformanceOverlay: showPerformanceOverlay ?? this.showPerformanceOverlay,
showOffscreenLayersCheckerboard: showOffscreenLayersCheckerboard ?? this.showOffscreenLayersCheckerboard,
showRasterCacheImagesCheckerboard: showRasterCacheImagesCheckerboard ?? this.showRasterCacheImagesCheckerboard,
);
}
@override
bool operator ==(dynamic other) {
if (runtimeType != other.runtimeType)
return false;
final GalleryOptions typedOther = other;
return themeMode == typedOther.themeMode
&& textScaleFactor == typedOther.textScaleFactor
&& textDirection == typedOther.textDirection
&& platform == typedOther.platform
&& showPerformanceOverlay == typedOther.showPerformanceOverlay
&& showRasterCacheImagesCheckerboard == typedOther.showRasterCacheImagesCheckerboard
&& showOffscreenLayersCheckerboard == typedOther.showRasterCacheImagesCheckerboard;
}
@override
int get hashCode => hashValues(
themeMode,
textScaleFactor,
textDirection,
timeDilation,
platform,
showPerformanceOverlay,
showRasterCacheImagesCheckerboard,
showOffscreenLayersCheckerboard,
);
@override
String toString() {
return '$runtimeType($themeMode)';
}
}
const double _kItemHeight = 48.0;
const EdgeInsetsDirectional _kItemPadding = EdgeInsetsDirectional.only(start: 56.0);
class _OptionsItem extends StatelessWidget {
const _OptionsItem({ Key key, this.child }) : super(key: key);
final Widget child;
@override
Widget build(BuildContext context) {
final double textScaleFactor = MediaQuery.textScaleFactorOf(context);
return MergeSemantics(
child: Container(
constraints: BoxConstraints(minHeight: _kItemHeight * textScaleFactor),
padding: _kItemPadding,
alignment: AlignmentDirectional.centerStart,
child: DefaultTextStyle(
style: DefaultTextStyle.of(context).style,
maxLines: 2,
overflow: TextOverflow.fade,
child: IconTheme(
data: Theme.of(context).primaryIconTheme,
child: child,
),
),
),
);
}
}
class _BooleanItem extends StatelessWidget {
const _BooleanItem(this.title, this.value, this.onChanged, { this.switchKey });
final String title;
final bool value;
final ValueChanged<bool> onChanged;
// [switchKey] is used for accessing the switch from driver tests.
final Key switchKey;
@override
Widget build(BuildContext context) {
final bool isDark = Theme.of(context).brightness == Brightness.dark;
return _OptionsItem(
child: Row(
children: <Widget>[
Expanded(child: Text(title)),
Switch(
key: switchKey,
value: value,
onChanged: onChanged,
activeColor: const Color(0xFF39CEFD),
activeTrackColor: isDark ? Colors.white30 : Colors.black26,
),
],
),
);
}
}
class _ActionItem extends StatelessWidget {
const _ActionItem(this.text, this.onTap);
final String text;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return _OptionsItem(
child: _FlatButton(
onPressed: onTap,
child: Text(text),
),
);
}
}
class _FlatButton extends StatelessWidget {
const _FlatButton({ Key key, this.onPressed, this.child }) : super(key: key);
final VoidCallback onPressed;
final Widget child;
@override
Widget build(BuildContext context) {
return FlatButton(
padding: EdgeInsets.zero,
onPressed: onPressed,
child: DefaultTextStyle(
style: Theme.of(context).primaryTextTheme.subhead,
child: child,
),
);
}
}
class _Heading extends StatelessWidget {
const _Heading(this.text);
final String text;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return _OptionsItem(
child: DefaultTextStyle(
style: theme.textTheme.body1.copyWith(
fontFamily: 'GoogleSans',
color: theme.accentColor,
),
child: Semantics(
child: Text(text),
header: true,
),
),
);
}
}
class _ThemeModeItem extends StatelessWidget {
const _ThemeModeItem(this.options, this.onOptionsChanged);
final GalleryOptions options;
final ValueChanged<GalleryOptions> onOptionsChanged;
static final Map<ThemeMode, String> modeLabels = <ThemeMode, String>{
ThemeMode.system: 'System Default',
ThemeMode.light: 'Light',
ThemeMode.dark: 'Dark',
};
@override
Widget build(BuildContext context) {
return _OptionsItem(
child: Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Theme'),
Text(
'${modeLabels[options.themeMode]}',
style: Theme.of(context).primaryTextTheme.body1,
),
],
),
),
PopupMenuButton<ThemeMode>(
padding: const EdgeInsetsDirectional.only(end: 16.0),
icon: const Icon(Icons.arrow_drop_down),
initialValue: options.themeMode,
itemBuilder: (BuildContext context) {
return ThemeMode.values.map<PopupMenuItem<ThemeMode>>((ThemeMode mode) {
return PopupMenuItem<ThemeMode>(
value: mode,
child: Text(modeLabels[mode]),
);
}).toList();
},
onSelected: (ThemeMode mode) {
onOptionsChanged(
options.copyWith(themeMode: mode),
);
},
),
],
),
);
}
}
class _TextScaleFactorItem extends StatelessWidget {
const _TextScaleFactorItem(this.options, this.onOptionsChanged);
final GalleryOptions options;
final ValueChanged<GalleryOptions> onOptionsChanged;
@override
Widget build(BuildContext context) {
return _OptionsItem(
child: Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Text size'),
Text(
'${options.textScaleFactor.label}',
style: Theme.of(context).primaryTextTheme.body1,
),
],
),
),
PopupMenuButton<GalleryTextScaleValue>(
padding: const EdgeInsetsDirectional.only(end: 16.0),
icon: const Icon(Icons.arrow_drop_down),
itemBuilder: (BuildContext context) {
return kAllGalleryTextScaleValues.map<PopupMenuItem<GalleryTextScaleValue>>((GalleryTextScaleValue scaleValue) {
return PopupMenuItem<GalleryTextScaleValue>(
value: scaleValue,
child: Text(scaleValue.label),
);
}).toList();
},
onSelected: (GalleryTextScaleValue scaleValue) {
onOptionsChanged(
options.copyWith(textScaleFactor: scaleValue),
);
},
),
],
),
);
}
}
class _TextDirectionItem extends StatelessWidget {
const _TextDirectionItem(this.options, this.onOptionsChanged);
final GalleryOptions options;
final ValueChanged<GalleryOptions> onOptionsChanged;
@override
Widget build(BuildContext context) {
return _BooleanItem(
'Force RTL',
options.textDirection == TextDirection.rtl,
(bool value) {
onOptionsChanged(
options.copyWith(
textDirection: value ? TextDirection.rtl : TextDirection.ltr,
),
);
},
switchKey: const Key('text_direction'),
);
}
}
class _TimeDilationItem extends StatelessWidget {
const _TimeDilationItem(this.options, this.onOptionsChanged);
final GalleryOptions options;
final ValueChanged<GalleryOptions> onOptionsChanged;
@override
Widget build(BuildContext context) {
return _BooleanItem(
'Slow motion',
options.timeDilation != 1.0,
(bool value) {
onOptionsChanged(
options.copyWith(
timeDilation: value ? 20.0 : 1.0,
),
);
},
switchKey: const Key('slow_motion'),
);
}
}
class _PlatformItem extends StatelessWidget {
const _PlatformItem(this.options, this.onOptionsChanged);
final GalleryOptions options;
final ValueChanged<GalleryOptions> onOptionsChanged;
String _platformLabel(TargetPlatform platform) {
switch(platform) {
case TargetPlatform.android:
return 'Mountain View';
case TargetPlatform.fuchsia:
return 'Fuchsia';
case TargetPlatform.iOS:
return 'Cupertino';
case TargetPlatform.macOS:
return 'Material Desktop (macOS)';
}
assert(false);
return null;
}
@override
Widget build(BuildContext context) {
return _OptionsItem(
child: Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Platform mechanics'),
Text(
'${_platformLabel(options.platform)}',
style: Theme.of(context).primaryTextTheme.body1,
),
],
),
),
PopupMenuButton<TargetPlatform>(
padding: const EdgeInsetsDirectional.only(end: 16.0),
icon: const Icon(Icons.arrow_drop_down),
itemBuilder: (BuildContext context) {
return TargetPlatform.values.map((TargetPlatform platform) {
return PopupMenuItem<TargetPlatform>(
value: platform,
child: Text(_platformLabel(platform)),
);
}).toList();
},
onSelected: (TargetPlatform platform) {
onOptionsChanged(
options.copyWith(platform: platform),
);
},
),
],
),
);
}
}
class GalleryOptionsPage extends StatelessWidget {
const GalleryOptionsPage({
Key key,
this.options,
this.onOptionsChanged,
this.onSendFeedback,
}) : super(key: key);
final GalleryOptions options;
final ValueChanged<GalleryOptions> onOptionsChanged;
final VoidCallback onSendFeedback;
List<Widget> _enabledDiagnosticItems() {
// Boolean showFoo options with a value of null: don't display
// the showFoo option at all.
if (options.showOffscreenLayersCheckerboard == null &&
options.showRasterCacheImagesCheckerboard == null &&
options.showPerformanceOverlay == null)
return const <Widget>[];
return <Widget>[
const Divider(),
const _Heading('Diagnostics'),
if (options.showOffscreenLayersCheckerboard != null)
_BooleanItem(
'Highlight offscreen layers',
options.showOffscreenLayersCheckerboard,
(bool value) {
onOptionsChanged(options.copyWith(showOffscreenLayersCheckerboard: value));
},
),
if (options.showRasterCacheImagesCheckerboard != null)
_BooleanItem(
'Highlight raster cache images',
options.showRasterCacheImagesCheckerboard,
(bool value) {
onOptionsChanged(options.copyWith(showRasterCacheImagesCheckerboard: value));
},
),
if (options.showPerformanceOverlay != null)
_BooleanItem(
'Show performance overlay',
options.showPerformanceOverlay,
(bool value) {
onOptionsChanged(options.copyWith(showPerformanceOverlay: value));
},
),
];
}
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return DefaultTextStyle(
style: theme.primaryTextTheme.subhead,
child: ListView(
padding: const EdgeInsets.only(bottom: 124.0),
children: <Widget>[
const _Heading('Display'),
_ThemeModeItem(options, onOptionsChanged),
_TextScaleFactorItem(options, onOptionsChanged),
_TextDirectionItem(options, onOptionsChanged),
_TimeDilationItem(options, onOptionsChanged),
const Divider(),
const _Heading('Platform mechanics'),
_PlatformItem(options, onOptionsChanged),
..._enabledDiagnosticItems(),
const Divider(),
const _Heading('Flutter gallery'),
_ActionItem('About Flutter Gallery', () {
showGalleryAboutDialog(context);
}),
_ActionItem('Send feedback', onSendFeedback),
],
),
);
}
}