Commit 79c88b4d authored by Kris Giesing's avatar Kris Giesing

Merge remote-tracking branch 'upstream/master'

parents 43a5dff8 cf17dd96
......@@ -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,6 +126,7 @@ class StockHomeState extends State<StockHome> {
Widget buildToolBar() {
return new ToolBar(
level: 0,
left: new IconButton(
icon: "navigation/menu",
onPressed: _showDrawer
......@@ -217,30 +214,20 @@ class StockHomeState extends State<StockHome> {
}
void _handleUndo() {
setState(() {
_isSnackBarShowing = false;
});
config.navigator.pop();
}
GlobalKey snackBarKey = new GlobalKey(label: 'snackbar');
Widget buildSnackBar() {
if (_snackBarStatus == PerformanceStatus.dismissed)
return null;
return new SnackBar(
showing: _isSnackBarShowing,
void _handleStockPurchased() {
showSnackBar(
navigator: config.navigator,
placeholderKey: _snackBarPlaceholderKey,
content: new Text("Stock purchased!"),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
onDismissed: () { setState(() { _snackBarStatus = PerformanceStatus.dismissed; }); }
actions: [
new SnackBarAction(label: "UNDO", onPressed: _handleUndo)
]
);
}
void _handleStockPurchased() {
setState(() {
_isSnackBarShowing = true;
_snackBarStatus = PerformanceStatus.forward;
});
}
Widget buildFloatingActionButton() {
return new FloatingActionButton(
child: new Icon(type: 'content/add', size: 24),
......@@ -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));
});
}
}
......
// 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));
}
// 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;
......
......@@ -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()) {
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 {
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()';
......
// 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,8 +74,6 @@ 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],
......@@ -108,7 +105,6 @@ class PopupMenu extends StatelessComponent {
);
}
)
)
);
}
}
......@@ -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;
}
final Map<ScaffoldSlots, Widget> _children = new Map<ScaffoldSlots, Widget>();
RenderScaffold createRenderObject() => new RenderScaffold();
ScaffoldElement createElement() => new ScaffoldElement(this);
}
class ScaffoldElement extends RenderObjectElement<Scaffold> {
ScaffoldElement(Scaffold widget) : super(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);
}
}
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);
}
}
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));
}
this.body,
this.statusBar,
this.toolBar,
this.snackBar,
this.floatingActionButton
}) : super(key: key);
final Widget body;
final Widget statusBar;
final Widget toolBar;
final Widget snackBar;
final Widget floatingActionButton;
Widget build(BuildContext context) {
double toolBarHeight = 0.0;
if (toolBar != null)
toolBarHeight = kToolBarHeight + sky.view.paddingTop;
double statusBarHeight = 0.0;
if (statusBar != null)
statusBarHeight = kStatusBarHeight;
List<Widget> children = <Widget>[];
if (body != null) {
children.add(new Positioned(
top: toolBarHeight, right: 0.0, bottom: statusBarHeight, left: 0.0,
child: body
));
}
if (statusBar != null) {
children.add(new Positioned(
right: 0.0, bottom: 0.0, left: 0.0,
child: new SizedBox(
height: statusBarHeight,
child: statusBar
)
));
}
if (toolBar != null) {
children.add(new Positioned(
top: 0.0, right: 0.0, left: 0.0,
child: new SizedBox(
height: toolBarHeight,
child: toolBar
)
));
}
if (snackBar != null || floatingActionButton != null) {
List<Widget> floatingChildren = <Widget>[];
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 insertChildRenderObject(RenderObject child, ScaffoldSlots slot) {
renderObject[slot] = child;
// TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568
if (snackBar != null) {
floatingChildren.add(new ConstrainedBox(
constraints: const BoxConstraints(maxHeight: kSnackBarHeight),
child: snackBar
));
}
void moveChildRenderObject(RenderObject child, dynamic slot) {
removeChildRenderObject(child);
insertChildRenderObject(child, slot);
children.add(new Positioned(
right: 0.0, bottom: statusBarHeight, left: 0.0,
child: new Column(floatingChildren, alignItems: FlexAlignItems.end)
));
}
void removeChildRenderObject(RenderObject child) {
assert(renderObject == child.parent);
renderObject.remove(child);
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,18 +541,25 @@ class TabBarState extends ScrollableState<TabBar> {
)
);
if (!config.isScrollable)
return tabBar;
return new SizeObserver(
if (config.isScrollable) {
content = new SizeObserver(
callback: _handleViewportSizeChanged,
child: new Viewport(
scrollDirection: ScrollDirection.horizontal,
scrollOffset: new Offset(scrollOffset, 0.0),
child: tabBar
child: content
)
);
}
return new AnimatedContainer(
decoration: new BoxDecoration(
backgroundColor: backgroundColor
),
duration: kThemeChangeDuration,
child: content
);
}
}
class TabNavigatorView {
......@@ -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';
......
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;
double get density => _density;
set density(double density) {
_density = density;
if (_body == null)
return;
for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) {
f.setDensity(density);
}
}
double _friction;
double get friction => _friction;
set friction(double friction) {
_friction = friction;
if (_body == null)
return;
for (box2d.Fixture f = _body.getFixtureList(); f != null; f = f.getNext()) {
f.setFriction(friction);
}
}
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;
Offset linearVelocity;
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;
double linearDampening;
set angularDampening(double angularDampening) {
_angularDampening = angularDampening;
double angularDampening;
if (_body != null)
_body.angularDamping = angularDampening;
}
bool _allowSleep;
bool allowSleep;
bool get allowSleep => _allowSleep;
bool awake;
set allowSleep(bool allowSleep) {
_allowSleep = allowSleep;
bool fixedRotation;
if (_body != null)
_body.setSleepingAllowed(allowSleep);
}
bool bullet;
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 active;
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) {
......
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);
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment