drawer.dart 10.3 KB
Newer Older
1 2 3 4
// 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.

5 6
import 'dart:math' as math;

7
import 'package:flutter/foundation.dart' show defaultTargetPlatform, required;
8
import 'package:flutter/gestures.dart';
9
import 'package:flutter/material.dart';
10 11

import 'package:url_launcher/url_launcher.dart';
12 13

class LinkTextSpan extends TextSpan {
14 15 16 17 18 19 20 21 22 23 24 25 26 27

  // 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.

28 29 30 31
  LinkTextSpan({ TextStyle style, String url, String text }) : super(
    style: style,
    text: text ?? url,
    recognizer: new TapGestureRecognizer()..onTap = () {
32
      launch(url);
33 34 35
    }
  );
}
36

37
class GalleryDrawerHeader extends StatefulWidget {
38 39 40
  const GalleryDrawerHeader({ Key key, this.light }) : super(key: key);

  final bool light;
41 42 43 44 45 46 47 48

  @override
  _GalleryDrawerHeaderState createState() => new _GalleryDrawerHeaderState();
}

class _GalleryDrawerHeaderState extends State<GalleryDrawerHeader> {
  bool _logoHasName = true;
  bool _logoHorizontal = true;
49
  MaterialColor _logoColor = Colors.blue;
50 51 52 53 54 55 56 57 58 59 60

  @override
  Widget build(BuildContext context) {
    final double systemTopPadding = MediaQuery.of(context).padding.top;

    return 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,
61 62
        lightColor: _logoColor.shade400,
        darkColor: _logoColor.shade900,
63
        textColor: widget.light ? const Color(0xFF616161) : const Color(0xFF9E9E9E),
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
      ),
      duration: const Duration(milliseconds: 750),
      child: new GestureDetector(
        onLongPress: () {
          setState(() {
            _logoHorizontal = !_logoHorizontal;
            if (!_logoHasName)
              _logoHasName = true;
          });
        },
        onTap: () {
          setState(() {
            _logoHasName = !_logoHasName;
          });
        },
        onDoubleTap: () {
          setState(() {
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
            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)];
97 98 99 100 101 102 103
          });
        }
      )
    );
  }
}

104
class GalleryDrawer extends StatelessWidget {
105
  const GalleryDrawer({
106
    Key key,
Eric Seidel's avatar
Eric Seidel committed
107
    this.useLightTheme,
108
    @required this.onThemeChanged,
109
    this.timeDilation,
110
    @required this.onTimeDilationChanged,
Eric Seidel's avatar
Eric Seidel committed
111
    this.showPerformanceOverlay,
112
    this.onShowPerformanceOverlayChanged,
113 114
    this.checkerboardRasterCacheImages,
    this.onCheckerboardRasterCacheImagesChanged,
115 116
    this.checkerboardOffscreenLayers,
    this.onCheckerboardOffscreenLayersChanged,
117
    this.onPlatformChanged,
118
    this.onSendFeedback,
119 120 121
  }) : assert(onThemeChanged != null),
       assert(onTimeDilationChanged != null),
       super(key: key);
122

Eric Seidel's avatar
Eric Seidel committed
123
  final bool useLightTheme;
124 125 126 127
  final ValueChanged<bool> onThemeChanged;

