binding.dart 8.75 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:developer';
6 7
import 'dart:ui' as ui show window;
import 'dart:ui' show AppLifecycleState, Locale;
8

Ian Hickson's avatar
Ian Hickson committed
9
import 'package:flutter/gestures.dart';
10
import 'package:flutter/rendering.dart';
11
import 'package:flutter/scheduler.dart';
Ian Hickson's avatar
Ian Hickson committed
12
import 'package:flutter/services.dart';
13 14

import 'framework.dart';
15

16 17
export 'dart:ui' show AppLifecycleState, Locale;

Ian Hickson's avatar
Ian Hickson committed
18 19
class BindingObserver {
  bool didPopRoute() => false;
20
  void didChangeMetrics() { }
21 22
  void didChangeLocale(Locale locale) { }
  void didChangeAppLifecycleState(AppLifecycleState state) { }
Ian Hickson's avatar
Ian Hickson committed
23
}
24

Ian Hickson's avatar
Ian Hickson committed
25 26
/// A concrete binding for applications based on the Widgets framework.
/// This is the glue that binds the framework to the Flutter engine.
27
class WidgetFlutterBinding extends BindingBase with Scheduler, Gesturer, MojoShell, Renderer {
Ian Hickson's avatar
Ian Hickson committed
28 29 30 31 32 33 34 35 36 37

  WidgetFlutterBinding._();

  /// Creates and initializes the WidgetFlutterBinding. This constructor is
  /// idempotent; calling it a second time will just return the
  /// previously-created instance.
  static WidgetFlutterBinding ensureInitialized() {
    if (_instance == null)
      new WidgetFlutterBinding._();
    return _instance;
38 39
  }

Ian Hickson's avatar
Ian Hickson committed
40 41 42 43 44 45
  initInstances() {
    super.initInstances();
    _instance = this;
    BuildableElement.scheduleBuildFor = scheduleBuildFor;
    ui.window.onLocaleChanged = handleLocaleChanged;
    ui.window.onPopRoute = handlePopRoute;
46
    ui.window.onAppLifecycleStateChanged = handleAppLifecycleStateChanged;
47 48
  }

49
  /// The one static instance of this class.
50
  ///
Ian Hickson's avatar
Ian Hickson committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
  /// Only valid after the WidgetFlutterBinding constructor) has been called.
  /// Only one binding class can be instantiated per process. If another
  /// BindingBase implementation has been instantiated before this one (e.g.
  /// bindings from other frameworks based on the Flutter "rendering" library),
  /// then WidgetFlutterBinding.instance will not be valid (and will throw in
  /// checked mode).
  static WidgetFlutterBinding _instance;
  static WidgetFlutterBinding get instance => _instance;

  final List<BindingObserver> _observers = new List<BindingObserver>();

  void addObserver(BindingObserver observer) => _observers.add(observer);
  bool removeObserver(BindingObserver observer) => _observers.remove(observer);

  void handleMetricsChanged() {
    super.handleMetricsChanged();
    for (BindingObserver observer in _observers)
68
      observer.didChangeMetrics();
Ian Hickson's avatar
Ian Hickson committed
69 70 71 72 73 74
  }

  void handleLocaleChanged() {
    dispatchLocaleChanged(ui.window.locale);
  }

75
  void dispatchLocaleChanged(Locale locale) {
Ian Hickson's avatar
Ian Hickson committed
76 77 78 79 80 81 82
    for (BindingObserver observer in _observers)
      observer.didChangeLocale(locale);
  }

