Home > database >  How to reduce amount of Boolean statements when programming
How to reduce amount of Boolean statements when programming

Time:02-10

So the current language I am using is flutter, although I believe this applies to any programming language. I am creating an app where I have a requirement to have an implemented custom keypad. The keypad has three configurations for three different text fields used in pipette.dart, I added part of the code for ASPIRATE SPEED and DISPENSE SPEED, althogh the same logic applies to volume shown in the image below.

When each text field is pressed the current highlight is switched to the container where the text field is ( The blue highlight below the text field). The keypad is also switched to control the text field which is pressed, shown in toggleKeyPad(). So for example when you click Aspirate Speed, isAspirate is true and isAspirateText is true, isAspirate is then passed to blueHighlightSpeed() (shown at the bottom of pipette.dart), which then highlights the container. when IsAspirateText is true it sets the keypad to use aspirateController which is the text editing controller for the text field in Aspirate Speed, the same logic applies to Dispense speed and Volume. Hopefully this helps anyone understand what my code is doing, I will update if I missed anything.

The question that I have is I would like to know if there is a way to minimize the amount of Boolean statements that I am using. Currently every time I click a new text field I have to change 8 different Booleans which can become very tedious and for other programmers will probably make the code harder to read and understand. I have only been programming for a few years now and would just like to know if there is a way to create this same logic without as many Booleans.

Is it maybe possible to use a switch statement here?

enter image description here

functions.dart


import 'package:flutter/material.dart';
import '../keyboard/text_key.dart';
import '../keyboard/textkey_functions.dart';

bool keypadUp = false;
bool isTextFieldShown = false;
bool isVolume = false;
bool isVolumeText = false;
bool isAspirate = false;
bool isAspirateText = true;
bool isDispense = false;
bool isDispenseText = false;
bool depthMemoryTop = false;
bool depthMemoryBottom = false;

TextEditingController aspirateController = TextEditingController();
TextEditingController dispenseController = TextEditingController();
TextEditingController volumeController = TextEditingController();
TextEditingController machController = TextEditingController();

Widget toggleKeyPad() {
  if (isAspirateText == true) {
    return KeyPad(
      onTextInput: (myText) {
        insertText(myText, aspirateController); //Accepts and inputs text into aspirateController
      },
      onBackspace: () {
        backspace(aspirateController);
      },
    );
  }
  if (isVolumeText == true) {
    return KeyPad(
      onTextInput: (myText) {
        insertText(myText, volumeController); //Accepts and inputs text into volumeController
      },
      onBackspace: () {
        backspace(volumeController);
      },
    );
  }
  if (isDispenseText == true) {
    return KeyPad(
      onTextInput: (myText) {
        insertText(myText, dispenseController); //Accepts and inputs text into dispenseController
      },
      onBackspace: () {
        backspace(dispenseController);
      },
    );
  } else {
    return KeyPad(
      onTextInput: (myText) {
        insertText(myText, machController); // I didn't need the else statement so I created a fake controller which takes no input. 
      },
      onBackspace: () {
        backspace(machController);
      },
    );
  }
}

Padding blueHighlightVolume (bool isSelect) {
  if (isSelect == true) {
    return Padding(
      padding: const EdgeInsets.only(top: 190),
      child: Container(
        width: 140,
        height: 10,
        color: Color.fromRGBO(2, 58, 107, 1),
      ),
    );
  } else {
    return Padding(
      padding: const EdgeInsets.only(top: 190),
      child: Container(
        width: 140,
        height: 10,
      ),
    );
  }
}

Padding blueHighlightSpeed (bool isSelect) {
  if (isSelect == true) {
    return Padding(
      padding: const EdgeInsets.only(top: 90, left: 1),
      child: Container(
        width: 140,
        height: 10,
        color: Color.fromRGBO(2, 58, 107, 1),
      ),
    );
  } else {
    return Padding(
      padding: const EdgeInsets.only(top: 90, left: 1),
      child: Container(
        width: 140,
        height: 10,
      ),
    );
  }
}

Padding blueHighlightDepthTop (bool isSelect) {
  if (isSelect == true) {
    return Padding(
      padding: const EdgeInsets.only(top: 89, left: 15),
      child: Container(
        width: 350,
        height: 10,
        color: Color.fromRGBO(2, 58, 107, 1),
      ),
    );
  } else {
    return Padding(
      padding: const EdgeInsets.only(top: 90, left: 15),
      child: Container(
        width: 350,
        height: 10,
      ),
    );
  }
}