  final double timeDilation;
  final ValueChanged<double> onTimeDilationChanged;
128

Eric Seidel's avatar
Eric Seidel committed
129 130 131
  final bool showPerformanceOverlay;
  final ValueChanged<bool> onShowPerformanceOverlayChanged;

132 133 134
  final bool checkerboardRasterCacheImages;
  final ValueChanged<bool> onCheckerboardRasterCacheImagesChanged;

135 136 137
  final bool checkerboardOffscreenLayers;
  final ValueChanged<bool> onCheckerboardOffscreenLayersChanged;

138 139
  final ValueChanged<TargetPlatform> onPlatformChanged;

140 141
  final VoidCallback onSendFeedback;

142
  @override
143
  Widget build(BuildContext context) {
144 145
    final ThemeData themeData = Theme.of(context);
    final TextStyle aboutTextStyle = themeData.textTheme.body2;
146
    final TextStyle linkStyle = themeData.textTheme.body2.copyWith(color: themeData.accentColor);
147

148 149
    final Widget lightThemeItem = new RadioListTile<bool>(
      secondary: const Icon(Icons.brightness_5),
150
      title: const Text('Light'),
151 152 153
      value: true,
      groupValue: useLightTheme,
      onChanged: onThemeChanged,
154 155 156
      selected: useLightTheme,
    );

157 158
    final Widget darkThemeItem = new RadioListTile<bool>(
      secondary: const Icon(Icons.brightness_7),
159
      title: const Text('Dark'),
160 161 162
      value: false,
      groupValue: useLightTheme,
      onChanged: onThemeChanged,
163
      selected: !useLightTheme,
164 165
    );

166
    final Widget mountainViewItem = new RadioListTile<TargetPlatform>(
167
      // on iOS, we don't want to show an Android phone icon
168
      secondary: new Icon(defaultTargetPlatform == TargetPlatform.iOS ? Icons.star : Icons.phone_android),
169
      title: const Text('Android'),
170 171 172
      value: TargetPlatform.android,
      groupValue: Theme.of(context).platform,
      onChanged: onPlatformChanged,
173 174 175
      selected: Theme.of(context).platform == TargetPlatform.android,
    );

176
    final Widget cupertinoItem = new RadioListTile<TargetPlatform>(
177
      // on iOS, we don't want to show the iPhone icon
178
      secondary: new Icon(defaultTargetPlatform == TargetPlatform.iOS ? Icons.star_border : Icons.phone_iphone),
179
      title: const Text('iOS'),
180 181 182
      value: TargetPlatform.iOS,
      groupValue: Theme.of(context).platform,
      onChanged: onPlatformChanged,
183 184 185
      selected: Theme.of(context).platform == TargetPlatform.iOS,
    );

186
    final Widget animateSlowlyItem = new CheckboxListTile(
187
      title: const Text('Animate Slowly'),
188 189 190
      value: timeDilation != 1.0,
      onChanged: (bool value) {
        onTimeDilationChanged(value ? 20.0 : 1.0);
191
      },
192 193
      secondary: const Icon(Icons.hourglass_empty),
      selected: timeDilation != 1.0,
194 195
    );

196
    final Widget sendFeedbackItem = new ListTile(
197 198
      leading: const Icon(Icons.report),
      title: const Text('Send feedback'),
199
      onTap: onSendFeedback ?? () {
200
        launch('https://github.com/flutter/flutter/issues/new');
201
      },
202 203
    );

204
    final Widget aboutItem = new AboutListTile(
205
      icon: const FlutterLogo(),
206
      applicationVersion: 'April 2017 Preview',
207
      applicationIcon: const FlutterLogo(),
208
      applicationLegalese: '© 2017 The Chromium Authors',
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
      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 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: "."
Eric Seidel's avatar
Eric Seidel committed
239 240 241
                )
              ]
            )
242
          )
243 244
        )
      ]
245
    );
246 247 248 249 250

    final List<Widget> allDrawerItems = <Widget>[
      new GalleryDrawerHeader(light: useLightTheme),
      lightThemeItem,
      darkThemeItem,
251
      const Divider(),
252 253
      mountainViewItem,
      cupertinoItem,
254
      const Divider(),
255
      animateSlowlyItem,
256
      // index 8, optional: Performance Overlay
257
      sendFeedbackItem,
258 259 260 261
      aboutItem
    ];

    if (onShowPerformanceOverlayChanged != null) {
262
      allDrawerItems.insert(8, new CheckboxListTile(
263
        title: const Text('Performance Overlay'),
264 265 266
        value: showPerformanceOverlay,
        onChanged: onShowPerformanceOverlayChanged,
        secondary: const Icon(Icons.assessment),
267 268 269 270
        selected: showPerformanceOverlay,
      ));
    }

271
    if (onCheckerboardRasterCacheImagesChanged != null) {
272
      allDrawerItems.insert(8, new CheckboxListTile(
273
        title: const Text('Checkerboard Raster Cache Images'),
274 275 276
        value: checkerboardRasterCacheImages,
        onChanged: onCheckerboardRasterCacheImagesChanged,
        secondary: const Icon(Icons.assessment),
277 278 279 280
        selected: checkerboardRasterCacheImages,
      ));
    }

281
    if (onCheckerboardOffscreenLayersChanged != null) {
282
      allDrawerItems.insert(8, new CheckboxListTile(
283
        title: const Text('Checkerboard Offscreen Layers'),
284 285 286
        value: checkerboardOffscreenLayers,
        onChanged: onCheckerboardOffscreenLayersChanged,
        secondary: const Icon(Icons.assessment),
287 288 289 290
        selected: checkerboardOffscreenLayers,
      ));
    }

291
    return new Drawer(child: new ListView(primary: false, children: allDrawerItems));
292 293
  }
}