Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
79c88b4d7c
@ -53,14 +53,12 @@ class AddressBookHome extends StatelessComponent {
|
||||
);
|
||||
}
|
||||
|
||||
static final GlobalKey nameKey = new GlobalKey();
|
||||
static final GlobalKey phoneKey = new GlobalKey();
|
||||
static final GlobalKey emailKey = new GlobalKey();
|
||||
static final GlobalKey addressKey = new GlobalKey();
|
||||
static final GlobalKey ringtoneKey = new GlobalKey();
|
||||
static final GlobalKey noteKey = new GlobalKey();
|
||||
static final GlobalKey fillKey = new GlobalKey();
|
||||
static final GlobalKey emoticonKey = new GlobalKey();
|
||||
static final GlobalKey nameKey = new GlobalKey(label: 'name field');
|
||||
static final GlobalKey phoneKey = new GlobalKey(label: 'phone field');
|
||||
static final GlobalKey emailKey = new GlobalKey(label: 'email field');
|
||||
static final GlobalKey addressKey = new GlobalKey(label: 'address field');
|
||||
static final GlobalKey ringtoneKey = new GlobalKey(label: 'ringtone field');
|
||||
static final GlobalKey noteKey = new GlobalKey(label: 'note field');
|
||||
|
||||
Widget buildBody(BuildContext context) {
|
||||
return new Material(
|
||||
@ -83,7 +81,7 @@ class AddressBookHome extends StatelessComponent {
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: buildToolBar(context),
|
||||
toolBar: buildToolBar(context),
|
||||
body: buildBody(context),
|
||||
floatingActionButton: buildFloatingActionButton(context)
|
||||
);
|
||||
|
@ -192,7 +192,7 @@ final ThemeData _theme = new ThemeData(
|
||||
class DemoHome extends StatelessComponent {
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: new ToolBar(center: new Text('Sky Demos')),
|
||||
toolBar: new ToolBar(center: new Text('Sky Demos')),
|
||||
body: new Material(
|
||||
type: MaterialType.canvas,
|
||||
child: new DemoList()
|
||||
|
@ -58,11 +58,9 @@ class FeedFragment extends StatefulComponent {
|
||||
}
|
||||
|
||||
class FeedFragmentState extends State<FeedFragment> {
|
||||
final GlobalKey<PlaceholderState> _snackBarPlaceholderKey = new GlobalKey<PlaceholderState>();
|
||||
FitnessMode _fitnessMode = FitnessMode.feed;
|
||||
|
||||
PerformanceStatus _snackBarStatus = PerformanceStatus.dismissed;
|
||||
bool _isShowingSnackBar = false;
|
||||
|
||||
void _handleFitnessModeChange(FitnessMode value) {
|
||||
setState(() {
|
||||
_fitnessMode = value;
|
||||
@ -119,15 +117,17 @@ class FeedFragmentState extends State<FeedFragment> {
|
||||
);
|
||||
}
|
||||
|
||||
FitnessItem _undoItem;
|
||||
|
||||
void _handleItemDismissed(FitnessItem item) {
|
||||
config.onItemDeleted(item);
|
||||
setState(() {
|
||||
_undoItem = item;
|
||||
_isShowingSnackBar = true;
|
||||
_snackBarStatus = PerformanceStatus.forward;
|
||||
});
|
||||
showSnackBar(
|
||||
navigator: config.navigator,
|
||||
placeholderKey: _snackBarPlaceholderKey,
|
||||
content: new Text("Item deleted."),
|
||||
actions: [new SnackBarAction(label: "UNDO", onPressed: () {
|
||||
config.onItemCreated(item);
|
||||
config.navigator.pop();
|
||||
})]
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildChart() {
|
||||
@ -198,25 +198,6 @@ class FeedFragmentState extends State<FeedFragment> {
|
||||
}
|
||||
}
|
||||
|
||||
void _handleUndo() {
|
||||
config.onItemCreated(_undoItem);
|
||||
setState(() {
|
||||
_undoItem = null;
|
||||
_isShowingSnackBar = false;
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildSnackBar() {
|
||||
if (_snackBarStatus == PerformanceStatus.dismissed)
|
||||
return null;
|
||||
return new SnackBar(
|
||||
showing: _isShowingSnackBar,
|
||||
content: new Text("Item deleted."),
|
||||
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
|
||||
onDismissed: () { setState(() { _snackBarStatus = PerformanceStatus.dismissed; }); }
|
||||
);
|
||||
}
|
||||
|
||||
void _handleActionButtonPressed() {
|
||||
showDialog(config.navigator, (NavigatorState navigator) => new AddItemDialog(navigator)).then((routeName) {
|
||||
if (routeName != null)
|
||||
@ -238,9 +219,9 @@ class FeedFragmentState extends State<FeedFragment> {
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: buildToolBar(),
|
||||
toolBar: buildToolBar(),
|
||||
body: buildBody(),
|
||||
snackBar: buildSnackBar(),
|
||||
snackBar: new Placeholder(key: _snackBarPlaceholderKey),
|
||||
floatingActionButton: buildFloatingActionButton()
|
||||
);
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
library fitness;
|
||||
|
||||
import 'package:playfair/playfair.dart' as playfair;
|
||||
import 'package:sky/animation.dart';
|
||||
import 'package:sky/material.dart';
|
||||
import 'package:sky/painting.dart';
|
||||
import 'package:sky/widgets.dart';
|
||||
|
@ -105,7 +105,7 @@ class MealFragmentState extends State<MealFragment> {
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: buildToolBar(),
|
||||
toolBar: buildToolBar(),
|
||||
body: buildBody()
|
||||
);
|
||||
}
|
||||
|
@ -112,9 +112,10 @@ class MeasurementFragment extends StatefulComponent {
|
||||
}
|
||||
|
||||
class MeasurementFragmentState extends State<MeasurementFragment> {
|
||||
final GlobalKey<PlaceholderState> _snackBarPlaceholderKey = new GlobalKey<PlaceholderState>();
|
||||
|
||||
String _weight = "";
|
||||
DateTime _when = new DateTime.now();
|
||||
String _errorMessage = null;
|
||||
|
||||
void _handleSave() {
|
||||
double parsedWeight;
|
||||
@ -122,9 +123,11 @@ class MeasurementFragmentState extends State<MeasurementFragment> {
|
||||
parsedWeight = double.parse(_weight);
|
||||
} on FormatException catch(e) {
|
||||
print("Exception $e");
|
||||
setState(() {
|
||||
_errorMessage = "Save failed";
|
||||
});
|
||||
showSnackBar(
|
||||
navigator: config.navigator,
|
||||
placeholderKey: _snackBarPlaceholderKey,
|
||||
content: new Text('Save failed')
|
||||
);
|
||||
}
|
||||
config.onCreated(new Measurement(when: _when, weight: parsedWeight));
|
||||
config.navigator.pop();
|
||||
@ -195,18 +198,11 @@ class MeasurementFragmentState extends State<MeasurementFragment> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSnackBar() {
|
||||
if (_errorMessage == null)
|
||||
return null;
|
||||
// TODO(jackson): This doesn't show up, unclear why.
|
||||
return new SnackBar(content: new Text(_errorMessage), showing: true);
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: buildToolBar(),
|
||||
toolBar: buildToolBar(),
|
||||
body: buildBody(context),
|
||||
snackBar: buildSnackBar()
|
||||
snackBar: new Placeholder(key: _snackBarPlaceholderKey)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ class SettingsFragmentState extends State<SettingsFragment> {
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: buildToolBar(),
|
||||
toolBar: buildToolBar(),
|
||||
body: buildSettingsPane(context)
|
||||
);
|
||||
}
|
||||
|
@ -119,8 +119,10 @@ class GameDemoState extends State<GameDemo> {
|
||||
width: 128.0,
|
||||
height: 128.0
|
||||
),
|
||||
new Text(
|
||||
"Last Score: $_lastScore",
|
||||
new DefaultTextStyle(
|
||||
child: new Text(
|
||||
"Last Score: $_lastScore"
|
||||
),
|
||||
style: new TextStyle(fontSize:20.0)
|
||||
)
|
||||
],
|
||||
|
@ -182,7 +182,7 @@ class MineDiggerState extends State<MineDigger> {
|
||||
return new Title(
|
||||
title: 'Mine Digger',
|
||||
child: new Scaffold(
|
||||
toolbar: buildToolBar(context),
|
||||
toolBar: buildToolBar(context),
|
||||
body: new Container(
|
||||
child: new Center(child: board),
|
||||
decoration: new BoxDecoration(backgroundColor: Colors.grey[50])
|
||||
|
@ -8,7 +8,6 @@ import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:sky' as sky;
|
||||
|
||||
import 'package:sky/animation.dart';
|
||||
import 'package:sky/gestures.dart';
|
||||
import 'package:sky/material.dart';
|
||||
import 'package:sky/painting.dart';
|
||||
|
@ -6,8 +6,6 @@ part of stocks;
|
||||
|
||||
typedef void ModeUpdater(StockMode mode);
|
||||
|
||||
const Duration _kSnackbarSlideDuration = const Duration(milliseconds: 200);
|
||||
|
||||
class StockHome extends StatefulComponent {
|
||||
StockHome(this.navigator, this.stocks, this.symbols, this.stockMode, this.modeUpdater);
|
||||
|
||||
@ -22,12 +20,10 @@ class StockHome extends StatefulComponent {
|
||||
|
||||
class StockHomeState extends State<StockHome> {
|
||||
|
||||
final GlobalKey<PlaceholderState> _snackBarPlaceholderKey = new GlobalKey<PlaceholderState>();
|
||||
bool _isSearching = false;
|
||||
String _searchQuery;
|
||||
|
||||
PerformanceStatus _snackBarStatus = PerformanceStatus.dismissed;
|
||||
bool _isSnackBarShowing = false;
|
||||
|
||||
void _handleSearchBegin() {
|
||||
config.navigator.pushState(this, (_) {
|
||||
setState(() {
|
||||
@ -130,22 +126,23 @@ class StockHomeState extends State<StockHome> {
|
||||
|
||||
Widget buildToolBar() {
|
||||
return new ToolBar(
|
||||
left: new IconButton(
|
||||
icon: "navigation/menu",
|
||||
onPressed: _showDrawer
|
||||
level: 0,
|
||||
left: new IconButton(
|
||||
icon: "navigation/menu",
|
||||
onPressed: _showDrawer
|
||||
),
|
||||
center: new Text('Stocks'),
|
||||
right: [
|
||||
new IconButton(
|
||||
icon: "action/search",
|
||||
onPressed: _handleSearchBegin
|
||||
),
|
||||
center: new Text('Stocks'),
|
||||
right: [
|
||||
new IconButton(
|
||||
icon: "action/search",
|
||||
onPressed: _handleSearchBegin
|
||||
),
|
||||
new IconButton(
|
||||
icon: "navigation/more_vert",
|
||||
onPressed: _handleMenuShow
|
||||
)
|
||||
]
|
||||
);
|
||||
new IconButton(
|
||||
icon: "navigation/more_vert",
|
||||
onPressed: _handleMenuShow
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
int selectedTabIndex = 0;
|
||||
@ -217,28 +214,18 @@ class StockHomeState extends State<StockHome> {
|
||||
}
|
||||
|
||||
void _handleUndo() {
|
||||
setState(() {
|
||||
_isSnackBarShowing = false;
|
||||
});
|
||||
}
|
||||
|
||||
GlobalKey snackBarKey = new GlobalKey(label: 'snackbar');
|
||||
Widget buildSnackBar() {
|
||||
if (_snackBarStatus == PerformanceStatus.dismissed)
|
||||
return null;
|
||||
return new SnackBar(
|
||||
showing: _isSnackBarShowing,
|
||||
content: new Text("Stock purchased!"),
|
||||
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
|
||||
onDismissed: () { setState(() { _snackBarStatus = PerformanceStatus.dismissed; }); }
|
||||
);
|
||||
config.navigator.pop();
|
||||
}
|
||||
|
||||
void _handleStockPurchased() {
|
||||
setState(() {
|
||||
_isSnackBarShowing = true;
|
||||
_snackBarStatus = PerformanceStatus.forward;
|
||||
});
|
||||
showSnackBar(
|
||||
navigator: config.navigator,
|
||||
placeholderKey: _snackBarPlaceholderKey,
|
||||
content: new Text("Stock purchased!"),
|
||||
actions: [
|
||||
new SnackBarAction(label: "UNDO", onPressed: _handleUndo)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildFloatingActionButton() {
|
||||
@ -251,9 +238,9 @@ class StockHomeState extends State<StockHome> {
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: _isSearching ? buildSearchBar() : buildToolBar(),
|
||||
toolBar: _isSearching ? buildSearchBar() : buildToolBar(),
|
||||
body: buildTabNavigator(),
|
||||
snackBar: buildSnackBar(),
|
||||
snackBar: new Placeholder(key: _snackBarPlaceholderKey),
|
||||
floatingActionButton: buildFloatingActionButton()
|
||||
);
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ part of stocks;
|
||||
|
||||
enum _MenuItems { autorefresh, autorefreshCheckbox, add, remove }
|
||||
|
||||
const double _kMenuMargin = 16.0; // 24.0 on tablet
|
||||
|
||||
Future showStockMenu(NavigatorState navigator, { bool autorefresh, ValueChanged onAutorefreshChanged }) async {
|
||||
switch (await showMenu(
|
||||
navigator: navigator,
|
||||
position: new MenuPosition(
|
||||
right: sky.view.paddingRight,
|
||||
top: sky.view.paddingTop
|
||||
right: sky.view.paddingRight + _kMenuMargin,
|
||||
top: sky.view.paddingTop + _kMenuMargin
|
||||
),
|
||||
builder: (NavigatorState navigator) {
|
||||
return <PopupMenuItem>[
|
||||
|
@ -119,7 +119,7 @@ class StockSettingsState extends State<StockSettings> {
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: buildToolBar(context),
|
||||
toolBar: buildToolBar(context),
|
||||
body: buildSettingsPane(context)
|
||||
);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class StockSymbolViewerState extends State<StockSymbolViewer> {
|
||||
TextStyle headings = Theme.of(context).text.body2;
|
||||
|
||||
return new Scaffold(
|
||||
toolbar: new ToolBar(
|
||||
toolBar: new ToolBar(
|
||||
left: new IconButton(
|
||||
icon: 'navigation/arrow_back',
|
||||
onPressed: config.navigator.pop
|
||||
|
@ -342,7 +342,7 @@ class CardCollectionState extends State<CardCollection> {
|
||||
}
|
||||
|
||||
return new Scaffold(
|
||||
toolbar: buildToolBar(),
|
||||
toolBar: buildToolBar(),
|
||||
body: body
|
||||
);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class DatePickerDemoState extends State<DatePickerDemo> {
|
||||
),
|
||||
child: new Stack([
|
||||
new Scaffold(
|
||||
toolbar: new ToolBar(center: new Text("Date Picker")),
|
||||
toolBar: new ToolBar(center: new Text("Date Picker")),
|
||||
body: new Material(
|
||||
child: new Row(
|
||||
[new Text(_dateTime.toString())],
|
||||
|
@ -101,7 +101,7 @@ class DragAndDropApp extends StatefulComponent {
|
||||
class DragAndDropAppState extends State<DragAndDropApp> {
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
toolbar: new ToolBar(
|
||||
toolBar: new ToolBar(
|
||||
center: new Text('Drag and Drop Flutter Demo')
|
||||
),
|
||||
body: new Material(
|
||||
|
@ -89,7 +89,7 @@ class EnsureVisibleApp extends App {
|
||||
child: new Title(
|
||||
title: 'Cards',
|
||||
child: new Scaffold(
|
||||
toolbar: new ToolBar(center: new Text('Tap a Card')),
|
||||
toolBar: new ToolBar(center: new Text('Tap a Card')),
|
||||
body: cardCollection
|
||||
)
|
||||
)
|
||||
|
@ -138,7 +138,7 @@ class OverlayGeometryAppState extends State<OverlayGeometryApp> {
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> layers = <Widget>[
|
||||
new Scaffold(
|
||||
toolbar: new ToolBar(center: new Text('Tap a Card')),
|
||||
toolBar: new ToolBar(center: new Text('Tap a Card')),
|
||||
body: new Container(
|
||||
padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
|
||||
decoration: new BoxDecoration(backgroundColor: Theme.of(context).primarySwatch[50]),
|
||||
|
@ -149,7 +149,7 @@ class PageableListAppState extends State<PageableListApp> {
|
||||
return new IconTheme(
|
||||
data: const IconThemeData(color: IconThemeColor.white),
|
||||
child: new Scaffold(
|
||||
toolbar: buildToolBar(),
|
||||
toolBar: buildToolBar(),
|
||||
body: buildBody(context)
|
||||
)
|
||||
);
|
||||
|
@ -103,7 +103,7 @@ class ProgressIndicatorAppState extends State<ProgressIndicatorApp> {
|
||||
child: new Title(
|
||||
title: 'Progress Indicators',
|
||||
child: new Scaffold(
|
||||
toolbar: new ToolBar(center: new Text('Progress Indicators')),
|
||||
toolBar: new ToolBar(center: new Text('Progress Indicators')),
|
||||
body: new DefaultTextStyle(
|
||||
style: Theme.of(context).text.title,
|
||||
child: body
|
||||
|
@ -59,7 +59,7 @@ class ScaleAppState extends State<ScaleApp> {
|
||||
return new Theme(
|
||||
data: new ThemeData.dark(),
|
||||
child: new Scaffold(
|
||||
toolbar: new ToolBar(
|
||||
toolBar: new ToolBar(
|
||||
center: new Text('Scale Demo')),
|
||||
body: new Material(
|
||||
type: MaterialType.canvas,
|
||||
|
@ -124,7 +124,7 @@ class SectorApp extends App {
|
||||
child: new Title(
|
||||
title: 'Sector Layout',
|
||||
child: new Scaffold(
|
||||
toolbar: new ToolBar(
|
||||
toolBar: new ToolBar(
|
||||
center: new Text('Sector Layout in a Widget Tree')
|
||||
),
|
||||
body: buildBody()
|
||||
|
@ -106,7 +106,7 @@ HAL: This mission is too important for me to allow you to jeopardize it.''';
|
||||
color: Colors.grey[50],
|
||||
child: interactiveBody
|
||||
),
|
||||
toolbar: new ToolBar(
|
||||
toolBar: new ToolBar(
|
||||
center: new Text('Hal and Dave')
|
||||
)
|
||||
)
|
||||
|
@ -131,7 +131,7 @@ class TabbedNavigatorAppState extends State<TabbedNavigatorApp> {
|
||||
);
|
||||
|
||||
return new Scaffold(
|
||||
toolbar: toolbar,
|
||||
toolBar: toolbar,
|
||||
body: tabNavigator
|
||||
);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ export 'src/services/asset_bundle.dart';
|
||||
export 'src/services/embedder.dart';
|
||||
export 'src/services/fetch.dart';
|
||||
export 'src/services/image_cache.dart';
|
||||
export 'src/services/image_decoder.dart';
|
||||
export 'src/services/image_resource.dart';
|
||||
export 'src/services/keyboard.dart';
|
||||
export 'src/services/shell.dart';
|
||||
|
@ -14,10 +14,12 @@ const double kStatusBarHeight = 50.0;
|
||||
// Mobile Portrait: 56dp
|
||||
// Tablet/Desktop: 64dp
|
||||
const double kToolBarHeight = 56.0;
|
||||
const double kSnackBarHeight = 52.0;
|
||||
|
||||
const double kMaterialDrawerHeight = 140.0;
|
||||
const double kScrollbarSize = 10.0;
|
||||
const double kScrollbarFadeDuration = 250.0;
|
||||
const double kScrollbarFadeDelay = 300.0;
|
||||
const Duration kScrollbarFadeDuration = const Duration(milliseconds: 250);
|
||||
const Duration kScrollbarFadeDelay = const Duration(milliseconds: 300);
|
||||
const double kFadingEdgeLength = 12.0;
|
||||
const double kPressedStateDuration = 64.0;
|
||||
const double kPressedStateDuration = 64.0; // units?
|
||||
const Duration kThemeChangeDuration = const Duration(milliseconds: 200);
|
||||
|
@ -11,6 +11,7 @@ import 'package:mojo/core.dart' as core;
|
||||
import 'package:mojo_services/mojo/asset_bundle/asset_bundle.mojom.dart';
|
||||
import 'package:sky/src/services/fetch.dart';
|
||||
import 'package:sky/src/services/image_cache.dart';
|
||||
import 'package:sky/src/services/image_decoder.dart';
|
||||
import 'package:sky/src/services/image_resource.dart';
|
||||
import 'package:sky/src/services/shell.dart';
|
||||
|
||||
@ -66,13 +67,13 @@ class MojoAssetBundle extends AssetBundle {
|
||||
_imageCache = null;
|
||||
}
|
||||
|
||||
Future<sky.Image> _fetchImage(String key) async {
|
||||
return await decodeImageFromDataPipe(await load(key));
|
||||
}
|
||||
|
||||
ImageResource loadImage(String key) {
|
||||
return _imageCache.putIfAbsent(key, () {
|
||||
Completer<sky.Image> completer = new Completer<sky.Image>();
|
||||
load(key).then((assetData) {
|
||||
new sky.ImageDecoder.consume(assetData.handle.h, completer.complete);
|
||||
});
|
||||
return new ImageResource(completer.future);
|
||||
return new ImageResource(_fetchImage(key));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,18 @@ import 'dart:collection';
|
||||
import 'dart:sky' as sky;
|
||||
|
||||
import 'package:mojo/mojo/url_response.mojom.dart';
|
||||
import 'package:sky/src/services/image_resource.dart';
|
||||
import 'package:sky/src/services/fetch.dart';
|
||||
import 'package:sky/src/services/image_decoder.dart';
|
||||
import 'package:sky/src/services/image_resource.dart';
|
||||
|
||||
Future<sky.Image> _fetchImage(String url) async {
|
||||
UrlResponse response = await fetchUrl(url);
|
||||
if (response.statusCode >= 400) {
|
||||
print("Failed (${response.statusCode}) to load image ${url}");
|
||||
return null;
|
||||
}
|
||||
return await decodeImageFromDataPipe(response.body);
|
||||
}
|
||||
|
||||
class _ImageCache {
|
||||
_ImageCache._();
|
||||
@ -17,16 +27,7 @@ class _ImageCache {
|
||||
|
||||
ImageResource load(String url) {
|
||||
return _cache.putIfAbsent(url, () {
|
||||
Completer<sky.Image> completer = new Completer<sky.Image>();
|
||||
fetchUrl(url).then((UrlResponse response) {
|
||||
if (response.statusCode >= 400) {
|
||||
print("Failed (${response.statusCode}) to load image ${url}");
|
||||
completer.complete(null);
|
||||
} else {
|
||||
new sky.ImageDecoder.consume(response.body.handle.h, completer.complete);
|
||||
}
|
||||
});
|
||||
return new ImageResource(completer.future);
|
||||
return new ImageResource(_fetchImage(url));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
32
packages/flutter/lib/src/services/image_decoder.dart
Normal file
32
packages/flutter/lib/src/services/image_decoder.dart
Normal file
@ -0,0 +1,32 @@
|
||||
// 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:sky' show Image, ImageDecoder, ImageDecoderCallback;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:mojo/core.dart' show MojoDataPipeConsumer;
|
||||
|
||||
final Set<ImageDecoder> _activeDecoders = new Set<ImageDecoder>();
|
||||
|
||||
typedef ImageDecoder _DecoderFactory(ImageDecoderCallback callback);
|
||||
|
||||
Future<Image> _decode(_DecoderFactory createDecoder) {
|
||||
Completer<Image> completer = new Completer<Image>();
|
||||
ImageDecoder decoder;
|
||||
decoder = createDecoder((Image image) {
|
||||
_activeDecoders.remove(decoder);
|
||||
completer.complete(image);
|
||||
});
|
||||
_activeDecoders.add(decoder);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<Image> decodeImageFromDataPipe(MojoDataPipeConsumer consumerHandle) {
|
||||
return _decode((ImageDecoderCallback callback) => new ImageDecoder.consume(consumerHandle.handle.h, callback));
|
||||
}
|
||||
|
||||
Future<Image> decodeImageFromList(Uint8List list) {
|
||||
return _decode((ImageDecoderCallback callback) => new ImageDecoder.fromList(list, callback));
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
// 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 'package:sky/animation.dart';
|
||||
import 'package:sky/src/widgets/framework.dart';
|
||||
|
||||
abstract class AnimatedComponent extends StatefulComponent {
|
||||
const AnimatedComponent({ Key key, this.direction, this.duration }) : super(key: key);
|
||||
|
||||
final Duration duration;
|
||||
final AnimationDirection direction;
|
||||
}
|
||||
|
||||
abstract class AnimatedState<T extends AnimatedComponent> extends State<T> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_performance = new Performance(duration: config.duration);
|
||||
performance.addStatusListener(_handleAnimationStatusChanged);
|
||||
if (buildDependsOnPerformance) {
|
||||
performance.addListener(() {
|
||||
setState(() {
|
||||
// We don't actually have any state to change, per se,
|
||||
// we just know that we have in fact changed state.
|
||||
});
|
||||
});
|
||||
}
|
||||
performance.play(config.direction);
|
||||
}
|
||||
|
||||
void didUpdateConfig(T oldConfig) {
|
||||
performance.duration = config.duration;
|
||||
if (config.duration != oldConfig.duration || config.direction != oldConfig.direction)
|
||||
performance.play(config.direction);
|
||||
}
|
||||
|
||||
Performance get performance => _performance;
|
||||
Performance _performance;
|
||||
|
||||
void _handleAnimationStatusChanged(PerformanceStatus status) {
|
||||
if (status == PerformanceStatus.completed)
|
||||
handleCompleted();
|
||||
else if (status == PerformanceStatus.dismissed)
|
||||
handleDismissed();
|
||||
}
|
||||
|
||||
bool get buildDependsOnPerformance => false;
|
||||
void handleCompleted() { }
|
||||
void handleDismissed() { }
|
||||
}
|
@ -77,10 +77,10 @@ class AnimatedContainer extends StatefulComponent {
|
||||
final Curve curve;
|
||||
final Duration duration;
|
||||
|
||||
AnimatedContainerState createState() => new AnimatedContainerState();
|
||||
_AnimatedContainerState createState() => new _AnimatedContainerState();
|
||||
}
|
||||
|
||||
class AnimatedContainerState extends State<AnimatedContainer> {
|
||||
class _AnimatedContainerState extends State<AnimatedContainer> {
|
||||
AnimatedBoxConstraintsValue _constraints;
|
||||
AnimatedBoxDecorationValue _decoration;
|
||||
AnimatedBoxDecorationValue _foregroundDecoration;
|
||||
|
@ -38,7 +38,7 @@ class App extends StatefulComponent {
|
||||
'This might be a sign that you have not upgraded to our new Widgets framework.';
|
||||
'For more details see: https://groups.google.com/forum/#!topic/flutter-dev/hcX3OvLws9c';
|
||||
'...or look at our examples: https://github.com/flutter/engine/tree/master/examples';
|
||||
return routes != null;
|
||||
return routes != null;
|
||||
});
|
||||
}
|
||||
|
||||
@ -47,10 +47,10 @@ class App extends StatefulComponent {
|
||||
final Map<String, RouteBuilder> routes;
|
||||
final RouteGenerator onGenerateRoute;
|
||||
|
||||
AppState createState() => new AppState();
|
||||
_AppState createState() => new _AppState();
|
||||
}
|
||||
|
||||
class AppState extends State<App> {
|
||||
class _AppState extends State<App> {
|
||||
|
||||
GlobalObjectKey _navigator;
|
||||
|
||||
|
@ -808,10 +808,10 @@ class ImageListener extends StatefulComponent {
|
||||
final ImageFit fit;
|
||||
final ImageRepeat repeat;
|
||||
|
||||
ImageListenerState createState() => new ImageListenerState();
|
||||
_ImageListenerState createState() => new _ImageListenerState();
|
||||
}
|
||||
|
||||
class ImageListenerState extends State<ImageListener> {
|
||||
class _ImageListenerState extends State<ImageListener> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
config.image.addListener(_handleImageChanged);
|
||||
|
@ -41,7 +41,7 @@ class WidgetFlutterBinding extends FlutterBinding {
|
||||
Element.finalizeTree();
|
||||
}
|
||||
|
||||
List<BuildableElement> _dirtyElements = new List<BuildableElement>();
|
||||
List<BuildableElement> _dirtyElements = <BuildableElement>[];
|
||||
|
||||
/// Adds an element to the dirty elements list so that it will be rebuilt
|
||||
/// when buildDirtyElements is called.
|
||||
@ -62,10 +62,19 @@ class WidgetFlutterBinding extends FlutterBinding {
|
||||
return;
|
||||
BuildableElement.lockState(() {
|
||||
_dirtyElements.sort((BuildableElement a, BuildableElement b) => a.depth - b.depth);
|
||||
for (BuildableElement element in _dirtyElements)
|
||||
element.rebuild();
|
||||
int dirtyCount = _dirtyElements.length;
|
||||
int index = 0;
|
||||
while (index < dirtyCount) {
|
||||
_dirtyElements[index].rebuild();
|
||||
index += 1;
|
||||
if (dirtyCount < _dirtyElements.length) {
|
||||
_dirtyElements.sort((BuildableElement a, BuildableElement b) => a.depth - b.depth);
|
||||
dirtyCount = _dirtyElements.length;
|
||||
}
|
||||
}
|
||||
assert(!_dirtyElements.any((BuildableElement element) => element.dirty));
|
||||
_dirtyElements.clear();
|
||||
});
|
||||
}, building: true);
|
||||
assert(_dirtyElements.isEmpty);
|
||||
}
|
||||
}
|
||||
@ -76,7 +85,7 @@ void runApp(Widget app) {
|
||||
WidgetFlutterBinding.instance.renderViewElement.update(
|
||||
WidgetFlutterBinding.instance.describeApp(app)
|
||||
);
|
||||
});
|
||||
}, building: true);
|
||||
}
|
||||
|
||||
void debugDumpApp() {
|
||||
|
@ -39,10 +39,10 @@ class DatePicker extends StatefulComponent {
|
||||
final DateTime firstDate;
|
||||
final DateTime lastDate;
|
||||
|
||||
DatePickerState createState() => new DatePickerState();
|
||||
_DatePickerState createState() => new _DatePickerState();
|
||||
}
|
||||
|
||||
class DatePickerState extends State<DatePicker> {
|
||||
class _DatePickerState extends State<DatePicker> {
|
||||
DatePickerMode _mode = DatePickerMode.day;
|
||||
|
||||
void _handleModeChanged(DatePickerMode mode) {
|
||||
@ -70,7 +70,7 @@ class DatePickerState extends State<DatePicker> {
|
||||
static const double _calendarHeight = 210.0;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
Widget header = new DatePickerHeader(
|
||||
Widget header = new _DatePickerHeader(
|
||||
selectedDate: config.selectedDate,
|
||||
mode: _mode,
|
||||
onModeChanged: _handleModeChanged
|
||||
@ -107,8 +107,8 @@ class DatePickerState extends State<DatePicker> {
|
||||
}
|
||||
|
||||
// Shows the selected date in large font and toggles between year and day mode
|
||||
class DatePickerHeader extends StatelessComponent {
|
||||
DatePickerHeader({ this.selectedDate, this.mode, this.onModeChanged }) {
|
||||
class _DatePickerHeader extends StatelessComponent {
|
||||
_DatePickerHeader({ this.selectedDate, this.mode, this.onModeChanged }) {
|
||||
assert(selectedDate != null);
|
||||
assert(mode != null);
|
||||
}
|
||||
@ -290,10 +290,10 @@ class MonthPicker extends ScrollableWidgetList {
|
||||
final DateTime firstDate;
|
||||
final DateTime lastDate;
|
||||
|
||||
MonthPickerState createState() => new MonthPickerState();
|
||||
_MonthPickerState createState() => new _MonthPickerState();
|
||||
}
|
||||
|
||||
class MonthPickerState extends ScrollableWidgetListState<MonthPicker> {
|
||||
class _MonthPickerState extends ScrollableWidgetListState<MonthPicker> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_updateCurrentDate();
|
||||
@ -363,10 +363,10 @@ class YearPicker extends ScrollableWidgetList {
|
||||
final DateTime firstDate;
|
||||
final DateTime lastDate;
|
||||
|
||||
YearPickerState createState() => new YearPickerState();
|
||||
_YearPickerState createState() => new _YearPickerState();
|
||||
}
|
||||
|
||||
class YearPickerState extends ScrollableWidgetListState<YearPicker> {
|
||||
class _YearPickerState extends ScrollableWidgetListState<YearPicker> {
|
||||
int get itemCount => config.lastDate.year - config.firstDate.year + 1;
|
||||
|
||||
List<Widget> buildItems(BuildContext context, int start, int count) {
|
||||
|
@ -131,16 +131,15 @@ class Dialog extends StatelessComponent {
|
||||
}
|
||||
}
|
||||
|
||||
const Duration _kTransitionDuration = const Duration(milliseconds: 150);
|
||||
|
||||
class DialogRoute extends Route {
|
||||
DialogRoute({ this.completer, this.builder });
|
||||
class _DialogRoute extends Route {
|
||||
_DialogRoute({ this.completer, this.builder });
|
||||
|
||||
final Completer completer;
|
||||
final RouteBuilder builder;
|
||||
|
||||
Duration get transitionDuration => _kTransitionDuration;
|
||||
bool get opaque => false;
|
||||
Duration get transitionDuration => const Duration(milliseconds: 150);
|
||||
|
||||
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) {
|
||||
return new FadeTransition(
|
||||
performance: performance,
|
||||
@ -157,7 +156,7 @@ class DialogRoute extends Route {
|
||||
|
||||
Future showDialog(NavigatorState navigator, DialogBuilder builder) {
|
||||
Completer completer = new Completer();
|
||||
navigator.push(new DialogRoute(
|
||||
navigator.push(new _DialogRoute(
|
||||
completer: completer,
|
||||
builder: (RouteArguments args) {
|
||||
return new Focus(
|
||||
|
@ -39,15 +39,15 @@ class Dismissable extends StatefulComponent {
|
||||
this.direction: DismissDirection.horizontal
|
||||
}) : super(key: key);
|
||||
|
||||
Widget child;
|
||||
ResizedCallback onResized;
|
||||
DismissedCallback onDismissed;
|
||||
DismissDirection direction;
|
||||
final Widget child;
|
||||
final ResizedCallback onResized;
|
||||
final DismissedCallback onDismissed;
|
||||
final DismissDirection direction;
|
||||
|
||||
DismissableState createState() => new DismissableState();
|
||||
_DismissableState createState() => new _DismissableState();
|
||||
}
|
||||
|
||||
class DismissableState extends State<Dismissable> {
|
||||
class _DismissableState extends State<Dismissable> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fadePerformance = new Performance(duration: _kCardDismissFadeout);
|
||||
|
@ -62,10 +62,10 @@ class Draggable extends StatefulComponent {
|
||||
final Offset feedbackOffset;
|
||||
final DragAnchor dragAnchor;
|
||||
|
||||
DraggableState createState() => new DraggableState();
|
||||
_DraggableState createState() => new _DraggableState();
|
||||
}
|
||||
|
||||
class DraggableState extends State<Draggable> {
|
||||
class _DraggableState extends State<Draggable> {
|
||||
DragRoute _route;
|
||||
|
||||
void _startDrag(sky.PointerEvent event) {
|
||||
|
@ -23,10 +23,10 @@ class DrawerItem extends StatefulComponent {
|
||||
final GestureTapCallback onPressed;
|
||||
final bool selected;
|
||||
|
||||
DrawerItemState createState() => new DrawerItemState();
|
||||
_DrawerItemState createState() => new _DrawerItemState();
|
||||
}
|
||||
|
||||
class DrawerItemState extends ButtonState<DrawerItem> {
|
||||
class _DrawerItemState extends ButtonState<DrawerItem> {
|
||||
TextStyle _getTextStyle(ThemeData themeData) {
|
||||
TextStyle result = themeData.text.body2;
|
||||
if (config.selected)
|
||||
|
@ -20,10 +20,10 @@ class FlatButton extends MaterialButton {
|
||||
enabled: enabled,
|
||||
onPressed: onPressed);
|
||||
|
||||
FlatButtonState createState() => new FlatButtonState();
|
||||
_FlatButtonState createState() => new _FlatButtonState();
|
||||
}
|
||||
|
||||
class FlatButtonState extends MaterialButtonState<FlatButton> {
|
||||
class _FlatButtonState extends MaterialButtonState<FlatButton> {
|
||||
Color getColor(BuildContext context) {
|
||||
if (!config.enabled || !highlight)
|
||||
return null;
|
||||
|
@ -27,10 +27,10 @@ class FloatingActionButton extends StatefulComponent {
|
||||
final Color backgroundColor;
|
||||
final GestureTapCallback onPressed;
|
||||
|
||||
FloatingActionButtonState createState() => new FloatingActionButtonState();
|
||||
_FloatingActionButtonState createState() => new _FloatingActionButtonState();
|
||||
}
|
||||
|
||||
class FloatingActionButtonState extends ButtonState<FloatingActionButton> {
|
||||
class _FloatingActionButtonState extends ButtonState<FloatingActionButton> {
|
||||
Widget buildContent(BuildContext context) {
|
||||
IconThemeColor iconThemeColor = IconThemeColor.white;
|
||||
Color materialColor = config.backgroundColor;
|
||||
|
@ -75,6 +75,52 @@ class Focus extends StatefulComponent {
|
||||
final bool autofocus;
|
||||
final Widget child;
|
||||
|
||||
static bool at(BuildContext context, Widget widget, { bool autofocus: true }) {
|
||||
assert(widget != null);
|
||||
assert(widget.key is GlobalKey);
|
||||
_FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope);
|
||||
if (focusScope != null) {
|
||||
if (autofocus)
|
||||
focusScope._setFocusedWidgetIfUnset(widget.key);
|
||||
return focusScope.scopeFocused &&
|
||||
focusScope.focusedScope == null &&
|
||||
focusScope.focusedWidget == widget.key;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _atScope(BuildContext context, Widget widget, { bool autofocus: true }) {
|
||||
assert(widget != null);
|
||||
_FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope);
|
||||
if (focusScope != null) {
|
||||
if (autofocus)
|
||||
focusScope._setFocusedScopeIfUnset(widget.key);
|
||||
assert(widget.key != null);
|
||||
return focusScope.scopeFocused &&
|
||||
focusScope.focusedScope == widget.key;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't call moveTo() from your build() function, it's intended to be called
|
||||
// from event listeners, e.g. in response to a finger tap or tab key.
|
||||
|
||||
static void moveTo(BuildContext context, Widget widget) {
|
||||
assert(widget != null);
|
||||
assert(widget.key is GlobalKey);
|
||||
_FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope);
|
||||
if (focusScope != null)
|
||||
focusScope.focusState._setFocusedWidget(widget.key);
|
||||
}
|
||||
|
||||
static void _moveScopeTo(BuildContext context, Focus component) {
|
||||
assert(component != null);
|
||||
assert(component.key != null);
|
||||
_FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope);
|
||||
if (focusScope != null)
|
||||
focusScope.focusState._setFocusedScope(component.key);
|
||||
}
|
||||
|
||||
FocusState createState() => new FocusState();
|
||||
}
|
||||
|
||||
@ -155,7 +201,7 @@ class FocusState extends State<Focus> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (config.autofocus)
|
||||
FocusState._moveScopeTo(context, config);
|
||||
Focus._moveScopeTo(context, config);
|
||||
_updateWidgetRemovalListener(_focusedWidget);
|
||||
_updateScopeRemovalListener(_focusedScope);
|
||||
}
|
||||
@ -169,56 +215,10 @@ class FocusState extends State<Focus> {
|
||||
Widget build(BuildContext context) {
|
||||
return new _FocusScope(
|
||||
focusState: this,
|
||||
scopeFocused: FocusState._atScope(context, config),
|
||||
scopeFocused: Focus._atScope(context, config),
|
||||
focusedScope: _focusedScope == _noFocusedScope ? null : _focusedScope,
|
||||
focusedWidget: _focusedWidget,
|
||||
child: config.child
|
||||
);
|
||||
}
|
||||
|
||||
static bool at(BuildContext context, Widget widget, { bool autofocus: true }) {
|
||||
assert(widget != null);
|
||||
assert(widget.key is GlobalKey);
|
||||
_FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope);
|
||||
if (focusScope != null) {
|
||||
if (autofocus)
|
||||
focusScope._setFocusedWidgetIfUnset(widget.key);
|
||||
return focusScope.scopeFocused &&
|
||||
focusScope.focusedScope == null &&
|
||||
focusScope.focusedWidget == widget.key;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _atScope(BuildContext context, Widget widget, { bool autofocus: true }) {
|
||||
assert(widget != null);
|
||||
_FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope);
|
||||
if (focusScope != null) {
|
||||
if (autofocus)
|
||||
focusScope._setFocusedScopeIfUnset(widget.key);
|
||||
assert(widget.key != null);
|
||||
return focusScope.scopeFocused &&
|
||||
focusScope.focusedScope == widget.key;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't call moveTo() from your build() function, it's intended to be called
|
||||
// from event listeners, e.g. in response to a finger tap or tab key.
|
||||
|
||||
static void moveTo(BuildContext context, Widget widget) {
|
||||
assert(widget != null);
|
||||
assert(widget.key is GlobalKey);
|
||||
_FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope);
|
||||
if (focusScope != null)
|
||||
focusScope.focusState._setFocusedWidget(widget.key);
|
||||
}
|
||||
|
||||
static void _moveScopeTo(BuildContext context, Focus component) {
|
||||
assert(component != null);
|
||||
assert(component.key != null);
|
||||
_FocusScope focusScope = context.inheritedWidgetOfType(_FocusScope);
|
||||
if (focusScope != null)
|
||||
focusScope.focusState._setFocusedScope(component.key);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ typedef void GlobalKeyRemoveListener(GlobalKey key);
|
||||
/// A GlobalKey is one that must be unique across the entire application. It is
|
||||
/// used by components that need to communicate with other components across the
|
||||
/// application's element tree.
|
||||
abstract class GlobalKey extends Key {
|
||||
abstract class GlobalKey<T extends State> extends Key {
|
||||
const GlobalKey.constructor() : super.constructor(); // so that subclasses can call us, since the Key() factory constructor shadows the implicit constructor
|
||||
|
||||
/// Constructs a LabeledGlobalKey, which is a GlobalKey with a label used for debugging.
|
||||
@ -96,9 +96,9 @@ abstract class GlobalKey extends Key {
|
||||
Element get _currentElement => _registry[this];
|
||||
BuildContext get currentContext => _currentElement;
|
||||
Widget get currentWidget => _currentElement?.widget;
|
||||
State get currentState {
|
||||
T get currentState {
|
||||
Element element = _currentElement;
|
||||
if (element is StatefulComponentElement)
|
||||
if (element is StatefulComponentElement<dynamic, T>)
|
||||
return element.state;
|
||||
return null;
|
||||
}
|
||||
@ -186,8 +186,8 @@ abstract class Widget {
|
||||
final List<String> data = <String>[];
|
||||
debugFillDescription(data);
|
||||
if (data.isEmpty)
|
||||
return 'name';
|
||||
return 'name(${data.join("; ")})';
|
||||
return '$name';
|
||||
return '$name(${data.join("; ")})';
|
||||
}
|
||||
|
||||
void debugFillDescription(List<String> description) { }
|
||||
@ -550,7 +550,7 @@ abstract class Element<T extends Widget> implements BuildContext {
|
||||
/// Wrapper around visitChildren for BuildContext.
|
||||
void visitChildElements(void visitor(Element element)) {
|
||||
// don't allow visitChildElements() during build, since children aren't necessarily built yet
|
||||
assert(BuildableElement._debugStateLockLevel == 0);
|
||||
assert(!BuildableElement._debugStateLocked);
|
||||
visitChildren(visitor);
|
||||
}
|
||||
|
||||
@ -858,6 +858,8 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
|
||||
assert(_child != null);
|
||||
}
|
||||
|
||||
static BuildableElement _debugCurrentBuildTarget;
|
||||
|
||||
/// Reinvokes the build() method of the StatelessComponent object (for
|
||||
/// stateless components) or the State object (for stateful components) and
|
||||
/// then updates the widget tree.
|
||||
@ -874,6 +876,12 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
|
||||
assert(_debugLifecycleState == _ElementLifecycle.active);
|
||||
assert(_debugStateLocked);
|
||||
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true));
|
||||
BuildableElement debugPreviousBuildTarget;
|
||||
assert(() {
|
||||
debugPreviousBuildTarget = _debugCurrentBuildTarget;
|
||||
_debugCurrentBuildTarget = this;
|
||||
return true;
|
||||
});
|
||||
Widget built;
|
||||
try {
|
||||
built = _builder(this);
|
||||
@ -896,12 +904,19 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
|
||||
built = new ErrorWidget();
|
||||
_child = updateChild(null, built, slot);
|
||||
}
|
||||
|
||||
assert(() {
|
||||
assert(_debugCurrentBuildTarget == this);
|
||||
_debugCurrentBuildTarget = debugPreviousBuildTarget;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static BuildScheduler scheduleBuildFor;
|
||||
|
||||
static int _debugStateLockLevel = 0;
|
||||
static bool get _debugStateLocked => _debugStateLockLevel > 0;
|
||||
static bool _debugBuilding = false;
|
||||
|
||||
/// Establishes a scope in which component build functions can run.
|
||||
///
|
||||
@ -913,13 +928,31 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
|
||||
/// After unwinding the last build scope on the stack, the framework verifies
|
||||
/// that each global key is used at most once and notifies listeners about
|
||||
/// changes to global keys.
|
||||
static void lockState(void callback()) {
|
||||
_debugStateLockLevel += 1;
|
||||
static void lockState(void callback(), { bool building: false }) {
|
||||
assert(_debugStateLockLevel >= 0);
|
||||
assert(() {
|
||||
if (building) {
|
||||
assert(!_debugBuilding);
|
||||
assert(_debugCurrentBuildTarget == null);
|
||||
_debugBuilding = true;
|
||||
}
|
||||
_debugStateLockLevel += 1;
|
||||
return true;
|
||||
});
|
||||
try {
|
||||
callback();
|
||||
} finally {
|
||||
_debugStateLockLevel -= 1;
|
||||
assert(() {
|
||||
_debugStateLockLevel -= 1;
|
||||
if (building) {
|
||||
assert(_debugBuilding);
|
||||
assert(_debugCurrentBuildTarget == null);
|
||||
_debugBuilding = false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
assert(_debugStateLockLevel >= 0);
|
||||
}
|
||||
|
||||
/// Marks the element as dirty and adds it to the global list of widgets to
|
||||
@ -934,10 +967,23 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
|
||||
if (!_active)
|
||||
return;
|
||||
assert(_debugLifecycleState == _ElementLifecycle.active);
|
||||
assert(!_debugStateLocked || (_debugAllowIgnoredCallsToMarkNeedsBuild && dirty));
|
||||
assert(() {
|
||||
if (_debugBuilding) {
|
||||
bool foundTarget = false;
|
||||
visitAncestorElements((Element element) {
|
||||
if (element == _debugCurrentBuildTarget) {
|
||||
foundTarget = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (foundTarget)
|
||||
return true;
|
||||
}
|
||||
return !_debugStateLocked || (_debugAllowIgnoredCallsToMarkNeedsBuild && dirty);
|
||||
});
|
||||
if (dirty)
|
||||
return;
|
||||
assert(!_debugStateLocked);
|
||||
_dirty = true;
|
||||
assert(scheduleBuildFor != null);
|
||||
scheduleBuildFor(this);
|
||||
@ -1101,11 +1147,12 @@ class InheritedElement extends StatelessComponentElement<InheritedWidget> {
|
||||
}
|
||||
|
||||
void _notifyDescendants() {
|
||||
final Type ourRuntimeType = runtimeType;
|
||||
final Type ourRuntimeType = widget.runtimeType;
|
||||
void notifyChildren(Element child) {
|
||||
if (child._dependencies != null &&
|
||||
child._dependencies.contains(ourRuntimeType))
|
||||
child._dependencies.contains(ourRuntimeType)) {
|
||||
child.dependenciesChanged();
|
||||
}
|
||||
if (child.runtimeType != ourRuntimeType)
|
||||
child.visitChildren(notifyChildren);
|
||||
}
|
||||
|
@ -59,10 +59,10 @@ class GestureDetector extends StatefulComponent {
|
||||
final GestureScaleUpdateCallback onScaleUpdate;
|
||||
final GestureScaleEndCallback onScaleEnd;
|
||||
|
||||
GestureDetectorState createState() => new GestureDetectorState();
|
||||
_GestureDetectorState createState() => new _GestureDetectorState();
|
||||
}
|
||||
|
||||
class GestureDetectorState extends State<GestureDetector> {
|
||||
class _GestureDetectorState extends State<GestureDetector> {
|
||||
final PointerRouter _router = FlutterBinding.instance.pointerRouter;
|
||||
|
||||
TapGestureRecognizer _tap;
|
||||
|
@ -30,7 +30,7 @@ class HomogeneousViewport extends RenderObjectWidget {
|
||||
final ScrollDirection direction;
|
||||
final double startOffset;
|
||||
|
||||
HomogeneousViewportElement createElement() => new HomogeneousViewportElement(this);
|
||||
_HomogeneousViewportElement createElement() => new _HomogeneousViewportElement(this);
|
||||
|
||||
// we don't pass constructor arguments to the RenderBlockViewport() because until
|
||||
// we know our children, the constructor arguments we could give have no effect
|
||||
@ -48,8 +48,8 @@ class HomogeneousViewport extends RenderObjectWidget {
|
||||
// all the actual work is done in the element
|
||||
}
|
||||
|
||||
class HomogeneousViewportElement extends RenderObjectElement<HomogeneousViewport> {
|
||||
HomogeneousViewportElement(HomogeneousViewport widget) : super(widget);
|
||||
class _HomogeneousViewportElement extends RenderObjectElement<HomogeneousViewport> {
|
||||
_HomogeneousViewportElement(HomogeneousViewport widget) : super(widget);
|
||||
|
||||
List<Element> _children = const <Element>[];
|
||||
int _layoutFirstIndex;
|
||||
|
@ -61,7 +61,7 @@ class Icon extends StatelessComponent {
|
||||
final IconThemeColor color;
|
||||
final sky.ColorFilter colorFilter;
|
||||
|
||||
String getColorSuffix(BuildContext context) {
|
||||
String _getColorSuffix(BuildContext context) {
|
||||
IconThemeColor iconThemeColor = color;
|
||||
if (iconThemeColor == null) {
|
||||
IconThemeData iconThemeData = IconTheme.of(context);
|
||||
@ -90,7 +90,7 @@ class Icon extends StatelessComponent {
|
||||
// TODO(eseidel): This clearly isn't correct. Not sure what would be.
|
||||
// Should we use the ios images on ios?
|
||||
String density = 'drawable-xxhdpi';
|
||||
String colorSuffix = getColorSuffix(context);
|
||||
String colorSuffix = _getColorSuffix(context);
|
||||
return new AssetImage(
|
||||
bundle: _iconBundle,
|
||||
name: '${category}/${density}/ic_${subtype}_${colorSuffix}_${size}dp.png',
|
||||
|
@ -26,8 +26,8 @@ double _getSplashTargetSize(Size bounds, Point position) {
|
||||
return math.max(math.max(d1, d2), math.max(d3, d4)).ceil().toDouble();
|
||||
}
|
||||
|
||||
class InkSplash {
|
||||
InkSplash(this.position, this.well) {
|
||||
class _InkSplash {
|
||||
_InkSplash(this.position, this.well) {
|
||||
_targetRadius = _getSplashTargetSize(well.size, position);
|
||||
_radius = new AnimatedValue<double>(
|
||||
_kSplashInitialSize, end: _targetRadius, curve: easeOut);
|
||||
@ -42,7 +42,7 @@ class InkSplash {
|
||||
}
|
||||
|
||||
final Point position;
|
||||
final RenderInkWell well;
|
||||
final _RenderInkWell well;
|
||||
|
||||
double _targetRadius;
|
||||
double _pinnedRadius;
|
||||
@ -98,8 +98,8 @@ class InkSplash {
|
||||
}
|
||||
}
|
||||
|
||||
class RenderInkWell extends RenderProxyBox {
|
||||
RenderInkWell({
|
||||
class _RenderInkWell extends RenderProxyBox {
|
||||
_RenderInkWell({
|
||||
RenderBox child,
|
||||
GestureTapCallback onTap,
|
||||
GestureLongPressCallback onLongPress
|
||||
@ -122,7 +122,7 @@ class RenderInkWell extends RenderProxyBox {
|
||||
_syncLongPressRecognizer();
|
||||
}
|
||||
|
||||
final List<InkSplash> _splashes = new List<InkSplash>();
|
||||
final List<_InkSplash> _splashes = new List<_InkSplash>();
|
||||
|
||||
TapGestureRecognizer _tap;
|
||||
LongPressGestureRecognizer _longPress;
|
||||
@ -131,7 +131,7 @@ class RenderInkWell extends RenderProxyBox {
|
||||
if (event.type == 'pointerdown' && (_tap != null || _longPress != null)) {
|
||||
_tap?.addPointer(event);
|
||||
_longPress?.addPointer(event);
|
||||
_splashes.add(new InkSplash(entry.localPosition, this));
|
||||
_splashes.add(new _InkSplash(entry.localPosition, this));
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ class RenderInkWell extends RenderProxyBox {
|
||||
canvas.save();
|
||||
canvas.translate(offset.dx, offset.dy);
|
||||
canvas.clipRect(Point.origin & size);
|
||||
for (InkSplash splash in _splashes)
|
||||
for (_InkSplash splash in _splashes)
|
||||
splash.paint(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
@ -215,9 +215,9 @@ class InkWell extends OneChildRenderObjectWidget {
|
||||
final GestureTapCallback onTap;
|
||||
final GestureLongPressCallback onLongPress;
|
||||
|
||||
RenderInkWell createRenderObject() => new RenderInkWell(onTap: onTap, onLongPress: onLongPress);
|
||||
_RenderInkWell createRenderObject() => new _RenderInkWell(onTap: onTap, onLongPress: onLongPress);
|
||||
|
||||
void updateRenderObject(RenderInkWell renderObject, InkWell oldWidget) {
|
||||
void updateRenderObject(_RenderInkWell renderObject, InkWell oldWidget) {
|
||||
renderObject.onTap = onTap;
|
||||
renderObject.onLongPress = onLongPress;
|
||||
}
|
||||
|
@ -39,10 +39,10 @@ class Input extends Scrollable {
|
||||
final String placeholder;
|
||||
final StringValueChanged onChanged;
|
||||
|
||||
InputState createState() => new InputState();
|
||||
_InputState createState() => new _InputState();
|
||||
}
|
||||
|
||||
class InputState extends ScrollableState<Input> {
|
||||
class _InputState extends ScrollableState<Input> {
|
||||
String _value;
|
||||
EditableString _editableValue;
|
||||
KeyboardHandle _keyboardHandle = KeyboardHandle.unattached;
|
||||
@ -71,7 +71,7 @@ class InputState extends ScrollableState<Input> {
|
||||
|
||||
Widget buildContent(BuildContext context) {
|
||||
ThemeData themeData = Theme.of(context);
|
||||
bool focused = FocusState.at(context, config);
|
||||
bool focused = Focus.at(context, config);
|
||||
|
||||
if (focused && !_keyboardHandle.attached) {
|
||||
_keyboardHandle = keyboard.show(_editableValue.stub, config.keyboardType);
|
||||
@ -122,11 +122,11 @@ class InputState extends ScrollableState<Input> {
|
||||
)
|
||||
),
|
||||
onPointerDown: (_) {
|
||||
if (FocusState.at(context, config)) {
|
||||
if (Focus.at(context, config)) {
|
||||
assert(_keyboardHandle.attached);
|
||||
_keyboardHandle.showByRequest();
|
||||
} else {
|
||||
FocusState.moveTo(context, config);
|
||||
Focus.moveTo(context, config);
|
||||
// we'll get told to rebuild and we'll take care of the keyboard then
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import 'package:sky/src/widgets/theme.dart';
|
||||
|
||||
enum MaterialType { canvas, card, circle, button }
|
||||
|
||||
const Map<MaterialType, double> edges = const {
|
||||
const Map<MaterialType, double> _kEdges = const <MaterialType, double>{
|
||||
MaterialType.canvas: null,
|
||||
MaterialType.card: 2.0,
|
||||
MaterialType.circle: null,
|
||||
@ -35,7 +35,7 @@ class Material extends StatelessComponent {
|
||||
final int level;
|
||||
final Color color;
|
||||
|
||||
Color getBackgroundColor(BuildContext context) {
|
||||
Color _getBackgroundColor(BuildContext context) {
|
||||
if (color != null)
|
||||
return color;
|
||||
switch (type) {
|
||||
@ -55,10 +55,10 @@ class Material extends StatelessComponent {
|
||||
style: Theme.of(context).text.body1,
|
||||
child: contents
|
||||
);
|
||||
if (edges[type] != null) {
|
||||
if (_kEdges[type] != null) {
|
||||
contents = new ClipRRect(
|
||||
xRadius: edges[type],
|
||||
yRadius: edges[type],
|
||||
xRadius: _kEdges[type],
|
||||
yRadius: _kEdges[type],
|
||||
child: contents
|
||||
);
|
||||
}
|
||||
@ -67,10 +67,10 @@ class Material extends StatelessComponent {
|
||||
style: Theme.of(context).text.body1,
|
||||
child: new AnimatedContainer(
|
||||
curve: ease,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
duration: kThemeChangeDuration,
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: getBackgroundColor(context),
|
||||
borderRadius: edges[type],
|
||||
backgroundColor: _getBackgroundColor(context),
|
||||
borderRadius: _kEdges[type],
|
||||
boxShadow: level == 0 ? null : shadows[level],
|
||||
shape: type == MaterialType.circle ? Shape.circle : Shape.rectangle
|
||||
),
|
||||
|
@ -9,7 +9,7 @@ import 'package:sky/src/widgets/framework.dart';
|
||||
import 'package:sky/src/widgets/ink_well.dart';
|
||||
import 'package:sky/src/widgets/material.dart';
|
||||
|
||||
// Rather than using this class directly, please use FlatButton or RaisedButton.
|
||||
/// Rather than using this class directly, please use FlatButton or RaisedButton.
|
||||
abstract class MaterialButton extends StatefulComponent {
|
||||
MaterialButton({
|
||||
Key key,
|
||||
|
@ -31,7 +31,7 @@ class MixedViewport extends RenderObjectWidget {
|
||||
final ExtentsUpdateCallback onExtentsUpdate;
|
||||
final InvalidatorAvailableCallback onInvalidatorAvailable;
|
||||
|
||||
MixedViewportElement createElement() => new MixedViewportElement(this);
|
||||
_MixedViewportElement createElement() => new _MixedViewportElement(this);
|
||||
|
||||
// we don't pass constructor arguments to the RenderBlockViewport() because until
|
||||
// we know our children, the constructor arguments we could give have no effect
|
||||
@ -60,8 +60,8 @@ class _ChildKey {
|
||||
String toString() => "_ChildKey(type: $type, key: $key)";
|
||||
}
|
||||
|
||||
class MixedViewportElement extends RenderObjectElement<MixedViewport> {
|
||||
MixedViewportElement(MixedViewport widget) : super(widget) {
|
||||
class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
|
||||
_MixedViewportElement(MixedViewport widget) : super(widget) {
|
||||
if (widget.onInvalidatorAvailable != null)
|
||||
widget.onInvalidatorAvailable(invalidate);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class RouteArguments {
|
||||
typedef Widget RouteBuilder(RouteArguments args);
|
||||
typedef RouteBuilder RouteGenerator(String name);
|
||||
typedef void StateRouteCallback(StateRoute route);
|
||||
typedef void NotificationCallback();
|
||||
typedef void _RouteCallback(Route route);
|
||||
|
||||
class Navigator extends StatefulComponent {
|
||||
Navigator({
|
||||
@ -51,7 +51,7 @@ class NavigatorState extends State<Navigator> {
|
||||
PageRoute route = new PageRoute(config.routes['/']);
|
||||
assert(route != null);
|
||||
assert(!route.ephemeral);
|
||||
_history.add(route);
|
||||
_insertRoute(route);
|
||||
}
|
||||
|
||||
void pushState(State owner, Function callback) {
|
||||
@ -63,29 +63,30 @@ class NavigatorState extends State<Navigator> {
|
||||
}
|
||||
|
||||
void pushNamed(String name) {
|
||||
RouteBuilder builder;
|
||||
if (!config.routes.containsKey(name)) {
|
||||
RouteBuilder generateRoute() {
|
||||
assert(config.onGenerateRoute != null);
|
||||
builder = config.onGenerateRoute(name);
|
||||
} else {
|
||||
builder = config.routes[name];
|
||||
return config.onGenerateRoute(name);
|
||||
}
|
||||
if (builder == null)
|
||||
builder = config.onUnknownRoute; // 404!
|
||||
assert(builder != null); // 404 getting your 404!
|
||||
RouteBuilder builder = config.routes[name] ?? generateRoute() ?? config.onUnknownRoute;
|
||||
push(new PageRoute(builder));
|
||||
}
|
||||
|
||||
void _insertRoute(Route route) {
|
||||
_history.insert(_currentPosition, route);
|
||||
route._onDismissed = _handleRouteDismissed;
|
||||
route._onRemoveRoute = _handleRemoveRoute;
|
||||
route.didPush();
|
||||
}
|
||||
|
||||
void push(Route route) {
|
||||
assert(!_debugCurrentlyHaveRoute(route));
|
||||
setState(() {
|
||||
while (currentRoute.ephemeral) {
|
||||
assert(currentRoute.ephemeral);
|
||||
currentRoute.didPop(null);
|
||||
_currentPosition -= 1;
|
||||
}
|
||||
_history.insert(_currentPosition + 1, route);
|
||||
_currentPosition += 1;
|
||||
_insertRoute(route);
|
||||
});
|
||||
}
|
||||
|
||||
@ -107,10 +108,6 @@ class NavigatorState extends State<Navigator> {
|
||||
|
||||
void pop([dynamic result]) {
|
||||
setState(() {
|
||||
while (currentRoute.ephemeral) {
|
||||
currentRoute.didPop(null);
|
||||
_currentPosition -= 1;
|
||||
}
|
||||
assert(_currentPosition > 0);
|
||||
currentRoute.didPop(result);
|
||||
_currentPosition -= 1;
|
||||
@ -122,6 +119,19 @@ class NavigatorState extends State<Navigator> {
|
||||
return index >= 0 && index <= _currentPosition;
|
||||
}
|
||||
|
||||
void _handleRouteDismissed(Route route) {
|
||||
assert(_history.contains(route));
|
||||
if (_history.lastIndexOf(route) <= _currentPosition)
|
||||
popRoute(route);
|
||||
}
|
||||
|
||||
void _handleRemoveRoute(Route route) {
|
||||
assert(_history.contains(route));
|
||||
setState(() {
|
||||
_history.remove(route);
|
||||
});
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> visibleRoutes = new List<Widget>();
|
||||
bool alreadyInsertModalBarrier = false;
|
||||
@ -132,20 +142,6 @@ class NavigatorState extends State<Navigator> {
|
||||
assert(!route.modal);
|
||||
continue;
|
||||
}
|
||||
route.ensurePerformance(
|
||||
direction: (i <= _currentPosition) ? AnimationDirection.forward : AnimationDirection.reverse
|
||||
);
|
||||
route._onDismissed = () {
|
||||
assert(_history.contains(route));
|
||||
if (_history.lastIndexOf(route) <= _currentPosition)
|
||||
popRoute(route);
|
||||
};
|
||||
route._onRemoveRoute = () {
|
||||
assert(_history.contains(route));
|
||||
setState(() {
|
||||
_history.remove(route);
|
||||
});
|
||||
};
|
||||
visibleRoutes.add(
|
||||
new KeyedSubtree(
|
||||
key: new ObjectKey(route),
|
||||
@ -171,11 +167,14 @@ class NavigatorState extends State<Navigator> {
|
||||
|
||||
|
||||
abstract class Route {
|
||||
Route() {
|
||||
_performance = createPerformance();
|
||||
}
|
||||
|
||||
PerformanceView get performance => _performance?.view;
|
||||
Performance _performance;
|
||||
NotificationCallback _onDismissed;
|
||||
NotificationCallback _onRemoveRoute;
|
||||
_RouteCallback _onDismissed;
|
||||
_RouteCallback _onRemoveRoute;
|
||||
|
||||
Performance createPerformance() {
|
||||
Duration duration = transitionDuration;
|
||||
@ -184,26 +183,15 @@ abstract class Route {
|
||||
..addStatusListener((PerformanceStatus status) {
|
||||
if (status == PerformanceStatus.dismissed) {
|
||||
if (_onDismissed != null)
|
||||
_onDismissed();
|
||||
_onDismissed(this);
|
||||
if (_onRemoveRoute != null)
|
||||
_onRemoveRoute();
|
||||
_onRemoveRoute(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void ensurePerformance({ AnimationDirection direction }) {
|
||||
assert(direction != null);
|
||||
if (_performance == null)
|
||||
_performance = createPerformance();
|
||||
if (_performance != null) {
|
||||
PerformanceStatus desiredStatus = direction == AnimationDirection.forward ? PerformanceStatus.forward : PerformanceStatus.reverse;
|
||||
if (_performance.status != desiredStatus)
|
||||
_performance.play(direction);
|
||||
}
|
||||
}
|
||||
|
||||
/// If hasContent is true, then the route represents some on-screen state.
|
||||
///
|
||||
/// If hasContent is false, then no performance will be created, and the values of
|
||||
@ -262,9 +250,15 @@ abstract class Route {
|
||||
bool get isActuallyOpaque => (performance == null || _performance.isCompleted) && opaque;
|
||||
|
||||
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance);
|
||||
|
||||
void didPush() {
|
||||
_performance?.forward();
|
||||
}
|
||||
|
||||
void didPop([dynamic result]) {
|
||||
_performance?.reverse();
|
||||
if (performance == null && _onRemoveRoute != null)
|
||||
_onRemoveRoute();
|
||||
_onRemoveRoute(this);
|
||||
}
|
||||
|
||||
String toString() => '$runtimeType()';
|
||||
|
30
packages/flutter/lib/src/widgets/placeholder.dart
Normal file
30
packages/flutter/lib/src/widgets/placeholder.dart
Normal file
@ -0,0 +1,30 @@
|
||||
// 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 'package:sky/src/widgets/basic.dart';
|
||||
import 'package:sky/src/widgets/framework.dart';
|
||||
|
||||
class Placeholder extends StatefulComponent {
|
||||
Placeholder({ Key key }) : super(key: key);
|
||||
|
||||
PlaceholderState createState() => new PlaceholderState();
|
||||
}
|
||||
|
||||
class PlaceholderState extends State<Placeholder> {
|
||||
Widget get child => _child;
|
||||
Widget _child;
|
||||
void set child(Widget child) {
|
||||
if (_child == child)
|
||||
return;
|
||||
setState(() {
|
||||
_child = child;
|
||||
});
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
if (_child != null)
|
||||
return child;
|
||||
return new SizedBox(width: 0.0, height: 0.0);
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ import 'package:sky/src/widgets/transitions.dart';
|
||||
const Duration _kMenuDuration = const Duration(milliseconds: 300);
|
||||
const double _kMenuCloseIntervalEnd = 2.0 / 3.0;
|
||||
const double _kMenuWidthStep = 56.0;
|
||||
const double _kMenuMargin = 16.0; // 24.0 on tablet
|
||||
const double _kMenuMinWidth = 2.0 * _kMenuWidthStep;
|
||||
const double _kMenuMaxWidth = 5.0 * _kMenuWidthStep;
|
||||
const double _kMenuHorizontalPadding = 16.0;
|
||||
@ -75,39 +74,36 @@ class PopupMenu extends StatelessComponent {
|
||||
return new FadeTransition(
|
||||
performance: performance,
|
||||
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, 1.0 / 3.0)),
|
||||
child: new Container(
|
||||
margin: new EdgeDims.all(_kMenuMargin),
|
||||
child: new BuilderTransition(
|
||||
performance: performance,
|
||||
variables: [width, height],
|
||||
builder: (BuildContext context) {
|
||||
return new CustomPaint(
|
||||
callback: (sky.Canvas canvas, Size size) {
|
||||
double widthValue = width.value * size.width;
|
||||
double heightValue = height.value * size.height;
|
||||
painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue));
|
||||
},
|
||||
child: new ConstrainedBox(
|
||||
constraints: new BoxConstraints(
|
||||
minWidth: _kMenuMinWidth,
|
||||
maxWidth: _kMenuMaxWidth
|
||||
),
|
||||
child: new IntrinsicWidth(
|
||||
stepWidth: _kMenuWidthStep,
|
||||
child: new ScrollableViewport(
|
||||
child: new Container(
|
||||
padding: const EdgeDims.symmetric(
|
||||
horizontal: _kMenuHorizontalPadding,
|
||||
vertical: _kMenuVerticalPadding
|
||||
),
|
||||
child: new BlockBody(children)
|
||||
)
|
||||
child: new BuilderTransition(
|
||||
performance: performance,
|
||||
variables: [width, height],
|
||||
builder: (BuildContext context) {
|
||||
return new CustomPaint(
|
||||
callback: (sky.Canvas canvas, Size size) {
|
||||
double widthValue = width.value * size.width;
|
||||
double heightValue = height.value * size.height;
|
||||
painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue));
|
||||
},
|
||||
child: new ConstrainedBox(
|
||||
constraints: new BoxConstraints(
|
||||
minWidth: _kMenuMinWidth,
|
||||
maxWidth: _kMenuMaxWidth
|
||||
),
|
||||
child: new IntrinsicWidth(
|
||||
stepWidth: _kMenuWidthStep,
|
||||
child: new ScrollableViewport(
|
||||
child: new Container(
|
||||
padding: const EdgeDims.symmetric(
|
||||
horizontal: _kMenuHorizontalPadding,
|
||||
vertical: _kMenuVerticalPadding
|
||||
),
|
||||
child: new BlockBody(children)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -121,8 +117,8 @@ class MenuPosition {
|
||||
final double left;
|
||||
}
|
||||
|
||||
class MenuRoute extends Route {
|
||||
MenuRoute({ this.completer, this.position, this.builder, this.level });
|
||||
class _MenuRoute extends Route {
|
||||
_MenuRoute({ this.completer, this.position, this.builder, this.level });
|
||||
|
||||
final Completer completer;
|
||||
final MenuPosition position;
|
||||
@ -137,7 +133,7 @@ class MenuRoute extends Route {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool get ephemeral => false; // we could make this true, but then we'd have to use popRoute(), not pop(), in menus
|
||||
bool get ephemeral => true;
|
||||
bool get modal => true;
|
||||
bool get opaque => false;
|
||||
Duration get transitionDuration => _kMenuDuration;
|
||||
@ -169,7 +165,7 @@ class MenuRoute extends Route {
|
||||
|
||||
Future showMenu({ NavigatorState navigator, MenuPosition position, PopupMenuItemsBuilder builder, int level: 4 }) {
|
||||
Completer completer = new Completer();
|
||||
navigator.push(new MenuRoute(
|
||||
navigator.push(new _MenuRoute(
|
||||
completer: completer,
|
||||
position: position,
|
||||
builder: builder,
|
||||
|
@ -31,10 +31,10 @@ abstract class ProgressIndicator extends StatefulComponent {
|
||||
|
||||
Widget _buildIndicator(BuildContext context, double performanceValue);
|
||||
|
||||
ProgressIndicatorState createState() => new ProgressIndicatorState();
|
||||
_ProgressIndicatorState createState() => new _ProgressIndicatorState();
|
||||
}
|
||||
|
||||
class ProgressIndicatorState extends State<ProgressIndicator> {
|
||||
class _ProgressIndicatorState extends State<ProgressIndicator> {
|
||||
|
||||
ValuePerformance<double> _performance;
|
||||
|
||||
|
@ -14,7 +14,7 @@ const sky.Color _kDarkOffColor = const sky.Color(0xB2FFFFFF);
|
||||
|
||||
typedef RadioValueChanged(Object value);
|
||||
|
||||
class Radio extends StatefulComponent {
|
||||
class Radio extends StatelessComponent {
|
||||
Radio({
|
||||
Key key,
|
||||
this.value,
|
||||
@ -28,13 +28,9 @@ class Radio extends StatefulComponent {
|
||||
final Object groupValue;
|
||||
final RadioValueChanged onChanged;
|
||||
|
||||
RadioState createState() => new RadioState();
|
||||
}
|
||||
|
||||
class RadioState extends State<Radio> {
|
||||
Color _getColor(BuildContext context) {
|
||||
ThemeData themeData = Theme.of(context);
|
||||
if (config.value == config.groupValue)
|
||||
if (value == groupValue)
|
||||
return themeData.accentColor;
|
||||
return themeData.brightness == ThemeBrightness.light ? _kLightOffColor : _kDarkOffColor;
|
||||
}
|
||||
@ -44,7 +40,7 @@ class RadioState extends State<Radio> {
|
||||
const double kOuterRadius = kDiameter / 2;
|
||||
const double kInnerRadius = 5.0;
|
||||
return new GestureDetector(
|
||||
onTap: () => config.onChanged(config.value),
|
||||
onTap: () => onChanged(value),
|
||||
child: new Container(
|
||||
margin: const EdgeDims.symmetric(horizontal: 5.0),
|
||||
width: kDiameter,
|
||||
@ -60,7 +56,7 @@ class RadioState extends State<Radio> {
|
||||
canvas.drawCircle(const Point(kOuterRadius, kOuterRadius), kOuterRadius, paint);
|
||||
|
||||
// Draw the inner circle
|
||||
if (config.value == config.groupValue) {
|
||||
if (value == groupValue) {
|
||||
paint.setStyle(sky.PaintingStyle.fill);
|
||||
canvas.drawCircle(const Point(kOuterRadius, kOuterRadius), kInnerRadius, paint);
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ class RaisedButton extends MaterialButton {
|
||||
assert(enabled != null);
|
||||
}
|
||||
|
||||
RaisedButtonState createState() => new RaisedButtonState();
|
||||
_RaisedButtonState createState() => new _RaisedButtonState();
|
||||
}
|
||||
|
||||
class RaisedButtonState extends MaterialButtonState<RaisedButton> {
|
||||
class _RaisedButtonState extends MaterialButtonState<RaisedButton> {
|
||||
Color getColor(BuildContext context) {
|
||||
if (config.enabled) {
|
||||
switch (Theme.of(context).brightness) {
|
||||
|
@ -6,247 +6,88 @@ import 'dart:sky' as sky;
|
||||
|
||||
import 'package:sky/material.dart';
|
||||
import 'package:sky/rendering.dart';
|
||||
import 'package:sky/src/widgets/basic.dart';
|
||||
import 'package:sky/src/widgets/framework.dart';
|
||||
|
||||
// Slots are painted in this order and hit tested in reverse of this order
|
||||
enum ScaffoldSlots {
|
||||
body,
|
||||
statusBar,
|
||||
toolbar,
|
||||
snackBar,
|
||||
floatingActionButton,
|
||||
drawer
|
||||
}
|
||||
|
||||
class RenderScaffold extends RenderBox {
|
||||
|
||||
RenderScaffold({
|
||||
RenderBox body,
|
||||
RenderBox statusBar,
|
||||
RenderBox toolbar,
|
||||
RenderBox snackBar,
|
||||
RenderBox floatingActionButton,
|
||||
RenderBox drawer
|
||||
}) {
|
||||
this[ScaffoldSlots.body] = body;
|
||||
this[ScaffoldSlots.statusBar] = statusBar;
|
||||
this[ScaffoldSlots.toolbar] = toolbar;
|
||||
this[ScaffoldSlots.snackBar] = snackBar;
|
||||
this[ScaffoldSlots.floatingActionButton] = floatingActionButton;
|
||||
this[ScaffoldSlots.drawer] = drawer;
|
||||
}
|
||||
|
||||
Map<ScaffoldSlots, RenderBox> _slots = new Map<ScaffoldSlots, RenderBox>();
|
||||
RenderBox operator[] (ScaffoldSlots slot) => _slots[slot];
|
||||
void operator[]= (ScaffoldSlots slot, RenderBox value) {
|
||||
RenderBox old = _slots[slot];
|
||||
if (old == value)
|
||||
return;
|
||||
if (old != null)
|
||||
dropChild(old);
|
||||
if (value == null) {
|
||||
_slots.remove(slot);
|
||||
} else {
|
||||
_slots[slot] = value;
|
||||
adoptChild(value);
|
||||
}
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
void attachChildren() {
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values) {
|
||||
RenderBox box = _slots[slot];
|
||||
if (box != null)
|
||||
box.attach();
|
||||
}
|
||||
}
|
||||
|
||||
void detachChildren() {
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values) {
|
||||
RenderBox box = _slots[slot];
|
||||
if (box != null)
|
||||
box.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void visitChildren(RenderObjectVisitor visitor) {
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values) {
|
||||
RenderBox box = _slots[slot];
|
||||
if (box != null)
|
||||
visitor(box);
|
||||
}
|
||||
}
|
||||
|
||||
ScaffoldSlots remove(RenderBox child) {
|
||||
assert(child != null);
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values) {
|
||||
if (_slots[slot] == child) {
|
||||
this[slot] = null;
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool get sizedByParent => true;
|
||||
void performResize() {
|
||||
size = constraints.biggest;
|
||||
assert(!size.isInfinite);
|
||||
}
|
||||
|
||||
// TODO(eseidel): These change based on device size!
|
||||
// http://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing
|
||||
static const kButtonX = 16.0; // left from right edge of body
|
||||
static const kButtonY = 16.0; // up from bottom edge of body
|
||||
|
||||
void performLayout() {
|
||||
double bodyHeight = size.height;
|
||||
double bodyPosition = 0.0;
|
||||
double fabOffset = 0.0;
|
||||
if (_slots[ScaffoldSlots.statusBar] != null) {
|
||||
RenderBox statusBar = _slots[ScaffoldSlots.statusBar];
|
||||
statusBar.layout(new BoxConstraints.tight(new Size(size.width, kStatusBarHeight)));
|
||||
assert(statusBar.parentData is BoxParentData);
|
||||
statusBar.parentData.position = new Point(0.0, size.height - kStatusBarHeight);
|
||||
bodyHeight -= kStatusBarHeight;
|
||||
}
|
||||
if (_slots[ScaffoldSlots.toolbar] != null) {
|
||||
RenderBox toolbar = _slots[ScaffoldSlots.toolbar];
|
||||
double toolbarHeight = kToolBarHeight + sky.view.paddingTop;
|
||||
toolbar.layout(new BoxConstraints.tight(new Size(size.width, toolbarHeight)));
|
||||
assert(toolbar.parentData is BoxParentData);
|
||||
toolbar.parentData.position = Point.origin;
|
||||
bodyPosition += toolbarHeight;
|
||||
bodyHeight -= toolbarHeight;
|
||||
}
|
||||
if (_slots[ScaffoldSlots.body] != null) {
|
||||
RenderBox body = _slots[ScaffoldSlots.body];
|
||||
body.layout(new BoxConstraints.tight(new Size(size.width, bodyHeight)));
|
||||
assert(body.parentData is BoxParentData);
|
||||
body.parentData.position = new Point(0.0, bodyPosition);
|
||||
}
|
||||
if (_slots[ScaffoldSlots.snackBar] != null) {
|
||||
RenderBox snackBar = _slots[ScaffoldSlots.snackBar];
|
||||
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568
|
||||
snackBar.layout(
|
||||
new BoxConstraints(minWidth: size.width, maxWidth: size.width, minHeight: 0.0, maxHeight: bodyHeight),
|
||||
parentUsesSize: true
|
||||
);
|
||||
assert(snackBar.parentData is BoxParentData);
|
||||
snackBar.parentData.position = new Point(0.0, bodyPosition + bodyHeight - snackBar.size.height);
|
||||
fabOffset += snackBar.size.height;
|
||||
}
|
||||
if (_slots[ScaffoldSlots.floatingActionButton] != null) {
|
||||
RenderBox floatingActionButton = _slots[ScaffoldSlots.floatingActionButton];
|
||||
Size area = new Size(size.width - kButtonX, size.height - kButtonY);
|
||||
floatingActionButton.layout(new BoxConstraints.loose(area), parentUsesSize: true);
|
||||
assert(floatingActionButton.parentData is BoxParentData);
|
||||
floatingActionButton.parentData.position = (area - floatingActionButton.size).toPoint() + new Offset(0.0, -fabOffset);
|
||||
}
|
||||
if (_slots[ScaffoldSlots.drawer] != null) {
|
||||
RenderBox drawer = _slots[ScaffoldSlots.drawer];
|
||||
drawer.layout(new BoxConstraints(minWidth: 0.0, maxWidth: size.width, minHeight: size.height, maxHeight: size.height));
|
||||
assert(drawer.parentData is BoxParentData);
|
||||
drawer.parentData.position = Point.origin;
|
||||
}
|
||||
}
|
||||
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values) {
|
||||
RenderBox box = _slots[slot];
|
||||
if (box != null) {
|
||||
assert(box.parentData is BoxParentData);
|
||||
context.paintChild(box, box.parentData.position + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hitTestChildren(HitTestResult result, { Point position }) {
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values.reversed) {
|
||||
RenderBox box = _slots[slot];
|
||||
if (box != null) {
|
||||
assert(box.parentData is BoxParentData);
|
||||
if (box.hitTest(result, position: (position - box.parentData.position).toPoint()))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String debugDescribeChildren(String prefix) {
|
||||
return _slots.keys.map((slot) => '${prefix}${slot}: ${_slots[slot].toStringDeep(prefix)}').join();
|
||||
}
|
||||
}
|
||||
|
||||
class Scaffold extends RenderObjectWidget {
|
||||
class Scaffold extends StatelessComponent {
|
||||
Scaffold({
|
||||
Key key,
|
||||
Widget body,
|
||||
Widget statusBar,
|
||||
Widget toolbar,
|
||||
Widget snackBar,
|
||||
Widget floatingActionButton,
|
||||
Widget drawer
|
||||
}) : super(key: key) {
|
||||
_children[ScaffoldSlots.body] = body;
|
||||
_children[ScaffoldSlots.statusBar] = statusBar;
|
||||
_children[ScaffoldSlots.toolbar] = toolbar;
|
||||
_children[ScaffoldSlots.snackBar] = snackBar;
|
||||
_children[ScaffoldSlots.floatingActionButton] = floatingActionButton;
|
||||
_children[ScaffoldSlots.drawer] = drawer;
|
||||
}
|
||||
this.body,
|
||||
this.statusBar,
|
||||
this.toolBar,
|
||||
this.snackBar,
|
||||
this.floatingActionButton
|
||||
}) : super(key: key);
|
||||
|
||||
final Map<ScaffoldSlots, Widget> _children = new Map<ScaffoldSlots, Widget>();
|
||||
final Widget body;
|
||||
final Widget statusBar;
|
||||
final Widget toolBar;
|
||||
final Widget snackBar;
|
||||
final Widget floatingActionButton;
|
||||
|
||||
RenderScaffold createRenderObject() => new RenderScaffold();
|
||||
Widget build(BuildContext context) {
|
||||
double toolBarHeight = 0.0;
|
||||
if (toolBar != null)
|
||||
toolBarHeight = kToolBarHeight + sky.view.paddingTop;
|
||||
|
||||
ScaffoldElement createElement() => new ScaffoldElement(this);
|
||||
}
|
||||
double statusBarHeight = 0.0;
|
||||
if (statusBar != null)
|
||||
statusBarHeight = kStatusBarHeight;
|
||||
|
||||
class ScaffoldElement extends RenderObjectElement<Scaffold> {
|
||||
ScaffoldElement(Scaffold widget) : super(widget);
|
||||
List<Widget> children = <Widget>[];
|
||||
|
||||
Map<ScaffoldSlots, Element> _children;
|
||||
|
||||
RenderScaffold get renderObject => super.renderObject;
|
||||
|
||||
void visitChildren(ElementVisitor visitor) {
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values) {
|
||||
Element element = _children[slot];
|
||||
if (element != null)
|
||||
visitor(element);
|
||||
if (body != null) {
|
||||
children.add(new Positioned(
|
||||
top: toolBarHeight, right: 0.0, bottom: statusBarHeight, left: 0.0,
|
||||
child: body
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void mount(Element parent, dynamic newSlot) {
|
||||
super.mount(parent, newSlot);
|
||||
_children = new Map<ScaffoldSlots, Element>();
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values) {
|
||||
Element newChild = widget._children[slot]?.createElement();
|
||||
_children[slot] = newChild;
|
||||
newChild?.mount(this, slot);
|
||||
if (statusBar != null) {
|
||||
children.add(new Positioned(
|
||||
right: 0.0, bottom: 0.0, left: 0.0,
|
||||
child: new SizedBox(
|
||||
height: statusBarHeight,
|
||||
child: statusBar
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void update(Scaffold newWidget) {
|
||||
super.update(newWidget);
|
||||
assert(widget == newWidget);
|
||||
for (ScaffoldSlots slot in ScaffoldSlots.values) {
|
||||
_children[slot] = updateChild(_children[slot], widget._children[slot], slot);
|
||||
assert((_children[slot] == null) == (widget._children[slot] == null));
|
||||
if (toolBar != null) {
|
||||
children.add(new Positioned(
|
||||
top: 0.0, right: 0.0, left: 0.0,
|
||||
child: new SizedBox(
|
||||
height: toolBarHeight,
|
||||
child: toolBar
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void insertChildRenderObject(RenderObject child, ScaffoldSlots slot) {
|
||||
renderObject[slot] = child;
|
||||
}
|
||||
if (snackBar != null || floatingActionButton != null) {
|
||||
List<Widget> floatingChildren = <Widget>[];
|
||||
|
||||
void moveChildRenderObject(RenderObject child, dynamic slot) {
|
||||
removeChildRenderObject(child);
|
||||
insertChildRenderObject(child, slot);
|
||||
}
|
||||
if (floatingActionButton != null) {
|
||||
floatingChildren.add(new Padding(
|
||||
// TODO(eseidel): These change based on device size!
|
||||
padding: const EdgeDims.only(right: 16.0, bottom: 16.0),
|
||||
child: floatingActionButton
|
||||
));
|
||||
}
|
||||
|
||||
void removeChildRenderObject(RenderObject child) {
|
||||
assert(renderObject == child.parent);
|
||||
renderObject.remove(child);
|
||||
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568
|
||||
if (snackBar != null) {
|
||||
floatingChildren.add(new ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: kSnackBarHeight),
|
||||
child: snackBar
|
||||
));
|
||||
}
|
||||
|
||||
children.add(new Positioned(
|
||||
right: 0.0, bottom: statusBarHeight, left: 0.0,
|
||||
child: new Column(floatingChildren, alignItems: FlexAlignItems.end)
|
||||
));
|
||||
}
|
||||
|
||||
return new Stack(children);
|
||||
}
|
||||
}
|
||||
|
@ -575,7 +575,7 @@ typedef void PageChangedCallback(int newPage);
|
||||
class PageableList<T> extends ScrollableList<T> {
|
||||
PageableList({
|
||||
Key key,
|
||||
double initialScrollOffset,
|
||||
int initialPage,
|
||||
ScrollDirection scrollDirection: ScrollDirection.horizontal,
|
||||
ScrollListener onScroll,
|
||||
List<T> items,
|
||||
@ -588,7 +588,7 @@ class PageableList<T> extends ScrollableList<T> {
|
||||
this.curve: ease
|
||||
}) : super(
|
||||
key: key,
|
||||
initialScrollOffset: initialScrollOffset,
|
||||
initialScrollOffset: initialPage == null ? null : initialPage * itemExtent,
|
||||
scrollDirection: scrollDirection,
|
||||
onScroll: onScroll,
|
||||
items: items,
|
||||
|
@ -6,21 +6,18 @@ import 'package:sky/animation.dart';
|
||||
import 'package:sky/gestures.dart';
|
||||
import 'package:sky/material.dart';
|
||||
import 'package:sky/painting.dart';
|
||||
import 'package:sky/src/widgets/animated_component.dart';
|
||||
import 'package:sky/src/widgets/basic.dart';
|
||||
import 'package:sky/src/widgets/framework.dart';
|
||||
import 'package:sky/src/widgets/gesture_detector.dart';
|
||||
import 'package:sky/src/widgets/material.dart';
|
||||
import 'package:sky/src/widgets/navigator.dart';
|
||||
import 'package:sky/src/widgets/placeholder.dart';
|
||||
import 'package:sky/src/widgets/theme.dart';
|
||||
import 'package:sky/src/widgets/transitions.dart';
|
||||
|
||||
typedef void SnackBarDismissedCallback();
|
||||
|
||||
const Duration _kSlideInDuration = const Duration(milliseconds: 200);
|
||||
const double kSnackHeight = 52.0;
|
||||
const double kSideMargins = 24.0;
|
||||
const double kVerticalPadding = 14.0;
|
||||
const Color kSnackBackground = const Color(0xFF323232);
|
||||
const double _kSideMargins = 24.0;
|
||||
const double _kVerticalPadding = 14.0;
|
||||
const Color _kSnackBackground = const Color(0xFF323232);
|
||||
|
||||
class SnackBarAction extends StatelessComponent {
|
||||
SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) {
|
||||
@ -34,70 +31,60 @@ class SnackBarAction extends StatelessComponent {
|
||||
return new GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: new Container(
|
||||
margin: const EdgeDims.only(left: kSideMargins),
|
||||
padding: const EdgeDims.symmetric(vertical: kVerticalPadding),
|
||||
margin: const EdgeDims.only(left: _kSideMargins),
|
||||
padding: const EdgeDims.symmetric(vertical: _kVerticalPadding),
|
||||
child: new Text(label)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SnackBar extends AnimatedComponent {
|
||||
class SnackBar extends StatelessComponent {
|
||||
SnackBar({
|
||||
Key key,
|
||||
this.content,
|
||||
this.actions,
|
||||
bool showing,
|
||||
this.onDismissed
|
||||
}) : super(key: key, direction: showing ? AnimationDirection.forward : AnimationDirection.reverse, duration: _kSlideInDuration) {
|
||||
this.performance
|
||||
}) : super(key: key) {
|
||||
assert(content != null);
|
||||
}
|
||||
|
||||
final Widget content;
|
||||
final List<SnackBarAction> actions;
|
||||
final SnackBarDismissedCallback onDismissed;
|
||||
|
||||
SnackBarState createState() => new SnackBarState();
|
||||
}
|
||||
|
||||
class SnackBarState extends AnimatedState<SnackBar> {
|
||||
void handleDismissed() {
|
||||
if (config.onDismissed != null)
|
||||
config.onDismissed();
|
||||
}
|
||||
final PerformanceView performance;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> children = [
|
||||
new Flexible(
|
||||
child: new Container(
|
||||
margin: const EdgeDims.symmetric(vertical: kVerticalPadding),
|
||||
margin: const EdgeDims.symmetric(vertical: _kVerticalPadding),
|
||||
child: new DefaultTextStyle(
|
||||
style: Typography.white.subhead,
|
||||
child: config.content
|
||||
child: content
|
||||
)
|
||||
)
|
||||
)
|
||||
];
|
||||
if (config.actions != null)
|
||||
children.addAll(config.actions);
|
||||
if (actions != null)
|
||||
children.addAll(actions);
|
||||
return new SquashTransition(
|
||||
performance: performance.view,
|
||||
performance: performance,
|
||||
height: new AnimatedValue<double>(
|
||||
0.0,
|
||||
end: kSnackHeight,
|
||||
end: kSnackBarHeight,
|
||||
curve: easeIn,
|
||||
reverseCurve: easeOut
|
||||
),
|
||||
child: new ClipRect(
|
||||
child: new OverflowBox(
|
||||
minHeight: kSnackHeight,
|
||||
maxHeight: kSnackHeight,
|
||||
minHeight: kSnackBarHeight,
|
||||
maxHeight: kSnackBarHeight,
|
||||
child: new Material(
|
||||
level: 2,
|
||||
color: kSnackBackground,
|
||||
color: _kSnackBackground,
|
||||
type: MaterialType.canvas,
|
||||
child: new Container(
|
||||
margin: const EdgeDims.symmetric(horizontal: kSideMargins),
|
||||
margin: const EdgeDims.symmetric(horizontal: _kSideMargins),
|
||||
child: new DefaultTextStyle(
|
||||
style: new TextStyle(color: Theme.of(context).accentColor),
|
||||
child: new Row(children)
|
||||
@ -109,3 +96,28 @@ class SnackBarState extends AnimatedState<SnackBar> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SnackBarRoute extends Route {
|
||||
_SnackBarRoute({ this.content, this.actions });
|
||||
|
||||
final Widget content;
|
||||
final List<SnackBarAction> actions;
|
||||
|
||||
bool get hasContent => false;
|
||||
bool get ephemeral => true;
|
||||
bool get modal => false;
|
||||
Duration get transitionDuration => const Duration(milliseconds: 200);
|
||||
|
||||
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) => null;
|
||||
}
|
||||
|
||||
void showSnackBar({ NavigatorState navigator, GlobalKey<PlaceholderState> placeholderKey, Widget content, List<SnackBarAction> actions }) {
|
||||
Route route = new _SnackBarRoute();
|
||||
SnackBar snackBar = new SnackBar(
|
||||
content: content,
|
||||
actions: actions,
|
||||
performance: route.performance
|
||||
);
|
||||
placeholderKey.currentState.child = snackBar;
|
||||
navigator.push(route);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import 'package:sky/gestures.dart';
|
||||
import 'package:sky/material.dart';
|
||||
import 'package:sky/painting.dart';
|
||||
import 'package:sky/rendering.dart';
|
||||
import 'package:sky/src/widgets/animated_container.dart';
|
||||
import 'package:sky/src/widgets/basic.dart';
|
||||
import 'package:sky/src/widgets/framework.dart';
|
||||
import 'package:sky/src/widgets/icon.dart';
|
||||
@ -19,8 +20,8 @@ import 'package:sky/src/widgets/scrollable.dart';
|
||||
import 'package:sky/src/widgets/theme.dart';
|
||||
import 'package:sky/src/widgets/transitions.dart';
|
||||
|
||||
typedef void SelectedIndexChanged(int selectedIndex);
|
||||
typedef void LayoutChanged(Size size, List<double> widths);
|
||||
typedef void TabSelectedIndexChanged(int selectedIndex);
|
||||
typedef void TabLayoutChanged(Size size, List<double> widths);
|
||||
|
||||
// See https://www.google.com/design/spec/components/tabs.html#tabs-specs
|
||||
const double _kTabHeight = 46.0;
|
||||
@ -33,14 +34,14 @@ const int _kTabIconSize = 24;
|
||||
const double _kTabBarScrollDrag = 0.025;
|
||||
const Duration _kTabBarScroll = const Duration(milliseconds: 200);
|
||||
|
||||
class TabBarParentData extends BoxParentData with
|
||||
class _TabBarParentData extends BoxParentData with
|
||||
ContainerParentDataMixin<RenderBox> { }
|
||||
|
||||
class RenderTabBar extends RenderBox with
|
||||
ContainerRenderObjectMixin<RenderBox, TabBarParentData>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox, TabBarParentData> {
|
||||
class _RenderTabBar extends RenderBox with
|
||||
ContainerRenderObjectMixin<RenderBox, _TabBarParentData>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox, _TabBarParentData> {
|
||||
|
||||
RenderTabBar(this.onLayoutChanged);
|
||||
_RenderTabBar(this.onLayoutChanged);
|
||||
|
||||
int _selectedIndex;
|
||||
int get selectedIndex => _selectedIndex;
|
||||
@ -51,15 +52,6 @@ class RenderTabBar extends RenderBox with
|
||||
}
|
||||
}
|
||||
|
||||
Color _backgroundColor;
|
||||
Color get backgroundColor => _backgroundColor;
|
||||
void set backgroundColor(Color value) {
|
||||
if (_backgroundColor != value) {
|
||||
_backgroundColor = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
}
|
||||
|
||||
Color _indicatorColor;
|
||||
Color get indicatorColor => _indicatorColor;
|
||||
void set indicatorColor(Color value) {
|
||||
@ -97,8 +89,8 @@ class RenderTabBar extends RenderBox with
|
||||
}
|
||||
|
||||
void setupParentData(RenderBox child) {
|
||||
if (child.parentData is! TabBarParentData)
|
||||
child.parentData = new TabBarParentData();
|
||||
if (child.parentData is! _TabBarParentData)
|
||||
child.parentData = new _TabBarParentData();
|
||||
}
|
||||
|
||||
double getMinIntrinsicWidth(BoxConstraints constraints) {
|
||||
@ -109,7 +101,7 @@ class RenderTabBar extends RenderBox with
|
||||
RenderBox child = firstChild;
|
||||
while (child != null) {
|
||||
maxWidth = math.max(maxWidth, child.getMinIntrinsicWidth(widthConstraints));
|
||||
assert(child.parentData is TabBarParentData);
|
||||
assert(child.parentData is _TabBarParentData);
|
||||
child = child.parentData.nextSibling;
|
||||
}
|
||||
double width = isScrollable ? maxWidth : maxWidth * childCount;
|
||||
@ -124,7 +116,7 @@ class RenderTabBar extends RenderBox with
|
||||
RenderBox child = firstChild;
|
||||
while (child != null) {
|
||||
maxWidth = math.max(maxWidth, child.getMaxIntrinsicWidth(widthConstraints));
|
||||
assert(child.parentData is TabBarParentData);
|
||||
assert(child.parentData is _TabBarParentData);
|
||||
child = child.parentData.nextSibling;
|
||||
}
|
||||
double width = isScrollable ? maxWidth : maxWidth * childCount;
|
||||
@ -148,7 +140,7 @@ class RenderTabBar extends RenderBox with
|
||||
RenderBox child = firstChild;
|
||||
while (child != null) {
|
||||
child.layout(tabConstraints);
|
||||
assert(child.parentData is TabBarParentData);
|
||||
assert(child.parentData is _TabBarParentData);
|
||||
child.parentData.position = new Point(x, 0.0);
|
||||
x += tabWidth;
|
||||
child = child.parentData.nextSibling;
|
||||
@ -166,7 +158,7 @@ class RenderTabBar extends RenderBox with
|
||||
RenderBox child = firstChild;
|
||||
while (child != null) {
|
||||
child.layout(tabConstraints, parentUsesSize: true);
|
||||
assert(child.parentData is TabBarParentData);
|
||||
assert(child.parentData is _TabBarParentData);
|
||||
child.parentData.position = new Point(x, 0.0);
|
||||
x += child.size.width;
|
||||
child = child.parentData.nextSibling;
|
||||
@ -176,7 +168,7 @@ class RenderTabBar extends RenderBox with
|
||||
|
||||
Size layoutSize;
|
||||
List<double> layoutWidths;
|
||||
LayoutChanged onLayoutChanged;
|
||||
TabLayoutChanged onLayoutChanged;
|
||||
|
||||
void reportLayoutChangedIfNeeded() {
|
||||
assert(onLayoutChanged != null);
|
||||
@ -240,17 +232,10 @@ class RenderTabBar extends RenderBox with
|
||||
}
|
||||
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (backgroundColor != null) {
|
||||
double width = layoutWidths != null
|
||||
? layoutWidths.reduce((sum, width) => sum + width)
|
||||
: size.width;
|
||||
Rect rect = offset & new Size(width, size.height);
|
||||
context.canvas.drawRect(rect, new Paint()..color = backgroundColor);
|
||||
}
|
||||
int index = 0;
|
||||
RenderBox child = firstChild;
|
||||
while (child != null) {
|
||||
assert(child.parentData is TabBarParentData);
|
||||
assert(child.parentData is _TabBarParentData);
|
||||
context.paintChild(child, child.parentData.position + offset);
|
||||
if (index++ == selectedIndex)
|
||||
_paintIndicator(context.canvas, child, offset);
|
||||
@ -264,7 +249,6 @@ class _TabBarWrapper extends MultiChildRenderObjectWidget {
|
||||
Key key,
|
||||
List<Widget> children,
|
||||
this.selectedIndex,
|
||||
this.backgroundColor,
|
||||
this.indicatorColor,
|
||||
this.indicatorRect,
|
||||
this.textAndIcons,
|
||||
@ -273,22 +257,20 @@ class _TabBarWrapper extends MultiChildRenderObjectWidget {
|
||||
}) : super(key: key, children: children);
|
||||
|
||||
final int selectedIndex;
|
||||
final Color backgroundColor;
|
||||
final Color indicatorColor;
|
||||
final Rect indicatorRect;
|
||||
final bool textAndIcons;
|
||||
final bool isScrollable;
|
||||
final LayoutChanged onLayoutChanged;
|
||||
final TabLayoutChanged onLayoutChanged;
|
||||
|
||||
RenderTabBar createRenderObject() {
|
||||
RenderTabBar result = new RenderTabBar(onLayoutChanged);
|
||||
_RenderTabBar createRenderObject() {
|
||||
_RenderTabBar result = new _RenderTabBar(onLayoutChanged);
|
||||
updateRenderObject(result, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
void updateRenderObject(RenderTabBar renderObject, _TabBarWrapper oldWidget) {
|
||||
void updateRenderObject(_RenderTabBar renderObject, _TabBarWrapper oldWidget) {
|
||||
renderObject.selectedIndex = selectedIndex;
|
||||
renderObject.backgroundColor = backgroundColor;
|
||||
renderObject.indicatorColor = indicatorColor;
|
||||
renderObject.indicatorRect = indicatorRect;
|
||||
renderObject.textAndIcons = textAndIcons;
|
||||
@ -399,13 +381,13 @@ class TabBar extends Scrollable {
|
||||
|
||||
final Iterable<TabLabel> labels;
|
||||
final int selectedIndex;
|
||||
final SelectedIndexChanged onChanged;
|
||||
final TabSelectedIndexChanged onChanged;
|
||||
final bool isScrollable;
|
||||
|
||||
TabBarState createState() => new TabBarState();
|
||||
_TabBarState createState() => new _TabBarState();
|
||||
}
|
||||
|
||||
class TabBarState extends ScrollableState<TabBar> {
|
||||
class _TabBarState extends ScrollableState<TabBar> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_indicatorAnimation = new ValuePerformance<Rect>()
|
||||
@ -537,7 +519,7 @@ class TabBarState extends ScrollableState<TabBar> {
|
||||
textAndIcons = true;
|
||||
}
|
||||
|
||||
Widget tabBar = new IconTheme(
|
||||
Widget content = new IconTheme(
|
||||
data: new IconThemeData(color: iconThemeColor),
|
||||
child: new DefaultTextStyle(
|
||||
style: textStyle,
|
||||
@ -548,7 +530,6 @@ class TabBarState extends ScrollableState<TabBar> {
|
||||
return new _TabBarWrapper(
|
||||
children: tabs,
|
||||
selectedIndex: config.selectedIndex,
|
||||
backgroundColor: backgroundColor,
|
||||
indicatorColor: indicatorColor,
|
||||
indicatorRect: _indicatorRect.value,
|
||||
textAndIcons: textAndIcons,
|
||||
@ -560,16 +541,23 @@ class TabBarState extends ScrollableState<TabBar> {
|
||||
)
|
||||
);
|
||||
|
||||
if (!config.isScrollable)
|
||||
return tabBar;
|
||||
if (config.isScrollable) {
|
||||
content = new SizeObserver(
|
||||
callback: _handleViewportSizeChanged,
|
||||
child: new Viewport(
|
||||
scrollDirection: ScrollDirection.horizontal,
|
||||
scrollOffset: new Offset(scrollOffset, 0.0),
|
||||
child: content
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return new SizeObserver(
|
||||
callback: _handleViewportSizeChanged,
|
||||
child: new Viewport(
|
||||
scrollDirection: ScrollDirection.horizontal,
|
||||
scrollOffset: new Offset(scrollOffset, 0.0),
|
||||
child: tabBar
|
||||
)
|
||||
return new AnimatedContainer(
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: backgroundColor
|
||||
),
|
||||
duration: kThemeChangeDuration,
|
||||
child: content
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -598,21 +586,16 @@ class TabNavigator extends StatelessComponent {
|
||||
|
||||
final List<TabNavigatorView> views;
|
||||
final int selectedIndex;
|
||||
final SelectedIndexChanged onChanged;
|
||||
final TabSelectedIndexChanged onChanged;
|
||||
final bool isScrollable;
|
||||
|
||||
void _handleSelectedIndexChanged(int tabIndex) {
|
||||
if (onChanged != null)
|
||||
onChanged(tabIndex);
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
assert(views != null && views.isNotEmpty);
|
||||
assert(selectedIndex >= 0 && selectedIndex < views.length);
|
||||
return new Column([
|
||||
new TabBar(
|
||||
labels: views.map((view) => view.label),
|
||||
onChanged: _handleSelectedIndexChanged,
|
||||
onChanged: onChanged,
|
||||
selectedIndex: selectedIndex,
|
||||
isScrollable: isScrollable
|
||||
),
|
||||
|
@ -4,35 +4,36 @@
|
||||
|
||||
import 'package:sky/material.dart';
|
||||
import 'package:sky/painting.dart';
|
||||
import 'package:sky/src/widgets/animated_container.dart';
|
||||
import 'package:sky/src/widgets/basic.dart';
|
||||
import 'package:sky/src/widgets/framework.dart';
|
||||
import 'package:sky/src/widgets/icon.dart';
|
||||
import 'package:sky/src/widgets/theme.dart';
|
||||
import 'package:sky/src/rendering/flex.dart';
|
||||
|
||||
class ToolBar extends StatelessComponent {
|
||||
|
||||
ToolBar({
|
||||
Key key,
|
||||
this.left,
|
||||
this.center,
|
||||
this.right,
|
||||
this.level: 2,
|
||||
this.backgroundColor
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget left;
|
||||
final Widget center;
|
||||
final List<Widget> right;
|
||||
final int level;
|
||||
final Color backgroundColor;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
Color toolbarColor = backgroundColor;
|
||||
Color color = backgroundColor;
|
||||
IconThemeData iconThemeData;
|
||||
TextStyle centerStyle = Typography.white.title;
|
||||
TextStyle sideStyle = Typography.white.body1;
|
||||
if (toolbarColor == null) {
|
||||
if (color == null) {
|
||||
ThemeData themeData = Theme.of(context);
|
||||
toolbarColor = themeData.primaryColor;
|
||||
color = themeData.primaryColor;
|
||||
if (themeData.primaryColorBrightness == ThemeBrightness.light) {
|
||||
centerStyle = Typography.black.title;
|
||||
sideStyle = Typography.black.body2;
|
||||
@ -44,11 +45,9 @@ class ToolBar extends StatelessComponent {
|
||||
|
||||
List<Widget> children = new List<Widget>();
|
||||
|
||||
// left children
|
||||
if (left != null)
|
||||
children.add(left);
|
||||
|
||||
// center children (left-aligned, but takes all remaining space)
|
||||
children.add(
|
||||
new Flexible(
|
||||
child: new Padding(
|
||||
@ -58,11 +57,16 @@ class ToolBar extends StatelessComponent {
|
||||
)
|
||||
);
|
||||
|
||||
// right children
|
||||
if (right != null)
|
||||
children.addAll(right);
|
||||
|
||||
Widget content = new Container(
|
||||
Widget content = new AnimatedContainer(
|
||||
duration: kThemeChangeDuration,
|
||||
padding: new EdgeDims.symmetric(horizontal: 8.0),
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: color,
|
||||
boxShadow: level == 0 ? null : shadows[2]
|
||||
),
|
||||
child: new DefaultTextStyle(
|
||||
style: sideStyle,
|
||||
child: new Column([
|
||||
@ -73,11 +77,6 @@ class ToolBar extends StatelessComponent {
|
||||
],
|
||||
justifyContent: FlexJustifyContent.end
|
||||
)
|
||||
),
|
||||
padding: new EdgeDims.symmetric(horizontal: 8.0),
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: toolbarColor,
|
||||
boxShadow: shadows[2]
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -21,10 +21,10 @@ abstract class TransitionComponent extends StatefulComponent {
|
||||
|
||||
Widget build(BuildContext context);
|
||||
|
||||
TransitionState createState() => new TransitionState();
|
||||
_TransitionState createState() => new _TransitionState();
|
||||
}
|
||||
|
||||
class TransitionState extends State<TransitionComponent> {
|
||||
class _TransitionState extends State<TransitionComponent> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
config.performance.addListener(_performanceChanged);
|
||||
|
@ -5,7 +5,6 @@
|
||||
/// The Flutter widget framework.
|
||||
library widgets;
|
||||
|
||||
export 'src/widgets/animated_component.dart';
|
||||
export 'src/widgets/animated_container.dart';
|
||||
export 'src/widgets/app.dart';
|
||||
export 'src/widgets/basic.dart';
|
||||
@ -37,6 +36,7 @@ export 'src/widgets/material_button.dart';
|
||||
export 'src/widgets/mimic.dart';
|
||||
export 'src/widgets/mixed_viewport.dart';
|
||||
export 'src/widgets/navigator.dart';
|
||||
export 'src/widgets/placeholder.dart';
|
||||
export 'src/widgets/popup_menu.dart';
|
||||
export 'src/widgets/popup_menu_item.dart';
|
||||
export 'src/widgets/progress_indicator.dart';
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: sky
|
||||
version: 0.0.52
|
||||
version: 0.0.53
|
||||
author: Flutter Authors <flutter-dev@googlegroups.com>
|
||||
description: A framework for writing Flutter applications
|
||||
homepage: http://flutter.io
|
||||
@ -9,8 +9,8 @@ dependencies:
|
||||
mojo_services: '>=0.1.0 <0.2.0'
|
||||
mojo: '>=0.1.0 <0.2.0'
|
||||
newton: ^0.1.4
|
||||
sky_engine: ^0.0.30
|
||||
sky_services: ^0.0.30
|
||||
sky_engine: ^0.0.31
|
||||
sky_services: ^0.0.31
|
||||
sky_tools: ^0.0.15
|
||||
vector_math: ^1.4.3
|
||||
intl: ^0.12.4+2
|
||||
|
@ -9,49 +9,219 @@ class PhysicsBody {
|
||||
PhysicsBody(this.shape, {
|
||||
this.tag: null,
|
||||
this.type: PhysicsBodyType.dynamic,
|
||||
this.density: 1.0,
|
||||
this.friction: 0.0,
|
||||
this.restitution: 0.0,
|
||||
this.isSensor: false,
|
||||
this.linearVelocity: Offset.zero,
|
||||
this.angularVelocity: 0.0,
|
||||
double density: 1.0,
|
||||
double friction: 0.0,
|
||||
double restitution: 0.0,
|
||||
bool isSensor: false,
|
||||
Offset linearVelocity: Offset.zero,
|
||||
double angularVelocity: 0.0,
|
||||
this.linearDampening: 0.0,
|
||||
this.angularDampening: 0.0,
|
||||
this.allowSleep: true,
|
||||
this.awake: true,
|
||||
this.fixedRotation: false,
|
||||
this.bullet: false,
|
||||
this.active: true,
|
||||
double awakeangularDampening: 0.0,
|
||||
bool allowSleep: true,
|
||||
bool awake: true,
|
||||
bool fixedRotation: false,
|
||||
bool bullet: false,
|
||||
bool active: true,
|
||||
this.gravityScale: 1.0
|
||||
});
|
||||
}) {
|
||||
this.density = density;
|
||||
this.friction = friction;
|
||||
this.restitution = restitution;
|
||||
this.isSensor = isSensor;
|
||||
|
||||
this.linearVelocity = linearVelocity;
|
||||
this.angularVelocity = angularVelocity;
|
||||
this.angularDampening = angularDampening;
|
||||
|
||||
this.allowSleep = allowSleep;
|
||||
this.awake = awake;
|
||||
this.fixedRotation = fixedRotation;
|
||||
this.bullet = bullet;
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
Object tag;
|
||||
|
||||
PhysicsShape shape;
|
||||
final PhysicsShape shape;
|
||||
|
||||
PhysicsBodyType type;
|
||||
|
||||
double density;
|
||||
double friction;
|
||||
double restitution;
|
||||
bool isSensor;
|
||||
double _density;
|
||||
|
||||
Offset linearVelocity;
|
||||
double angularVelocity;
|
||||
double get density => _density;
|
||||
|
||||
double linearDampening;
|
||||
set density(double density) {
|
||||
_density = density;
|
||||
|
||||
double angularDampening;
|
||||
if (_body == null)
|
||||
return;
|
||||
for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) {
|
||||
f.setDensity(density);
|
||||
}
|
||||
}
|
||||
|
||||
bool allowSleep;
|
||||
double _friction;
|
||||
|
||||
bool awake;
|
||||
double get friction => _friction;
|
||||
|
||||
bool fixedRotation;
|
||||
set friction(double friction) {
|
||||
_friction = friction;
|
||||
|
||||
bool bullet;
|
||||
if (_body == null)
|
||||
return;
|
||||
for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) {
|
||||
f.setFriction(friction);
|
||||
}
|
||||
}
|
||||
|
||||
bool active;
|
||||
double _restitution;
|
||||
|
||||
double get restitution => _restitution;
|
||||
|
||||
set restitution(double restitution) {
|
||||
_restitution = restitution;
|
||||
|
||||
if (_body == null)
|
||||
return;
|
||||
for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) {
|
||||
f.setRestitution(restitution);
|
||||
}
|
||||
}
|
||||
|
||||
bool _isSensor;
|
||||
|
||||
bool get isSensor => _isSensor;
|
||||
|
||||
set isSensor(bool isSensor) {
|
||||
_isSensor = isSensor;
|
||||
|
||||
if (_body == null)
|
||||
return;
|
||||
for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) {
|
||||
f.setSensor(isSensor);
|
||||
}
|
||||
}
|
||||
|
||||
Offset _linearVelocity;
|
||||
|
||||
Offset get linearVelocity {
|
||||
if (_body == null)
|
||||
return _linearVelocity;
|
||||
else {
|
||||
double dx = _body.linearVelocity.x * _physicsNode.b2WorldToNodeConversionFactor;
|
||||
double dy = _body.linearVelocity.y * _physicsNode.b2WorldToNodeConversionFactor;
|
||||
return new Offset(dx, dy);
|
||||
}
|
||||
}
|
||||
|
||||
set linearVelocity(Offset linearVelocity) {
|
||||
_linearVelocity = linearVelocity;
|
||||
|
||||
if (_body != null) {
|
||||
Vector2 vec = new Vector2(
|
||||
linearVelocity.dx / _physicsNode.b2WorldToNodeConversionFactor,
|
||||
linearVelocity.dy / _physicsNode.b2WorldToNodeConversionFactor
|
||||
);
|
||||
_body.linearVelocity = vec;
|
||||
}
|
||||
}
|
||||
|
||||
double _angularVelocity;
|
||||
|
||||
double get angularVelocity {
|
||||
if (_body == null)
|
||||
return _angularVelocity;
|
||||
else
|
||||
return _body.angularVelocity;
|
||||
}
|
||||
|
||||
set angularVelocity(double angularVelocity) {
|
||||
_angularVelocity = angularVelocity;
|
||||
|
||||
if (_body != null) {
|
||||
_body.angularVelocity = angularVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should this be editable in box2d.Body ?
|
||||
final double linearDampening;
|
||||
|
||||
double _angularDampening;
|
||||
|
||||
double get angularDampening => _angularDampening;
|
||||
|
||||
set angularDampening(double angularDampening) {
|
||||
_angularDampening = angularDampening;
|
||||
|
||||
if (_body != null)
|
||||
_body.angularDamping = angularDampening;
|
||||
}
|
||||
|
||||
bool _allowSleep;
|
||||
|
||||
bool get allowSleep => _allowSleep;
|
||||
|
||||
set allowSleep(bool allowSleep) {
|
||||
_allowSleep = allowSleep;
|
||||
|
||||
if (_body != null)
|
||||
_body.setSleepingAllowed(allowSleep);
|
||||
}
|
||||
|
||||
bool _awake;
|
||||
|
||||
bool get awake {
|
||||
if (_body != null)
|
||||
return _body.isAwake();
|
||||
else
|
||||
return _awake;
|
||||
}
|
||||
|
||||
set awake(bool awake) {
|
||||
_awake = awake;
|
||||
|
||||
if (_body != null)
|
||||
_body.setAwake(awake);
|
||||
}
|
||||
|
||||
bool _fixedRotation;
|
||||
|
||||
bool get fixedRotation => _fixedRotation;
|
||||
|
||||
set fixedRotation(bool fixedRotation) {
|
||||
_fixedRotation = fixedRotation;
|
||||
|
||||
if (_body != null)
|
||||
_body.setFixedRotation(fixedRotation);
|
||||
}
|
||||
|
||||
bool _bullet;
|
||||
|
||||
bool get bullet => _bullet;
|
||||
|
||||
set bullet(bool bullet) {
|
||||
_bullet = bullet;
|
||||
|
||||
if (_body != null) {
|
||||
_body.setBullet(bullet);
|
||||
}
|
||||
}
|
||||
|
||||
bool _active;
|
||||
|
||||
bool get active {
|
||||
if (_body != null)
|
||||
return _body.isActive();
|
||||
else
|
||||
return _active;
|
||||
}
|
||||
|
||||
set active(bool active) {
|
||||
_active = active;
|
||||
|
||||
if (_body != null)
|
||||
_body.setActive(active);
|
||||
}
|
||||
|
||||
double gravityScale;
|
||||
|
||||
|
@ -66,6 +66,10 @@ class BadDisposeWidgetState extends State<BadDisposeWidget> {
|
||||
void main() {
|
||||
dynamic cachedException;
|
||||
|
||||
// ** WARNING **
|
||||
// THIS TEST OVERRIDES THE NORMAL EXCEPTION HANDLING
|
||||
// AND DOES NOT REPORT EXCEPTIONS FROM THE FRAMEWORK
|
||||
|
||||
setUp(() {
|
||||
assert(cachedException == null);
|
||||
debugWidgetsExceptionHandler = (String context, dynamic exception, StackTrace stack) {
|
||||
|
63
packages/unit/test/widget/focus_test.dart
Normal file
63
packages/unit/test/widget/focus_test.dart
Normal file
@ -0,0 +1,63 @@
|
||||
import 'package:sky/widgets.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'widget_tester.dart';
|
||||
|
||||
class TestFocusable extends StatelessComponent {
|
||||
TestFocusable(this.no, this.yes, GlobalKey key) : super(key: key);
|
||||
final String no;
|
||||
final String yes;
|
||||
Widget build(BuildContext context) {
|
||||
bool focused = Focus.at(context, this);
|
||||
return new GestureDetector(
|
||||
onTap: () { Focus.moveTo(context, this); },
|
||||
child: new Text(focused ? yes : no)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('Can have multiple focused children and they update accordingly', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
GlobalKey keyA = new GlobalKey();
|
||||
GlobalKey keyB = new GlobalKey();
|
||||
tester.pumpWidget(
|
||||
new Focus(
|
||||
child: new Column([
|
||||
// reverse these when you fix https://github.com/flutter/engine/issues/1495
|
||||
new TestFocusable('b', 'B FOCUSED', keyB),
|
||||
new TestFocusable('a', 'A FOCUSED', keyA),
|
||||
])
|
||||
)
|
||||
);
|
||||
expect(tester.findText('a'), isNull);
|
||||
expect(tester.findText('A FOCUSED'), isNotNull);
|
||||
expect(tester.findText('b'), isNotNull);
|
||||
expect(tester.findText('B FOCUSED'), isNull);
|
||||
tester.tap(tester.findText('A FOCUSED'));
|
||||
tester.pump();
|
||||
expect(tester.findText('a'), isNull);
|
||||
expect(tester.findText('A FOCUSED'), isNotNull);
|
||||
expect(tester.findText('b'), isNotNull);
|
||||
expect(tester.findText('B FOCUSED'), isNull);
|
||||
tester.tap(tester.findText('A FOCUSED'));
|
||||
tester.pump();
|
||||
expect(tester.findText('a'), isNull);
|
||||
expect(tester.findText('A FOCUSED'), isNotNull);
|
||||
expect(tester.findText('b'), isNotNull);
|
||||
expect(tester.findText('B FOCUSED'), isNull);
|
||||
tester.tap(tester.findText('b'));
|
||||
tester.pump();
|
||||
expect(tester.findText('a'), isNotNull);
|
||||
expect(tester.findText('A FOCUSED'), isNull);
|
||||
expect(tester.findText('b'), isNull);
|
||||
expect(tester.findText('B FOCUSED'), isNotNull);
|
||||
tester.tap(tester.findText('a'));
|
||||
tester.pump();
|
||||
expect(tester.findText('a'), isNull);
|
||||
expect(tester.findText('A FOCUSED'), isNotNull);
|
||||
expect(tester.findText('b'), isNotNull);
|
||||
expect(tester.findText('B FOCUSED'), isNull);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user