diff --git a/packages/flutter/CHANGELOG.md b/packages/flutter/CHANGELOG.md deleted file mode 100644 index 7dd98e6c8a..0000000000 --- a/packages/flutter/CHANGELOG.md +++ /dev/null @@ -1,85 +0,0 @@ -## 0.0.20 - - - 167 changes: https://github.com/domokit/mojo/compare/f2830c7...603f589 - -## 0.0.19 - - - 49 changes: https://github.com/domokit/mojo/compare/a64559a...1b8968c - -## 0.0.18 - - - 41 changes: https://github.com/domokit/mojo/compare/246e279...c3119f6 - -## 0.0.17 - - - 18 changes: https://github.com/domokit/mojo/compare/e7433cf...8879bfd - -## 0.0.16 - - - 27 changes: https://github.com/domokit/mojo/compare/e028733...e7433cf - -## 0.0.15 - - - 6 changes: https://github.com/domokit/mojo/compare/4df2d39...e028733 - -## 0.0.14 - - - 42 changes: https://github.com/domokit/mojo/compare/3de9766...cf84c48 - -## 0.0.13 - - - 70 changes: https://github.com/domokit/mojo/compare/889091e...136e0d4 - -## 0.0.12 - - - 29 changes: https://github.com/domokit/mojo/compare/e25e3e2...432ce45 - - Fixed sky_tool to work again. - -## 0.0.11 - - - 197 changes: https://github.com/domokit/mojo/compare/bdbb0c7...fb1b726 - -## 0.0.10 - - - 23 changes: https://github.com/domokit/mojo/compare/1b7bcee...be9dad7 - -## 0.0.8 - - - Fix 2 crashes in SkyDemo.apk, updated widgets. 0.0.7 was skipped. - -## 0.0.6 - - - First release after Dart summit. Adds new main.dart based workflow. - -## 0.0.5+dart-summit-7 - - - Fix crash in sky_tool stop_tracing. - -## 0.0.5+dart-summit-6 - - - Fix missing include in sky_tool causing failure. - -## 0.0.5+dart-summit-5 - - - Added sky_tool start_tracing and stop_tracing. - -## 0.0.5+dart-summit-4 - - - Added download_material_design_icons script. - -## 0.0.5+dart-summit-3 - - - Fix typo in lib/sky_tool causing syntax error when run. - -## 0.0.5+dart-summit-2 - - - Various demo fixes and added ChangeLogs. - -## 0.0.5+dart-summit-1 - - - Branched from mojo trunk to stabalize for Dart summit. - -## 0.0.2 - - - sdk_additions now includes previously missing imports. - - added lib/sky_tool, and supporting apks/SkyDemo.apk diff --git a/packages/flutter/lib/http.dart b/packages/flutter/lib/http.dart index c34467c147..d6ed90774e 100644 --- a/packages/flutter/lib/http.dart +++ b/packages/flutter/lib/http.dart @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/// Service exposed to Flutter apps that implements a subset of Dart's -/// http package API. +/// A [Future]-based library for making HTTP requests. /// -/// This library will probably be moved into a separate package eventually. +/// This library is based on Dart's `http` package, but differs in that it is a +/// `mojo`-based HTTP client and does not have a dependency on mirrors. /// /// This library depends only on core Dart libraries as well as the `mojo`, -/// `mojo_services`, and `sky_services` and packages. +/// `mojo_services`, and `sky_services` packages. library http; export 'src/http/http.dart'; +export 'src/http/mojo_client.dart'; export 'src/http/response.dart'; diff --git a/packages/flutter/lib/src/http/http.dart b/packages/flutter/lib/src/http/http.dart index b62b6e1b05..7e3f24d16e 100644 --- a/packages/flutter/lib/src/http/http.dart +++ b/packages/flutter/lib/src/http/http.dart @@ -2,12 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -/// A [Future]-based library for making HTTP requests. -/// -/// This library is based on Dart's `http` package, but we have removed the -/// dependency on mirrors and added a `mojo`-based HTTP client. -library http; - import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; @@ -18,28 +12,48 @@ import 'response.dart'; /// Sends an HTTP HEAD request with the given headers to the given URL, which /// can be a [Uri] or a [String]. /// -/// This automatically initializes a new [Client] and closes that client once +/// This automatically initializes a new [MojoClient] and closes that client once /// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -Future head(url) => - _withClient((client) => client.head(url)); +/// the same server, you should use a single [MojoClient] for all of those requests. +Future head(dynamic url) { + return _withClient/**/((MojoClient client) => client.head(url)); +} /// Sends an HTTP GET request with the given headers to the given URL, which can /// be a [Uri] or a [String]. /// -/// This automatically initializes a new [Client] and closes that client once +/// This automatically initializes a new [MojoClient] and closes that client once /// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -Future get(url, {Map headers}) => - _withClient((client) => client.get(url, headers: headers)); +/// the same server, you should use a single [MojoClient] for all of those requests. +Future get(dynamic url, { Map headers }) { + return _withClient/**/((MojoClient client) => client.get(url, headers: headers)); +} /// Sends an HTTP POST request with the given headers and body to the given URL, /// which can be a [Uri] or a [String]. /// -/// [body] sets the body of the request. -Future post(url, {Map headers, body}) => - _withClient((client) => client.post(url, - headers: headers, body: body)); +/// [body] sets the body of the request. It can be a [String], a [List] or +/// a [Map]. If it's a String, it's encoded using [encoding] and +/// used as the body of the request. The content-type of the request will +/// default to "text/plain". +/// +/// If [body] is a List, it's used as a list of bytes for the body of the +/// request. +/// +/// If [body] is a Map, it's encoded as form fields using [encoding]. The +/// content-type of the request will be set to +/// `"application/x-www-form-urlencoded"`; this cannot be overridden. +/// +/// [encoding] defaults to [UTF8]. +/// +/// This automatically initializes a new [MojoClient] and closes that client once +/// the request is complete. If you're planning on making multiple requests to +/// the same server, you should use a single [MojoClient] for all of those requests. +Future post(dynamic url, { Map headers, dynamic body, Encoding encoding: UTF8 }) { + return _withClient/**/((MojoClient client) { + return client.post(url, headers: headers, body: body, encoding: encoding); + }); +} /// Sends an HTTP PUT request with the given headers and body to the given URL, /// which can be a [Uri] or a [String]. @@ -48,9 +62,24 @@ Future post(url, {Map headers, body}) => /// a [Map]. If it's a String, it's encoded using [encoding] and /// used as the body of the request. The content-type of the request will /// default to "text/plain". -Future put(url, {Map headers, body}) => - _withClient((client) => client.put(url, - headers: headers, body: body)); +/// +/// If [body] is a List, it's used as a list of bytes for the body of the +/// request. +/// +/// If [body] is a Map, it's encoded as form fields using [encoding]. The +/// content-type of the request will be set to +/// `"application/x-www-form-urlencoded"`; this cannot be overridden. +/// +/// [encoding] defaults to [UTF8]. +/// +/// This automatically initializes a new [MojoClient] and closes that client once +/// the request is complete. If you're planning on making multiple requests to +/// the same server, you should use a single [MojoClient] for all of those requests. +Future put(dynamic url, { Map headers, dynamic body, Encoding encoding: UTF8 }) { + return _withClient/**/((MojoClient client) { + return client.put(url, headers: headers, body: body, encoding: encoding); + }); +} /// Sends an HTTP PATCH request with the given headers and body to the given /// URL, which can be a [Uri] or a [String]. @@ -69,22 +98,24 @@ Future put(url, {Map headers, body}) => /// /// [encoding] defaults to [UTF8]. /// -/// For more fine-grained control over the request, use [Request] or -/// [StreamedRequest] instead. -Future patch(url, {Map headers, body}) => - _withClient((client) => client.patch(url, - headers: headers, body: body)); +/// This automatically initializes a new [MojoClient] and closes that client once +/// the request is complete. If you're planning on making multiple requests to +/// the same server, you should use a single [MojoClient] for all of those requests. +Future patch(dynamic url, { Map headers, dynamic body, Encoding encoding: UTF8 }) { + return _withClient/**/((MojoClient client) { + return client.patch(url, headers: headers, body: body, encoding: encoding); + }); +} /// Sends an HTTP DELETE request with the given headers to the given URL, which /// can be a [Uri] or a [String]. /// -/// This automatically initializes a new [Client] and closes that client once +/// This automatically initializes a new [MojoClient] and closes that client once /// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -/// -/// For more fine-grained control over the request, use [Request] instead. -Future delete(url, {Map headers}) => - _withClient((client) => client.delete(url, headers: headers)); +/// the same server, you should use a single [MojoClient] for all of those requests. +Future delete(dynamic url, { Map headers }) { + return _withClient/**/((MojoClient client) => client.delete(url, headers: headers)); +} /// Sends an HTTP GET request with the given headers to the given URL, which can /// be a [Uri] or a [String], and returns a Future that completes to the body of @@ -93,14 +124,12 @@ Future delete(url, {Map headers}) => /// The Future will emit a [ClientException] if the response doesn't have a /// success status code. /// -/// This automatically initializes a new [Client] and closes that client once +/// This automatically initializes a new [MojoClient] and closes that client once /// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -/// -/// For more fine-grained control over the request and response, use [Request] -/// instead. -Future read(url, {Map headers}) => - _withClient((client) => client.read(url, headers: headers)); +/// the same server, you should use a single [MojoClient] for all of those requests. +Future read(dynamic url, { Map headers }) { + return _withClient/**/((MojoClient client) => client.read(url, headers: headers)); +} /// Sends an HTTP GET request with the given headers to the given URL, which can /// be a [Uri] or a [String], and returns a Future that completes to the body of @@ -109,17 +138,13 @@ Future read(url, {Map headers}) => /// The Future will emit a [ClientException] if the response doesn't have a /// success status code. /// -/// This automatically initializes a new [Client] and closes that client once +/// This automatically initializes a new [MojoClient] and closes that client once /// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -/// -/// For more fine-grained control over the request and response, use [Request] -/// instead. -Future readBytes(url, {Map headers}) => - _withClient((client) => client.readBytes(url, headers: headers)); - -Future _withClient(Future fn(MojoClient client)) { - var client = new MojoClient(); - var future = fn(client); - return future.whenComplete(client.close); +/// the same server, you should use a single [MojoClient] for all of those requests. +Future readBytes(dynamic url, { Map headers }) { + return _withClient/**/((MojoClient client) => client.readBytes(url, headers: headers)); +} + +Future/**/ _withClient/**/(Future/**/ fn(MojoClient client)) { + return fn(new MojoClient()); } diff --git a/packages/flutter/lib/src/http/mojo_client.dart b/packages/flutter/lib/src/http/mojo_client.dart index ce579da9ac..d4ddad6fb3 100644 --- a/packages/flutter/lib/src/http/mojo_client.dart +++ b/packages/flutter/lib/src/http/mojo_client.dart @@ -19,43 +19,111 @@ import 'response.dart'; /// A `mojo`-based HTTP client. class MojoClient { - Future head(url, {Map headers}) => - _send("HEAD", url, headers); + /// Sends an HTTP HEAD request with the given headers to the given URL, which + /// can be a [Uri] or a [String]. + Future head(dynamic url, { Map headers }) { + return _send("HEAD", url, headers); + } - Future get(url, {Map headers}) => - _send("GET", url, headers); + /// Sends an HTTP GET request with the given headers to the given URL, which can + /// be a [Uri] or a [String]. + Future get(dynamic url, { Map headers }) { + return _send("GET", url, headers); + } - Future post(url, {Map headers, body, - Encoding encoding}) => - _send("POST", url, headers, body, encoding); + /// Sends an HTTP POST request with the given headers and body to the given URL, + /// which can be a [Uri] or a [String]. + /// + /// [body] sets the body of the request. It can be a [String], a [List] or + /// a [Map]. If it's a String, it's encoded using [encoding] and + /// used as the body of the request. The content-type of the request will + /// default to "text/plain". + /// + /// If [body] is a List, it's used as a list of bytes for the body of the + /// request. + /// + /// If [body] is a Map, it's encoded as form fields using [encoding]. The + /// content-type of the request will be set to + /// `"application/x-www-form-urlencoded"`; this cannot be overridden. + /// + /// [encoding] defaults to [UTF8]. + Future post(dynamic url, { Map headers, dynamic body, Encoding encoding: UTF8 }) { + return _send("POST", url, headers, body, encoding); + } - Future put(url, {Map headers, body, - Encoding encoding}) => - _send("PUT", url, headers, body, encoding); + /// Sends an HTTP PUT request with the given headers and body to the given URL, + /// which can be a [Uri] or a [String]. + /// + /// [body] sets the body of the request. It can be a [String], a [List] or + /// a [Map]. If it's a String, it's encoded using [encoding] and + /// used as the body of the request. The content-type of the request will + /// default to "text/plain". + /// + /// If [body] is a List, it's used as a list of bytes for the body of the + /// request. + /// + /// If [body] is a Map, it's encoded as form fields using [encoding]. The + /// content-type of the request will be set to + /// `"application/x-www-form-urlencoded"`; this cannot be overridden. + /// + /// [encoding] defaults to [UTF8]. + Future put(dynamic url, { Map headers, dynamic body, Encoding encoding: UTF8 }) { + return _send("PUT", url, headers, body, encoding); + } - Future patch(url, {Map headers, body, - Encoding encoding}) => - _send("PATCH", url, headers, body, encoding); + /// Sends an HTTP PATCH request with the given headers and body to the given + /// URL, which can be a [Uri] or a [String]. + /// + /// [body] sets the body of the request. It can be a [String], a [List] or + /// a [Map]. If it's a String, it's encoded using [encoding] and + /// used as the body of the request. The content-type of the request will + /// default to "text/plain". + /// + /// If [body] is a List, it's used as a list of bytes for the body of the + /// request. + /// + /// If [body] is a Map, it's encoded as form fields using [encoding]. The + /// content-type of the request will be set to + /// `"application/x-www-form-urlencoded"`; this cannot be overridden. + /// + /// [encoding] defaults to [UTF8]. + Future patch(dynamic url, {Map headers, dynamic body, Encoding encoding: UTF8 }) { + return _send("PATCH", url, headers, body, encoding); + } - Future delete(url, {Map headers}) => - _send("DELETE", url, headers); + /// Sends an HTTP DELETE request with the given headers to the given URL, which + /// can be a [Uri] or a [String]. + Future delete(dynamic url, { Map headers }) { + return _send("DELETE", url, headers); + } - Future read(url, {Map headers}) { - return get(url, headers: headers).then((response) { + /// Sends an HTTP GET request with the given headers to the given URL, which can + /// be a [Uri] or a [String], and returns a Future that completes to the body of + /// the response as a [String]. + /// + /// The Future will emit a [ClientException] if the response doesn't have a + /// success status code. + Future read(dynamic url, { Map headers }) { + return get(url, headers: headers).then((Response response) { _checkResponseSuccess(url, response); return response.body; }); } - Future readBytes(url, {Map headers}) { + /// Sends an HTTP GET request with the given headers to the given URL, which can + /// be a [Uri] or a [String], and returns a Future that completes to the body of + /// the response as a list of bytes. + /// + /// The Future will emit a [ClientException] if the response doesn't have a + /// success status code. + Future readBytes(dynamic url, { Map headers }) { return get(url, headers: headers).then((Response response) { _checkResponseSuccess(url, response); return response.bodyBytes; }); } - Future _send(String method, url, - Map headers, [body, Encoding encoding]) async { + Future _send(String method, dynamic url, Map headers, [dynamic body, Encoding encoding = UTF8]) async { mojo.UrlLoaderProxy loader = new mojo.UrlLoaderProxy.unbound(); List mojoHeaders = []; headers?.forEach((String name, String value) { @@ -71,7 +139,7 @@ class MojoClient { if (body != null) { mojo.MojoDataPipe pipe = new mojo.MojoDataPipe(); request.body = [pipe.consumer]; - Uint8List encodedBody = UTF8.encode(body); + Uint8List encodedBody = encoding.encode(body); ByteData data = new ByteData.view(encodedBody.buffer); mojo.DataPipeFiller.fillHandle(pipe.producer, data); } @@ -90,14 +158,12 @@ class MojoClient { } } - void _checkResponseSuccess(url, Response response) { + void _checkResponseSuccess(dynamic url, Response response) { if (response.statusCode < 400) return; throw new Exception("Request to $url failed with status ${response.statusCode}."); } - void close() {} - static mojo.NetworkServiceProxy _initNetworkService() { mojo.NetworkServiceProxy proxy = new mojo.NetworkServiceProxy.unbound(); shell.connectToService("mojo:authenticated_network_service", proxy); diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart index d69430f230..f9832022a6 100644 --- a/packages/flutter/lib/src/material/app.dart +++ b/packages/flutter/lib/src/material/app.dart @@ -28,8 +28,7 @@ const TextStyle _errorTextStyle = const TextStyle( AssetBundle _initDefaultBundle() { if (rootBundle != null) return rootBundle; - const String _kAssetBase = '/packages/material_design_icons/icons/'; - return new NetworkAssetBundle(Uri.base.resolve(_kAssetBase)); + return new NetworkAssetBundle(Uri.base); } final AssetBundle _defaultBundle = _initDefaultBundle(); diff --git a/packages/flutter/lib/src/material/shadows.dart b/packages/flutter/lib/src/material/shadows.dart index c621be9671..98cf23b44b 100644 --- a/packages/flutter/lib/src/material/shadows.dart +++ b/packages/flutter/lib/src/material/shadows.dart @@ -10,11 +10,20 @@ import 'package:flutter/painting.dart'; // Currently, only the elevation values that are bound to one or more components are // defined here. +/// Map of elevation offsets used by material design to [BoxShadow] definitions. +/// +/// The following elevations have defined shadows: 1, 2, 3, 4, 6, 8, 9, 12, 16, 24 +/// +/// Each entry has three shadows which must be combined to obtain the defined +/// effect for that elevation. +/// +/// See also: +const Map> elevationToShadow = _elevationToShadow; // to hide the literal from the docs + const Color _kKeyUmbraOpacity = const Color(0x33000000); // alpha = 0.2 const Color _kKeyPenumbraOpacity = const Color(0x24000000); // alpha = 0.14 const Color _kAmbientShadowOpacity = const Color(0x1F000000); // alpha = 0.12 - -const Map> elevationToShadow = const >{ +const Map> _elevationToShadow = const >{ 1: const [ const BoxShadow(offset: const Offset(0.0, 2.0), blurRadius: 1.0, spreadRadius: -1.0, color: _kKeyUmbraOpacity), const BoxShadow(offset: const Offset(0.0, 1.0), blurRadius: 1.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity), diff --git a/packages/flutter/lib/src/rendering/debug.dart b/packages/flutter/lib/src/rendering/debug.dart index ee1a8076f2..396b98bc83 100644 --- a/packages/flutter/lib/src/rendering/debug.dart +++ b/packages/flutter/lib/src/rendering/debug.dart @@ -53,7 +53,7 @@ bool debugPaintPointersEnabled = false; /// The color to use when reporting pointers. int debugPaintPointersColorValue = 0x00BBBB; -/// The color to use when painting RenderError boxes in checked mode. +/// The color to use when painting [RenderErrorBox] objects in checked mode. Color debugErrorBoxColor = const Color(0xFFFF0000); /// Overlay a rotating set of colors when repainting layers in checked mode. diff --git a/packages/flutter/lib/src/rendering/viewport.dart b/packages/flutter/lib/src/rendering/viewport.dart index 1f3319400d..f6c0245266 100644 --- a/packages/flutter/lib/src/rendering/viewport.dart +++ b/packages/flutter/lib/src/rendering/viewport.dart @@ -9,20 +9,33 @@ import 'package:vector_math/vector_math_64.dart'; import 'box.dart'; import 'object.dart'; +/// The end of the viewport from which the paint offset is computed. enum ViewportAnchor { + /// The start (e.g., top or left, depending on the axis) of the first item + /// should be aligned with the start (e.g., top or left, depending on the + /// axis) of the viewport. start, + + /// The end (e.g., bottom or right, depending on the axis) of the last item + /// should be aligned with the end (e.g., bottom or right, depending on the + /// axis) of the viewport. end, } +/// The interior and exterior dimensions of a viewport. class ViewportDimensions { const ViewportDimensions({ this.contentSize: Size.zero, this.containerSize: Size.zero }); + /// A viewport that has zero size, both inside and outside. static const ViewportDimensions zero = const ViewportDimensions(); + /// The size of the content inside the viewport. final Size contentSize; + + /// The size of the outside of the viewport. final Size containerSize; bool get _debugHasAtLeastOneCommonDimension { @@ -30,6 +43,8 @@ class ViewportDimensions { || contentSize.height == containerSize.height; } + /// Returns the offset at which to paint the content, accounting for the given + /// anchor and the dimensions of the viewport. Offset getAbsolutePaintOffset({ Offset paintOffset, ViewportAnchor anchor }) { assert(_debugHasAtLeastOneCommonDimension); switch (anchor) { @@ -55,7 +70,9 @@ class ViewportDimensions { String toString() => 'ViewportDimensions(container: $containerSize, content: $contentSize)'; } +/// An interface that indicates that an object has a scroll direction. abstract class HasScrollDirection { + /// Whether this object scrolls horizontally or vertically. Axis get scrollDirection; } @@ -119,6 +136,9 @@ class RenderViewportBase extends RenderBox implements HasScrollDirection { markNeedsLayout(); } + /// The end of the viewport from which the paint offset is computed. + /// + /// See [ViewportAnchor] for more detail. ViewportAnchor get scrollAnchor => _scrollAnchor; ViewportAnchor _scrollAnchor; void set scrollAnchor(ViewportAnchor value) { diff --git a/packages/flutter/lib/src/scheduler/ticker.dart b/packages/flutter/lib/src/scheduler/ticker.dart index 915fdba276..03980cc2b0 100644 --- a/packages/flutter/lib/src/scheduler/ticker.dart +++ b/packages/flutter/lib/src/scheduler/ticker.dart @@ -6,11 +6,12 @@ import 'dart:async'; import 'scheduler.dart'; +/// Signature for the [onTick] constructor argument of the [Ticker] class. typedef void TickerCallback(Duration elapsed); /// Calls its callback once per animation frame. class Ticker { - /// Constructs a ticker that will call onTick once per frame while running + /// Constructs a ticker that will call [onTick] once per frame while running. Ticker(TickerCallback onTick) : _onTick = onTick; final TickerCallback _onTick; diff --git a/packages/flutter/lib/src/widgets/pageable_list.dart b/packages/flutter/lib/src/widgets/pageable_list.dart index 68722256b2..1b6b2c2af9 100644 --- a/packages/flutter/lib/src/widgets/pageable_list.dart +++ b/packages/flutter/lib/src/widgets/pageable_list.dart @@ -202,7 +202,7 @@ class PageableListState extends ScrollableState { } } -class PageViewport extends VirtualViewport with VirtualViewportIterableMixin { +class PageViewport extends VirtualViewportFromIterable { PageViewport({ this.startOffset: 0.0, this.scrollDirection: Axis.vertical, diff --git a/packages/flutter/lib/src/widgets/scrollable_grid.dart b/packages/flutter/lib/src/widgets/scrollable_grid.dart index a8793129ab..6cdd873220 100644 --- a/packages/flutter/lib/src/widgets/scrollable_grid.dart +++ b/packages/flutter/lib/src/widgets/scrollable_grid.dart @@ -63,7 +63,7 @@ class _ScrollableGridState extends ScrollableState { } } -class GridViewport extends VirtualViewport with VirtualViewportIterableMixin { +class GridViewport extends VirtualViewportFromIterable { GridViewport({ this.startOffset, this.delegate, diff --git a/packages/flutter/lib/src/widgets/scrollable_list.dart b/packages/flutter/lib/src/widgets/scrollable_list.dart index 4a48ec147b..3c019296e5 100644 --- a/packages/flutter/lib/src/widgets/scrollable_list.dart +++ b/packages/flutter/lib/src/widgets/scrollable_list.dart @@ -240,7 +240,7 @@ class _VirtualListViewportElement extends VirtualViewportElement<_VirtualListVie } } -class ListViewport extends _VirtualListViewport with VirtualViewportIterableMixin { +class ListViewport extends _VirtualListViewport with VirtualViewportFromIterable { ListViewport({ ExtentsChangedCallback onExtentsChanged, double scrollOffset: 0.0, @@ -350,7 +350,7 @@ class _ScrollableLazyListState extends ScrollableState { } } -class LazyListViewport extends _VirtualListViewport with VirtualViewportLazyMixin { +class LazyListViewport extends _VirtualListViewport with VirtualViewportFromBuilder { LazyListViewport({ ExtentsChangedCallback onExtentsChanged, double scrollOffset: 0.0, diff --git a/packages/flutter/lib/src/widgets/virtual_viewport.dart b/packages/flutter/lib/src/widgets/virtual_viewport.dart index e55cb9eb01..28857f1239 100644 --- a/packages/flutter/lib/src/widgets/virtual_viewport.dart +++ b/packages/flutter/lib/src/widgets/virtual_viewport.dart @@ -11,7 +11,9 @@ import 'package:flutter/rendering.dart'; typedef void ExtentsChangedCallback(double contentExtent, double containerExtent); +/// An abstract widget whose children are not all materialized. abstract class VirtualViewport extends RenderObjectWidget { + /// The offset from the [ViewportAnchor] at which the viewport should start painting children. double get startOffset; _WidgetProvider _createWidgetProvider(); @@ -24,12 +26,24 @@ abstract class _WidgetProvider { Widget getChild(int i); } +/// Materializes a contiguous subset of its children. +/// +/// This class is a building block for building a widget that has more children +/// than it wishes to display at any given time. For example, [ScrollableList] +/// uses this element to materialize only those children that are visible. abstract class VirtualViewportElement extends RenderObjectElement { VirtualViewportElement(T widget) : super(widget); + /// The index of the first child to materialize. int get materializedChildBase; + + /// The number of children to materializes. int get materializedChildCount; + + /// The least offset for which [materializedChildBase] and [materializedChildCount] are valid. double get startOffsetBase; + + /// The greatest offset for which [materializedChildBase] and [materializedChildCount] are valid. double get startOffsetLimit; /// Returns the pixel offset for a scroll offset, accounting for the scroll @@ -124,6 +138,12 @@ abstract class VirtualViewportElement extends RenderO } } + /// Called by [RenderVirtualViewport] during layout. + /// + /// Subclasses should override this function to compute [materializedChildBase] + /// and [materializedChildCount]. Overrides should call this function to + /// update the [RenderVirtualViewport]'s paint offset and to materialize the + /// children. void layout(BoxConstraints constraints) { assert(startOffsetBase != null); assert(startOffsetLimit != null); @@ -162,7 +182,12 @@ abstract class VirtualViewportElement extends RenderO } } -abstract class VirtualViewportIterableMixin extends VirtualViewport { +/// A VirtualViewport that represents its children using [Iterable]. +/// +/// The iterator is advanced just far enough to obtain widgets for the children +/// that need to be materialized. +abstract class VirtualViewportFromIterable extends VirtualViewport { + /// The children, some of which might be materialized. Iterable get children; _IterableWidgetProvider _createWidgetProvider() => new _IterableWidgetProvider(); @@ -173,7 +198,7 @@ class _IterableWidgetProvider extends _WidgetProvider { Iterator _iterator; List _widgets; - void didUpdateWidget(VirtualViewportIterableMixin oldWidget, VirtualViewportIterableMixin newWidget) { + void didUpdateWidget(VirtualViewportFromIterable oldWidget, VirtualViewportFromIterable newWidget) { if (oldWidget == null || newWidget.children != oldWidget.children) { _iterator = null; _widgets = []; @@ -187,7 +212,7 @@ class _IterableWidgetProvider extends _WidgetProvider { int limit = base < 0 ? _length : math.min(_length, base + count); if (limit <= _widgets.length) return; - VirtualViewportIterableMixin widget = context.widget; + VirtualViewportFromIterable widget = context.widget; if (widget.children is List) { _widgets = widget.children; return; @@ -205,10 +230,21 @@ class _IterableWidgetProvider extends _WidgetProvider { Widget getChild(int i) => _widgets[(i % _length).abs()]; } +/// Signature of a callback that returns the sublist of widgets in the given range. typedef List ItemListBuilder(BuildContext context, int start, int count); -abstract class VirtualViewportLazyMixin extends VirtualViewport { +/// A VirtualViewport that represents its children using [ItemListBuilder]. +/// +/// This widget is less ergonomic than [VirtualViewportFromIterable] but scales to +/// unlimited numbers of children. +abstract class VirtualViewportFromBuilder extends VirtualViewport { + /// The total number of children that can be built. int get itemCount; + + /// A callback to build the subset of widgets that are needed to populate the + /// viewport. Not all of the returned widgets will actually be included in the + /// viewport (e.g., if we need to measure the size of non-visible children to + /// determine which children are visible). ItemListBuilder get itemBuilder; _LazyWidgetProvider _createWidgetProvider() => new _LazyWidgetProvider(); @@ -219,7 +255,7 @@ class _LazyWidgetProvider extends _WidgetProvider { int _base; List _widgets; - void didUpdateWidget(VirtualViewportLazyMixin oldWidget, VirtualViewportLazyMixin newWidget) { + void didUpdateWidget(VirtualViewportFromBuilder oldWidget, VirtualViewportFromBuilder newWidget) { if (_length != newWidget.itemCount || oldWidget?.itemBuilder != newWidget.itemBuilder) { _length = newWidget.itemCount; _base = null; @@ -232,7 +268,7 @@ class _LazyWidgetProvider extends _WidgetProvider { void prepareChildren(VirtualViewportElement context, int base, int count) { if (_widgets != null && _widgets.length == count && _base == base) return; - VirtualViewportLazyMixin widget = context.widget; + VirtualViewportFromBuilder widget = context.widget; _base = base; _widgets = widget.itemBuilder(context, base, count); } diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 4e240fb112..b566737ee4 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -18,10 +18,6 @@ dependencies: # analyzer, by the way!) complaining about the inconsistency. analyzer: 0.27.1 - # TODO(abarth): Remove once we've updated the iOS build to not assert that - # this package exists. - material_design_icons: '>=0.0.3 <0.1.0' - sky_engine: path: ../../bin/cache/pkg/sky_engine sky_services: diff --git a/packages/flutter_tools/lib/src/commands/analyze.dart b/packages/flutter_tools/lib/src/commands/analyze.dart index d0410ea9a5..242b10dba4 100644 --- a/packages/flutter_tools/lib/src/commands/analyze.dart +++ b/packages/flutter_tools/lib/src/commands/analyze.dart @@ -334,7 +334,7 @@ linter: if (dartFiles.length == 1) { printStatus('Analyzing ${dartFiles.first}...'); } else { - printStatus('Analyzing ${dartFiles.length} files...'); + printStatus('Analyzing ${dartFiles.length} entry points...'); } for (String file in dartFiles) printTrace(file); diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index 020d944f85..d13f9cac05 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -128,7 +128,10 @@ All done! In order to run your application, type: void _renderTemplates(String projectName, String dirPath, String flutterPackagesDirectory, { bool renderDriverTest: false }) { - String relativeFlutterPackagesDirectory = path.relative(flutterPackagesDirectory, from: dirPath); + String relativePackagesDirectory = path.relative( + flutterPackagesDirectory, + from: path.join(dirPath, 'pubspec.yaml') + ); printStatus('Creating project ${path.basename(projectName)}:'); @@ -139,7 +142,7 @@ All done! In order to run your application, type: 'androidIdentifier': _createAndroidIdentifier(projectName), 'iosIdentifier': _createUTIIdentifier(projectName), 'description': description, - 'flutterPackagesDirectory': relativeFlutterPackagesDirectory, + 'flutterPackagesDirectory': relativePackagesDirectory, 'androidMinApiLevel': android.minApiLevel }; diff --git a/packages/flutter_tools/test/create_test.dart b/packages/flutter_tools/test/create_test.dart index 00306f3a7e..84e337d91b 100644 --- a/packages/flutter_tools/test/create_test.dart +++ b/packages/flutter_tools/test/create_test.dart @@ -27,34 +27,28 @@ defineTests() { temp.deleteSync(recursive: true); }); - // This test consistently times out on our windows bot. The code is already - // covered on the linux one. - // Also fails on mac, with create --out returning '69' - // TODO(devoncarew): https://github.com/flutter/flutter/issues/1709 - if (Platform.isLinux) { - // Verify that we create a project that is well-formed. - testUsingContext('flutter-simple', () async { - ArtifactStore.flutterRoot = '../..'; - CreateCommand command = new CreateCommand(); - CommandRunner runner = new CommandRunner('test_flutter', '') - ..addCommand(command); - await runner.run(['create', '--out', temp.path]) - .then((int code) => expect(code, equals(0))); + // Verify that we create a project that is well-formed. + testUsingContext('flutter-simple', () async { + ArtifactStore.flutterRoot = '../..'; + CreateCommand command = new CreateCommand(); + CommandRunner runner = new CommandRunner('test_flutter', '') + ..addCommand(command); + await runner.run(['create', '--out', temp.path]) + .then((int code) => expect(code, equals(0))); - String mainPath = path.join(temp.path, 'lib', 'main.dart'); - expect(new File(mainPath).existsSync(), true); - ProcessResult exec = Process.runSync( - sdkBinaryName('dartanalyzer'), ['--fatal-warnings', mainPath], - workingDirectory: temp.path - ); - if (exec.exitCode != 0) { - print(exec.stdout); - print(exec.stderr); - } - expect(exec.exitCode, 0); - }, - // This test can take a while due to network requests. - timeout: new Timeout(new Duration(minutes: 2))); - } + String mainPath = path.join(temp.path, 'lib', 'main.dart'); + expect(new File(mainPath).existsSync(), true); + ProcessResult exec = Process.runSync( + sdkBinaryName('dartanalyzer'), ['--fatal-warnings', mainPath], + workingDirectory: temp.path + ); + if (exec.exitCode != 0) { + print(exec.stdout); + print(exec.stderr); + } + expect(exec.exitCode, 0); + }, + // This test can take a while due to network requests. + timeout: new Timeout(new Duration(minutes: 2))); }); }