app.dart 6.78 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
// 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.

import 'dart:async';
import 'dart:ui' as ui show Locale, WindowPadding, window;

import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';

import 'asset_vendor.dart';
import 'basic.dart';
import 'binding.dart';
import 'checked_mode_banner.dart';
import 'framework.dart';
import 'locale_query.dart';
import 'media_query.dart';
import 'navigator.dart';
import 'performance_overlay.dart';
import 'semantics_debugger.dart';
import 'title.dart';

AssetBundle _initDefaultBundle() {
  if (rootBundle != null)
    return rootBundle;
  return new NetworkAssetBundle(Uri.base);
}

final AssetBundle _defaultBundle = _initDefaultBundle();

class RouteArguments {
  const RouteArguments({ this.context });
  final BuildContext context;
}
typedef Widget RouteBuilder(RouteArguments args);

typedef Future<LocaleQueryData> LocaleChangedCallback(Locale locale);

class WidgetsApp extends StatefulComponent {
  WidgetsApp({
    Key key,
    this.title,
    this.textStyle,
    this.color,
    this.routes: const <String, RouteBuilder>{},
    this.onGenerateRoute,
    this.onLocaleChanged,
    this.showPerformanceOverlay: false,
    this.showSemanticsDebugger: false,
    this.debugShowCheckedModeBanner: true
  }) : super(key: key) {
    assert(routes != null);
    assert(routes.containsKey(Navigator.defaultRouteName) || onGenerateRoute != null);
    assert(showPerformanceOverlay != null);
    assert(showSemanticsDebugger != null);
  }

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

  /// The default text style for [Text] in the application.
  final TextStyle textStyle;

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

  /// 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.
  final Map<String, RouteBuilder> routes;

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

  /// Callback that is invoked when the operating system changes the
  /// current locale.
  final LocaleChangedCallback onLocaleChanged;

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

  /// Turns on an overlay that shows the accessibility information
  /// reported by the framework.
  final bool showSemanticsDebugger;

  /// 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
  /// WidgetsApp, 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;

  WidgetsAppState<WidgetsApp> createState() => new WidgetsAppState<WidgetsApp>();
}

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

class WidgetsAppState<T extends WidgetsApp> extends State<T> implements BindingObserver {

  GlobalObjectKey _navigator;

  LocaleQueryData _localeData;

  void initState() {
    super.initState();
    _navigator = new GlobalObjectKey(this);
    didChangeLocale(ui.window.locale);
    WidgetFlutterBinding.instance.addObserver(this);
  }

  void dispose() {
    WidgetFlutterBinding.instance.removeObserver(this);
    super.dispose();
  }

  bool didPopRoute() {
    assert(mounted);
    NavigatorState navigator = _navigator.currentState;
    assert(navigator != null);
    bool result = false;
    navigator.openTransaction((NavigatorTransaction transaction) {
      result = transaction.pop();
    });
    return result;
  }

  void didChangeMetrics() {
    setState(() {
      // 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.
    });
  }

  void didChangeLocale(Locale locale) {
    if (config.onLocaleChanged != null) {
      config.onLocaleChanged(locale).then((LocaleQueryData data) {
        if (mounted)
          setState(() { _localeData = data; });
      });
    }
  }

  void didChangeAppLifecycleState(AppLifecycleState state) { }

  NavigatorObserver get navigatorObserver => null;

  Widget build(BuildContext context) {
    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.
      // TODO(ianh): Make this unnecessary. See https://github.com/flutter/flutter/issues/1865
      return new Container();
    }

    Widget result = new MediaQuery(
      data: new MediaQueryData(
        size: ui.window.size,
        devicePixelRatio: ui.window.devicePixelRatio,
        padding: _getPadding(ui.window.padding)
      ),
      child: new LocaleQuery(
        data: _localeData,
        child: new DefaultTextStyle(
          style: config.textStyle,
          child: new AssetVendor(
            bundle: _defaultBundle,
            devicePixelRatio: ui.window.devicePixelRatio,
            child: new Title(
              title: config.title,
              color: config.color,
              child: new Navigator(
                key: _navigator,
                initialRoute: ui.window.defaultRouteName,
                onGenerateRoute: config.onGenerateRoute,
                observer: navigatorObserver
              )
            )
          )
        )
      )
    );
    if (config.showPerformanceOverlay) {
      result = new Stack(
        children: <Widget>[
          result,
          new Positioned(bottom: 0.0, left: 0.0, right: 0.0, child: new PerformanceOverlay.allEnabled()),
        ]
      );
    }
    if (config.showSemanticsDebugger) {
      result = new SemanticsDebugger(
        child: result
      );
    }
    assert(() {
      if (config.debugShowCheckedModeBanner) {
        result = new CheckedModeBanner(
          child: result
        );
      }
      return true;
    });
    return result;
  }

}