app.dart 8.74 KB
Newer Older
1 2 3 4
// Copyright 2015 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
import 'package:flutter/rendering.dart';
6
import 'package:flutter/foundation.dart';
7
import 'package:flutter/widgets.dart';
8

9
import 'arc.dart';
10
import 'colors.dart';
11
import 'page.dart';
12
import 'theme.dart';
13

14 15
export 'dart:ui' show Locale;

16 17 18 19 20
const TextStyle _errorTextStyle = const TextStyle(
  color: const Color(0xD0FF0000),
  fontFamily: 'monospace',
  fontSize: 48.0,
  fontWeight: FontWeight.w900,
21
  decoration: TextDecoration.underline,
22
  decorationColor: const Color(0xFFFFFF00),
23 24 25
  decorationStyle: TextDecorationStyle.double
);

26 27 28
/// An application that uses material design.
///
/// A convenience widget that wraps a number of widgets that are commonly
29
/// required for material design applications. It builds upon a
30 31
/// [WidgetsApp] by adding material-design specific functionality, such as
/// [AnimatedTheme] and [GridPaper]. This widget also configures the top-level
32
/// [Navigator]'s observer to perform [Hero] animations.
33 34 35
///
/// See also:
///
36 37 38 39
///  * [Scaffold], which provides standard app elements like an [AppBar] and a [Drawer].
///  * [Navigator], which is used to manage the app's stack of pages.
///  * [MaterialPageRoute], which defines an app page that transitions in a material-specific way.
///  * [WidgetsApp], which defines the basic app elements but does not depend on the material library.
40 41
class MaterialApp extends StatefulWidget {

42 43 44 45 46 47
  /// Creates a MaterialApp.
  ///
  /// At least one of [home], [routes], or [onGenerateRoute] must be
  /// given. If only [routes] is given, it must include an entry for
  /// the [Navigator.defaultRouteName] (`'/'`).
  ///
48
  /// This class creates an instance of [WidgetsApp].
Adam Barth's avatar
Adam Barth committed
49
  MaterialApp({
50
    Key key,
51
    this.title,
52
    this.color,
53 54 55
    this.theme,
    this.home,
    this.routes: const <String, WidgetBuilder>{},
56
    this.initialRoute,
57 58
    this.onGenerateRoute,
    this.onLocaleChanged,
59
    this.navigatorObservers: const <NavigatorObserver>[],
60
    this.debugShowMaterialGrid: false,
61
    this.showPerformanceOverlay: false,
62
    this.checkerboardRasterCacheImages: false,
63 64 65
    this.showSemanticsDebugger: false,
    this.debugShowCheckedModeBanner: true
  }) : super(key: key) {
Ian Hickson's avatar
Ian Hickson committed
66
    assert(debugShowMaterialGrid != null);
67 68 69
    assert(routes != null);
    assert(!routes.containsKey(Navigator.defaultRouteName) || (home == null));
    assert(routes.containsKey(Navigator.defaultRouteName) || (home != null) || (onGenerateRoute != null));
70
 }
71

72 73
  /// A one-line description of this app for use in the window manager.
  final String title;
74

75
  /// The colors to use for the application's widgets.
76
  final ThemeData theme;
77

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
  /// The widget for the default route of the app
  /// ([Navigator.defaultRouteName], which is `'/'`).
  ///
  /// This is the page that is displayed first when the application is
  /// started normally.
  ///
  /// To be able to directly call [Theme.of], [MediaQuery.of],
  /// [LocaleQuery.of], etc, in the code sets the [home] argument in
  /// the constructor, you can use a [Builder] widget to get a
  /// [BuildContext].
  ///
  /// If this is not specified, then either the route with name `'/'`
  /// must be given in [routes], or the [onGenerateRoute] callback
  /// must be able to build a widget for that route.
  final Widget home;

94 95 96 97 98 99 100
  /// The primary color to use for the application in the operating system
  /// interface.
  ///
  /// For example, on Android this is the color used for the application in the
  /// application switcher.
  final Color color;

101 102 103 104 105 106
  /// The application's top-level routing table.
  ///
  /// When a named route is pushed with [Navigator.pushNamed], the route name is
  /// looked up in this map. If the name is present, the associated
  /// [WidgetBuilder] is used to construct a [MaterialPageRoute] that performs
  /// an appropriate transition, including [Hero] animations, to the new route.
107 108 109 110 111 112 113
  ///
  /// If the app only has one page, then you can specify it using [home] instead.
  ///
  /// If [home] is specified, then it is an error to provide a route
  /// in this map for the [Navigator.defaultRouteName] route (`'/'`).
  ///
  /// If a route is requested that is not specified in this table (or
114
  /// by [home]), then the [onGenerateRoute] callback is called to
115
  /// build the page instead.
Ian Hickson's avatar
Ian Hickson committed
116 117
  final Map<String, WidgetBuilder> routes;

118 119 120 121 122
  /// The name of the first route to show.
  ///
  /// Defaults to [Window.defaultRouteName].
  final String initialRoute;

123 124 125 126
  /// The route generator callback used when the app is navigated to a
  /// named route.
  final RouteFactory onGenerateRoute;

127
  /// Callback that is called when the operating system changes the
128 129 130 131 132 133 134
  /// current locale.
  final LocaleChangedCallback onLocaleChanged;

