mirror of
https://github.com/flutter/samples.git
synced 2025-11-10 14:58:34 +00:00
Update web/ samples to work with Flutter SDK (#134)
* add package:http dependency in dad_jokes * add package:http dependency in filipino_cuisine * don't build package:http demos until flutter/flutter#34858 is resolved * update gallery * update github_dataviz * update particle_background * don't build github_dataviz (uses package:http) * update slide_puzzle * update spinning_square * update timeflow * update vision_challenge * update charts * update dad_jokes * update filipino cuisine * ignore build output * update timeflow and vision_challenge * update slide_puzzle * don't commit build/ directory * move preview.png images to assets * fix path url join * update readme * update web/readme.md
This commit is contained in:
270
web/gallery/lib/demo/calculator/home.dart
Normal file
270
web/gallery/lib/demo/calculator/home.dart
Normal file
@@ -0,0 +1,270 @@
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
342
web/gallery/lib/demo/calculator/logic.dart
Normal file
342
web/gallery/lib/demo/calculator/logic.dart
Normal file
@@ -0,0 +1,342 @@
|
||||
// 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.
|
||||
|
||||
/// A token that composes an expression. There are several kinds of tokens
|
||||
/// that represent arithmetic operation symbols, numbers and pieces of numbers.
|
||||
/// We need to represent pieces of numbers because the user may have only
|
||||
/// entered a partial expression so far.
|
||||
class ExpressionToken {
|
||||
ExpressionToken(this.stringRep);
|
||||
|
||||
final String stringRep;
|
||||
|
||||
@override
|
||||
String toString() => stringRep;
|
||||
}
|
||||
|
||||
/// A token that represents a number.
|
||||
class NumberToken extends ExpressionToken {
|
||||
NumberToken(String stringRep, this.number) : super(stringRep);
|
||||
|
||||
NumberToken.fromNumber(num number) : this('$number', number);
|
||||
|
||||
final num number;
|
||||
}
|
||||
|
||||
/// A token that represents an integer.
|
||||
class IntToken extends NumberToken {
|
||||
IntToken(String stringRep) : super(stringRep, int.parse(stringRep));
|
||||
}
|
||||
|
||||
/// A token that represents a floating point number.
|
||||
class FloatToken extends NumberToken {
|
||||
FloatToken(String stringRep) : super(stringRep, _parse(stringRep));
|
||||
|
||||
static double _parse(String stringRep) {
|
||||
String toParse = stringRep;
|
||||
if (toParse.startsWith('.'))
|
||||
toParse = '0' + toParse;
|
||||
if (toParse.endsWith('.'))
|
||||
toParse = toParse + '0';
|
||||
return double.parse(toParse);
|
||||
}
|
||||
}
|
||||
|
||||
/// A token that represents a number that is the result of a computation.
|
||||
class ResultToken extends NumberToken {
|
||||
ResultToken(num number) : super.fromNumber(round(number));
|
||||
|
||||
/// rounds `number` to 14 digits of precision. A double precision
|
||||
/// floating point number is guaranteed to have at least this many
|
||||
/// decimal digits of precision.
|
||||
static num round(num number) {
|
||||
if (number is int)
|
||||
return number;
|
||||
return double.parse(number.toStringAsPrecision(14));
|
||||
}
|
||||
}
|
||||
|
||||
/// A token that represents the unary minus prefix.
|
||||
class LeadingNegToken extends ExpressionToken {
|
||||
LeadingNegToken() : super('-');
|
||||
}
|
||||
|
||||
enum Operation { Addition, Subtraction, Multiplication, Division }
|
||||
|
||||
/// A token that represents an arithmetic operation symbol.
|
||||
class OperationToken extends ExpressionToken {
|
||||
OperationToken(this.operation)
|
||||
: super(opString(operation));
|
||||
|
||||
Operation operation;
|
||||
|
||||
static String opString(Operation operation) {
|
||||
switch (operation) {
|
||||
case Operation.Addition:
|
||||
return ' + ';
|
||||
case Operation.Subtraction:
|
||||
return ' - ';
|
||||
case Operation.Multiplication:
|
||||
return ' \u00D7 ';
|
||||
case Operation.Division:
|
||||
return ' \u00F7 ';
|
||||
}
|
||||
assert(operation != null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// As the user taps different keys the current expression can be in one
|
||||
/// of several states.
|
||||
enum ExpressionState {
|
||||
/// The expression is empty or an operation symbol was just entered.
|
||||
/// A new number must be started now.
|
||||
Start,
|
||||
|
||||
/// A minus sign was entered as a leading negative prefix.
|
||||
LeadingNeg,
|
||||
|
||||
/// We are in the midst of a number without a point.
|
||||
Number,
|
||||
|
||||
/// A point was just entered.
|
||||
Point,
|
||||
|
||||
/// We are in the midst of a number with a point.
|
||||
NumberWithPoint,
|
||||
|
||||
/// A result is being displayed
|
||||
Result,
|
||||
}
|
||||
|
||||
/// An expression that can be displayed in a calculator. It is the result
|
||||
/// of a sequence of user entries. It is represented by a sequence of tokens.
|
||||
///
|
||||
/// The tokens are not in one to one correspondence with the key taps because we
|
||||
/// use one token per number, not one token per digit. A [CalcExpression] is
|
||||
/// immutable. The `append*` methods return a new [CalcExpression] that
|
||||
/// represents the appropriate expression when one additional key tap occurs.
|
||||
class CalcExpression {
|
||||
CalcExpression(this._list, this.state);
|
||||
|
||||
CalcExpression.empty()
|
||||
: this(<ExpressionToken>[], ExpressionState.Start);
|
||||
|
||||
CalcExpression.result(FloatToken result)
|
||||
: _list = <ExpressionToken>[],
|
||||
state = ExpressionState.Result {
|
||||
_list.add(result);
|
||||
}
|
||||
|
||||
/// The tokens comprising the expression.
|
||||
final List<ExpressionToken> _list;
|
||||
/// The state of the expression.
|
||||
final ExpressionState state;
|
||||
|
||||
/// The string representation of the expression. This will be displayed
|
||||
/// in the calculator's display panel.
|
||||
@override
|
||||
String toString() {
|
||||
final StringBuffer buffer = StringBuffer('');
|
||||
buffer.writeAll(_list);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Append a digit to the current expression and return a new expression
|
||||
/// representing the result. Returns null to indicate that it is not legal
|
||||
/// to append a digit in the current state.
|
||||
CalcExpression appendDigit(int digit) {
|
||||
ExpressionState newState = ExpressionState.Number;
|
||||
ExpressionToken newToken;
|
||||
final List<ExpressionToken> outList = _list.toList();
|
||||
switch (state) {
|
||||
case ExpressionState.Start:
|
||||
// Start a new number with digit.
|
||||
newToken = IntToken('$digit');
|
||||
break;
|
||||
case ExpressionState.LeadingNeg:
|
||||
// Replace the leading neg with a negative number starting with digit.
|
||||
outList.removeLast();
|
||||
newToken = IntToken('-$digit');
|
||||
break;
|
||||
case ExpressionState.Number:
|
||||
final ExpressionToken last = outList.removeLast();
|
||||
newToken = IntToken('${last.stringRep}$digit');
|
||||
break;
|
||||
case ExpressionState.Point:
|
||||
case ExpressionState.NumberWithPoint:
|
||||
final ExpressionToken last = outList.removeLast();
|
||||
newState = ExpressionState.NumberWithPoint;
|
||||
newToken = FloatToken('${last.stringRep}$digit');
|
||||
break;
|
||||
case ExpressionState.Result:
|
||||
// Cannot enter a number now
|
||||
return null;
|
||||
}
|
||||
outList.add(newToken);
|
||||
return CalcExpression(outList, newState);
|
||||
}
|
||||
|
||||
/// Append a point to the current expression and return a new expression
|
||||
/// representing the result. Returns null to indicate that it is not legal
|
||||
/// to append a point in the current state.
|
||||
CalcExpression appendPoint() {
|
||||
ExpressionToken newToken;
|
||||
final List<ExpressionToken> outList = _list.toList();
|
||||
switch (state) {
|
||||
case ExpressionState.Start:
|
||||
newToken = FloatToken('.');
|
||||
break;
|
||||
case ExpressionState.LeadingNeg:
|
||||
case ExpressionState.Number:
|
||||
final ExpressionToken last = outList.removeLast();
|
||||
newToken = FloatToken(last.stringRep + '.');
|
||||
break;
|
||||
case ExpressionState.Point:
|
||||
case ExpressionState.NumberWithPoint:
|
||||
case ExpressionState.Result:
|
||||
// Cannot enter a point now
|
||||
return null;
|
||||
}
|
||||
outList.add(newToken);
|
||||
return CalcExpression(outList, ExpressionState.Point);
|
||||
}
|
||||
|
||||
/// Append an operation symbol to the current expression and return a new
|
||||
/// expression representing the result. Returns null to indicate that it is not
|
||||
/// legal to append an operation symbol in the current state.
|
||||
CalcExpression appendOperation(Operation op) {
|
||||
switch (state) {
|
||||
case ExpressionState.Start:
|
||||
case ExpressionState.LeadingNeg:
|
||||
case ExpressionState.Point:
|
||||
// Cannot enter operation now.
|
||||
return null;
|
||||
case ExpressionState.Number:
|
||||
case ExpressionState.NumberWithPoint:
|
||||
case ExpressionState.Result:
|
||||
break;
|
||||
}
|
||||
final List<ExpressionToken> outList = _list.toList();
|
||||
outList.add(OperationToken(op));
|
||||
return CalcExpression(outList, ExpressionState.Start);
|
||||
}
|
||||
|
||||
/// Append a leading minus sign to the current expression and return a new
|
||||
/// expression representing the result. Returns null to indicate that it is not
|
||||
/// legal to append a leading minus sign in the current state.
|
||||
CalcExpression appendLeadingNeg() {
|
||||
switch (state) {
|
||||
case ExpressionState.Start:
|
||||
break;
|
||||
case ExpressionState.LeadingNeg:
|
||||
case ExpressionState.Point:
|
||||
case ExpressionState.Number:
|
||||
case ExpressionState.NumberWithPoint:
|
||||
case ExpressionState.Result:
|
||||
// Cannot enter leading neg now.
|
||||
return null;
|
||||
}
|
||||
final List<ExpressionToken> outList = _list.toList();
|
||||
outList.add(LeadingNegToken());
|
||||
return CalcExpression(outList, ExpressionState.LeadingNeg);
|
||||
}
|
||||
|
||||
/// Append a minus sign to the current expression and return a new expression
|
||||
/// representing the result. Returns null to indicate that it is not legal
|
||||
/// to append a minus sign in the current state. Depending on the current
|
||||
/// state the minus sign will be interpreted as either a leading negative
|
||||
/// sign or a subtraction operation.
|
||||
CalcExpression appendMinus() {
|
||||
switch (state) {
|
||||
case ExpressionState.Start:
|
||||
return appendLeadingNeg();
|
||||
case ExpressionState.LeadingNeg:
|
||||
case ExpressionState.Point:
|
||||
case ExpressionState.Number:
|
||||
case ExpressionState.NumberWithPoint:
|
||||
case ExpressionState.Result:
|
||||
return appendOperation(Operation.Subtraction);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the result of the current expression and returns a new
|
||||
/// ResultExpression containing the result. Returns null to indicate that
|
||||
/// it is not legal to compute a result in the current state.
|
||||
CalcExpression computeResult() {
|
||||
switch (state) {
|
||||
case ExpressionState.Start:
|
||||
case ExpressionState.LeadingNeg:
|
||||
case ExpressionState.Point:
|
||||
case ExpressionState.Result:
|
||||
// Cannot compute result now.
|
||||
return null;
|
||||
case ExpressionState.Number:
|
||||
case ExpressionState.NumberWithPoint:
|
||||
break;
|
||||
}
|
||||
|
||||
// We make a copy of _list because CalcExpressions are supposed to
|
||||
// be immutable.
|
||||
final List<ExpressionToken> list = _list.toList();
|
||||
// We obey order-of-operations by computing the sum of the 'terms',
|
||||
// where a "term" is defined to be a sequence of numbers separated by
|
||||
// multiplication or division symbols.
|
||||
num currentTermValue = removeNextTerm(list);
|
||||
while (list.isNotEmpty) {
|
||||
final OperationToken opToken = list.removeAt(0);
|
||||
final num nextTermValue = removeNextTerm(list);
|
||||
switch (opToken.operation) {
|
||||
case Operation.Addition:
|
||||
currentTermValue += nextTermValue;
|
||||
break;
|
||||
case Operation.Subtraction:
|
||||
currentTermValue -= nextTermValue;
|
||||
break;
|
||||
case Operation.Multiplication:
|
||||
case Operation.Division:
|
||||
// Logic error.
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
final List<ExpressionToken> outList = <ExpressionToken>[];
|
||||
outList.add(ResultToken(currentTermValue));
|
||||
return CalcExpression(outList, ExpressionState.Result);
|
||||
}
|
||||
|
||||
/// Removes the next "term" from `list` and returns its numeric value.
|
||||
/// A "term" is a sequence of number tokens separated by multiplication
|
||||
/// and division symbols.
|
||||
static num removeNextTerm(List<ExpressionToken> list) {
|
||||
assert(list != null && list.isNotEmpty);
|
||||
final NumberToken firstNumToken = list.removeAt(0);
|
||||
num currentValue = firstNumToken.number;
|
||||
while (list.isNotEmpty) {
|
||||
bool isDivision = false;
|
||||
final OperationToken nextOpToken = list.first;
|
||||
switch (nextOpToken.operation) {
|
||||
case Operation.Addition:
|
||||
case Operation.Subtraction:
|
||||
// We have reached the end of the current term
|
||||
return currentValue;
|
||||
case Operation.Multiplication:
|
||||
break;
|
||||
case Operation.Division:
|
||||
isDivision = true;
|
||||
}
|
||||
// Remove the operation token.
|
||||
list.removeAt(0);
|
||||
// Remove the next number token.
|
||||
final NumberToken nextNumToken = list.removeAt(0);
|
||||
final num nextNumber = nextNumToken.number;
|
||||
if (isDivision)
|
||||
currentValue /= nextNumber;
|
||||
else
|
||||
currentValue *= nextNumber;
|
||||
}
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user