  void handlePopRoute() {
    for (BindingObserver observer in _observers) {
      if (observer.didPopRoute())
83
        return;
Ian Hickson's avatar
Ian Hickson committed
84
    }
85
    activity.finishCurrentActivity();
Ian Hickson's avatar
Ian Hickson committed
86
  }
Hixie's avatar
Hixie committed
87

88
  void handleAppLifecycleStateChanged(AppLifecycleState state) {
89 90 91 92
    for (BindingObserver observer in _observers)
      observer.didChangeAppLifecycleState(state);
  }

93
  void beginFrame() {
94
    buildDirtyElements();
95
    super.beginFrame();
96
    Element.finalizeTree();
97 98
  }

Hixie's avatar
Hixie committed
99
  List<BuildableElement> _dirtyElements = <BuildableElement>[];
100 101 102 103 104 105 106

  /// Adds an element to the dirty elements list so that it will be rebuilt
  /// when buildDirtyElements is called.
  void scheduleBuildFor(BuildableElement element) {
    assert(!_dirtyElements.contains(element));
    assert(element.dirty);
    if (_dirtyElements.isEmpty)
Ian Hickson's avatar
Ian Hickson committed
107
      ensureVisualUpdate();
108 109 110
    _dirtyElements.add(element);
  }

111 112 113 114 115 116 117 118 119 120 121 122
  static int _elementSort(BuildableElement a, BuildableElement b) {
    if (a.depth < b.depth)
      return -1;
    if (b.depth < a.depth)
      return 1;
    if (b.dirty && !a.dirty)
      return -1;
    if (a.dirty && !b.dirty)
      return 1;
    return 0;
  }

123 124 125 126 127 128 129
  /// Builds all the elements that were marked as dirty using schedule(), in depth order.
  /// If elements are marked as dirty while this runs, they must be deeper than the algorithm
  /// has yet reached.
  /// This is called by beginFrame().
  void buildDirtyElements() {
    if (_dirtyElements.isEmpty)
      return;
130
    Timeline.startSync('Build');
131
    BuildableElement.lockState(() {
132
      _dirtyElements.sort(_elementSort);
Hixie's avatar
Hixie committed
133 134 135 136 137 138
      int dirtyCount = _dirtyElements.length;
      int index = 0;
      while (index < dirtyCount) {
        _dirtyElements[index].rebuild();
        index += 1;
        if (dirtyCount < _dirtyElements.length) {
139
          _dirtyElements.sort(_elementSort);
Hixie's avatar
Hixie committed
140 141 142 143
          dirtyCount = _dirtyElements.length;
        }
      }
      assert(!_dirtyElements.any((BuildableElement element) => element.dirty));
144
      _dirtyElements.clear();
Hixie's avatar
Hixie committed
145
    }, building: true);
146
    assert(_dirtyElements.isEmpty);
147
    Timeline.finishSync();
148
  }
149 150 151 152 153 154 155 156

  /// The [Element] that is at the root of the hierarchy (and which wraps the
  /// [RenderView] object at the root of the rendering hierarchy).
  Element get renderViewElement => _renderViewElement;
  Element _renderViewElement;
  void _runApp(Widget app) {
    _renderViewElement = new RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
157
      debugShortDescription: '[root]',
158 159
      child: app
    ).attachToRenderTree(_renderViewElement);
160
    beginFrame();
161
  }
162
}
Hixie's avatar
Hixie committed
163

164
/// Inflate the given widget and attach it to the screen.
Hixie's avatar
Hixie committed
165
void runApp(Widget app) {
Ian Hickson's avatar
Ian Hickson committed
166
  WidgetFlutterBinding.ensureInitialized()._runApp(app);
Hixie's avatar
Hixie committed
167 168
}

169
/// Print a string representation of the currently running app.
170 171 172
void debugDumpApp() {
  assert(WidgetFlutterBinding.instance != null);
  assert(WidgetFlutterBinding.instance.renderViewElement != null);
Hixie's avatar
Hixie committed
173 174
  String mode = 'RELEASE MODE';
  assert(() { mode = 'CHECKED MODE'; return true; });
175 176
  debugPrint('${WidgetFlutterBinding.instance.runtimeType} - $mode');
  debugPrint(WidgetFlutterBinding.instance.renderViewElement.toStringDeep());
177 178
}

