
This auto-formats all *.dart files in the repository outside of the `engine` subdirectory and enforces that these files stay formatted with a presubmit check. **Reviewers:** Please carefully review all the commits except for the one titled "formatted". The "formatted" commit was auto-generated by running `dev/tools/format.sh -a -f`. The other commits were hand-crafted to prepare the repo for the formatting change. I recommend reviewing the commits one-by-one via the "Commits" tab and avoiding Github's "Files changed" tab as it will likely slow down your browser because of the size of this PR. --------- Co-authored-by: Kate Lovett <katelovett@google.com> Co-authored-by: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com>
494 lines
15 KiB
Dart
494 lines
15 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';
|
|
|
|
@immutable
|
|
class GalleryOptions {
|
|
const GalleryOptions({
|
|
this.themeMode,
|
|
this.textScaleFactor,
|
|
this.visualDensity,
|
|
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 GalleryVisualDensityValue? visualDensity;
|
|
final TextDirection textDirection;
|
|
final double timeDilation;
|
|
final TargetPlatform? platform;
|
|
final bool showPerformanceOverlay;
|
|
final bool showRasterCacheImagesCheckerboard;
|
|
final bool showOffscreenLayersCheckerboard;
|
|
|
|
GalleryOptions copyWith({
|
|
ThemeMode? themeMode,
|
|
GalleryTextScaleValue? textScaleFactor,
|
|
GalleryVisualDensityValue? visualDensity,
|
|
TextDirection? textDirection,
|
|
double? timeDilation,
|
|
TargetPlatform? platform,
|
|
bool? showPerformanceOverlay,
|
|
bool? showRasterCacheImagesCheckerboard,
|
|
bool? showOffscreenLayersCheckerboard,
|
|
}) {
|
|
return GalleryOptions(
|
|
themeMode: themeMode ?? this.themeMode,
|
|
textScaleFactor: textScaleFactor ?? this.textScaleFactor,
|
|
visualDensity: visualDensity ?? this.visualDensity,
|
|
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 ==(Object other) {
|
|
if (other.runtimeType != runtimeType) {
|
|
return false;
|
|
}
|
|
return other is GalleryOptions &&
|
|
other.themeMode == themeMode &&
|
|
other.textScaleFactor == textScaleFactor &&
|
|
other.visualDensity == visualDensity &&
|
|
other.textDirection == textDirection &&
|
|
other.platform == platform &&
|
|
other.showPerformanceOverlay == showPerformanceOverlay &&
|
|
other.showRasterCacheImagesCheckerboard == showRasterCacheImagesCheckerboard &&
|
|
other.showOffscreenLayersCheckerboard == showRasterCacheImagesCheckerboard;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => Object.hash(
|
|
themeMode,
|
|
textScaleFactor,
|
|
visualDensity,
|
|
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({this.child});
|
|
|
|
final Widget? child;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final double textScaleFactor = MediaQuery.textScalerOf(context).textScaleFactor;
|
|
|
|
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: _TextButton(onPressed: onTap, child: Text(text)));
|
|
}
|
|
}
|
|
|
|
class _TextButton extends StatelessWidget {
|
|
const _TextButton({this.onPressed, this.child});
|
|
|
|
final VoidCallback? onPressed;
|
|
final Widget? child;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final ThemeData theme = Theme.of(context);
|
|
return TextButton(
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: theme.colorScheme.onPrimary,
|
|
textStyle: theme.textTheme.titleMedium,
|
|
padding: EdgeInsets.zero,
|
|
),
|
|
onPressed: onPressed,
|
|
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.titleLarge!.copyWith(
|
|
fontFamily: 'GoogleSans',
|
|
color: theme.colorScheme.onPrimary,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
child: Semantics(header: true, child: Text(text)),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
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.bodyMedium,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
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.bodyMedium,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
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 _VisualDensityItem extends StatelessWidget {
|
|
const _VisualDensityItem(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('Visual density'),
|
|
Text(
|
|
options!.visualDensity!.label,
|
|
style: Theme.of(context).primaryTextTheme.bodyMedium,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
PopupMenuButton<GalleryVisualDensityValue>(
|
|
padding: const EdgeInsetsDirectional.only(end: 16.0),
|
|
icon: const Icon(Icons.arrow_drop_down),
|
|
itemBuilder: (BuildContext context) {
|
|
return kAllGalleryVisualDensityValues.map<PopupMenuItem<GalleryVisualDensityValue>>((
|
|
GalleryVisualDensityValue densityValue,
|
|
) {
|
|
return PopupMenuItem<GalleryVisualDensityValue>(
|
|
value: densityValue,
|
|
child: Text(densityValue.label),
|
|
);
|
|
}).toList();
|
|
},
|
|
onSelected: (GalleryVisualDensityValue densityValue) {
|
|
onOptionsChanged!(options!.copyWith(visualDensity: densityValue));
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
return switch (platform) {
|
|
TargetPlatform.android => 'Mountain View',
|
|
TargetPlatform.fuchsia => 'Fuchsia',
|
|
TargetPlatform.iOS => 'Cupertino',
|
|
TargetPlatform.linux => 'Material Desktop (linux)',
|
|
TargetPlatform.macOS => 'Material Desktop (macOS)',
|
|
TargetPlatform.windows => 'Material Desktop (Windows)',
|
|
};
|
|
}
|
|
|
|
@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.bodyMedium,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
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({super.key, this.options, this.onOptionsChanged, this.onSendFeedback});
|
|
|
|
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 == null) {
|
|
return const <Widget>[];
|
|
}
|
|
|
|
return <Widget>[
|
|
const Divider(),
|
|
const _Heading('Diagnostics'),
|
|
_BooleanItem('Highlight offscreen layers', options!.showOffscreenLayersCheckerboard, (
|
|
bool value,
|
|
) {
|
|
onOptionsChanged!(options!.copyWith(showOffscreenLayersCheckerboard: value));
|
|
}),
|
|
_BooleanItem('Highlight raster cache images', options!.showRasterCacheImagesCheckerboard, (
|
|
bool value,
|
|
) {
|
|
onOptionsChanged!(options!.copyWith(showRasterCacheImagesCheckerboard: value));
|
|
}),
|
|
_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.titleMedium!,
|
|
child: ListView(
|
|
padding: const EdgeInsets.only(bottom: 124.0),
|
|
children: <Widget>[
|
|
const _Heading('Display'),
|
|
_ThemeModeItem(options, onOptionsChanged),
|
|
_TextScaleFactorItem(options, onOptionsChanged),
|
|
_VisualDensityItem(options, onOptionsChanged),
|
|
_TextDirectionItem(options, onOptionsChanged),
|
|
_TimeDilationItem(options, onOptionsChanged),
|
|
const Divider(),
|
|
const ExcludeSemantics(child: _Heading('Platform mechanics')),
|
|
_PlatformItem(options, onOptionsChanged),
|
|
..._enabledDiagnosticItems(),
|
|
const Divider(),
|
|
const _Heading('Flutter gallery'),
|
|
_ActionItem('About Flutter Gallery', () {
|
|
showGalleryAboutDialog(context);
|
|
}),
|
|
_ActionItem('Send feedback', onSendFeedback),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|