// Copyright 2014 The Flutter 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); }); }