diff --git a/lib/bloc/getcalc_bloc.dart b/lib/bloc/getcalc_bloc.dart new file mode 100644 index 0000000..bdb6653 --- /dev/null +++ b/lib/bloc/getcalc_bloc.dart @@ -0,0 +1,94 @@ +import 'dart:async'; +import 'package:cloudtech_calculator/extension.dart'; +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; +part 'getcalc_event.dart'; +part 'getcalc_state.dart'; + +class CalcEventBloc extends Bloc { + CalcEventBloc() : super(const EmptyState()); + + @override + Stream mapEventToState( + CalcEvent event, + ) async* { + num a; + num b; + final lastState = state; + if (event is NumberEvent) { + if (lastState is EmptyState) { + yield FirstState(a: event.value); + } else if (lastState is FirstState) { + a = lastState.a.concat(pointer: false, value: event.value); + yield FirstState(a: a); + } else if (lastState is OperatorState && + lastState.pointStatus == true && + lastState.operator == Operator.point && + lastState.b == null) { + a = lastState.a.concat(pointer: true, value: event.value); + yield FirstState(a: a); + } else if (lastState is OperatorState && + lastState.operator != Operator.point && + lastState.b == null) { + yield SecondState( + a: lastState.a, b: event.value, operator: lastState.operator); + } else if (lastState is SecondState && lastState.b != null) { + b = lastState.b.concat(pointer: false, value: event.value); + yield SecondState(a: lastState.a, b: b, operator: lastState.operator); + } else if (lastState is OperatorState && lastState.b != null) { + b = lastState.b.concat(pointer: true, value: event.value); + yield SecondState(a: lastState.a, b: b, operator: lastState.operator); + } + } else if (event is PlusOperationEvent) { + yield OperatorState(a: (state as FirstState).a, operator: Operator.plus); + } else if (event is MinusOperationEvent) { + yield OperatorState(a: (state as FirstState).a, operator: Operator.minus); + } else if (event is DivisionOperationEvent) { + yield OperatorState(a: (state as FirstState).a, operator: Operator.div); + } else if (event is MultiOperationEvent) { + yield OperatorState(a: (state as FirstState).a, operator: Operator.multi); + } else if (event is PointOperationEvent) { + if (lastState is FirstState) { + yield OperatorState( + a: lastState.a, operator: Operator.point, pointStatus: true); + } else if (lastState is SecondState) { + yield OperatorState( + a: lastState.a, + b: lastState.b, + operator: lastState.operator, + pointStatus: true); + } + } else if (event is PercentOperationEvent) { + if (lastState is FirstState) { + a = lastState.a / 100; + yield FirstState(a: a); + } else if (lastState is SecondState) { + b = lastState.b / 100; + yield SecondState(a: lastState.a, b: b, operator: lastState.operator); + } + } else if (event is PlusMinusOperationEvent) { + if (lastState is FirstState) { + a = lastState.a * (-1); + yield FirstState(a: a); + } else if (lastState is SecondState) { + b = lastState.b * (-1); + yield SecondState(a: lastState.a, b: b, operator: lastState.operator); + } + } else if (event is BackspaceOperationEvent) { + if (lastState is FirstState) { + a = lastState.a.backspace(); + yield FirstState(a: a); + } else if (lastState is SecondState) { + b = lastState.b.backspace(); + yield SecondState(a: lastState.a, b: b, operator: lastState.operator); + } + } else if (event is ClearAllOperationEvent) { + yield const EmptyState(); + } else if (event is ResultOperationEvent) { + if (lastState is SecondState) { + yield FirstState( + a: operationEvent(lastState.a, lastState.b, lastState.operator)); + } + } + } +} diff --git a/lib/bloc/getcalc_event.dart b/lib/bloc/getcalc_event.dart new file mode 100644 index 0000000..5f52d10 --- /dev/null +++ b/lib/bloc/getcalc_event.dart @@ -0,0 +1,29 @@ +part of 'getcalc_bloc.dart'; + +@immutable +abstract class CalcEvent {} + +class NumberEvent extends CalcEvent { + final int value; + NumberEvent(this.value); +} + +class PlusOperationEvent extends CalcEvent {} + +class MinusOperationEvent extends CalcEvent {} + +class DivisionOperationEvent extends CalcEvent {} + +class MultiOperationEvent extends CalcEvent {} + +class PointOperationEvent extends CalcEvent {} + +class PercentOperationEvent extends CalcEvent {} + +class PlusMinusOperationEvent extends CalcEvent {} + +class BackspaceOperationEvent extends CalcEvent {} + +class ClearAllOperationEvent extends CalcEvent {} + +class ResultOperationEvent extends CalcEvent {} diff --git a/lib/bloc/getcalc_state.dart b/lib/bloc/getcalc_state.dart new file mode 100644 index 0000000..2cb4278 --- /dev/null +++ b/lib/bloc/getcalc_state.dart @@ -0,0 +1,36 @@ +part of 'getcalc_bloc.dart'; + +@immutable +abstract class CalcEventState { + const CalcEventState(); +} + +class EmptyState extends CalcEventState { + const EmptyState(); +} + +class FirstState extends CalcEventState { + final num a; + const FirstState({this.a}); +} + +class OperatorState extends CalcEventState { + final num a; + final num b; + final Operator operator; + final bool pointStatus; + const OperatorState({this.a, this.b, this.operator, this.pointStatus}); +} + +class SecondState extends CalcEventState { + final num a, b; + final Operator operator; + const SecondState({this.a, this.b, this.operator}); +} + +class ResultState extends CalcEventState { + final num a, b; + final Operator operator; + final num result; + const ResultState({this.a, this.b, this.operator, this.result}); +} \ No newline at end of file diff --git a/lib/extension.dart b/lib/extension.dart new file mode 100644 index 0000000..38f4628 --- /dev/null +++ b/lib/extension.dart @@ -0,0 +1,81 @@ +enum Operator { + plus, + minus, + div, + multi, + percent, + plusminus, + backspace, + clearall, + point, + result +} + +extension OperationExtension on Operator { + String getString() { + switch (this) { + case Operator.plus: + return " + "; + case Operator.minus: + return " - "; + case Operator.multi: + return " x "; + case Operator.div: + return " ÷ "; + case Operator.point: + return "."; + case Operator.result: + return " = "; + default: + throw UnimplementedError(); + } + } +} + +extension NumbersExtension on num { + num backspace() { + String pull = "$this"; + pull = pull.substring(0, pull.length - 1); + if (pull.isNotEmpty && pull.split('').last.contains('.')) { + pull = pull.substring(0, pull.length - 1); + } + return num.parse(pull, (pull) => 0); + } + + num concat({bool pointer, int value}) { + if (pointer == true) { + final String pull = "${this}." + "$value"; + return num.parse(pull); + } else { + final String pull = "$this" + "$value"; + return num.parse(pull); + } + } +} + +num operationEvent(num x, num y, Operator op) { + num result = 0; + switch (op) { + case Operator.plus: + result = x + y; + break; + case Operator.minus: + result = x - y; + break; + case Operator.multi: + result = x * y; + break; + case Operator.div: + result = x / y; + break; + default: + result = 0; + break; + } + + if (result % 1 == 0) { + return result.toInt(); + } else { + return result; + } +} diff --git a/lib/main.dart b/lib/main.dart index 875a020..6b7f983 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,66 +1,13 @@ import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cloudtech_calculator/extension.dart'; import 'package:cloudtech_calculator/theme_model.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; + +import 'bloc/getcalc_bloc.dart'; import 'route.dart'; -enum Operation { - plus, - minus, - multi, - div, - point, - res, - percent, - plusMinus, - backspace, - clear -} - -extension OperationExtension on Operation { - String getString() { - switch (this) { - case Operation.plus: - return "+"; - case Operation.minus: - return "-"; - case Operation.multi: - return "x"; - case Operation.div: - return "÷"; - case Operation.point: - return "."; - case Operation.res: - return "="; - default: - throw UnimplementedError(); - } - } -} - -enum Numbers { zero, one, two, three, four, five, six, seven, eight, nine } - -extension NumbersExtension on num { - num backspace() { - String pull = "$this"; - pull = pull.substring(0, pull.length - 1); - if (pull.isNotEmpty && pull.split('').last.contains('.')) { - pull = pull.substring(0, pull.length - 1); - } - return num.parse(pull, (pull) => 0); - } - - num concat({bool pointer, int value}) { - if (pointer == true) { - final String pull = "${this}." + "$value"; - return num.parse(pull); - } else { - final String pull = "$this" + "$value"; - return num.parse(pull); - } - } -} - void main() => runApp(ChangeNotifierProvider( create: (BuildContext context) => ThemeModel(), child: MyApp())); @@ -71,185 +18,20 @@ class MyApp extends StatelessWidget { return MaterialApp( debugShowCheckedModeBanner: false, theme: Provider.of(context).currentTheme, - home: const MyHomePage(title: 'Калькулятор'), + home: MyHomePage(title: 'Калькулятор'), ); } } -class MyHomePage extends StatefulWidget { - const MyHomePage({Key key, this.title}) : super(key: key); +class MyHomePage extends StatelessWidget { + MyHomePage({Key key, this.title}) : super(key: key); final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - num a, b; - bool pointStatus; - dynamic operator; - String textResh = "0"; - - void getCalc(dynamic type) { - if (type is Numbers) { - if (a == null) { - a = type.index; - - setState(() { - textResh = '$a'; - }); - } else if (a != null && operator == null) { - if (pointStatus == true) { - a = a.concat(pointer: true, value: type.index); - pointStatus = false; - } else { - a = a.concat(pointer: false, value: type.index); - } - - setState(() { - textResh = '$a'; - }); - } else if (operator != null) { - if (b == null) { - b = type.index; - setState(() { - textResh = "$a ${(operator as Operation).getString()} $b"; - }); - } else if (b != null) { - if (pointStatus == true) { - b = b.concat(pointer: true, value: type.index); - pointStatus = false; - } else { - b = b.concat(pointer: false, value: type.index); - } - pointStatus = false; - - setState(() { - textResh = "$a ${(operator as Operation).getString()} $b"; - }); - } - } - } else if (type is Operation) { - if (type == Operation.res) { - setState(() { - textResh = "$a ${(operator as Operation).getString()} $b"; - }); - a = operationEvent(a, b, operator as Operation); - b = null; - operator = null; - setState(() { - textResh = "$a"; - }); - } else if (type == Operation.point) { - if (a != null && b == null) { - if (a is double) { - pointStatus = false; - setState(() { - textResh = "$a"; - }); - } else { - pointStatus = true; - setState(() { - textResh = "$a."; - }); - } - } else { - if (b is double) { - pointStatus = false; - setState(() { - textResh = "$a ${(operator as Operation).getString()} $b"; - }); - } else { - pointStatus = true; - setState(() { - textResh = "$a ${(operator as Operation).getString()} $b."; - }); - } - } - } else if (type == Operation.clear) { - a = 0; - b = null; - operator = null; - setState(() { - textResh = "$a"; - }); - } else if (type == Operation.backspace) { - if (a != null && b == null) { - a = a.backspace(); - setState(() { - textResh = "$a"; - }); - } else { - b = b.backspace(); - setState(() { - textResh = "$a ${(operator as Operation).getString()} $b"; - }); - } - } else if (type == Operation.plusMinus) { - if (a != null && b == null) { - a = a * -1; - setState(() { - textResh = "$a"; - }); - } else { - b = b * -1; - setState(() { - textResh = "$a ${(operator as Operation).getString()} $b"; - }); - } - } else if (type == Operation.percent) { - if (a != null && b == null) { - a = a / 100; - setState(() { - textResh = "$a"; - }); - } else { - b = b / 100; - setState(() { - textResh = "$a ${(operator as Operation).getString()} $b"; - }); - } - } else { - operator = type; - setState(() { - textResh = "$a ${(operator as Operation).getString()}"; - }); - } - } - } - - num operationEvent(num x, num y, Operation op) { - num result = 0; - switch (op) { - case Operation.plus: - result = x + y; - break; - case Operation.minus: - result = x - y; - break; - case Operation.multi: - result = x * y; - break; - case Operation.div: - result = x / y; - break; - default: - result = 0; - break; - } - - if (result % 1 == 0) { - return result.toInt(); - } else { - return result; - } - } - + final _getCalc = CalcEventBloc(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.title, + title: Text(title, style: TextStyle(color: Theme.of(context).accentColor)), shadowColor: const Color(0x00000000), actions: [ @@ -275,85 +57,148 @@ class _MyHomePageState extends State { children: [ Expanded( child: Align( - alignment: Alignment.centerRight, - child: AutoSizeText( - textResh, - style: TextStyle( - fontSize: 70, color: Theme.of(context).accentColor), - maxLines: 1, - )), + alignment: Alignment.centerRight, + child: BlocBuilder( + cubit: _getCalc, + builder: (context, state) { + if (state is EmptyState) { + return AutoSizeText( + "0", + style: TextStyle( + fontSize: 70, + color: Theme.of(context).accentColor), + maxLines: 1, + ); + } else if (state is FirstState) { + return AutoSizeText( + state.a.toString(), + style: TextStyle( + fontSize: 70, + color: Theme.of(context).accentColor), + maxLines: 1, + ); + } else if (state is OperatorState && + state.b == null) { + return AutoSizeText( + "${state.a}${state.operator.getString()}", + style: TextStyle( + fontSize: 70, + color: Theme.of(context).accentColor), + maxLines: 1, + ); + } else if (state is OperatorState && + state.b != null) { + return AutoSizeText( + "${state.a}${state.operator.getString()}${state.b}.", + style: TextStyle( + fontSize: 70, + color: Theme.of(context).accentColor), + maxLines: 1, + ); + } else if (state is SecondState) { + return AutoSizeText( + "${state.a}${state.operator.getString()}${state.b}", + style: TextStyle( + fontSize: 70, + color: Theme.of(context).accentColor), + maxLines: 1, + ); + } else if (state is ResultState) { + return AutoSizeText( + "${state.result}", + style: TextStyle( + fontSize: 70, + color: Theme.of(context).accentColor), + maxLines: 1, + ); + } + + return const SizedBox.shrink(); + }), + ), ), Row( children: [ CallButton( - text: "±", - value: Operation.plusMinus, - onPressed: getCalc, - ), + text: "±", + onPressed: () => + _getCalc.add(PlusMinusOperationEvent())), CallButton( text: "%", - value: Operation.percent, - onPressed: getCalc), + onPressed: () => _getCalc.add(PercentOperationEvent())), CallButton( text: "⌫", - value: Operation.backspace, - onPressed: getCalc), + onPressed: () => + _getCalc.add(BackspaceOperationEvent())), CallButton( - text: "AC", value: Operation.clear, onPressed: getCalc), + text: "AC", + onPressed: () => + _getCalc.add(ClearAllOperationEvent())), ], ), Row( children: [ CallButton( - text: "7", - value: Numbers.seven, - onPressed: getCalc, - ), + text: "7", + onPressed: () => _getCalc.add(NumberEvent(7))), CallButton( - text: "8", value: Numbers.eight, onPressed: getCalc), + text: "8", + onPressed: () => _getCalc.add(NumberEvent(8))), CallButton( - text: "9", value: Numbers.nine, onPressed: getCalc), + text: "9", + onPressed: () => _getCalc.add(NumberEvent(9))), CallButton( - text: "/", value: Operation.div, onPressed: getCalc), + text: "/", + onPressed: () => + _getCalc.add(DivisionOperationEvent())), ], ), Row( children: [ CallButton( - text: "4", value: Numbers.four, onPressed: getCalc), + text: "4", + onPressed: () => _getCalc.add(NumberEvent(4))), CallButton( - text: "5", value: Numbers.five, onPressed: getCalc), + text: "5", + onPressed: () => _getCalc.add(NumberEvent(5))), CallButton( - text: "6", value: Numbers.six, onPressed: getCalc), + text: "6", + onPressed: () => _getCalc.add(NumberEvent(6))), CallButton( - text: "*", value: Operation.multi, onPressed: getCalc), + text: "*", + onPressed: () => _getCalc.add(MultiOperationEvent())), ], ), Row( children: [ CallButton( - text: "1", value: Numbers.one, onPressed: getCalc), + text: "1", + onPressed: () => _getCalc.add(NumberEvent(1))), CallButton( - text: "2", value: Numbers.two, onPressed: getCalc), + text: "2", + onPressed: () => _getCalc.add(NumberEvent(2))), CallButton( - text: "3", value: Numbers.three, onPressed: getCalc), + text: "3", + onPressed: () => _getCalc.add(NumberEvent(3))), CallButton( - text: "-", value: Operation.minus, onPressed: getCalc), + text: "-", + onPressed: () => _getCalc.add(MinusOperationEvent())), ], ), Row( children: [ CallButton( - text: "0", value: Numbers.zero, onPressed: getCalc), + text: "0", + onPressed: () => _getCalc.add(NumberEvent(0))), CallButton( - text: ".", value: Operation.point, onPressed: getCalc), + text: ".", + onPressed: () => _getCalc.add(PointOperationEvent())), CallButton( - text: "=", value: Operation.res, onPressed: getCalc), + text: "=", + onPressed: () => _getCalc.add(ResultOperationEvent())), CallButton( - text: "+", - value: Operation.plus, - onPressed: getCalc, - ), + text: "+", + onPressed: () => _getCalc.add(PlusOperationEvent())), ], ), ], @@ -365,12 +210,8 @@ class _MyHomePageState extends State { class CallButton extends StatelessWidget { final String text; - final dynamic value; - - final void Function(dynamic) onPressed; - - const CallButton( - {@required this.text, @required this.value, @required this.onPressed}); + final void Function() onPressed; + const CallButton({@required this.text, @required this.onPressed}); @override Widget build(BuildContext context) { @@ -385,7 +226,7 @@ class CallButton extends StatelessWidget { color: const Color(0x00000000)), child: FlatButton( height: 60, - onPressed: () => onPressed(value), + onPressed: () => onPressed(), child: Text( text, style: diff --git a/pubspec.lock b/pubspec.lock index 7723eb8..8f0ba9d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -15,6 +15,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + bloc: + dependency: "direct main" + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.3" boolean_selector: dependency: transitive description: @@ -69,6 +76,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.6" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 8182ff9..50ba785 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,12 +23,14 @@ environment: dependencies: flutter: sdk: flutter + auto_size_text: ^2.1.0 + bloc: ^6.0.3 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 + flutter_bloc: ^6.0.6 provider: ^4.3.2+2 - auto_size_text: ^2.1.0 dev_dependencies: flutter_test: @@ -71,4 +73,3 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -