home.dart 7.27 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9
// 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 {
10
  const Calculator({Key? key}) : super(key: key);
11 12

  @override
13
  State<Calculator> createState() => CalculatorState();
14 15
}

16
class CalculatorState extends State<Calculator> {
17 18 19 20
  /// 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>[];
21
  CalcExpression _expression = CalcExpression.empty();
22 23 24 25 26 27 28 29 30 31

  // 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() {
32
    if (_expressionStack.isNotEmpty) {
33 34
      _expression = _expressionStack.removeLast();
    } else {
35
      _expression = CalcExpression.empty();
36 37 38
    }
  }

39
  /// Set `resultExpression` to the current expression and clear the stack.
40 41 42 43 44 45
  void setResult(CalcExpression resultExpression) {
    _expressionStack.clear();
    _expression = resultExpression;
  }

  void handleNumberTap(int n) {
46
    final CalcExpression? expression = _expression.appendDigit(n);
47 48 49 50 51 52 53 54
    if (expression != null) {
      setState(() {
        pushExpression(expression);
      });
    }
  }

  void handlePointTap() {
55
    final CalcExpression? expression = _expression.appendPoint();
56 57 58 59 60 61 62 63
    if (expression != null) {
      setState(() {
        pushExpression(expression);
      });
    }
  }

  void handlePlusTap() {
64
    final CalcExpression? expression = _expression.appendOperation(Operation.Addition);
65 66 67 68 69 70 71 72
    if (expression != null) {
      setState(() {
        pushExpression(expression);
      });
    }
  }

  void handleMinusTap() {
73
    final CalcExpression? expression = _expression.appendMinus();
74 75 76 77 78 79 80 81
    if (expression != null) {
      setState(() {
        pushExpression(expression);
      });
    }
  }

  void handleMultTap() {
82
    final CalcExpression? expression = _expression.appendOperation(Operation.Multiplication);
83 84 85 86 87 88 89 90
    if (expression != null) {
      setState(() {
        pushExpression(expression);
      });
    }
  }

  void handleDivTap() {
91
    final CalcExpression? expression = _expression.appendOperation(Operation.Division);
92 93 94 95 96 97 98 99
    if (expression != null) {
      setState(() {
        pushExpression(expression);
      });
    }
  }

  void handleEqualsTap() {
100
    final CalcExpression? resultExpression = _expression.computeResult();
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
    if (resultExpression != null) {
      setState(() {
        setResult(resultExpression);
      });
    }
  }

  void handleDelTap() {
    setState(() {
      popCalcExpression();
    });
  }

  @override
  Widget build(BuildContext context) {
116 117
    return Scaffold(
      appBar: AppBar(
118
        backgroundColor: Theme.of(context).canvasColor,
119
        elevation: 0.0,
120
      ),
121
      body: Column(
122 123 124
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          // Give the key-pad 3/5 of the vertical space and the display 2/5.
125
          Expanded(
126
            flex: 2,
127
            child: CalcDisplay(content: _expression.toString()),
128
          ),
129
          const Divider(height: 1.0),
130
          Expanded(
131
            flex: 3,
132 133 134 135
            child: KeyPad(calcState: this),
          ),
        ],
      ),
136 137 138 139 140
    );
  }
}

class CalcDisplay extends StatelessWidget {
141
  const CalcDisplay({ Key? key, this.content}) : super(key: key);
142

143
  final String? content;
144 145 146

  @override
  Widget build(BuildContext context) {
147 148
    return Center(
      child: Text(
149
        content!,
150 151
        style: const TextStyle(fontSize: 24.0),
      ),
152 153 154 155 156
    );
  }
}

class KeyPad extends StatelessWidget {
157
  const KeyPad({ Key? key, this.calcState }) : super(key: key);
158

159
  final CalculatorState? calcState;
160 161 162

  @override
  Widget build(BuildContext context) {
163
    final ThemeData themeData = ThemeData(
164
      primarySwatch: Colors.purple,
165 166
      brightness: Brightness.dark,
      platform: Theme.of(context).platform,
167
    );
168
    return Theme(
169
      data: themeData,
170 171
      child: Material(
        child: Row(
172
          children: <Widget>[
173
            Expanded(
174 175 176 177
              // 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,
178
              child: Column(
179
                children: <Widget>[
180 181 182
                  KeyRow(<Widget>[
                    NumberKey(7, calcState),
                    NumberKey(8, calcState),
183
                    NumberKey(9, calcState),
184
                  ]),
185 186 187
                  KeyRow(<Widget>[
                    NumberKey(4, calcState),
                    NumberKey(5, calcState),
188
                    NumberKey(6, calcState),
189
                  ]),
190 191 192
                  KeyRow(<Widget>[
                    NumberKey(1, calcState),
                    NumberKey(2, calcState),
193
                    NumberKey(3, calcState),
194
                  ]),
195
                  KeyRow(<Widget>[
196
                    CalcKey('.', calcState!.handlePointTap),
197
                    NumberKey(0, calcState),
198
                    CalcKey('=', calcState!.handleEqualsTap),
199 200 201
                  ]),
                ],
              ),
202
            ),
203 204
            Expanded(
              child: Material(
205
                color: themeData.backgroundColor,
206
                child: Column(
207
                  children: <Widget>[
208 209 210 211 212
                    CalcKey('\u232B', calcState!.handleDelTap),
                    CalcKey('\u00F7', calcState!.handleDivTap),
                    CalcKey('\u00D7', calcState!.handleMultTap),
                    CalcKey('-', calcState!.handleMinusTap),
                    CalcKey('+', calcState!.handlePlusTap),
213 214 215
                  ],
                ),
              ),
216
            ),
217 218 219
          ],
        ),
      ),
220 221 222 223 224
    );
  }
}

class KeyRow extends StatelessWidget {
225
  const KeyRow(this.keys, {Key? key}) : super(key: key);
226 227 228 229 230

  final List<Widget> keys;

  @override
  Widget build(BuildContext context) {
231 232
    return Expanded(
      child: Row(
233
        mainAxisAlignment: MainAxisAlignment.center,
234 235
        children: keys,
      ),
236 237 238 239 240
    );
  }
}

class CalcKey extends StatelessWidget {
241
  const CalcKey(this.text, this.onTap, {Key? key}) : super(key: key);
242 243 244 245 246 247 248

  final String text;
  final GestureTapCallback onTap;

  @override
  Widget build(BuildContext context) {
    final Orientation orientation = MediaQuery.of(context).orientation;
249 250
    return Expanded(
      child: InkResponse(
251
        onTap: onTap,
252 253
        child: Center(
          child: Text(
254
            text,
255
            style: TextStyle(
256 257
              // This line is used as a sentinel in the hot reload tests: hot_mode_test.dart
              // in the devicelab.
258
              fontSize: (orientation == Orientation.portrait) ? 32.0 : 24.0
259 260 261 262
            ),
          ),
        ),
      ),
263 264 265 266 267
    );
  }
}

class NumberKey extends CalcKey {
268
  NumberKey(int value, CalculatorState? calcState, {Key? key})
269
    : super('$value', () {
270
        calcState!.handleNumberTap(value);
271
      }, key: key);
272
}