  /// Turns on a performance overlay.
  /// https://flutter.io/debugging/#performanceoverlay
  final bool showPerformanceOverlay;

135 136 137
  /// Turns on checkerboarding of raster cache images.
  final bool checkerboardRasterCacheImages;

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
  /// Turns on an overlay that shows the accessibility information
  /// reported by the framework.
  final bool showSemanticsDebugger;

  /// Turns on a little "SLOW MODE" banner in checked mode to indicate
  /// that the app is in checked mode. This is on by default (in
  /// checked mode), to turn it off, set the constructor argument to
  /// false. In release mode this has no effect.
  ///
  /// To get this banner in your application if you're not using
  /// WidgetsApp, include a [CheckedModeBanner] widget in your app.
  ///
  /// This banner is intended to deter people from complaining that your
  /// app is slow when it's in checked mode. In checked mode, Flutter
  /// enables a large number of expensive diagnostics to aid in
  /// development, and so performance in checked mode is not
  /// representative of what will happen in release mode.
  final bool debugShowCheckedModeBanner;

157 158 159
  /// The list of observers for the [Navigator] created for this app.
  final List<NavigatorObserver> navigatorObservers;

160 161
  /// Turns on a [GridPaper] overlay that paints a baseline grid
  /// Material apps:
162
  /// https://material.google.com/layout/metrics-keylines.html
163
  /// Only available in checked mode.
Ian Hickson's avatar
Ian Hickson committed
164
  final bool debugShowMaterialGrid;
165

166
  @override
Adam Barth's avatar
Adam Barth committed
167
  _MaterialAppState createState() => new _MaterialAppState();
168 169
}

Adam Barth's avatar
Adam Barth committed
170
class _MaterialScrollBehavior extends ScrollBehavior {
171 172 173 174 175 176 177 178 179 180 181
  @override
  TargetPlatform getPlatform(BuildContext context) {
    return Theme.of(context).platform;
  }

  @override
  Color getGlowColor(BuildContext context) {
    return Theme.of(context).accentColor;
  }
}

182
class _MaterialAppState extends State<MaterialApp> {
183 184 185 186 187 188 189 190 191 192 193
  HeroController _heroController;

  @override
  void initState() {
    super.initState();
    _heroController = new HeroController(createRectTween: _createRectTween);
  }

  RectTween _createRectTween(Rect begin, Rect end) {
    return new MaterialRectArcTween(begin: begin, end: end);
  }
194

195 196 197 198 199 200 201 202 203 204 205 206 207 208
  Route<dynamic> _onGenerateRoute(RouteSettings settings) {
    WidgetBuilder builder = config.routes[settings.name];
    if (builder == null && config.home != null && settings.name == Navigator.defaultRouteName)
      builder = (BuildContext context) => config.home;
    if (builder != null) {
      return new MaterialPageRoute<Null>(
        builder: builder,
        settings: settings
      );
    }
    if (config.onGenerateRoute != null)
      return config.onGenerateRoute(settings);
    return null;
  }
209

210
  @override
211
  Widget build(BuildContext context) {
212
    final ThemeData theme = config.theme ?? new ThemeData.fallback();
213 214
    Widget result = new AnimatedTheme(
      data: theme,
215
      isMaterialAppTheme: true,
216
      child: new WidgetsApp(
217
        key: new GlobalObjectKey(this),
218 219
        title: config.title,
        textStyle: _errorTextStyle,
220 221
        // blue[500] is the primary color of the default theme
        color: config.color ?? theme?.primaryColor ?? Colors.blue[500],
222 223 224
        navigatorObservers:
            new List<NavigatorObserver>.from(config.navigatorObservers)
              ..add(_heroController),
225
        initialRoute: config.initialRoute,
226 227 228
        onGenerateRoute: _onGenerateRoute,
        onLocaleChanged: config.onLocaleChanged,
        showPerformanceOverlay: config.showPerformanceOverlay,
229
        checkerboardRasterCacheImages: config.checkerboardRasterCacheImages,
230 231 232
        showSemanticsDebugger: config.showSemanticsDebugger,
        debugShowCheckedModeBanner: config.debugShowCheckedModeBanner
      )
233
    );
234

Ian Hickson's avatar
Ian Hickson committed
235 236 237 238 239 240 241 242 243 244 245 246
    assert(() {
      if (config.debugShowMaterialGrid) {
        result = new GridPaper(
          color: const Color(0xE0F9BBE0),
          interval: 8.0,
          divisions: 2,
          subDivisions: 1,
          child: result
        );
      }
      return true;
    });
247

Adam Barth's avatar
Adam Barth committed
248
    return new ScrollConfiguration(
249
      behavior: new _MaterialScrollBehavior(),
250 251
      child: result
    );
252
  }
253
}