blob: 4dfe73269f5f79348e1c3c31c3185e969d8ad82e [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show defaultTargetPlatform, required;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'theme.dart';
class LinkTextSpan extends TextSpan {
// Beware!
//
// This class is only safe because the TapGestureRecognizer is not
// given a deadline and therefore never allocates any resources.
//
// In any other situation -- setting a deadline, using any of the less trivial
// recognizers, etc -- you would have to manage the gesture recognizer's
// lifetime and call dispose() when the TextSpan was no longer being rendered.
//
// Since TextSpan itself is @immutable, this means that you would have to
// manage the recognizer from outside the TextSpan, e.g. in the State of a
// stateful widget that then hands the recognizer to the TextSpan.
LinkTextSpan({ TextStyle style, String url, String text }) : super(
style: style,
text: text ?? url,
recognizer: new TapGestureRecognizer()..onTap = () {
launch(url, forceSafariVC: false);
}
);
}
class GalleryDrawerHeader extends StatefulWidget {
const GalleryDrawerHeader({ Key key, this.light }) : super(key: key);
final bool light;
@override
_GalleryDrawerHeaderState createState() => new _GalleryDrawerHeaderState();
}
class _GalleryDrawerHeaderState extends State<GalleryDrawerHeader> {
bool _logoHasName = true;
bool _logoHorizontal = true;
MaterialColor _logoColor = Colors.blue;
@override
Widget build(BuildContext context) {
final double systemTopPadding = MediaQuery.of(context).padding.top;
return new Semantics(
label: 'Flutter',
child: new DrawerHeader(
decoration: new FlutterLogoDecoration(
margin: new EdgeInsets.fromLTRB(12.0, 12.0 + systemTopPadding, 12.0, 12.0),
style: _logoHasName ? _logoHorizontal ? FlutterLogoStyle.horizontal
: FlutterLogoStyle.stacked
: FlutterLogoStyle.markOnly,
lightColor: _logoColor.shade400,
darkColor: _logoColor.shade900,
textColor: widget.light ? const Color(0xFF616161) : const Color(0xFF9E9E9E),
),
duration: const Duration(milliseconds: 750),
child: new GestureDetector(
onLongPress: () {
setState(() {
_logoHorizontal = !_logoHorizontal;
if (!_logoHasName)
_logoHasName = true;
});
},
onTap: () {
setState(() {
_logoHasName = !_logoHasName;
});
},
onDoubleTap: () {
setState(() {
final List<MaterialColor> options = <MaterialColor>[];
if (_logoColor != Colors.blue)
options.addAll(<MaterialColor>[Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue]);
if (_logoColor != Colors.amber)
options.addAll(<MaterialColor>[Colors.amber, Colors.amber, Colors.amber]);
if (_logoColor != Colors.red)
options.addAll(<MaterialColor>[Colors.red, Colors.red, Colors.red]);
if (_logoColor != Colors.indigo)
options.addAll(<MaterialColor>[Colors.indigo, Colors.indigo, Colors.indigo]);
if (_logoColor != Colors.pink)
options.addAll(<MaterialColor>[Colors.pink]);
if (_logoColor != Colors.purple)
options.addAll(<MaterialColor>[Colors.purple]);
if (_logoColor != Colors.cyan)
options.addAll(<MaterialColor>[Colors.cyan]);
_logoColor = options[new math.Random().nextInt(options.length)];
});
}
),
),
);
}
}
class GalleryDrawer extends StatelessWidget {
const GalleryDrawer({
Key key,
this.galleryTheme,
@required this.onThemeChanged,
this.timeDilation,
@required this.onTimeDilationChanged,
this.textScaleFactor,
this.onTextScaleFactorChanged,
this.showPerformanceOverlay,
this.onShowPerformanceOverlayChanged,
this.checkerboardRasterCacheImages,
this.onCheckerboardRasterCacheImagesChanged,
this.checkerboardOffscreenLayers,
this.onCheckerboardOffscreenLayersChanged,
this.onPlatformChanged,
this.overrideDirection: TextDirection.ltr,
this.onOverrideDirectionChanged,
this.onSendFeedback,
}) : assert(onThemeChanged != null),
assert(onTimeDilationChanged != null),
super(key: key);
final GalleryTheme galleryTheme;
final ValueChanged<GalleryTheme> onThemeChanged;
final double timeDilation;
final ValueChanged<double> onTimeDilationChanged;
final double textScaleFactor;
final ValueChanged<double> onTextScaleFactorChanged;
final bool showPerformanceOverlay;
final ValueChanged<bool> onShowPerformanceOverlayChanged;
final bool checkerboardRasterCacheImages;
final ValueChanged<bool> onCheckerboardRasterCacheImagesChanged;
final bool checkerboardOffscreenLayers;
final ValueChanged<bool> onCheckerboardOffscreenLayersChanged;
final ValueChanged<TargetPlatform> onPlatformChanged;
final TextDirection overrideDirection;
final ValueChanged<TextDirection> onOverrideDirectionChanged;
final VoidCallback onSendFeedback;
@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
final TextStyle aboutTextStyle = themeData.textTheme.body2;
final TextStyle linkStyle = themeData.textTheme.body2.copyWith(color: themeData.accentColor);
final List<Widget> themeItems = kAllGalleryThemes.map<Widget>((GalleryTheme theme) {
return new RadioListTile<GalleryTheme>(
title: new Text(theme.name),
secondary: new Icon(theme.icon),
value: theme,
groupValue: galleryTheme,
onChanged: onThemeChanged,
selected: galleryTheme == theme,
);
}).toList();
final Widget mountainViewItem = new RadioListTile<TargetPlatform>(
// on iOS, we don't want to show an Android phone icon
secondary: new Icon(defaultTargetPlatform == TargetPlatform.iOS ? Icons.star : Icons.phone_android),
title: new Text(defaultTargetPlatform == TargetPlatform.iOS ? 'Mountain View' : 'Android'),
value: TargetPlatform.android,
groupValue: Theme.of(context).platform,
onChanged: onPlatformChanged,
selected: Theme.of(context).platform == TargetPlatform.android,
);
final Widget cupertinoItem = new RadioListTile<TargetPlatform>(
// on iOS, we don't want to show the iPhone icon
secondary: new Icon(defaultTargetPlatform == TargetPlatform.iOS ? Icons.star_border : Icons.phone_iphone),
title: new Text(defaultTargetPlatform == TargetPlatform.iOS ? 'Cupertino' : 'iOS'),
value: TargetPlatform.iOS,
groupValue: Theme.of(context).platform,
onChanged: onPlatformChanged,
selected: Theme.of(context).platform == TargetPlatform.iOS,
);
final List<Widget> textSizeItems = <Widget>[];
final Map<double, String> textSizes = <double, String>{
null: 'System Default',
0.8: 'Small',
1.0: 'Normal',
1.3: 'Large',
2.0: 'Huge',
};
for (double size in textSizes.keys) {
textSizeItems.add(new RadioListTile<double>(
secondary: const Icon(Icons.text_fields),
title: new Text(textSizes[size]),
value: size,
groupValue: textScaleFactor,
onChanged: onTextScaleFactorChanged,
selected: textScaleFactor == size,
));
}
final Widget animateSlowlyItem = new CheckboxListTile(
title: const Text('Animate Slowly'),
value: timeDilation != 1.0,
onChanged: (bool value) {
onTimeDilationChanged(value ? 20.0 : 1.0);
},
secondary: const Icon(Icons.hourglass_empty),
selected: timeDilation != 1.0,
);
final Widget overrideDirectionItem = new CheckboxListTile(
title: const Text('Force RTL'),
value: overrideDirection == TextDirection.rtl,
onChanged: (bool value) {
onOverrideDirectionChanged(value ? TextDirection.rtl : TextDirection.ltr);
},
secondary: const Icon(Icons.format_textdirection_r_to_l),
selected: overrideDirection == TextDirection.rtl,
);
final Widget sendFeedbackItem = new ListTile(
leading: const Icon(Icons.report),
title: const Text('Send feedback'),
onTap: onSendFeedback ?? () {
launch('https://github.com/flutter/flutter/issues/new');
},
);
final Widget aboutItem = new AboutListTile(
icon: const FlutterLogo(),
applicationVersion: 'April 2018 Preview',
applicationIcon: const FlutterLogo(),
applicationLegalese: '© 2017 The Chromium Authors',
aboutBoxChildren: <Widget>[
new Padding(
padding: const EdgeInsets.only(top: 24.0),
child: new RichText(
text: new TextSpan(
children: <TextSpan>[
new TextSpan(
style: aboutTextStyle,
text: 'Flutter is an early-stage, open-source project to help developers '
'build high-performance, high-fidelity, mobile apps for '
'${defaultTargetPlatform == TargetPlatform.iOS ? 'multiple platforms' : 'iOS and Android'} '
'from a single codebase. This gallery is a preview of '
"Flutter's many widgets, behaviors, animations, layouts, "
'and more. Learn more about Flutter at '
),
new LinkTextSpan(
style: linkStyle,
url: 'https://flutter.io'
),
new TextSpan(
style: aboutTextStyle,
text: '.\n\nTo see the source code for this app, please visit the '
),
new LinkTextSpan(
style: linkStyle,
url: 'https://goo.gl/iv1p4G',
text: 'flutter github repo'
),
new TextSpan(
style: aboutTextStyle,
text: '.'
)
]
)
)
)
]
);
final List<Widget> allDrawerItems = <Widget>[
new GalleryDrawerHeader(
light: galleryTheme.theme.brightness == Brightness.light,
),
]
..addAll(themeItems)
..addAll(<Widget>[
const Divider(),
mountainViewItem,
cupertinoItem,
const Divider(),
])
..addAll(textSizeItems)
..addAll(<Widget>[
overrideDirectionItem,
const Divider(),
animateSlowlyItem,
const Divider(),
]);
bool addedOptionalItem = false;
if (onCheckerboardOffscreenLayersChanged != null) {
allDrawerItems.add(new CheckboxListTile(
title: const Text('Checkerboard Offscreen Layers'),
value: checkerboardOffscreenLayers,
onChanged: onCheckerboardOffscreenLayersChanged,
secondary: const Icon(Icons.assessment),
selected: checkerboardOffscreenLayers,
));
addedOptionalItem = true;
}
if (onCheckerboardRasterCacheImagesChanged != null) {
allDrawerItems.add(new CheckboxListTile(
title: const Text('Checkerboard Raster Cache Images'),
value: checkerboardRasterCacheImages,
onChanged: onCheckerboardRasterCacheImagesChanged,
secondary: const Icon(Icons.assessment),
selected: checkerboardRasterCacheImages,
));
addedOptionalItem = true;
}
if (onShowPerformanceOverlayChanged != null) {
allDrawerItems.add(new CheckboxListTile(
title: const Text('Performance Overlay'),
value: showPerformanceOverlay,
onChanged: onShowPerformanceOverlayChanged,
secondary: const Icon(Icons.assessment),
selected: showPerformanceOverlay,
));
addedOptionalItem = true;
}
if (addedOptionalItem)
allDrawerItems.add(const Divider());
allDrawerItems.addAll(<Widget>[
sendFeedbackItem,
aboutItem,
]);
return new Drawer(child: new ListView(primary: false, children: allDrawerItems));
}
}