material_app.dart 5.87 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 7 8
import 'dart:ui' as ui;

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 17 18 19 20

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

Adam Barth's avatar
Adam Barth committed
26 27 28 29 30 31 32 33 34
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();

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

41 42
typedef Future<LocaleQueryData> LocaleChangedCallback(ui.Locale locale);

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

  final String title;
  final ThemeData theme;
  final Map<String, RouteBuilder> routes;
65
  final RouteFactory onGenerateRoute;
66
  final LocaleChangedCallback onLocaleChanged;
Ian Hickson's avatar
Ian Hickson committed
67
  final bool debugShowMaterialGrid;
68
  final bool showPerformanceOverlay;
Hixie's avatar
Hixie committed
69
  final bool showSemanticsDebugger;
70

Adam Barth's avatar
Adam Barth committed
71
  _MaterialAppState createState() => new _MaterialAppState();
72 73
}

74
EdgeDims _getPadding(ui.WindowPadding padding) {
75 76 77
  return new EdgeDims.TRBL(padding.top, padding.right, padding.bottom, padding.left);
}

78
class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
79 80 81

  GlobalObjectKey _navigator;

82
  LocaleQueryData _localeData;
83

84 85
  void initState() {
    super.initState();
86
    _navigator = new GlobalObjectKey(this);
87
    didChangeLocale(ui.window.locale);
Ian Hickson's avatar
Ian Hickson committed
88
    WidgetFlutterBinding.instance.addObserver(this);
89 90 91
  }

  void dispose() {
Ian Hickson's avatar
Ian Hickson committed
92
    WidgetFlutterBinding.instance.removeObserver(this);
93 94 95
    super.dispose();
  }

96
  bool didPopRoute() {
97
    assert(mounted);
98 99
    NavigatorState navigator = _navigator.currentState;
    assert(navigator != null);
100
    bool result = false;
Hixie's avatar
Hixie committed
101
    navigator.openTransaction((NavigatorTransaction transaction) {
102
      result = transaction.pop();
Hixie's avatar
Hixie committed
103
    });
104
    return result;
105 106
  }

107
  void didChangeMetrics() {
108
    setState(() {
109 110
      // 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.
111 112
    });
  }
113

114 115 116 117 118 119 120 121 122
  void didChangeLocale(ui.Locale locale) {
    if (config.onLocaleChanged != null) {
      config.onLocaleChanged(locale).then((LocaleQueryData data) {
        if (mounted)
          setState(() { _localeData = data; });
      });
    }
  }

123 124
  void didChangeAppLifecycleState(ui.AppLifecycleState state) { }

Adam Barth's avatar
Adam Barth committed
125
  final HeroController _heroController = new HeroController();
126

127
  Route _generateRoute(RouteSettings settings) {
128 129 130 131 132 133 134 135 136 137 138 139
    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;
140 141
  }

142
  Widget build(BuildContext context) {
143 144 145 146 147 148
    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();
    }

149
    ThemeData theme = config.theme ?? new ThemeData.fallback();
Ian Hickson's avatar
Ian Hickson committed
150
    Widget result = new MediaQuery(
151 152 153
      data: new MediaQueryData(
        size: ui.window.size,
        devicePixelRatio: ui.window.devicePixelRatio,
154
        padding: _getPadding(ui.window.padding)
155
      ),
156 157
      child: new LocaleQuery(
        data: _localeData,
158
        child: new AnimatedTheme(
159
          data: theme,
160
          duration: kThemeAnimationDuration,
161 162
          child: new DefaultTextStyle(
            style: _errorTextStyle,
163
            child: new AssetVendor(
164
              bundle: _defaultBundle,
165
              devicePixelRatio: ui.window.devicePixelRatio,
166 167 168 169 170 171 172 173 174
              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
175
              )
Adam Barth's avatar
Adam Barth committed
176
            )
177 178 179 180
          )
        )
      )
    );
Ian Hickson's avatar
Ian Hickson committed
181 182 183 184 185 186 187 188 189 190 191 192
    assert(() {
      if (config.debugShowMaterialGrid) {
        result = new GridPaper(
          color: const Color(0xE0F9BBE0),
          interval: 8.0,
          divisions: 2,
          subDivisions: 1,
          child: result
        );
      }
      return true;
    });
193
    if (config.showPerformanceOverlay) {
194 195 196 197 198 199
      result = new Stack(
        children: <Widget>[
          result,
          new Positioned(bottom: 0.0, left: 0.0, right: 0.0, child: new PerformanceOverlay.allEnabled()),
        ]
      );
200
    }
Hixie's avatar
Hixie committed
201 202 203 204 205
    if (config.showSemanticsDebugger) {
      result = new SemanticsDebugger(
        child: result
      );
    }
Ian Hickson's avatar
Ian Hickson committed
206
    return result;
207 208
  }

209
}