Padding blueHighlightDepthBottom (bool isSelect) {
  if (isSelect == true) {
    return Padding(
      padding: const EdgeInsets.only(top: 90, left: 15),
      child: Container(
        width: 350,
        height: 10,
        color: Color.fromRGBO(2, 58, 107, 1),
      ),
    );
  } else {
    return Padding(
      padding: const EdgeInsets.only(top: 90, left: 15),
      child: Container(
        width: 350,
        height: 10,
      ),
    );
  }
}

part of pipette.dart

                                 Column(
                                children: [
                                  Stack(
                                    children: [
                                      Padding(
                                        padding: const EdgeInsets.all(8.0),
                                        child: Text(
                                          'ASPIRATE SPEED',
                                          style: TextStyle(
                                            fontFamily: 'Droid Sans',
                                            color: Color.fromRGBO(2, 58, 107, 1),
                                            fontSize: 12.0,
                                          ),
                                        ),
                                      ),
                                      Container(
                                        decoration: BoxDecoration(
                                          border: Border(
                                            left: BorderSide(width: 1, color: Colors.grey),
                                          ),
                                        ),
                                        width: 140,
                                        height: 100,
                                        //color: Colors.red,
                                      ),
                                      Padding(
                                        padding: const EdgeInsets.only(left: 20.0, top: 36.0),
                                        child: Container(
                                          width: 75,
                                          height: 40,
                                          child: TextField(
                                            controller: aspirateController,
                                            textAlign: TextAlign.center,
                                            readOnly: true,
                                            onTap: () {
                                              setState(() {
                                                /*
                                                * set isAspirate and isAspirateText to true, highlights aspirate speed container and
                                                * changes aspirateController to accept input from the keypad.
                                                *
                                                */
                                                isAspirateText = true;
                                                isDispenseText = false;
                                                isVolumeText = false;
                                                isAspirate = true;
                                                isDispense = false;
                                                isVolume = false;
                                                depthMemoryTop = false;
                                                depthMemoryBottom = false;
                                                print(isAspirate);
                                              });
                                            },
                                            decoration: InputDecoration(
                                              border: OutlineInputBorder(),
                                            ),
                                          ),
                                        ),
                                      ),
                                      blueHighlightSpeed(isAspirate),
                                    ],
                                  ),
                                  Stack(
                                    children: [
                                      Padding(
                                        padding: const EdgeInsets.all(8.0),
                                        child: Text(
                                            'DISPENSE SPEED',
                                            style: TextStyle(
                                            fontFamily: 'Droid Sans',
                                            color: Color.fromRGBO(2, 58, 107, 1),
                                            fontSize: 12.0,
                                          ),
                                        ),
                                      ),
                                      Container(
                                        decoration: BoxDecoration(
                                          border: Border(
                                            left: BorderSide(width: 1, color: Colors.grey),
                                          ),
                                        ),
                                        width: 140,
                                        height: 100,
                                        //color: Colors.red,
                                      ),
                                      Padding(
                                        padding: const EdgeInsets.only(left: 20.0, top: 36.0),
                                        child: Container(
                                          width: 75,
                                          height: 40,
                                          child: TextField(
                                            controller: dispenseController,
                                            textAlign: TextAlign.center,
                                            readOnly: true,
                                            onTap: () {
                                              setState(() {
                                                isVolumeText = false;
                                                isAspirateText = false;
                                                isDispenseText = true;
                                                isAspirate = false;
                                                isDispense = true;
                                                isVolume = false;
                                                depthMemoryTop = false;
                                                depthMemoryBottom = false;
                                                print(isAspirate);
                                              });
                                            },
                                            decoration: InputDecoration(
                                              border: OutlineInputBorder(),
                                            ),
                                          ),
                                        ),
                                      ),
                                      blueHighlightSpeed(isDispense),
                                    ],
                                  ),
                                ],
                              ),

keypad.dart


class KeyPad extends StatelessWidget
{

  KeyPad({
    required this.onTextInput,
    required this.onBackspace,
  });

  final double width = 275;
  final double height = 90;

  final ValueSetter<String> onTextInput;
  final VoidCallback onBackspace;

  void _textInputHandler(String text) => onTextInput.call(text);

  void _backspaceHandler() => onBackspace.call();

  Container buildRowOne()
  {
    return Container(
      width: width,
      height: height,
      child: Row(
        children: [
          TextKey(
            text: '7',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '8',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '9',
            onTextInput: _textInputHandler,
          ),

        ],
      ),
    );
  }

  Container buildRowTwo()
  {
    return Container(
      width: width,
      height: height,
      child: Row(
        children: [
          TextKey(
            text: '4',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '5',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '6',
            onTextInput: _textInputHandler,
          ),
        ],
      ),
    );
  }

  Container buildRowThree()
  {
    return Container(
      width: width,
      height: height,
      child: Row(
        children: [
          TextKey(
            text: '1',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '2',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '3',
            onTextInput: _textInputHandler,
          ),
        ],
      ),
    );
  }

