Reorganize the calculator demo (#4536)
This patch is a warmup to improving the visuals in the calculator demo. Related to #4535
This commit is contained in:
parent
d2e3493a6c
commit
1d5d7f4ad3
269
examples/flutter_gallery/lib/demo/calculator/home.dart
Normal file
269
examples/flutter_gallery/lib/demo/calculator/home.dart
Normal file
@ -0,0 +1,269 @@
|
||||
// Copyright 2016 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:flutter/material.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class Calculator extends StatefulWidget {
|
||||
Calculator({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_CalculatorState createState() => new _CalculatorState();
|
||||
}
|
||||
|
||||
class _CalculatorState extends State<Calculator> {
|
||||
/// As the user taps keys we update the current `_expression` and we also
|
||||
/// keep a stack of previous expressions so we can return to earlier states
|
||||
/// when the user hits the DEL key.
|
||||
final List<CalcExpression> _expressionStack = <CalcExpression>[];
|
||||
CalcExpression _expression = new CalcExpression.Empty();
|
||||
|
||||
// Make `expression` the current expression and push the previous current
|
||||
// expression onto the stack.
|
||||
void pushExpression(CalcExpression expression) {
|
||||
_expressionStack.add(_expression);
|
||||
_expression = expression;
|
||||
}
|
||||
|
||||
/// Pop the top expression off of the stack and make it the current expression.
|
||||
void popCalcExpression() {
|
||||
if (_expressionStack.length > 0) {
|
||||
_expression = _expressionStack.removeLast();
|
||||
} else {
|
||||
_expression = new CalcExpression.Empty();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set `resultExpression` to the currrent expression and clear the stack.
|
||||
void setResult(CalcExpression resultExpression) {
|
||||
_expressionStack.clear();
|
||||
_expression = resultExpression;
|
||||
}
|
||||
|
||||
void handleNumberTap(int n) {
|
||||
final CalcExpression expression = _expression.appendDigit(n);
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handlePointTap() {
|
||||
final CalcExpression expression = _expression.appendPoint();
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handlePlusTap() {
|
||||
final CalcExpression expression = _expression.appendOperation(Operation.Addition);
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleMinusTap() {
|
||||
final CalcExpression expression = _expression.appendMinus();
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleMultTap() {
|
||||
final CalcExpression expression = _expression.appendOperation(Operation.Multiplication);
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleDivTap() {
|
||||
final CalcExpression expression = _expression.appendOperation(Operation.Division);
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleEqualsTap() {
|
||||
final CalcExpression resultExpression = _expression.computeResult();
|
||||
if (resultExpression != null) {
|
||||
setState(() {
|
||||
setResult(resultExpression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleDelTap() {
|
||||
setState(() {
|
||||
popCalcExpression();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
appBar: new AppBar(
|
||||
backgroundColor: Theme.of(context).canvasColor,
|
||||
elevation: 0
|
||||
),
|
||||
body: new Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
// Give the key-pad 3/5 of the vertical space and the display 2/5.
|
||||
new Flexible(
|
||||
flex: 2,
|
||||
child: new CalcDisplay(content: _expression.toString())
|
||||
),
|
||||
new Divider(height: 1.0),
|
||||
new Flexible(
|
||||
flex: 3,
|
||||
child: new KeyPad(calcState: this)
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CalcDisplay extends StatelessWidget {
|
||||
CalcDisplay({ this.content });
|
||||
|
||||
final String content;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Center(
|
||||
child: new Text(
|
||||
content,
|
||||
style: const TextStyle(fontSize: 24.0)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KeyPad extends StatelessWidget {
|
||||
KeyPad({ this.calcState });
|
||||
|
||||
final _CalculatorState calcState;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData themeData = new ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
brightness: Brightness.dark
|
||||
);
|
||||
return new Theme(
|
||||
data: themeData,
|
||||
child: new Material(
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Flexible(
|
||||
// We set flex equal to the number of columns so that the main keypad
|
||||
// and the op keypad have sizes proportional to their number of
|
||||
// columns.
|
||||
flex: 3,
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new KeyRow(<Widget>[
|
||||
new NumberKey(7, calcState),
|
||||
new NumberKey(8, calcState),
|
||||
new NumberKey(9, calcState)
|
||||
]),
|
||||
new KeyRow(<Widget>[
|
||||
new NumberKey(4, calcState),
|
||||
new NumberKey(5, calcState),
|
||||
new NumberKey(6, calcState)
|
||||
]),
|
||||
new KeyRow(<Widget>[
|
||||
new NumberKey(1, calcState),
|
||||
new NumberKey(2, calcState),
|
||||
new NumberKey(3, calcState)
|
||||
]),
|
||||
new KeyRow(<Widget>[
|
||||
new CalcKey('.', calcState.handlePointTap),
|
||||
new NumberKey(0, calcState),
|
||||
new CalcKey('=', calcState.handleEqualsTap),
|
||||
])
|
||||
]
|
||||
)
|
||||
),
|
||||
new Flexible(
|
||||
child: new Material(
|
||||
color: themeData.backgroundColor,
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new CalcKey('\u232B', calcState.handleDelTap),
|
||||
new CalcKey('\u00F7', calcState.handleDivTap),
|
||||
new CalcKey('\u00D7', calcState.handleMultTap),
|
||||
new CalcKey('-', calcState.handleMinusTap),
|
||||
new CalcKey('+', calcState.handlePlusTap)
|
||||
]
|
||||
)
|
||||
)
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KeyRow extends StatelessWidget {
|
||||
KeyRow(this.keys);
|
||||
|
||||
final List<Widget> keys;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Flexible(
|
||||
child: new Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: this.keys
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CalcKey extends StatelessWidget {
|
||||
CalcKey(this.text, this.onTap);
|
||||
|
||||
final String text;
|
||||
final GestureTapCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Orientation orientation = MediaQuery.of(context).orientation;
|
||||
return new Flexible(
|
||||
child: new InkResponse(
|
||||
onTap: this.onTap,
|
||||
child: new Center(
|
||||
child: new Text(
|
||||
this.text,
|
||||
style: new TextStyle(
|
||||
fontSize: (orientation == Orientation.portrait) ? 32.0 : 24.0
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NumberKey extends CalcKey {
|
||||
NumberKey(int value, _CalculatorState calcState)
|
||||
: super('$value', () {
|
||||
calcState.handleNumberTap(value);
|
||||
});
|
||||
}
|
@ -4,287 +4,13 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'calculator_logic.dart';
|
||||
import 'calculator/home.dart';
|
||||
|
||||
class Calculator extends StatefulWidget {
|
||||
Calculator({Key key}) : super(key: key);
|
||||
class CalculatorDemo extends StatelessWidget {
|
||||
CalculatorDemo({Key key}) : super(key: key);
|
||||
|
||||
static const String routeName = '/calculator';
|
||||
|
||||
@override
|
||||
_CalculatorState createState() => new _CalculatorState();
|
||||
}
|
||||
|
||||
class _CalculatorState extends State<Calculator> {
|
||||
/// As the user taps keys we update the current `_expression` and we also
|
||||
/// keep a stack of previous expressions so we can return to earlier states
|
||||
/// when the user hits the DEL key.
|
||||
final List<CalcExpression> _expressionStack = <CalcExpression>[];
|
||||
CalcExpression _expression = new CalcExpression.Empty();
|
||||
|
||||
// Make `expression` the current expression and push the previous current
|
||||
// expression onto the stack.
|
||||
void pushExpression(CalcExpression expression) {
|
||||
_expressionStack.add(_expression);
|
||||
_expression = expression;
|
||||
}
|
||||
|
||||
/// Pop the top expression off of the stack and make it the current expression.
|
||||
void popCalcExpression() {
|
||||
if (_expressionStack.length > 0) {
|
||||
_expression = _expressionStack.removeLast();
|
||||
} else {
|
||||
_expression = new CalcExpression.Empty();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set `resultExpression` to the currrent expression and clear the stack.
|
||||
void setResult(CalcExpression resultExpression) {
|
||||
_expressionStack.clear();
|
||||
_expression = resultExpression;
|
||||
}
|
||||
|
||||
void handleNumberTap(int n) {
|
||||
final CalcExpression expression = _expression.appendDigit(n);
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handlePointTap() {
|
||||
final CalcExpression expression = _expression.appendPoint();
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handlePlusTap() {
|
||||
final CalcExpression expression = _expression.appendOperation(Operation.Addition);
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleMinusTap() {
|
||||
final CalcExpression expression = _expression.appendMinus();
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleMultTap() {
|
||||
final CalcExpression expression = _expression.appendOperation(Operation.Multiplication);
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleDivTap() {
|
||||
final CalcExpression expression = _expression.appendOperation(Operation.Division);
|
||||
if (expression != null) {
|
||||
setState(() {
|
||||
pushExpression(expression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleEqualsTap() {
|
||||
final CalcExpression resultExpression = _expression.computeResult();
|
||||
if (resultExpression != null) {
|
||||
setState(() {
|
||||
setResult(resultExpression);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleDelTap() {
|
||||
setState(() {
|
||||
popCalcExpression();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
appBar: new AppBar(title: new Text('Calculator')),
|
||||
body: new Column(
|
||||
children: <Widget>[
|
||||
// Give the key-pad 3/5 of the vertical space and the display 2/5.
|
||||
new CalcDisplay(2, _expression.toString()),
|
||||
new Divider(height: 1.0),
|
||||
new KeyPad(3, calcState: this)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CalcDisplay extends StatelessWidget {
|
||||
CalcDisplay(this._flex, this._contents);
|
||||
|
||||
final int _flex;
|
||||
final String _contents;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Flexible(
|
||||
flex: _flex,
|
||||
child: new Center(
|
||||
child: new Text(
|
||||
_contents,
|
||||
style: const TextStyle(fontSize: 24.0)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KeyPad extends StatelessWidget {
|
||||
KeyPad(this._flex, {this.calcState});
|
||||
|
||||
final int _flex;
|
||||
final _CalculatorState calcState;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Theme(
|
||||
data: new ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
brightness: Brightness.dark
|
||||
),
|
||||
child: new Flexible(
|
||||
flex: _flex,
|
||||
child: new Material(
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new MainKeyPad(calcState: calcState),
|
||||
new OpKeyPad(calcState: calcState),
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MainKeyPad extends StatelessWidget {
|
||||
MainKeyPad({this.calcState});
|
||||
|
||||
final _CalculatorState calcState;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Flexible(
|
||||
// We set flex equal to the number of columns so that the main keypad
|
||||
// and the op keypad have sizes proportional to their number of
|
||||
// columns.
|
||||
flex: 3,
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new KeyRow(<Widget>[
|
||||
new NumberKey(7, calcState),
|
||||
new NumberKey(8, calcState),
|
||||
new NumberKey(9, calcState)
|
||||
]),
|
||||
new KeyRow(<Widget>[
|
||||
new NumberKey(4, calcState),
|
||||
new NumberKey(5, calcState),
|
||||
new NumberKey(6, calcState)
|
||||
]),
|
||||
new KeyRow(<Widget>[
|
||||
new NumberKey(1, calcState),
|
||||
new NumberKey(2, calcState),
|
||||
new NumberKey(3, calcState)
|
||||
]),
|
||||
new KeyRow(<Widget>[
|
||||
new CalcKey('.', calcState.handlePointTap),
|
||||
new NumberKey(0, calcState),
|
||||
new CalcKey('=', calcState.handleEqualsTap),
|
||||
])
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OpKeyPad extends StatelessWidget {
|
||||
OpKeyPad({this.calcState});
|
||||
|
||||
final _CalculatorState calcState;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
return new Flexible(
|
||||
child: new Material(
|
||||
color: themeData.backgroundColor,
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new CalcKey('\u232B', calcState.handleDelTap),
|
||||
new CalcKey('\u00F7', calcState.handleDivTap),
|
||||
new CalcKey('\u00D7', calcState.handleMultTap),
|
||||
new CalcKey('-', calcState.handleMinusTap),
|
||||
new CalcKey('+', calcState.handlePlusTap)
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KeyRow extends StatelessWidget {
|
||||
KeyRow(this.keys);
|
||||
|
||||
final List<Widget> keys;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Flexible(
|
||||
child: new Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center, children: this.keys
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CalcKey extends StatelessWidget {
|
||||
CalcKey(this.text, this.onTap);
|
||||
|
||||
final String text;
|
||||
final GestureTapCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Orientation orientation = MediaQuery.of(context).orientation;
|
||||
return new Flexible(
|
||||
child: new InkResponse(
|
||||
onTap: this.onTap,
|
||||
child: new Center(
|
||||
child: new Text(
|
||||
this.text,
|
||||
style: new TextStyle(
|
||||
fontSize: (orientation == Orientation.portrait) ? 32.0 : 24.0
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NumberKey extends CalcKey {
|
||||
NumberKey(int value, _CalculatorState calcState)
|
||||
: super('$value', () {
|
||||
calcState.handleNumberTap(value);
|
||||
});
|
||||
Widget build(BuildContext context) => new Calculator();
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import 'home.dart';
|
||||
final Map<String, WidgetBuilder> kRoutes = <String, WidgetBuilder>{
|
||||
PestoDemo.routeName: (BuildContext context) => new PestoDemo(),
|
||||
ShrineDemo.routeName: (BuildContext context) => new ShrineDemo(),
|
||||
Calculator.routeName: (BuildContext context) => new Calculator(),
|
||||
CalculatorDemo.routeName: (BuildContext context) => new CalculatorDemo(),
|
||||
ContactsDemo.routeName: (BuildContext context) => new ContactsDemo(),
|
||||
ButtonsDemo.routeName: (BuildContext context) => new ButtonsDemo(),
|
||||
CardsDemo.routeName: (BuildContext context) => new CardsDemo(),
|
||||
|
@ -73,7 +73,7 @@ class GalleryHomeState extends State<GalleryHome> {
|
||||
children: <Widget>[
|
||||
new GalleryItem(title: 'Pesto', routeName: PestoDemo.routeName),
|
||||
new GalleryItem(title: 'Shrine', routeName: ShrineDemo.routeName),
|
||||
new GalleryItem(title: 'Calculator', routeName: Calculator.routeName),
|
||||
new GalleryItem(title: 'Calculator', routeName: CalculatorDemo.routeName),
|
||||
new GalleryItem(title: 'Contacts', routeName: ContactsDemo.routeName)
|
||||
]
|
||||
),
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_gallery/demo/calculator_logic.dart';
|
||||
import 'package:flutter_gallery/demo/calculator/logic.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -14,7 +14,7 @@ void main() {
|
||||
// We press the "1" and the "2" buttons and check that the display
|
||||
// reads "12".
|
||||
testWidgets('Flutter calculator app smoke test', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(new Calculator());
|
||||
await tester.pumpWidget(new CalculatorDemo());
|
||||
|
||||
Finder oneButton = find.widgetWithText(InkResponse, '1');
|
||||
expect(oneButton, findsOneWidget);
|
||||
|
Loading…
x
Reference in New Issue
Block a user