app.dart 7.84 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 'dart:async';
6
import 'dart:ui' as ui show WindowPadding, window;
7 8

import 'package:flutter/rendering.dart';
9 10
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
11

12
import 'page.dart';
13
import 'theme.dart';
14

15 16
export 'dart:ui' show Locale;

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

Adam Barth's avatar
Adam Barth committed
28 29 30 31 32 33 34 35 36
AssetBundle _initDefaultBundle() {
  if (rootBundle != null)
    return rootBundle;
  const String _kAssetBase = '/packages/material_design_icons/icons/';
  return new NetworkAssetBundle(Uri.base.resolve(_kAssetBase));
}

final AssetBundle _defaultBundle = _initDefaultBundle();

37 38 39 40 41 42
class RouteArguments {
  const RouteArguments({ this.context });
  final BuildContext context;
}
typedef Widget RouteBuilder(RouteArguments args);

43
typedef Future<LocaleQueryData> LocaleChangedCallback(Locale locale);
44

Adam Barth's avatar
Adam Barth committed
45 46
class MaterialApp extends StatefulComponent {
  MaterialApp({
47 48 49
    Key key,
    this.title,
    this.theme,
50
    this.routes: const <String, RouteBuilder>{},
51
    this.onGenerateRoute,
Ian Hickson's avatar
Ian Hickson committed
52
    this.onLocaleChanged,
53
    this.debugShowMaterialGrid: false,
Hixie's avatar
Hixie committed
54
    this.showPerformanceOverlay: false,
55 56
    this.showSemanticsDebugger: false,
    this.debugShowCheckedModeBanner: true
57 58 59
  }) : super(key: key) {
    assert(routes != null);
    assert(routes.containsKey(Navigator.defaultRouteName) || onGenerateRoute != null);
Ian Hickson's avatar
Ian Hickson committed
60
    assert(debugShowMaterialGrid != null);
61
    assert(showPerformanceOverlay != null);
Hixie's avatar
Hixie committed
62
    assert(showSemanticsDebugger != null);
63
  }
64

65
  /// A one-line description of this app for use in the window manager.
66
  final String title;
67 68

  /// The colors to use for the application's widgets.
69
  final ThemeData theme;
70 71 72 73 74

  /// The default table of routes for the application. When the
  /// [Navigator] is given a named route, the name will be looked up
  /// in this table first. If the name is not available, then
  /// [onGenerateRoute] will be called instead.
75
  final Map<String, RouteBuilder> routes;
76 77 78

  /// The route generator callback used when the app is navigated to a
  /// named route but the name is not in the [routes] table.
79
  final RouteFactory onGenerateRoute;
80 81 82

  /// Callback that is invoked when the operating system changes the
  /// current locale.
83
  final LocaleChangedCallback onLocaleChanged;
84 85 86 87 88

  /// Turns on a [GridPaper] overlay that paints a baseline grid
  /// Material apps:
  /// https://www.google.com/design/spec/layout/metrics-keylines.html
  /// Only available in checked mode.
Ian Hickson's avatar
Ian Hickson committed
89
  final bool debugShowMaterialGrid;
90 91 92

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

  /// Turns on an overlay that shows the accessibility information
  /// reported by the framework.
Hixie's avatar
Hixie committed
97
  final bool showSemanticsDebugger;
98

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
  /// Turns on a "SLOW MODE" little 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
  /// MaterialApp, include a [CheckedModeBanner] widget in your app.
  ///
  /// This banner is intended to avoid people 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;

Adam Barth's avatar
Adam Barth committed
114
  _MaterialAppState createState() => new _MaterialAppState();
115 116
}

117
EdgeDims _getPadding(ui.WindowPadding padding) {
118 119 120
  return new EdgeDims.TRBL(padding.top, padding.right, padding.bottom, padding.left);
}

121
class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
122 123 124

  GlobalObjectKey _navigator;

125
  LocaleQueryData _localeData;
126

127 128
  void initState() {
    super.initState();
129
    _navigator = new GlobalObjectKey(this);
130
    didChangeLocale(ui.window.locale);
Ian Hickson's avatar
Ian Hickson committed
131
    WidgetFlutterBinding.instance.addObserver(this);
132 133 134
  }

  void dispose() {
Ian Hickson's avatar
Ian Hickson committed
135
    WidgetFlutterBinding.instance.removeObserver(this);
136 137 138
    super.dispose();
  }