Hixie's avatar
Hixie committed
179 180 181 182 183 184
/// This class provides a bridge from a RenderObject to an Element tree. The
/// given container is the RenderObject that the Element tree should be inserted
/// into. It must be a RenderObject that implements the
/// RenderObjectWithChildMixin protocol. The type argument T is the kind of
/// RenderObject that the container expects as its child.
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
185 186 187 188 189
  RenderObjectToWidgetAdapter({
    this.child,
    RenderObjectWithChildMixin<T> container,
    this.debugShortDescription
  }) : container = container, super(key: new GlobalObjectKey(container));
Hixie's avatar
Hixie committed
190 191 192

  final Widget child;
  final RenderObjectWithChildMixin<T> container;
193
  final String debugShortDescription;
Hixie's avatar
Hixie committed
194 195 196 197 198 199

  RenderObjectToWidgetElement<T> createElement() => new RenderObjectToWidgetElement<T>(this);

  RenderObjectWithChildMixin<T> createRenderObject() => container;

  void updateRenderObject(RenderObject renderObject, RenderObjectWidget oldWidget) { }
200 201 202 203 204 205 206 207 208 209 210 211

  RenderObjectToWidgetElement<T> attachToRenderTree([RenderObjectToWidgetElement<T> element]) {
    BuildableElement.lockState(() {
      if (element == null) {
        element = createElement();
        element.mount(null, null);
      } else {
        element.update(this);
      }
    }, building: true);
    return element;
  }
212 213

  String toStringShort() => debugShortDescription ?? super.toStringShort();
Hixie's avatar
Hixie committed
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
}

/// This element class is the instantiation of a [RenderObjectToWidgetAdapter].
/// It can only be used as the root of an Element tree (it cannot be mounted
/// into another Element, it's parent must be null).
///
/// In typical usage, it will be instantiated for a RenderObjectToWidgetAdapter
/// whose container is the RenderView that connects to the Flutter engine. In
/// this usage, it is normally instantiated by the bootstrapping logic in the
/// WidgetFlutterBinding singleton created by runApp().
class RenderObjectToWidgetElement<T extends RenderObject> extends RenderObjectElement {
  RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);

  Element _child;

Ian Hickson's avatar
Ian Hickson committed
229
  static const _rootChildSlot = const Object();
Hixie's avatar
Hixie committed
230 231 232 233 234 235

  void visitChildren(ElementVisitor visitor) {
    if (_child != null)
      visitor(_child);
  }

Hixie's avatar
Hixie committed
236
  void mount(Element parent, dynamic newSlot) {
Hixie's avatar
Hixie committed
237
    assert(parent == null);
Hixie's avatar
Hixie committed
238
    super.mount(parent, newSlot);
Ian Hickson's avatar
Ian Hickson committed
239
    _child = updateChild(_child, widget.child, _rootChildSlot);
Hixie's avatar
Hixie committed
240 241 242 243 244
  }

  void update(RenderObjectToWidgetAdapter<T> newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
Ian Hickson's avatar
Ian Hickson committed
245
    _child = updateChild(_child, widget.child, _rootChildSlot);
Hixie's avatar
Hixie committed
246 247 248 249 250
  }

  RenderObjectWithChildMixin<T> get renderObject => super.renderObject;

  void insertChildRenderObject(RenderObject child, dynamic slot) {
Ian Hickson's avatar
Ian Hickson committed
251
    assert(slot == _rootChildSlot);
Hixie's avatar
Hixie committed
252 253 254
    renderObject.child = child;
  }

Adam Barth's avatar
Adam Barth committed
255 256 257 258
  void moveChildRenderObject(RenderObject child, dynamic slot) {
    assert(false);
  }

Hixie's avatar
Hixie committed
259 260 261 262
  void removeChildRenderObject(RenderObject child) {
    assert(renderObject.child == child);
    renderObject.child = null;
  }
Adam Barth's avatar
Adam Barth committed
263
}