Merge pull request #1484 from abarth/snackbar_route
Use Navigator to drive SnackBar
This commit is contained in:
commit
5a359dce9f
@ -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)
|
||||
@ -240,7 +221,7 @@ class FeedFragmentState extends State<FeedFragment> {
|
||||
return new Scaffold(
|
||||
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';
|
||||
|
@ -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(),
|
||||
body: buildBody(context),
|
||||
snackBar: buildSnackBar()
|
||||
snackBar: new Placeholder(key: _snackBarPlaceholderKey)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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(() {
|
||||
@ -217,28 +213,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() {
|
||||
@ -253,7 +239,7 @@ class StockHomeState extends State<StockHome> {
|
||||
return new Scaffold(
|
||||
toolbar: _isSearching ? buildSearchBar() : buildToolBar(),
|
||||
body: buildTabNavigator(),
|
||||
snackBar: buildSnackBar(),
|
||||
snackBar: new Placeholder(key: _snackBarPlaceholderKey),
|
||||
floatingActionButton: buildFloatingActionButton()
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -114,10 +114,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;
|
||||
|
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);
|
||||
}
|
||||
}
|
@ -137,7 +137,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;
|
||||
|
@ -6,21 +6,20 @@ 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 _kSnackHeight = 52.0;
|
||||
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 +33,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: _kSnackHeight,
|
||||
curve: easeIn,
|
||||
reverseCurve: easeOut
|
||||
),
|
||||
child: new ClipRect(
|
||||
child: new OverflowBox(
|
||||
minHeight: kSnackHeight,
|
||||
maxHeight: kSnackHeight,
|
||||
minHeight: _kSnackHeight,
|
||||
maxHeight: _kSnackHeight,
|
||||
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 +98,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 => _kSlideInDuration;
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -37,6 +37,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';
|
||||
|
Loading…
x
Reference in New Issue
Block a user