139
  bool didPopRoute() {
140
    assert(mounted);
141 142
    NavigatorState navigator = _navigator.currentState;
    assert(navigator != null);
143
    bool result = false;
Hixie's avatar
Hixie committed
144
    navigator.openTransaction((NavigatorTransaction transaction) {
145
      result = transaction.pop();
Hixie's avatar
Hixie committed
146
    });
147
    return result;
148 149
  }

150
  void didChangeMetrics() {
151
    setState(() {
152 153
      // The properties of ui.window have changed. We use them in our build
      // function, so we need setState(), but we don't cache anything locally.
154 155
    });
  }
156

157
  void didChangeLocale(Locale locale) {
158 159 160 161 162 163 164 165
    if (config.onLocaleChanged != null) {
      config.onLocaleChanged(locale).then((LocaleQueryData data) {
        if (mounted)
          setState(() { _localeData = data; });
      });
    }
  }

166
  void didChangeAppLifecycleState(AppLifecycleState state) { }
167

Adam Barth's avatar
Adam Barth committed
168
  final HeroController _heroController = new HeroController();
169

170
  Route _generateRoute(RouteSettings settings) {
171 172 173 174 175 176 177 178 179 180 181 182
    RouteBuilder builder = config.routes[settings.name];
    if (builder != null) {
      return new MaterialPageRoute(
        builder: (BuildContext context) {
          return builder(new RouteArguments(context: context));
        },
        settings: settings
      );
    }
    if (config.onGenerateRoute != null)
      return config.onGenerateRoute(settings);
    return null;
183 184
  }

185
  Widget build(BuildContext context) {
186 187 188 189 190 191
    if (config.onLocaleChanged != null && _localeData == null) {
      // If the app expects a locale but we don't yet know the locale, then
      // don't build the widgets now.
      return new Container();
    }

192
    ThemeData theme = config.theme ?? new ThemeData.fallback();
Ian Hickson's avatar
Ian Hickson committed
193
    Widget result = new MediaQuery(
194 195 196
      data: new MediaQueryData(
        size: ui.window.size,
        devicePixelRatio: ui.window.devicePixelRatio,
197
        padding: _getPadding(ui.window.padding)
198
      ),
199 200
      child: new LocaleQuery(
        data: _localeData,
201
        child: new AnimatedTheme(
202
          data: theme,
203
          duration: kThemeAnimationDuration,
204 205
          child: new DefaultTextStyle(
            style: _errorTextStyle,
206
            child: new AssetVendor(
207
              bundle: _defaultBundle,
208
              devicePixelRatio: ui.window.devicePixelRatio,
209 210 211 212 213 214 215 216 217
              child: new Title(
                title: config.title,
                color: theme.primaryColor,
                child: new Navigator(
                  key: _navigator,
                  initialRoute: ui.window.defaultRouteName,
                  onGenerateRoute: _generateRoute,
                  observer: _heroController
                )
Adam Barth's avatar
Adam Barth committed
218
              )
Adam Barth's avatar
Adam Barth committed
219
            )
220 221 222 223
          )
        )
      )
    );
Ian Hickson's avatar
Ian Hickson committed
224 225 226 227 228 229 230 231 232 233 234 235
    assert(() {
      if (config.debugShowMaterialGrid) {
        result = new GridPaper(
          color: const Color(0xE0F9BBE0),
          interval: 8.0,
          divisions: 2,
          subDivisions: 1,
          child: result
        );
      }
      return true;
    });
236
    if (config.showPerformanceOverlay) {
237 238 239 240 241 242
      result = new Stack(
        children: <Widget>[
          result,
          new Positioned(bottom: 0.0, left: 0.0, right: 0.0, child: new PerformanceOverlay.allEnabled()),
        ]
      );
243
    }
Hixie's avatar
Hixie committed
244 245 246 247 248
    if (config.showSemanticsDebugger) {
      result = new SemanticsDebugger(
        child: result
      );
    }
249 250 251 252 253 254 255 256
    assert(() {
      if (config.debugShowCheckedModeBanner) {
        result = new CheckedModeBanner(
          child: result
        );
      }
      return true;
    });
Ian Hickson's avatar
Ian Hickson committed
257
    return result;
258 259
  }

260
}