From 8983b72e22d25e1765ce0f83174a89a43bad00ff Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 14:46:40 +0200 Subject: [PATCH 1/9] add math_expression package --- Frontend/pubspec.lock | 8 ++++++++ Frontend/pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/Frontend/pubspec.lock b/Frontend/pubspec.lock index d6983355..a72646e3 100644 --- a/Frontend/pubspec.lock +++ b/Frontend/pubspec.lock @@ -717,6 +717,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.11.1" + math_expressions: + dependency: "direct main" + description: + name: math_expressions + sha256: e32d803d758ace61cc6c4bdfed1226ff60a6a23646b35685670d28b5616139f8 + url: "https://pub.dev" + source: hosted + version: "2.6.0" meta: dependency: transitive description: diff --git a/Frontend/pubspec.yaml b/Frontend/pubspec.yaml index e0e03ba9..e6d6d810 100644 --- a/Frontend/pubspec.yaml +++ b/Frontend/pubspec.yaml @@ -68,6 +68,7 @@ dependencies: url_launcher: ^6.3.1 fl_downloader: ^2.0.2 local_auth: ^2.3.0 + math_expressions: ^2.6.0 dev_dependencies: flutter_test: From 8b364c032a2eccb514f8ba83fd942bfc809ee353 Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 14:47:29 +0200 Subject: [PATCH 2/9] Create number input field --- .../mih_number_input.dart | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 Frontend/lib/mih_components/mih_inputs_and_buttons/mih_number_input.dart diff --git a/Frontend/lib/mih_components/mih_inputs_and_buttons/mih_number_input.dart b/Frontend/lib/mih_components/mih_inputs_and_buttons/mih_number_input.dart new file mode 100644 index 00000000..dee3e614 --- /dev/null +++ b/Frontend/lib/mih_components/mih_inputs_and_buttons/mih_number_input.dart @@ -0,0 +1,203 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import '../../main.dart'; + +class MIHNumberField extends StatefulWidget { + final TextEditingController controller; + final String hintText; + final bool editable; + final bool required; + final bool enableDecimal; + final Iterable? autoFillHintGroup; + + const MIHNumberField({ + super.key, + required this.controller, + required this.hintText, + required this.editable, + required this.required, + required this.enableDecimal, + this.autoFillHintGroup, + }); + + @override + State createState() => _MIHNumberFieldState(); +} + +class _MIHNumberFieldState extends State { + bool startup = true; + final FocusNode _focus = FocusNode(); + + List allowDecimals() { + if (widget.enableDecimal) { + return [ + FilteringTextInputFormatter.allow(RegExp(r'^\d+(\.\d*)?')), + ]; + } else { + return [FilteringTextInputFormatter.digitsOnly]; + } + } + + bool makeEditable() { + if (widget.editable) { + return false; + } else { + return true; + } + } + + String? get _errorText { + final text = widget.controller.text; + String errorMessage = ''; + if (startup) { + return null; + } + if (!widget.required) { + return null; + } + if (text.isEmpty) { + return "${widget.hintText} is required"; + } + if (widget.hintText == "Email" && !isEmailValid(text)) { + errorMessage += "Enter a valid email address\n"; + } + // if (widget.hintText == "Username" && text.length < 8) { + // errorMessage += "• Username must contain at least 8 characters.\n"; + // } + if (widget.hintText == "Username" && !isUsernameValid(text)) { + errorMessage += "Let's create a great username for you!\n"; + errorMessage += "• Your username should start with a letter.\n"; + errorMessage += "• You can use letters, numbers, and/ or underscores.\n"; + errorMessage += "• Keep it between 6 and 30 characters.\n"; + errorMessage += "• Avoid special characters like @, #, or \$.\"\n"; + } + if (errorMessage.isEmpty) { + return null; + } + // If there are no error messages, the password is valid + return errorMessage; + } + + bool isUsernameValid(String username) { + return RegExp(r'^[a-zA-Z][a-zA-Z0-9_]{5,19}$').hasMatch(username); + } + + bool isEmailValid(String email) { + var regex = RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'); + return regex.hasMatch(email); + } + + void _onFocusChange() { + setState(() { + startup = false; + }); + } + + // List getAutoFillDetails(){ + // if(widget.autoFillHintGroup == null){ + // return []; + // } + // else{ + // return widget.autoFillHintGroup!; + // } + // } + + Widget setRequiredText() { + if (widget.required) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "*", + style: TextStyle( + color: MzanziInnovationHub.of(context)!.theme.errorColor()), + ), + const SizedBox( + width: 8.0, + ), + Text(widget.hintText, + style: TextStyle( + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor())), + ], + ); + } else { + return Text(widget.hintText, + style: TextStyle( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor())); + } + } + + @override + void dispose() { + _focus.dispose(); + super.dispose(); + } + + @override + void initState() { + _focus.addListener(_onFocusChange); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return TextField( + keyboardType: TextInputType.number, + inputFormatters: allowDecimals(), + autofillHints: widget.autoFillHintGroup, + style: TextStyle( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + controller: widget.controller, + focusNode: _focus, + readOnly: makeEditable(), + //enabled: !makeEditable(), + obscureText: false, + onChanged: (_) => setState(() { + startup = false; + }), + decoration: InputDecoration( + label: setRequiredText(), + //labelStyle: TextStyle(color: MzanziInnovationHub.of(context)!.theme.primaryColor()), + fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + filled: true, + errorText: _errorText, + errorStyle: TextStyle( + color: MzanziInnovationHub.of(context)!.theme.errorColor(), + fontWeight: FontWeight.bold), + //errorBorder: const InputBorder(), + //hintText: hintText, + //hintStyle: TextStyle(color: Colors.blueGrey[400]), + disabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + width: 2.0, + ), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + width: 2.0, + ), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: MzanziInnovationHub.of(context)!.theme.errorColor(), + width: 2.0, + ), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: MzanziInnovationHub.of(context)!.theme.errorColor(), + width: 2.0, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor()), + ), + ), + ); + } +} From 460edea56b47bfee894c7cfe7b2a6c6c6a9fe6f7 Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 14:47:56 +0200 Subject: [PATCH 3/9] add home screen tile --- .../lib/mih_packages/mih_home/mih_home.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Frontend/lib/mih_packages/mih_home/mih_home.dart b/Frontend/lib/mih_packages/mih_home/mih_home.dart index 7e9317e1..cfc8e8f1 100644 --- a/Frontend/lib/mih_packages/mih_home/mih_home.dart +++ b/Frontend/lib/mih_packages/mih_home/mih_home.dart @@ -291,6 +291,31 @@ class _MIHHomeState extends State { s: getSec(), )); + tileList.add(MIHTile( + videoID: "", + onTap: () { + Navigator.of(context).pushNamed( + '/calculator', + //arguments: widget.signedInUser, + ); + }, + tileName: "Calculator", + tileIcon: Center( + child: FaIcon( + FontAwesomeIcons.calculator, + color: getSec(), + size: 200, + ), + ), + // Icon( + // Icons.info_outline, + // color: getSec(), + // size: 230, + // ), + p: getPrim(), + s: getSec(), + )); + tileList.add(MIHTile( videoID: "hbKhlmY_56U", onTap: () { From 0fcc0b452084efd49dd36aba01b7238919805a35 Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 14:48:15 +0200 Subject: [PATCH 4/9] add calculator route --- Frontend/lib/mih_router/routeGenerator.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Frontend/lib/mih_router/routeGenerator.dart b/Frontend/lib/mih_router/routeGenerator.dart index 39128829..8ea87309 100644 --- a/Frontend/lib/mih_router/routeGenerator.dart +++ b/Frontend/lib/mih_router/routeGenerator.dart @@ -1,3 +1,4 @@ +import 'package:Mzansi_Innovation_Hub/mih_packages/calculator/calculator.dart'; import 'package:Mzansi_Innovation_Hub/mih_packages/mih_policy_tos/mih_privacy_polocy.dart'; import 'package:Mzansi_Innovation_Hub/mih_packages/mih_policy_tos/mih_terms_of_service.dart'; import 'package:Mzansi_Innovation_Hub/mih_packages/mzansi_wallet/mzansi_wallet.dart'; @@ -266,6 +267,12 @@ class RouteGenerator { ); } return _errorRoute(); + case '/calculator': + return MaterialPageRoute( + settings: settings, + builder: (_) => const MIHCalculator(), + ); + default: return _errorRoute(); } From 62284b55e171bc1b4606b5c2bd4ed8d88a0d2f42 Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 14:48:41 +0200 Subject: [PATCH 5/9] create calc screen with swipe options --- .../mih_packages/calculator/calculator.dart | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 Frontend/lib/mih_packages/calculator/calculator.dart diff --git a/Frontend/lib/mih_packages/calculator/calculator.dart b/Frontend/lib/mih_packages/calculator/calculator.dart new file mode 100644 index 00000000..3da3edfc --- /dev/null +++ b/Frontend/lib/mih_packages/calculator/calculator.dart @@ -0,0 +1,194 @@ +import 'package:Mzansi_Innovation_Hub/mih_components/mih_layout/mih_action.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_layout/mih_body.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_layout/mih_header.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_layout/mih_layout_builder.dart'; +import 'package:Mzansi_Innovation_Hub/mih_objects/arguments.dart'; +import 'package:Mzansi_Innovation_Hub/mih_packages/calculator/simple_calc.dart'; +import 'package:Mzansi_Innovation_Hub/mih_packages/calculator/tip_calc.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_swipe_detector/flutter_swipe_detector.dart'; + +class MIHCalculator extends StatefulWidget { + const MIHCalculator({super.key}); + + @override + State createState() => _MIHCalculatorState(); +} + +class _MIHCalculatorState extends State { + int _selectedIndex = 0; + + MIHAction getActionButton() { + return MIHAction( + icon: const Icon(Icons.arrow_back), + iconSize: 35, + onTap: () { + Navigator.of(context).pop(); + Navigator.of(context).popAndPushNamed( + '/', + arguments: AuthArguments(true, false), + ); + }, + ); + } + + MIHHeader getHeader() { + return const MIHHeader( + headerAlignment: MainAxisAlignment.center, + headerItems: [ + Text( + "", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 25, + ), + ), + ], + ); + } + + MIHHeader getSecAction() { + return MIHHeader( + headerAlignment: MainAxisAlignment.end, + headerItems: [ + //============ Simple Calc ================ + Visibility( + visible: _selectedIndex != 0, + child: IconButton( + onPressed: () { + setState(() { + _selectedIndex = 0; + }); + }, + icon: const Icon( + Icons.calculate, + size: 35, + ), + ), + ), + Visibility( + visible: _selectedIndex == 0, + child: IconButton.filled( + iconSize: 35, + onPressed: () { + setState(() { + _selectedIndex = 0; + }); + }, + icon: const Icon( + Icons.calculate, + ), + ), + ), + //============ Tip Calc ================ + Visibility( + visible: _selectedIndex != 1, + child: IconButton( + onPressed: () { + setState(() { + _selectedIndex = 1; + }); + }, + icon: const Icon( + Icons.money, + size: 35, + ), + ), + ), + Visibility( + visible: _selectedIndex == 1, + child: IconButton.filled( + onPressed: () { + setState(() { + _selectedIndex = 1; + }); + }, + icon: const Icon( + Icons.money, + size: 35, + ), + ), + ), + // //============ Patient Files ================ + // Visibility( + // visible: _selectedIndex != 2, + // child: IconButton( + // onPressed: () { + // setState(() { + // _selectedIndex = 2; + // }); + // }, + // icon: const Icon( + // Icons.file_present, + // size: 35, + // ), + // ), + // ), + // Visibility( + // visible: _selectedIndex == 2, + // child: IconButton.filled( + // onPressed: () { + // setState(() { + // _selectedIndex = 2; + // }); + // }, + // icon: const Icon( + // Icons.file_present, + // size: 35, + // ), + // ), + // ), + ], + ); + } + + MIHBody getBody() { + return MIHBody( + borderOn: true, + bodyItems: [showSelection(_selectedIndex)], + ); + } + + Widget showSelection(int index) { + if (index == 0) { + return const SimpleCalc(); + } else if (index == 1) { + return const TipCalc(); + } else { + return const Placeholder(); + } + } + + @override + Widget build(BuildContext context) { + return SwipeDetector( + onSwipeLeft: (offset) { + if (_selectedIndex < 1) { + setState(() { + _selectedIndex += 1; + }); + } + //print("swipe left"); + }, + onSwipeRight: (offset) { + if (_selectedIndex > 0) { + setState(() { + _selectedIndex -= 1; + }); + } + //print("swipe right"); + }, + child: MIHLayoutBuilder( + actionButton: getActionButton(), + header: getHeader(), + secondaryActionButton: getSecAction(), + body: getBody(), + actionDrawer: null, + secondaryActionDrawer: null, + bottomNavBar: null, + pullDownToRefresh: false, + onPullDown: () async {}, + ), + ); + } +} From fdf3c66e26cbcd801de67d4349597c9f361c70a2 Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 14:48:50 +0200 Subject: [PATCH 6/9] simple calc feature --- .../mih_packages/calculator/simple_calc.dart | 252 ++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 Frontend/lib/mih_packages/calculator/simple_calc.dart diff --git a/Frontend/lib/mih_packages/calculator/simple_calc.dart b/Frontend/lib/mih_packages/calculator/simple_calc.dart new file mode 100644 index 00000000..4e580be9 --- /dev/null +++ b/Frontend/lib/mih_packages/calculator/simple_calc.dart @@ -0,0 +1,252 @@ +import 'package:Mzansi_Innovation_Hub/main.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_button.dart'; +import 'package:flutter/material.dart'; +import 'package:math_expressions/math_expressions.dart'; + +class SimpleCalc extends StatefulWidget { + const SimpleCalc({super.key}); + + @override + State createState() => _SimpleCalcState(); +} + +class _SimpleCalcState extends State { + var userInput = ''; + var answer = '0'; + + // Array of button + final List buttons = [ + 'C', + '(', + ')', + 'Del', + '7', + '8', + '9', + '/', + '4', + '5', + '6', + 'x', + '1', + '2', + '3', + '-', + '0', + '.', + '=', + '+', + ]; + +// function to calculate the input operation + void equalPressed() { + String finaluserinput = userInput; + finaluserinput = userInput.replaceAll('x', '*'); + + Parser p = Parser(); + Expression exp = p.parse(finaluserinput); + ContextModel cm = ContextModel(); + double eval = exp.evaluate(EvaluationType.REAL, cm); + answer = eval.toString(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + "Simple Calculator", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()), + const SizedBox(height: 10), + Container( + //color: Colors.white, + padding: const EdgeInsets.all(20), + alignment: Alignment.centerRight, + child: Text( + userInput, + style: TextStyle( + fontSize: 18, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ), + Container( + width: double.infinity, + //color: Colors.white, + padding: const EdgeInsets.all(15), + alignment: Alignment.centerRight, + child: Text( + answer, + style: TextStyle( + fontSize: 30, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + fontWeight: FontWeight.bold), + ), + ), + GridView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + // padding: EdgeInsets.only( + // left: width / 10, + // right: width / 10, + // bottom: height / 15, + // //top: 20, + // ), + // shrinkWrap: true, + itemCount: buttons.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4), + itemBuilder: (context, index) { + // Clear Button + if (index == 0) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput = ''; + answer = '0'; + }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.messageTextColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + + // +/- button + else if (index == 1) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput += buttons[index]; + }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.messageTextColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // % Button + else if (index == 2) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput += buttons[index]; + }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.messageTextColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // Delete Button + else if (index == 3) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput = userInput.substring(0, userInput.length - 1); + }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.errorColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // Equal_to Button + else if (index == 18) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + equalPressed(); + }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.successColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // +, -, / x buttons + else if (index == 7 || index == 11 || index == 15 || index == 19) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + if (answer == "0") { + setState(() { + userInput += buttons[index]; + }); + } else { + setState(() { + userInput = answer; + answer = "0"; + userInput += buttons[index]; + }); + } + // setState(() { + // userInput += buttons[index]; + // }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.messageTextColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // other buttons + else { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput += buttons[index]; + }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + }, + ), + ], + ); + } +} From 18663829e223556444fc484b6903b8cc9cc5c69d Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 14:49:00 +0200 Subject: [PATCH 7/9] tip calc feature --- .../lib/mih_packages/calculator/tip_calc.dart | 330 ++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 Frontend/lib/mih_packages/calculator/tip_calc.dart diff --git a/Frontend/lib/mih_packages/calculator/tip_calc.dart b/Frontend/lib/mih_packages/calculator/tip_calc.dart new file mode 100644 index 00000000..8bb6efa8 --- /dev/null +++ b/Frontend/lib/mih_packages/calculator/tip_calc.dart @@ -0,0 +1,330 @@ +import 'package:Mzansi_Innovation_Hub/main.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_button.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_number_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_layout/mih_window.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:math_expressions/math_expressions.dart'; + +class TipCalc extends StatefulWidget { + const TipCalc({super.key}); + + @override + State createState() => _TipCalcState(); +} + +class _TipCalcState extends State { + TextEditingController billAmountController = TextEditingController(); + TextEditingController tipPercentageController = TextEditingController(); + TextEditingController splitBillController = TextEditingController(); + TextEditingController noPeopleController = TextEditingController(); + final ValueNotifier splitValue = ValueNotifier(""); + String tip = ""; + String total = ""; + String amountPerPerson = ""; + void splitSelected() { + if (splitBillController.text.isNotEmpty) { + splitValue.value = splitBillController.text; + } else { + splitValue.value = ""; + } + } + + void validateInput() async { + bool valid = false; + if (splitBillController.text.isNotEmpty && + splitBillController.text == "Yes") { + if (billAmountController.text.isEmpty || + tipPercentageController.text.isEmpty || + noPeopleController.text.isEmpty) { + valid = false; + } else { + valid = true; + } + } else if (splitBillController.text.isNotEmpty && + splitBillController.text == "No") { + if (billAmountController.text.isEmpty || + tipPercentageController.text.isEmpty) { + valid = false; + } else { + valid = true; + } + } else { + valid = false; + } + print("Is input valid: $valid"); + if (valid) { + calculatePressed(); + } else { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Input Error"); + }, + ); + } + } + + void calculatePressed() { + String tipCalc = + "${billAmountController.text}*(${tipPercentageController.text}/100)"; + Parser p = Parser(); + ContextModel cm = ContextModel(); + Expression exp = p.parse(tipCalc); + double eval = exp.evaluate(EvaluationType.REAL, cm); + tip = eval.toStringAsFixed(2); + //print("Tip: $tip"); + String totalCalc = "${billAmountController.text}+$tip"; + exp = p.parse(totalCalc); + eval = exp.evaluate(EvaluationType.REAL, cm); + total = eval.toStringAsFixed(2); + //print("Total Amount: $total"); + if (splitBillController.text == "Yes") { + String splitCalc = "$total/${noPeopleController.text}"; + exp = p.parse(splitCalc); + eval = exp.evaluate(EvaluationType.REAL, cm); + amountPerPerson = eval.toStringAsFixed(2); + } + + //print("Amount Per Person: $amountPerPerson"); + displayResult(); + } + + void clearInput() { + billAmountController.clear(); + tipPercentageController.clear(); + noPeopleController.clear(); + setState(() { + splitBillController.text = "No"; + }); + } + + @override + void dispose() { + billAmountController.dispose(); + tipPercentageController.dispose(); + splitBillController.dispose(); + noPeopleController.dispose(); + super.dispose(); + } + + void displayResult() { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => MIHWindow( + fullscreen: false, + windowTitle: "Calculation Results", + onWindowTapClose: () { + Navigator.pop(context); + }, + windowTools: const [], + windowBody: [ + // FaIcon( + // FontAwesomeIcons.moneyBills, + // color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + // size: 30, + // ), + // const Divider(), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FaIcon( + FontAwesomeIcons.coins, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + size: 35, + ), + const SizedBox(width: 15), + Text( + "Tip", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ), + Text( + tip, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + const Divider(), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FaIcon( + FontAwesomeIcons.moneyBills, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + size: 35, + ), + const SizedBox(width: 15), + Text( + "Total", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ), + Text( + total, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + const Divider(), + if (splitBillController.text == "Yes") + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FaIcon( + FontAwesomeIcons.peopleGroup, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + size: 35, + ), + const SizedBox(width: 15), + Text( + "Total per Person", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ), + if (splitBillController.text == "Yes") + Text( + amountPerPerson, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + // if (splitBillController.text == "Yes") const Divider(), + ], + ), + ); + } + + @override + void initState() { + super.initState(); + splitBillController.text = "No"; + splitBillController.addListener(splitSelected); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + "Tip Calculator", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()), + const SizedBox(height: 10), + MIHNumberField( + controller: billAmountController, + hintText: "Bill Amount", + editable: true, + required: true, + enableDecimal: true, + ), + const SizedBox(height: 10), + MIHNumberField( + controller: tipPercentageController, + hintText: "Tip %", + editable: true, + required: true, + enableDecimal: false, + ), + const SizedBox(height: 10), + MIHDropdownField( + controller: splitBillController, + hintText: "Split Bill", + dropdownOptions: const ["Yes", "No"], + required: true, + editable: true, + ), + const SizedBox(height: 10), + ValueListenableBuilder( + valueListenable: splitValue, + builder: (BuildContext context, String value, Widget? child) { + return Visibility( + visible: value == "Yes", + child: Column( + children: [ + MIHNumberField( + controller: noPeopleController, + hintText: "No. of People", + editable: true, + required: true, + enableDecimal: false, + ), + const SizedBox(height: 10), + ], + ), + ); + }, + ), + SizedBox( + width: double.infinity, + height: 50, + child: MIHButton( + onTap: () { + validateInput(); + }, + buttonText: "Calculate", + buttonColor: MzanziInnovationHub.of(context)!.theme.successColor(), + textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ), + const SizedBox(height: 10), + SizedBox( + width: double.infinity, + height: 50, + child: MIHButton( + onTap: () { + clearInput(); + }, + buttonText: "Clear", + buttonColor: MzanziInnovationHub.of(context)!.theme.errorColor(), + textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ), + ], + ); + } +} From 6dc7efc75354d70575b533b05a2ed6d09698abec Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 15:26:46 +0200 Subject: [PATCH 8/9] Hopme screen Search Mzansi "Apps hanged to "Tiles" --- Frontend/lib/mih_packages/mih_home/mih_home.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/lib/mih_packages/mih_home/mih_home.dart b/Frontend/lib/mih_packages/mih_home/mih_home.dart index cfc8e8f1..65416371 100644 --- a/Frontend/lib/mih_packages/mih_home/mih_home.dart +++ b/Frontend/lib/mih_packages/mih_home/mih_home.dart @@ -954,7 +954,7 @@ class _MIHHomeState extends State { child: SizedBox( child: MIHSearchField( controller: searchController, - hintText: "Search Mzansi Apps", + hintText: "Search Mzansi Tiles", required: false, editable: true, onTap: () { From 670042bf50a84b26e8d356fd3bddc53abf119800 Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 14 Jan 2025 15:28:45 +0200 Subject: [PATCH 9/9] dynamic calc for web window --- .../mih_packages/calculator/simple_calc.dart | 339 ++++++++++-------- 1 file changed, 185 insertions(+), 154 deletions(-) diff --git a/Frontend/lib/mih_packages/calculator/simple_calc.dart b/Frontend/lib/mih_packages/calculator/simple_calc.dart index 4e580be9..24bc2f15 100644 --- a/Frontend/lib/mih_packages/calculator/simple_calc.dart +++ b/Frontend/lib/mih_packages/calculator/simple_calc.dart @@ -52,8 +52,22 @@ class _SimpleCalcState extends State { @override Widget build(BuildContext context) { + double width = MediaQuery.sizeOf(context).width; + double height = MediaQuery.sizeOf(context).height; + var padding = MediaQuery.paddingOf(context); + double newheight = height - padding.top - padding.bottom; + print("width: $width"); + print("height: $height"); + print("newheight: $newheight"); + double calcWidth = 500; + if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") { + if (height < 700) { + calcWidth = 300; + } + } return Column( - mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, children: [ Text( "Simple Calculator", @@ -91,160 +105,177 @@ class _SimpleCalcState extends State { fontWeight: FontWeight.bold), ), ), - GridView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - // padding: EdgeInsets.only( - // left: width / 10, - // right: width / 10, - // bottom: height / 15, - // //top: 20, - // ), - // shrinkWrap: true, - itemCount: buttons.length, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 4), - itemBuilder: (context, index) { - // Clear Button - if (index == 0) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: MIHButton( - onTap: () { - setState(() { - userInput = ''; - answer = '0'; - }); - }, - buttonText: buttons[index], - buttonColor: - MzanziInnovationHub.of(context)!.theme.messageTextColor(), - textColor: - MzanziInnovationHub.of(context)!.theme.primaryColor(), - ), - ); - } + Container( + alignment: Alignment.centerRight, + child: SizedBox( + width: calcWidth, + child: GridView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + // padding: EdgeInsets.only( + // left: width / 10, + // right: width / 10, + // bottom: height / 15, + // //top: 20, + // ), + // shrinkWrap: true, + itemCount: buttons.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + //mainAxisExtent: 150, + ), + itemBuilder: (context, index) { + // Clear Button + if (index == 0) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput = ''; + answer = '0'; + }); + }, + buttonText: buttons[index], + buttonColor: MzanziInnovationHub.of(context)! + .theme + .messageTextColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } - // +/- button - else if (index == 1) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: MIHButton( - onTap: () { - setState(() { - userInput += buttons[index]; - }); - }, - buttonText: buttons[index], - buttonColor: - MzanziInnovationHub.of(context)!.theme.messageTextColor(), - textColor: - MzanziInnovationHub.of(context)!.theme.primaryColor(), - ), - ); - } - // % Button - else if (index == 2) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: MIHButton( - onTap: () { - setState(() { - userInput += buttons[index]; - }); - }, - buttonText: buttons[index], - buttonColor: - MzanziInnovationHub.of(context)!.theme.messageTextColor(), - textColor: - MzanziInnovationHub.of(context)!.theme.primaryColor(), - ), - ); - } - // Delete Button - else if (index == 3) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: MIHButton( - onTap: () { - setState(() { - userInput = userInput.substring(0, userInput.length - 1); - }); - }, - buttonText: buttons[index], - buttonColor: - MzanziInnovationHub.of(context)!.theme.errorColor(), - textColor: - MzanziInnovationHub.of(context)!.theme.primaryColor(), - ), - ); - } - // Equal_to Button - else if (index == 18) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: MIHButton( - onTap: () { - setState(() { - equalPressed(); - }); - }, - buttonText: buttons[index], - buttonColor: - MzanziInnovationHub.of(context)!.theme.successColor(), - textColor: - MzanziInnovationHub.of(context)!.theme.primaryColor(), - ), - ); - } - // +, -, / x buttons - else if (index == 7 || index == 11 || index == 15 || index == 19) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: MIHButton( - onTap: () { - if (answer == "0") { - setState(() { - userInput += buttons[index]; - }); - } else { - setState(() { - userInput = answer; - answer = "0"; - userInput += buttons[index]; - }); - } - // setState(() { - // userInput += buttons[index]; - // }); - }, - buttonText: buttons[index], - buttonColor: - MzanziInnovationHub.of(context)!.theme.messageTextColor(), - textColor: - MzanziInnovationHub.of(context)!.theme.primaryColor(), - ), - ); - } - // other buttons - else { - return Padding( - padding: const EdgeInsets.all(4.0), - child: MIHButton( - onTap: () { - setState(() { - userInput += buttons[index]; - }); - }, - buttonText: buttons[index], - buttonColor: - MzanziInnovationHub.of(context)!.theme.secondaryColor(), - textColor: - MzanziInnovationHub.of(context)!.theme.primaryColor(), - ), - ); - } - }, + // +/- button + else if (index == 1) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput += buttons[index]; + }); + }, + buttonText: buttons[index], + buttonColor: MzanziInnovationHub.of(context)! + .theme + .messageTextColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // % Button + else if (index == 2) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput += buttons[index]; + }); + }, + buttonText: buttons[index], + buttonColor: MzanziInnovationHub.of(context)! + .theme + .messageTextColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // Delete Button + else if (index == 3) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput = + userInput.substring(0, userInput.length - 1); + }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.errorColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // Equal_to Button + else if (index == 18) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + equalPressed(); + }); + }, + buttonText: buttons[index], + buttonColor: + MzanziInnovationHub.of(context)!.theme.successColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // +, -, / x buttons + else if (index == 7 || + index == 11 || + index == 15 || + index == 19) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + if (answer == "0") { + setState(() { + userInput += buttons[index]; + }); + } else { + setState(() { + userInput = answer; + answer = "0"; + userInput += buttons[index]; + }); + } + // setState(() { + // userInput += buttons[index]; + // }); + }, + buttonText: buttons[index], + buttonColor: MzanziInnovationHub.of(context)! + .theme + .messageTextColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + // other buttons + else { + return Padding( + padding: const EdgeInsets.all(4.0), + child: MIHButton( + onTap: () { + setState(() { + userInput += buttons[index]; + }); + }, + buttonText: buttons[index], + buttonColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ); + } + }, + ), + ), ), ], );