mirror of https://github.com/flutter/samples.git
271 lines
7.0 KiB
271 lines
7.0 KiB
// 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 {
|
|
const Calculator({Key key}) : super(key: key);
|
|
|
|
@override
|
|
_CalculatorState createState() => _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 = 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.isNotEmpty) {
|
|
_expression = _expressionStack.removeLast();
|
|
} else {
|
|
_expression = CalcExpression.empty();
|
|
}
|
|
}
|
|
|
|
/// Set `resultExpression` to the current 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 Scaffold(
|
|
appBar: AppBar(
|
|
backgroundColor: Theme.of(context).canvasColor,
|
|
elevation: 0.0,
|
|
),
|
|
body: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: <Widget>[
|
|
// Give the key-pad 3/5 of the vertical space and the display 2/5.
|
|
Expanded(
|
|
flex: 2,
|
|
child: CalcDisplay(content: _expression.toString()),
|
|
),
|
|
const Divider(height: 1.0),
|
|
Expanded(
|
|
flex: 3,
|
|
child: KeyPad(calcState: this),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class CalcDisplay extends StatelessWidget {
|
|
const CalcDisplay({ this.content });
|
|
|
|
final String content;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Center(
|
|
child: Text(
|
|
content,
|
|
style: const TextStyle(fontSize: 24.0),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class KeyPad extends StatelessWidget {
|
|
const KeyPad({ this.calcState });
|
|
|
|
final _CalculatorState calcState;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final ThemeData themeData = ThemeData(
|
|
primarySwatch: Colors.purple,
|
|
brightness: Brightness.dark,
|
|
platform: Theme.of(context).platform,
|
|
);
|
|
return Theme(
|
|
data: themeData,
|
|
child: Material(
|
|
child: Row(
|
|
children: <Widget>[
|
|
Expanded(
|
|
// 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: Column(
|
|
children: <Widget>[
|
|
KeyRow(<Widget>[
|
|
NumberKey(7, calcState),
|
|
NumberKey(8, calcState),
|
|
NumberKey(9, calcState),
|
|
]),
|
|
KeyRow(<Widget>[
|
|
NumberKey(4, calcState),
|
|
NumberKey(5, calcState),
|
|
NumberKey(6, calcState),
|
|
]),
|
|
KeyRow(<Widget>[
|
|
NumberKey(1, calcState),
|
|
NumberKey(2, calcState),
|
|
NumberKey(3, calcState),
|
|
]),
|
|
KeyRow(<Widget>[
|
|
CalcKey('.', calcState.handlePointTap),
|
|
NumberKey(0, calcState),
|
|
CalcKey('=', calcState.handleEqualsTap),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Material(
|
|
color: themeData.backgroundColor,
|
|
child: Column(
|
|
children: <Widget>[
|
|
CalcKey('\u232B', calcState.handleDelTap),
|
|
CalcKey('\u00F7', calcState.handleDivTap),
|
|
CalcKey('\u00D7', calcState.handleMultTap),
|
|
CalcKey('-', calcState.handleMinusTap),
|
|
CalcKey('+', calcState.handlePlusTap),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class KeyRow extends StatelessWidget {
|
|
const KeyRow(this.keys);
|
|
|
|
final List<Widget> keys;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Expanded(
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: keys,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class CalcKey extends StatelessWidget {
|
|
const CalcKey(this.text, this.onTap);
|
|
|
|
final String text;
|
|
final GestureTapCallback onTap;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final Orientation orientation = MediaQuery.of(context).orientation;
|
|
return Expanded(
|
|
child: InkResponse(
|
|
onTap: onTap,
|
|
child: Center(
|
|
child: Text(
|
|
text,
|
|
style: TextStyle(
|
|
fontSize: (orientation == Orientation.portrait) ? 32.0 : 24.0
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class NumberKey extends CalcKey {
|
|
NumberKey(int value, _CalculatorState calcState)
|
|
: super('$value', () {
|
|
calcState.handleNumberTap(value);
|
|
});
|
|
}
|