Merge branch 'master' into analysis_rework
This commit is contained in:
commit
2df43d50d3
2
bin/cache/engine.version
vendored
2
bin/cache/engine.version
vendored
@ -1 +1 @@
|
|||||||
d85ead8ec2556739924282e2b3f77ff077a45820
|
86837edd4ecbe5d28f16a770dff40a239f5b40b7
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
analyzer:
|
|
||||||
exclude:
|
|
||||||
- 'ios/.generated/**'
|
|
@ -7,7 +7,6 @@ import 'dart:math' as math;
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_sprites/flutter_sprites.dart';
|
import 'package:flutter_sprites/flutter_sprites.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart' as vec;
|
|
||||||
|
|
||||||
ImageMap _images;
|
ImageMap _images;
|
||||||
SpriteSheet _sprites;
|
SpriteSheet _sprites;
|
||||||
@ -581,7 +580,7 @@ class _FireworksNode extends NodeWithSize {
|
|||||||
speedVar: 50.0,
|
speedVar: 50.0,
|
||||||
startSize: 1.0,
|
startSize: 1.0,
|
||||||
startSizeVar: 0.5,
|
startSizeVar: 0.5,
|
||||||
gravity: new vec.Vector2(0.0, 30.0),
|
gravity: const Offset(0.0, 30.0),
|
||||||
colorSequence: new ColorSequence.fromStartAndEndColor(startColor, endColor)
|
colorSequence: new ColorSequence.fromStartAndEndColor(startColor, endColor)
|
||||||
);
|
);
|
||||||
system.position = new Point(randomDouble() * 1024.0, randomDouble() * 1024.0);
|
system.position = new Point(randomDouble() * 1024.0, randomDouble() * 1024.0);
|
||||||
|
3
examples/stocks/.analysis_options
Normal file
3
examples/stocks/.analysis_options
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
- 'lib/i18n/stock_messages_*.dart'
|
@ -28,7 +28,6 @@ class _DropDownMenuPainter extends CustomPainter {
|
|||||||
_DropDownMenuPainter({
|
_DropDownMenuPainter({
|
||||||
Color color,
|
Color color,
|
||||||
int elevation,
|
int elevation,
|
||||||
this.buttonRect,
|
|
||||||
this.selectedIndex,
|
this.selectedIndex,
|
||||||
Animation<double> resize
|
Animation<double> resize
|
||||||
}) : color = color,
|
}) : color = color,
|
||||||
@ -43,7 +42,6 @@ class _DropDownMenuPainter extends CustomPainter {
|
|||||||
|
|
||||||
final Color color;
|
final Color color;
|
||||||
final int elevation;
|
final int elevation;
|
||||||
final Rect buttonRect;
|
|
||||||
final int selectedIndex;
|
final int selectedIndex;
|
||||||
final Animation<double> resize;
|
final Animation<double> resize;
|
||||||
|
|
||||||
@ -52,12 +50,12 @@ class _DropDownMenuPainter extends CustomPainter {
|
|||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
final Tween<double> top = new Tween<double>(
|
final Tween<double> top = new Tween<double>(
|
||||||
begin: (selectedIndex * buttonRect.height + _kMenuVerticalPadding.top).clamp(0.0, size.height - buttonRect.height),
|
begin: (selectedIndex * _kMenuItemHeight + _kMenuVerticalPadding.top).clamp(0.0, size.height - _kMenuItemHeight),
|
||||||
end: 0.0
|
end: 0.0
|
||||||
);
|
);
|
||||||
|
|
||||||
final Tween<double> bottom = new Tween<double>(
|
final Tween<double> bottom = new Tween<double>(
|
||||||
begin: (top.begin + buttonRect.height).clamp(buttonRect.height, size.height),
|
begin: (top.begin + _kMenuItemHeight).clamp(_kMenuItemHeight, size.height),
|
||||||
end: size.height
|
end: size.height
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -68,7 +66,6 @@ class _DropDownMenuPainter extends CustomPainter {
|
|||||||
bool shouldRepaint(_DropDownMenuPainter oldPainter) {
|
bool shouldRepaint(_DropDownMenuPainter oldPainter) {
|
||||||
return oldPainter.color != color
|
return oldPainter.color != color
|
||||||
|| oldPainter.elevation != elevation
|
|| oldPainter.elevation != elevation
|
||||||
|| oldPainter.buttonRect != buttonRect
|
|
||||||
|| oldPainter.selectedIndex != selectedIndex
|
|| oldPainter.selectedIndex != selectedIndex
|
||||||
|| oldPainter.resize != resize;
|
|| oldPainter.resize != resize;
|
||||||
}
|
}
|
||||||
@ -130,7 +127,6 @@ class _DropDownMenu<T> extends StatusTransitionWidget {
|
|||||||
painter: new _DropDownMenuPainter(
|
painter: new _DropDownMenuPainter(
|
||||||
color: Theme.of(context).canvasColor,
|
color: Theme.of(context).canvasColor,
|
||||||
elevation: route.elevation,
|
elevation: route.elevation,
|
||||||
buttonRect: route.buttonRect,
|
|
||||||
selectedIndex: route.selectedIndex,
|
selectedIndex: route.selectedIndex,
|
||||||
resize: new CurvedAnimation(
|
resize: new CurvedAnimation(
|
||||||
parent: route.animation,
|
parent: route.animation,
|
||||||
@ -140,8 +136,9 @@ class _DropDownMenu<T> extends StatusTransitionWidget {
|
|||||||
),
|
),
|
||||||
child: new Material(
|
child: new Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: new Block(
|
child: new ScrollableList(
|
||||||
padding: _kMenuVerticalPadding,
|
padding: _kMenuVerticalPadding,
|
||||||
|
itemExtent: _kMenuItemHeight,
|
||||||
children: children
|
children: children
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -162,10 +159,11 @@ class _DropDownMenuRouteLayout extends SingleChildLayoutDelegate {
|
|||||||
// the view height. This ensures a tappable area outside of the simple menu
|
// the view height. This ensures a tappable area outside of the simple menu
|
||||||
// with which to dismiss the menu.
|
// with which to dismiss the menu.
|
||||||
// -- https://www.google.com/design/spec/components/menus.html#menus-simple-menus
|
// -- https://www.google.com/design/spec/components/menus.html#menus-simple-menus
|
||||||
final double maxHeight = math.max(0.0, constraints.maxHeight - 2 * buttonRect.height);
|
final double maxHeight = math.max(0.0, constraints.maxHeight - 2 * _kMenuItemHeight);
|
||||||
|
final double width = buttonRect.width;
|
||||||
return new BoxConstraints(
|
return new BoxConstraints(
|
||||||
minWidth: buttonRect.width,
|
minWidth: width,
|
||||||
maxWidth: buttonRect.width,
|
maxWidth: width,
|
||||||
minHeight: 0.0,
|
minHeight: 0.0,
|
||||||
maxHeight: maxHeight
|
maxHeight: maxHeight
|
||||||
);
|
);
|
||||||
@ -173,14 +171,15 @@ class _DropDownMenuRouteLayout extends SingleChildLayoutDelegate {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Offset getPositionForChild(Size size, Size childSize) {
|
Offset getPositionForChild(Size size, Size childSize) {
|
||||||
double top = buttonRect.top - selectedIndex * buttonRect.height - _kMenuVerticalPadding.top;
|
final double buttonTop = buttonRect.top;
|
||||||
double topPreferredLimit = buttonRect.height;
|
double top = buttonTop - selectedIndex * _kMenuItemHeight - _kMenuVerticalPadding.top;
|
||||||
|
double topPreferredLimit = _kMenuItemHeight;
|
||||||
if (top < topPreferredLimit)
|
if (top < topPreferredLimit)
|
||||||
top = math.min(buttonRect.top, topPreferredLimit);
|
top = math.min(buttonTop, topPreferredLimit);
|
||||||
double bottom = top + childSize.height;
|
double bottom = top + childSize.height;
|
||||||
double bottomPreferredLimit = size.height - buttonRect.height;
|
double bottomPreferredLimit = size.height - _kMenuItemHeight;
|
||||||
if (bottom > bottomPreferredLimit) {
|
if (bottom > bottomPreferredLimit) {
|
||||||
bottom = math.max(buttonRect.bottom, bottomPreferredLimit);
|
bottom = math.max(buttonTop + _kMenuItemHeight, bottomPreferredLimit);
|
||||||
top = bottom - childSize.height;
|
top = bottom - childSize.height;
|
||||||
}
|
}
|
||||||
assert(top >= 0.0);
|
assert(top >= 0.0);
|
||||||
@ -277,7 +276,7 @@ class DropDownMenuItem<T> extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new Container(
|
return new Container(
|
||||||
height: _kMenuItemHeight,
|
height: _kMenuItemHeight,
|
||||||
padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: _kTopMargin),
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
child: new DefaultTextStyle(
|
child: new DefaultTextStyle(
|
||||||
style: Theme.of(context).textTheme.subhead,
|
style: Theme.of(context).textTheme.subhead,
|
||||||
child: new Baseline(
|
child: new Baseline(
|
||||||
@ -363,7 +362,7 @@ class DropDownButton<T> extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DropDownButtonState<T> extends State<DropDownButton<T>> {
|
class _DropDownButtonState<T> extends State<DropDownButton<T>> {
|
||||||
final GlobalKey indexedStackKey = new GlobalKey(debugLabel: 'DropDownButton.IndexedStack');
|
final GlobalKey _itemKey = new GlobalKey(debugLabel: 'DropDownButton item key');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -390,13 +389,13 @@ class _DropDownButtonState<T> extends State<DropDownButton<T>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleTap() {
|
void _handleTap() {
|
||||||
final RenderBox renderBox = indexedStackKey.currentContext.findRenderObject();
|
final RenderBox itemBox = _itemKey.currentContext.findRenderObject();
|
||||||
final Rect buttonRect = renderBox.localToGlobal(Point.origin) & renderBox.size;
|
final Rect itemRect = itemBox.localToGlobal(Point.origin) & itemBox.size;
|
||||||
final Completer<_DropDownRouteResult<T>> completer = new Completer<_DropDownRouteResult<T>>();
|
final Completer<_DropDownRouteResult<T>> completer = new Completer<_DropDownRouteResult<T>>();
|
||||||
Navigator.push(context, new _DropDownRoute<T>(
|
Navigator.push(context, new _DropDownRoute<T>(
|
||||||
completer: completer,
|
completer: completer,
|
||||||
items: config.items,
|
items: config.items,
|
||||||
buttonRect: _kMenuHorizontalPadding.inflateRect(buttonRect),
|
buttonRect: _kMenuHorizontalPadding.inflateRect(itemRect),
|
||||||
selectedIndex: _selectedIndex,
|
selectedIndex: _selectedIndex,
|
||||||
elevation: config.elevation
|
elevation: config.elevation
|
||||||
));
|
));
|
||||||
@ -412,27 +411,27 @@ class _DropDownButtonState<T> extends State<DropDownButton<T>> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert(debugCheckHasMaterial(context));
|
assert(debugCheckHasMaterial(context));
|
||||||
Widget result = new Row(
|
Widget result = new Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.collapse,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
// We use an IndexedStack to make sure we have enough width to show any
|
||||||
|
// possible item as the selected item without changing size.
|
||||||
new IndexedStack(
|
new IndexedStack(
|
||||||
children: config.items,
|
children: config.items,
|
||||||
key: indexedStackKey,
|
key: _itemKey,
|
||||||
index: _selectedIndex,
|
index: _selectedIndex,
|
||||||
alignment: FractionalOffset.topCenter
|
alignment: FractionalOffset.topCenter
|
||||||
),
|
),
|
||||||
new Container(
|
new Icon(icon: Icons.arrow_drop_down, size: 36.0)
|
||||||
child: new Icon(icon: Icons.arrow_drop_down, size: 36.0),
|
]
|
||||||
padding: const EdgeInsets.only(top: _kTopMargin)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
mainAxisAlignment: MainAxisAlignment.collapse
|
|
||||||
);
|
);
|
||||||
if (DropDownButtonHideUnderline.at(context)) {
|
if (DropDownButtonHideUnderline.at(context)) {
|
||||||
result = new Padding(
|
result = new Padding(
|
||||||
padding: const EdgeInsets.only(bottom: _kBottomBorderHeight),
|
padding: const EdgeInsets.only(top: _kTopMargin, bottom: _kBottomBorderHeight),
|
||||||
child: result
|
child: result
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result = new Container(
|
result = new Container(
|
||||||
|
padding: const EdgeInsets.only(top: _kTopMargin),
|
||||||
decoration: const BoxDecoration(border: _kDropDownUnderline),
|
decoration: const BoxDecoration(border: _kDropDownUnderline),
|
||||||
child: result
|
child: result
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,18 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'framework.dart';
|
import 'framework.dart';
|
||||||
|
|
||||||
/// Displays performance statistics.
|
/// Displays performance statistics.
|
||||||
|
///
|
||||||
|
/// The overlay show two time series. The first shows how much time was required
|
||||||
|
/// on this thread to produce each frame. The second shows how much time was
|
||||||
|
/// required on the GPU thread to produce each frame. Ideally, both these values
|
||||||
|
/// would be less than the total frame budget for the hardware on which the app
|
||||||
|
/// is running. For example, if the hardware has a screen that updates at 60 Hz,
|
||||||
|
/// each thread should ideally spend less than 16 ms producing each frame. This
|
||||||
|
/// ideal condition is indicated by a green vertical line for each thread.
|
||||||
|
///
|
||||||
|
/// The simplest way to show the performance overlay is to set
|
||||||
|
/// [MaterialApp.showPerformanceOverlay] or [WidgetsApp.showPerformanceOverlay]
|
||||||
|
/// to `true`.
|
||||||
class PerformanceOverlay extends LeafRenderObjectWidget {
|
class PerformanceOverlay extends LeafRenderObjectWidget {
|
||||||
// TODO(abarth): We should have a page on the web site with a screenshot and
|
// TODO(abarth): We should have a page on the web site with a screenshot and
|
||||||
// an explanation of all the various readouts.
|
// an explanation of all the various readouts.
|
||||||
|
@ -38,6 +38,14 @@ void main() {
|
|||||||
tester.pump();
|
tester.pump();
|
||||||
tester.pump(const Duration(seconds: 1)); // finish the menu animation
|
tester.pump(const Duration(seconds: 1)); // finish the menu animation
|
||||||
|
|
||||||
|
// We should have two copies of item 5, one in the menu and one in the
|
||||||
|
// button itself.
|
||||||
|
expect(find.text('5').evaluate().length, 2);
|
||||||
|
|
||||||
|
// We should only have one copy of item 19, which is in the button itself.
|
||||||
|
// The copy in the menu shouldn't be in the tree because it's off-screen.
|
||||||
|
expect(find.text('19').evaluate().length, 1);
|
||||||
|
|
||||||
tester.tap(find.byConfig(button));
|
tester.tap(find.byConfig(button));
|
||||||
|
|
||||||
// Ideally this would be 4 because the menu would be overscrolled to the
|
// Ideally this would be 4 because the menu would be overscrolled to the
|
||||||
|
@ -41,6 +41,49 @@ class _ParticleAccelerations {
|
|||||||
/// number of particles can never exceed the [maxParticles] limit.
|
/// number of particles can never exceed the [maxParticles] limit.
|
||||||
class ParticleSystem extends Node {
|
class ParticleSystem extends Node {
|
||||||
|
|
||||||
|
ParticleSystem(this.texture,
|
||||||
|
{this.life: 1.5,
|
||||||
|
this.lifeVar: 1.0,
|
||||||
|
this.posVar: Point.origin,
|
||||||
|
this.startSize: 2.5,
|
||||||
|
this.startSizeVar: 0.5,
|
||||||
|
this.endSize: 0.0,
|
||||||
|
this.endSizeVar: 0.0,
|
||||||
|
this.startRotation: 0.0,
|
||||||
|
this.startRotationVar: 0.0,
|
||||||
|
this.endRotation: 0.0,
|
||||||
|
this.endRotationVar: 0.0,
|
||||||
|
this.rotateToMovement : false,
|
||||||
|
this.direction: 0.0,
|
||||||
|
this.directionVar: 360.0,
|
||||||
|
this.speed: 100.0,
|
||||||
|
this.speedVar: 50.0,
|
||||||
|
this.radialAcceleration: 0.0,
|
||||||
|
this.radialAccelerationVar: 0.0,
|
||||||
|
this.tangentialAcceleration: 0.0,
|
||||||
|
this.tangentialAccelerationVar: 0.0,
|
||||||
|
this.maxParticles: 100,
|
||||||
|
this.emissionRate: 50.0,
|
||||||
|
this.colorSequence,
|
||||||
|
this.alphaVar: 0,
|
||||||
|
this.redVar: 0,
|
||||||
|
this.greenVar: 0,
|
||||||
|
this.blueVar: 0,
|
||||||
|
this.transferMode: TransferMode.plus,
|
||||||
|
this.numParticlesToEmit: 0,
|
||||||
|
this.autoRemoveOnFinish: true,
|
||||||
|
Offset gravity
|
||||||
|
}) {
|
||||||
|
this.gravity = gravity;
|
||||||
|
_particles = new List<_Particle>();
|
||||||
|
_emitCounter = 0.0;
|
||||||
|
// _elapsedTime = 0.0;
|
||||||
|
if (_gravity == null)
|
||||||
|
_gravity = new Vector2.zero();
|
||||||
|
if (colorSequence == null)
|
||||||
|
colorSequence = new ColorSequence.fromStartAndEndColor(new Color(0xffffffff), new Color(0x00ffffff));
|
||||||
|
}
|
||||||
|
|
||||||
/// The texture used to draw each individual sprite.
|
/// The texture used to draw each individual sprite.
|
||||||
Texture texture;
|
Texture texture;
|
||||||
|
|
||||||
@ -108,7 +151,21 @@ class ParticleSystem extends Node {
|
|||||||
double tangentialAccelerationVar;
|
double tangentialAccelerationVar;
|
||||||
|
|
||||||
/// The gravity vector of the particle system.
|
/// The gravity vector of the particle system.
|
||||||
Vector2 gravity;
|
Offset get gravity {
|
||||||
|
if (_gravity == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new Offset(_gravity.x, _gravity.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 _gravity;
|
||||||
|
|
||||||
|
void set gravity(Offset gravity) {
|
||||||
|
if (gravity == null)
|
||||||
|
_gravity = null;
|
||||||
|
else
|
||||||
|
_gravity = new Vector2(gravity.dx, gravity.dy);
|
||||||
|
}
|
||||||
|
|
||||||
/// The maximum number of particles the system can display at a single time.
|
/// The maximum number of particles the system can display at a single time.
|
||||||
int maxParticles;
|
int maxParticles;
|
||||||
@ -157,45 +214,6 @@ class ParticleSystem extends Node {
|
|||||||
..filterQuality = FilterQuality.low
|
..filterQuality = FilterQuality.low
|
||||||
..isAntiAlias = false;
|
..isAntiAlias = false;
|
||||||
|
|
||||||
ParticleSystem(this.texture,
|
|
||||||
{this.life: 1.5,
|
|
||||||
this.lifeVar: 1.0,
|
|
||||||
this.posVar: Point.origin,
|
|
||||||
this.startSize: 2.5,
|
|
||||||
this.startSizeVar: 0.5,
|
|
||||||
this.endSize: 0.0,
|
|
||||||
this.endSizeVar: 0.0,
|
|
||||||
this.startRotation: 0.0,
|
|
||||||
this.startRotationVar: 0.0,
|
|
||||||
this.endRotation: 0.0,
|
|
||||||
this.endRotationVar: 0.0,
|
|
||||||
this.rotateToMovement : false,
|
|
||||||
this.direction: 0.0,
|
|
||||||
this.directionVar: 360.0,
|
|
||||||
this.speed: 100.0,
|
|
||||||
this.speedVar: 50.0,
|
|
||||||
this.radialAcceleration: 0.0,
|
|
||||||
this.radialAccelerationVar: 0.0,
|
|
||||||
this.tangentialAcceleration: 0.0,
|
|
||||||
this.tangentialAccelerationVar: 0.0,
|
|
||||||
this.gravity,
|
|
||||||
this.maxParticles: 100,
|
|
||||||
this.emissionRate: 50.0,
|
|
||||||
this.colorSequence,
|
|
||||||
this.alphaVar: 0,
|
|
||||||
this.redVar: 0,
|
|
||||||
this.greenVar: 0,
|
|
||||||
this.blueVar: 0,
|
|
||||||
this.transferMode: TransferMode.plus,
|
|
||||||
this.numParticlesToEmit: 0,
|
|
||||||
this.autoRemoveOnFinish: true}) {
|
|
||||||
_particles = new List<_Particle>();
|
|
||||||
_emitCounter = 0.0;
|
|
||||||
// _elapsedTime = 0.0;
|
|
||||||
if (gravity == null) gravity = new Vector2.zero();
|
|
||||||
if (colorSequence == null) colorSequence = new ColorSequence.fromStartAndEndColor(new Color(0xffffffff), new Color(0x00ffffff));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update(double dt) {
|
void update(double dt) {
|
||||||
// TODO: Fix this (it's a temp fix for low framerates)
|
// TODO: Fix this (it's a temp fix for low framerates)
|
||||||
@ -249,11 +267,11 @@ class ParticleSystem extends Node {
|
|||||||
tangential.scale(particle.accelerations.tangentialAccel);
|
tangential.scale(particle.accelerations.tangentialAccel);
|
||||||
|
|
||||||
// (gravity + radial + tangential) * dt
|
// (gravity + radial + tangential) * dt
|
||||||
Vector2 accel = (gravity + radial + tangential).scale(dt);
|
final Vector2 accel = (_gravity + radial + tangential).scale(dt);
|
||||||
particle.dir += accel;
|
particle.dir += accel;
|
||||||
} else if (gravity[0] != 0.0 || gravity[1] != 0) {
|
} else if (_gravity[0] != 0.0 || _gravity[1] != 0) {
|
||||||
// gravity
|
// gravity
|
||||||
Vector2 accel = new Vector2.copy(gravity).scale(dt);
|
final Vector2 accel = new Vector2.copy(_gravity).scale(dt);
|
||||||
particle.dir += accel;
|
particle.dir += accel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +380,8 @@ class ParticleSystem extends Node {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas) {
|
void paint(Canvas canvas) {
|
||||||
if (opacity == 0.0) return;
|
if (opacity == 0.0)
|
||||||
|
return;
|
||||||
|
|
||||||
List<RSTransform> transforms = <RSTransform>[];
|
List<RSTransform> transforms = <RSTransform>[];
|
||||||
List<Rect> rects = <Rect>[];
|
List<Rect> rects = <Rect>[];
|
||||||
|
@ -102,7 +102,7 @@ Future<Null> main(List<String> args) async {
|
|||||||
|
|
||||||
flutterUsage.sendException(error, chain);
|
flutterUsage.sendException(error, chain);
|
||||||
|
|
||||||
if (Platform.environment.containsKey('FLUTTER_DEV')) {
|
if (Platform.environment.containsKey('FLUTTER_DEV') || isRunningOnBot) {
|
||||||
// If we're working on the tools themselves, just print the stack trace.
|
// If we're working on the tools themselves, just print the stack trace.
|
||||||
stderr.writeln('$error');
|
stderr.writeln('$error');
|
||||||
stderr.writeln(chain.terse.toString());
|
stderr.writeln(chain.terse.toString());
|
||||||
|
@ -49,23 +49,49 @@ class AndroidDevice extends Device {
|
|||||||
final String modelID;
|
final String modelID;
|
||||||
final String deviceCodeName;
|
final String deviceCodeName;
|
||||||
|
|
||||||
|
Map<String, String> _properties;
|
||||||
bool _isLocalEmulator;
|
bool _isLocalEmulator;
|
||||||
|
TargetPlatform _platform;
|
||||||
|
|
||||||
|
String _getProperty(String name) {
|
||||||
|
if (_properties == null) {
|
||||||
|
String getpropOutput = runCheckedSync(adbCommandForDevice(<String>['shell', 'getprop']));
|
||||||
|
RegExp propertyExp = new RegExp(r'\[(.*?)\]: \[(.*?)\]');
|
||||||
|
_properties = <String, String>{};
|
||||||
|
for (Match m in propertyExp.allMatches(getpropOutput)) {
|
||||||
|
_properties[m.group(1)] = m.group(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _properties[name];
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get isLocalEmulator {
|
bool get isLocalEmulator {
|
||||||
if (_isLocalEmulator == null) {
|
if (_isLocalEmulator == null) {
|
||||||
|
String characteristics = _getProperty('ro.build.characteristics');
|
||||||
|
_isLocalEmulator = characteristics != null && characteristics.contains('emulator');
|
||||||
|
}
|
||||||
|
return _isLocalEmulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TargetPlatform get platform {
|
||||||
|
if (_platform == null) {
|
||||||
// http://developer.android.com/ndk/guides/abis.html (x86, armeabi-v7a, ...)
|
// http://developer.android.com/ndk/guides/abis.html (x86, armeabi-v7a, ...)
|
||||||
try {
|
switch (_getProperty('ro.product.cpu.abi')) {
|
||||||
String value = runCheckedSync(adbCommandForDevice(
|
case 'x86_64':
|
||||||
<String>['shell', 'getprop', 'ro.product.cpu.abi']
|
_platform = TargetPlatform.android_x64;
|
||||||
));
|
break;
|
||||||
_isLocalEmulator = value.startsWith('x86');
|
case 'x86':
|
||||||
} catch (error) {
|
_platform = TargetPlatform.android_x86;
|
||||||
_isLocalEmulator = false;
|
break;
|
||||||
|
default:
|
||||||
|
_platform = TargetPlatform.android_arm;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _isLocalEmulator;
|
return _platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
_AdbLogReader _logReader;
|
_AdbLogReader _logReader;
|
||||||
@ -123,9 +149,7 @@ class AndroidDevice extends Device {
|
|||||||
runCheckedSync(<String>[androidSdk.adbPath, 'start-server']);
|
runCheckedSync(<String>[androidSdk.adbPath, 'start-server']);
|
||||||
|
|
||||||
// Sample output: '22'
|
// Sample output: '22'
|
||||||
String sdkVersion = runCheckedSync(
|
String sdkVersion = _getProperty('ro.build.version.sdk');
|
||||||
adbCommandForDevice(<String>['shell', 'getprop', 'ro.build.version.sdk'])
|
|
||||||
).trimRight();
|
|
||||||
|
|
||||||
int sdkVersionParsed = int.parse(sdkVersion, onError: (String source) => null);
|
int sdkVersionParsed = int.parse(sdkVersion, onError: (String source) => null);
|
||||||
if (sdkVersionParsed == null) {
|
if (sdkVersionParsed == null) {
|
||||||
@ -333,9 +357,6 @@ class AndroidDevice extends Device {
|
|||||||
return runCommandAndStreamOutput(command).then((int exitCode) => exitCode == 0);
|
return runCommandAndStreamOutput(command).then((int exitCode) => exitCode == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
TargetPlatform get platform => isLocalEmulator ? TargetPlatform.android_x64 : TargetPlatform.android_arm;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void clearLogs() {
|
void clearLogs() {
|
||||||
runSync(adbCommandForDevice(<String>['logcat', '-c']));
|
runSync(adbCommandForDevice(<String>['logcat', '-c']));
|
||||||
|
@ -103,6 +103,7 @@ ApplicationPackage getApplicationPackageForPlatform(TargetPlatform platform) {
|
|||||||
switch (platform) {
|
switch (platform) {
|
||||||
case TargetPlatform.android_arm:
|
case TargetPlatform.android_arm:
|
||||||
case TargetPlatform.android_x64:
|
case TargetPlatform.android_x64:
|
||||||
|
case TargetPlatform.android_x86:
|
||||||
return new AndroidApk.fromCurrentDirectory();
|
return new AndroidApk.fromCurrentDirectory();
|
||||||
case TargetPlatform.ios:
|
case TargetPlatform.ios:
|
||||||
return new IOSApp.fromCurrentDirectory();
|
return new IOSApp.fromCurrentDirectory();
|
||||||
@ -122,6 +123,7 @@ class ApplicationPackageStore {
|
|||||||
switch (platform) {
|
switch (platform) {
|
||||||
case TargetPlatform.android_arm:
|
case TargetPlatform.android_arm:
|
||||||
case TargetPlatform.android_x64:
|
case TargetPlatform.android_x64:
|
||||||
|
case TargetPlatform.android_x86:
|
||||||
android ??= new AndroidApk.fromCurrentDirectory();
|
android ??= new AndroidApk.fromCurrentDirectory();
|
||||||
return android;
|
return android;
|
||||||
case TargetPlatform.ios:
|
case TargetPlatform.ios:
|
||||||
|
@ -8,6 +8,13 @@ import 'dart:io';
|
|||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
bool get isRunningOnBot {
|
||||||
|
// https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
|
||||||
|
return
|
||||||
|
Platform.environment['TRAVIS'] == 'true' ||
|
||||||
|
Platform.environment['CONTINUOUS_INTEGRATION'] == 'true';
|
||||||
|
}
|
||||||
|
|
||||||
String hex(List<int> bytes) {
|
String hex(List<int> bytes) {
|
||||||
StringBuffer result = new StringBuffer();
|
StringBuffer result = new StringBuffer();
|
||||||
for (int part in bytes)
|
for (int part in bytes)
|
||||||
|
@ -41,6 +41,7 @@ String getNameForHostPlatform(HostPlatform platform) {
|
|||||||
enum TargetPlatform {
|
enum TargetPlatform {
|
||||||
android_arm,
|
android_arm,
|
||||||
android_x64,
|
android_x64,
|
||||||
|
android_x86,
|
||||||
ios,
|
ios,
|
||||||
darwin_x64,
|
darwin_x64,
|
||||||
linux_x64
|
linux_x64
|
||||||
|
@ -192,7 +192,8 @@ class FlutterEngine {
|
|||||||
'android-arm',
|
'android-arm',
|
||||||
'android-arm-profile',
|
'android-arm-profile',
|
||||||
'android-arm-release',
|
'android-arm-release',
|
||||||
'android-x64'
|
'android-x64',
|
||||||
|
'android-x86',
|
||||||
];
|
];
|
||||||
|
|
||||||
if (Platform.isMacOS)
|
if (Platform.isMacOS)
|
||||||
|
@ -34,15 +34,8 @@ class AnalyzeCommand extends FlutterCommand {
|
|||||||
argParser.addFlag('watch', help: 'Run analysis continuously, watching the filesystem for changes.', negatable: false);
|
argParser.addFlag('watch', help: 'Run analysis continuously, watching the filesystem for changes.', negatable: false);
|
||||||
argParser.addOption('dart-sdk', help: 'The path to the Dart SDK.', hide: true);
|
argParser.addOption('dart-sdk', help: 'The path to the Dart SDK.', hide: true);
|
||||||
|
|
||||||
// Options to enable a benchmarking mode.
|
// Hidden option to enable a benchmarking mode.
|
||||||
argParser.addFlag('benchmark',
|
argParser.addFlag('benchmark', negatable: false, hide: true);
|
||||||
negatable: false,
|
|
||||||
hide: true
|
|
||||||
);
|
|
||||||
argParser.addOption('benchmark-expected',
|
|
||||||
hide: true,
|
|
||||||
help: 'The time (in seconds) that the benchmark is expected to run.'
|
|
||||||
);
|
|
||||||
|
|
||||||
usesPubOption();
|
usesPubOption();
|
||||||
}
|
}
|
||||||
@ -393,19 +386,12 @@ class AnalyzeCommand extends FlutterCommand {
|
|||||||
|
|
||||||
void _writeBenchmark(Stopwatch stopwatch, int errorCount) {
|
void _writeBenchmark(Stopwatch stopwatch, int errorCount) {
|
||||||
final String benchmarkOut = 'analysis_benchmark.json';
|
final String benchmarkOut = 'analysis_benchmark.json';
|
||||||
String expectedTime = argResults['benchmark-expected'];
|
|
||||||
|
|
||||||
Map<String, dynamic> data = <String, dynamic>{
|
Map<String, dynamic> data = <String, dynamic>{
|
||||||
'time': (stopwatch.elapsedMilliseconds / 1000.0),
|
'time': (stopwatch.elapsedMilliseconds / 1000.0),
|
||||||
'issues': errorCount
|
'issues': errorCount
|
||||||
};
|
};
|
||||||
|
|
||||||
if (expectedTime != null)
|
|
||||||
data['expected'] = expectedTime;
|
|
||||||
|
|
||||||
JsonEncoder encoder = new JsonEncoder.withIndent(' ');
|
JsonEncoder encoder = new JsonEncoder.withIndent(' ');
|
||||||
new File(benchmarkOut).writeAsStringSync(encoder.convert(data) + '\n');
|
new File(benchmarkOut).writeAsStringSync(encoder.convert(data) + '\n');
|
||||||
|
|
||||||
printStatus('Analysis benchmark written to $benchmarkOut.');
|
printStatus('Analysis benchmark written to $benchmarkOut.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,6 +261,21 @@ class BuildApkCommand extends FlutterCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the directory name within the APK that is used for native code libraries
|
||||||
|
// on the given platform.
|
||||||
|
String getAbiDirectory(TargetPlatform platform) {
|
||||||
|
switch (platform) {
|
||||||
|
case TargetPlatform.android_arm:
|
||||||
|
return 'armeabi-v7a';
|
||||||
|
case TargetPlatform.android_x64:
|
||||||
|
return 'x86_64';
|
||||||
|
case TargetPlatform.android_x86:
|
||||||
|
return 'x86';
|
||||||
|
default:
|
||||||
|
throw new Exception('Unsupported platform.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<_ApkComponents> _findApkComponents(
|
Future<_ApkComponents> _findApkComponents(
|
||||||
TargetPlatform platform,
|
TargetPlatform platform,
|
||||||
BuildMode buildMode,
|
BuildMode buildMode,
|
||||||
@ -274,7 +289,7 @@ Future<_ApkComponents> _findApkComponents(
|
|||||||
components.extraFiles = extraFiles != null ? extraFiles : <String, File>{};
|
components.extraFiles = extraFiles != null ? extraFiles : <String, File>{};
|
||||||
|
|
||||||
if (tools.isLocalEngine) {
|
if (tools.isLocalEngine) {
|
||||||
String abiDir = platform == TargetPlatform.android_arm ? 'armeabi-v7a' : 'x86_64';
|
String abiDir = getAbiDirectory(platform);
|
||||||
String enginePath = tools.engineSrcPath;
|
String enginePath = tools.engineSrcPath;
|
||||||
String buildDir = tools.getEngineArtifactsDirectory(platform, buildMode).path;
|
String buildDir = tools.getEngineArtifactsDirectory(platform, buildMode).path;
|
||||||
|
|
||||||
@ -348,7 +363,7 @@ int _buildApk(
|
|||||||
|
|
||||||
_AssetBuilder artifactBuilder = new _AssetBuilder(tempDir, 'artifacts');
|
_AssetBuilder artifactBuilder = new _AssetBuilder(tempDir, 'artifacts');
|
||||||
artifactBuilder.add(classesDex, 'classes.dex');
|
artifactBuilder.add(classesDex, 'classes.dex');
|
||||||
String abiDir = platform == TargetPlatform.android_arm ? 'armeabi-v7a' : 'x86_64';
|
String abiDir = getAbiDirectory(platform);
|
||||||
artifactBuilder.add(components.libSkyShell, 'lib/$abiDir/libsky_shell.so');
|
artifactBuilder.add(components.libSkyShell, 'lib/$abiDir/libsky_shell.so');
|
||||||
|
|
||||||
for (String relativePath in components.extraFiles.keys)
|
for (String relativePath in components.extraFiles.keys)
|
||||||
|
@ -10,7 +10,7 @@ import '../base/process.dart';
|
|||||||
import '../dart/pub.dart';
|
import '../dart/pub.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../runner/flutter_command.dart';
|
import '../runner/flutter_command.dart';
|
||||||
import '../runner/version.dart';
|
import '../version.dart';
|
||||||
|
|
||||||
class UpgradeCommand extends FlutterCommand {
|
class UpgradeCommand extends FlutterCommand {
|
||||||
@override
|
@override
|
||||||
|
@ -57,7 +57,7 @@ class DeviceManager {
|
|||||||
devices = devices.where((Device device) {
|
devices = devices.where((Device device) {
|
||||||
return (device.id.toLowerCase().startsWith(deviceId) ||
|
return (device.id.toLowerCase().startsWith(deviceId) ||
|
||||||
device.name.toLowerCase().startsWith(deviceId));
|
device.name.toLowerCase().startsWith(deviceId));
|
||||||
});
|
}).toList();
|
||||||
|
|
||||||
return devices.length == 1 ? devices.first : null;
|
return devices.length == 1 ? devices.first : null;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import 'base/context.dart';
|
|||||||
import 'base/os.dart';
|
import 'base/os.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
import 'ios/ios_workflow.dart';
|
import 'ios/ios_workflow.dart';
|
||||||
import 'runner/version.dart';
|
import 'version.dart';
|
||||||
|
|
||||||
const Map<String, String> _osNames = const <String, String>{
|
const Map<String, String> _osNames = const <String, String>{
|
||||||
'macos': 'Mac OS',
|
'macos': 'Mac OS',
|
||||||
|
@ -18,7 +18,7 @@ import '../build_configuration.dart';
|
|||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../package_map.dart';
|
import '../package_map.dart';
|
||||||
import '../toolchain.dart';
|
import '../toolchain.dart';
|
||||||
import 'version.dart';
|
import '../version.dart';
|
||||||
|
|
||||||
const String kFlutterRootEnvironmentVariableName = 'FLUTTER_ROOT'; // should point to //flutter/ (root of flutter/flutter repo)
|
const String kFlutterRootEnvironmentVariableName = 'FLUTTER_ROOT'; // should point to //flutter/ (root of flutter/flutter repo)
|
||||||
const String kFlutterEngineEnvironmentVariableName = 'FLUTTER_ENGINE'; // should point to //engine/src/ (root of flutter/engine repo)
|
const String kFlutterEngineEnvironmentVariableName = 'FLUTTER_ENGINE'; // should point to //engine/src/ (root of flutter/engine repo)
|
||||||
|
@ -144,10 +144,9 @@ class ToolConfiguration {
|
|||||||
|
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case TargetPlatform.android_arm:
|
case TargetPlatform.android_arm:
|
||||||
type = 'android';
|
|
||||||
break;
|
|
||||||
case TargetPlatform.android_x64:
|
case TargetPlatform.android_x64:
|
||||||
type = 'android_sim';
|
case TargetPlatform.android_x86:
|
||||||
|
type = 'android';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// TODO(devoncarew): We will need an ios vs ios_x86 target (for ios vs. ios_sim).
|
// TODO(devoncarew): We will need an ios vs ios_x86 target (for ios vs. ios_sim).
|
||||||
@ -166,6 +165,18 @@ class ToolConfiguration {
|
|||||||
if (isAotBuildMode(mode))
|
if (isAotBuildMode(mode))
|
||||||
buildOutputPath += '_Deploy';
|
buildOutputPath += '_Deploy';
|
||||||
|
|
||||||
|
// Add a suffix for the target architecture.
|
||||||
|
switch (platform) {
|
||||||
|
case TargetPlatform.android_x64:
|
||||||
|
buildOutputPath += '_x64';
|
||||||
|
break;
|
||||||
|
case TargetPlatform.android_x86:
|
||||||
|
buildOutputPath += '_x86';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return new Directory(path.join(engineSrcPath, buildOutputPath));
|
return new Directory(path.join(engineSrcPath, buildOutputPath));
|
||||||
} else {
|
} else {
|
||||||
String suffix = mode != BuildMode.debug ? '-${getModeName(mode)}' : '';
|
String suffix = mode != BuildMode.debug ? '-${getModeName(mode)}' : '';
|
||||||
|
@ -8,8 +8,9 @@ import 'package:usage/src/usage_impl_io.dart';
|
|||||||
import 'package:usage/usage.dart';
|
import 'package:usage/usage.dart';
|
||||||
|
|
||||||
import 'base/context.dart';
|
import 'base/context.dart';
|
||||||
|
import 'base/utils.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
import 'runner/version.dart';
|
import 'version.dart';
|
||||||
|
|
||||||
// TODO(devoncarew): We'll need to do some work on the user agent in order to
|
// TODO(devoncarew): We'll need to do some work on the user agent in order to
|
||||||
// correctly track usage by operating system (dart-lang/usage/issues/70).
|
// correctly track usage by operating system (dart-lang/usage/issues/70).
|
||||||
@ -22,7 +23,19 @@ class Usage {
|
|||||||
Usage() {
|
Usage() {
|
||||||
String version = FlutterVersion.getVersionString(whitelistBranchName: true);
|
String version = FlutterVersion.getVersionString(whitelistBranchName: true);
|
||||||
_analytics = new AnalyticsIO(_kFlutterUA, 'flutter', version);
|
_analytics = new AnalyticsIO(_kFlutterUA, 'flutter', version);
|
||||||
_analytics.analyticsOpt = AnalyticsOpt.optOut;
|
|
||||||
|
bool runningOnCI = false;
|
||||||
|
|
||||||
|
// Many CI systems don't do a full git checkout.
|
||||||
|
if (version.startsWith('unknown/'))
|
||||||
|
runningOnCI = true;
|
||||||
|
|
||||||
|
// Check for common CI systems.
|
||||||
|
if (isRunningOnBot)
|
||||||
|
runningOnCI = true;
|
||||||
|
|
||||||
|
// If we think we're running on a CI system, default to not sending analytics.
|
||||||
|
_analytics.analyticsOpt = runningOnCI ? AnalyticsOpt.optIn : AnalyticsOpt.optOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns [Usage] active in the current app context.
|
/// Returns [Usage] active in the current app context.
|
||||||
@ -82,10 +95,14 @@ class Usage {
|
|||||||
|
|
||||||
final String versionString = FlutterVersion.getVersionString(whitelistBranchName: true);
|
final String versionString = FlutterVersion.getVersionString(whitelistBranchName: true);
|
||||||
|
|
||||||
|
String welcomeString = 'Welcome to Flutter! - Flutter version $versionString - https://flutter.io';
|
||||||
|
welcomeString = welcomeString.padLeft((welcomeString.length + 100) ~/ 2);
|
||||||
|
welcomeString = welcomeString.padRight(100);
|
||||||
|
|
||||||
printStatus('');
|
printStatus('');
|
||||||
printStatus('''
|
printStatus('''
|
||||||
╔════════════════════════════════════════════════════════════════════════════════════════════════════╗
|
╔════════════════════════════════════════════════════════════════════════════════════════════════════╗
|
||||||
║ Welcome to Flutter! - Flutter version $versionString - https://flutter.io ║
|
║$welcomeString║
|
||||||
║ ║
|
║ ║
|
||||||
║ The Flutter tool anonymously reports feature usage statistics and basic crash reports to Google in ║
|
║ The Flutter tool anonymously reports feature usage statistics and basic crash reports to Google in ║
|
||||||
║ order to help Google contribute improvements to Flutter over time. See Google's privacy policy: ║
|
║ order to help Google contribute improvements to Flutter over time. See Google's privacy policy: ║
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import '../artifacts.dart';
|
import 'artifacts.dart';
|
||||||
import '../base/process.dart';
|
import 'base/process.dart';
|
||||||
|
|
||||||
final Set<String> kKnownBranchNames = new Set<String>.from(<String>[
|
final Set<String> kKnownBranchNames = new Set<String>.from(<String>[
|
||||||
'master',
|
'master',
|
||||||
@ -64,7 +64,7 @@ class FlutterVersion {
|
|||||||
return new FlutterVersion(flutterRoot != null ? flutterRoot : ArtifactStore.flutterRoot);
|
return new FlutterVersion(flutterRoot != null ? flutterRoot : ArtifactStore.flutterRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a short string for the version (`a76bc8e22b/alpha`).
|
/// Return a short string for the version (`alpha/a76bc8e22b`).
|
||||||
static String getVersionString({ bool whitelistBranchName: false }) {
|
static String getVersionString({ bool whitelistBranchName: false }) {
|
||||||
final String cwd = ArtifactStore.flutterRoot;
|
final String cwd = ArtifactStore.flutterRoot;
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class FlutterVersion {
|
|||||||
branch = 'dev';
|
branch = 'dev';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '$commit/$branch';
|
return '$branch/$commit';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
# Download dependencies flutter
|
# disable analytics on the bots and download Flutter dependencies
|
||||||
./bin/flutter --version
|
|
||||||
|
|
||||||
# Disable analytics on the bots (to avoid skewing analytics data).
|
|
||||||
./bin/flutter config --no-analytics
|
./bin/flutter config --no-analytics
|
||||||
|
|
||||||
|
# run pub get in all the repo packages
|
||||||
./bin/flutter update-packages
|
./bin/flutter update-packages
|
||||||
|
Loading…
x
Reference in New Issue
Block a user