  Container buildRowFour()
  {
    return Container(
      width: width,
      height: height,
      child: Row(
        children: [
          BackspaceKey(
            onBackspace: _backspaceHandler,
          ),
          TextKey(
            text: '0',
            onTextInput: _textInputHandler,
          ),
          TextKey(
            text: '.',
            onTextInput: _textInputHandler,
          ),
        ],
      ),
    );
  }

  Padding buildRowFive()
  {
    return Padding(
      padding: const EdgeInsets.only(top: 35),
      child: Container(
        decoration: BoxDecoration(
          border: Border.all(color: Color.fromRGBO(92, 103, 148, 1)),
              borderRadius: BorderRadius.circular(45.0),
        ),
        width: 175,
        height: 60,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            IconButton(
              icon: Image.asset('assets/text_key/done.png'),
              onPressed: () {},
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context)
  {
    return SafeArea(
      child: Container(
        child: Column(
          children: [
            buildRowOne(),
            buildRowTwo(),
            buildRowThree(),
            buildRowFour(),
            buildRowFive(),
          ],
        ),
      ),
    );
  }
}

CodePudding user response:

The best thing to use in this situation would be a state machine, which would be much easier to implement than trying to use enumerations or switch statements -- which do not necessarily reduce the complexity of your code.

This video does a great job of explaining this: https://www.youtube.com/watch?v=I1Mzx_tSpew

You don't need to know much about computational theory to implement a basic (finite) state machine, but it's a highly effective method for problems that can lead to infinite booleans.

Flutter also has access to a few good libraries for such a thing:

https://www.jawahar.tech/blog/finite-state-machine-flutter/ and https://pub.dev/packages/statemachine

CodePudding user response:

The first change I believe you should do is to get rid of the isSomethingText variables. The isSomethingText variables on your code are true if isSomething is true, which means that you could use only one of those variables for everything.

The problem

Your code is quite complex, making it harder for me to understand your question, so I will try to simplify the situation a bit: Let's say we have an app that will display a color, and in order to do so, it asks the user to mark some sort of radio button selection, but in order to do that we have the following booleans:

bool isRed;
bool isGreen;
bool isBlue;
bool isOrange;
bool isYellow;
bool isPurple;

Then when the user presses, for example, the purple button:

// set purple
onPressed: () => {
  isRed = false;
  isGreen = false;
  isBlue = false;
  isOrange = false;
  isYellow = false;
  isPurple = true;
}

And so on and so forth for every color, then when displaying the color:

late Color color;

if (isRed) {
  color = Color('#ff0000');
} else if (isGreen) {
  color = Color('#00ff00');
} else ...

return ColorDisplay(color: color);

You get the idea.

Now the key to this is that each of these booleans are blocking each other, if isRed is true, the other booleans should always, always, be false.

That's the most important part. we could have coded this in such a way that you could select both red and blue, and then purple would be displayed, but for this solution to work, each of the booleans must be only true when the rest are false.

If you want to make the other interpretation, you should actually make enough booleans for each combination, like this:

bool isRed;
bool isGreen;
bool isBlue;
bool isRedAndBlue;
bool isRedAndGreen;
bool isGreenAndBlue;

Anyway, just remember you can do this any time your booleans are dependent from each other.

The solution

To solve the above problem, we can use an enumerator.

An enumerator is basically a type that can have one of a number of values, they look like this:

enum PossibleColors {
  Red,
  Green,
  Blue,
  Orange,
  Yellow,
  Purple,
}

With the above, we can now make a PossibleColors variable and store the current selected color there:

OLD:

bool isRed;
bool isGreen;
bool isBlue;
bool isOrange;
bool isYellow;
bool isPurple;

NEW:

PossibleColors? currentColor = PossibleColors.Red; // default red.

Then when setting the color:

OLD:

// set purple
onPressed: () => {
  isRed = false;
  isGreen = false;
  isBlue = false;
  isOrange = false;
  isYellow = false;
  isPurple = true;
}

NEW:

// set purple
onPressed: () => currentColor = PossibleColors.Purple;

finally, to display:

OLD:

late Color color;

if (isRed) {
  color = Color('#ff0000');
} else if (isGreen) {
  color = Color('#00ff00');
} else ...

return ColorDisplay(color: color);

NEW:

late Color color;

switch (currentColor) {
  case PossibleColors.Red:
    color  = Color('#ff0000');
    break;
  case PossibleColors.Green:
    color = Color('#00ff00');
    break;
  default:
    throw UnimplementedError('no color yet...');
}

return ColorDisplay(color: color);
  • Related