My friend, Adewunmi, is studying to become an app developer. He is learning to use Flutter. Naturally, I am excited. My
friend + programming = more things to argue about. You know, stuff like ReactJS vs VueJS, Tabs vs Spaces and other interesting stuff. All in all, I am going to have fun. Earlier this week, he showed me a calculator application that he developed. It was a great demonstration of the concepts he had learnt. Of course, I asked him to add a twist to the application. A learning project can be more interesting with a twist. The twist was to add a button to reveal the scientific features of the calculator. Such a feature would allow him to think out of the box and do things by himself. The replies I got from him were very interesting.
He stated that he had to learn how to use "Stateful widgets" and didn't want to go too far ahead of the course that he is taking. To me, it's important to go on tangents when learning concepts in a course. You get more out of such content that way. If you're making a music player, try making a live internet radio station. If it is a text chat app, add a voice chatting feature. You'll hit obstacles and get better trying to overcome those. Like I said before, a twist makes a project more interesting. I challenged him to a bet. If I am able to add the scientific calculator feature in an hour despite never writing Dart before, he would have to do that too. He loved my energy and he took the challenge. A bet that I lost badly. Before I got started, the app looked like below.
Setting up a Flutter Environment
It took me a long time to set up my laptop for Flutter development. I was trying to use the Android Studio CLI tools without installing Android Studio. I installed the SDK manager but the
flutter doctor command kept on failing. It eventually worked out well after following this tutorial on setting up a Flutter environment on a Ubuntu Linux machine. It took the whole hour, I already lost my bet and had to go back on my resolve not to install Android Studio on my laptop. It is safe to say I won't use it for anything other than the android toolchain management. I would have loved having something similar to a GitHub codespaces setup for Flutter. I wouldn't have to install anything to get work started. If you haven't read my article on GitHub Codespaces, you should check it out.
Finding the right file to edit.
I finally had my laptop set up for action. "Show me the code and let me deal with it", I was ready to pounce on that codebase. Unfortunately, I couldn't find the file. Funny right? I was lost inside the maze of the codebase. I came across multiple
ios folders containing XML files. "What is going on today?" was all that came to my mind. I was tempted to create a new flutter application but time wasn't on my side. It was during my travel through the code and into the test folder That I saw the light. The light was in the form of VScode's wiggly yellow line in the test file. Pressing CTRL+ Click took me to the main file named
main.dart. Yay. Here we go! Time to get to work. Then I started messing around with the code.
Modifying the Codebase.
To be frank, Flutter code was very confusing at first. I was lost for a moment. Whenever I get lost in a codebase, I do the most strange things. I checkout the
main branch and I mess with the code. Yes, you heard me right. I'll try changing strings to numbers and duplicating several code blocks. The compiler complained severally and after a few strategic "mess-ups", I was able to recognise the block of code responsible for the buttons. It was a Row function containing several
buildButton widgets. When I see something that looks like the image shown below, of course, I'll change it and see if it reflects.
I changed the first argument of the
buildButton widget and I saw that it reflected both in appearance and function. The next thing was to check out the implementation of the
I understood that the argument
buttonText serves as both the displayed text and the parameter passed to the handler function
buttonPressed. This was okay for a basic calculator but I needed a little more flexibility than that without breaking existing code. Googling how to add an optional argument in a dart function led to a medium article with which I added an optional argument
displayText to the
buildButton Widget. I then used a ternary operation to set the text displayed to buttonText of
displayText defaults to null.
buildButton widget looks somewhat like this.
buildButton("cos(", 1, Colors.green, displayText: "cos"),
It worked out well and I was able to add buttons such as
sine. These buttons need to be displayed as
tan but input
tan( into the calculator. I added extra rows and columns to the buttons. I also added an extra button to do the switching from the basic calculator to the complex calculator mode. The button wasn't functional and I had to do something about it. The calculator at this point looked like I wanted it to look.
All this while, I was editing the existing
Row widget. It meant my calculator was simply a scientific calculator by default. I went back to the GitHub repo that my friend shared with me and copied the previous
Row widget. I wanted to rename it as a separate widget and return it based on the value of a variable that would be toggled by pressing the
ext button. Flutter didn't agree with me. First things first. I created a global variable named
status in the
_SimpleCalculator Class and made it default to
false. I then modified the
buttonPressed handler function to toggle
status if it receives
ext as an argument. All it took was an else- if statement.
To return a different
Row widget based on the value of
status, I tried using a ternary operation in the body widget. For reasons I don't know, it didn't work. I ended up using an if statement based on the value of
status. After all, a ternary operation is a "hard-to-read"
ifstatement. The code looks like shown below after everything.
Notice that we initially had one
Row widget in the code. Now, we return the needed Row widget based on if
false. Pretty simple, huh?
The code is not beautiful but it works. The Row on line 133 is the scientific Row(Keyboard) while the other is the basic Row(Keyboard). The result is as shown below.
In addition, I tried as much as possible to follow the flow of the codebase while modifying it. It allowed me to copy and paste several codeblocks without worrying if they will work or not. While some won't work, I usually end up checking Google for why it didn't work and having that at the back of my mind moving forward. I didn't browse how to do an if statement in Dart while writing this code. I simply copied and pasted it from the same codebase. I'm sure my friend would have a tough time knowing which code is his and which is mine.
Finally, using Google is a great way to learn and overcome obstacles. You are not the first to encounter problems and it is very important to know how to search the internet for solutions to such problems. One can argue that being a "FullStackOverflow developer" is an important part of being a software developer. Hone your googling skills and you won't be afraid of breaking codes once in a while.
Thank you for following my adventure into the world of Dart and Flutter. I am by no means a Flutter developer as I can't boast of being able to write a Flutter application by myself without the help of Google. I had fun though. I hope my friend still goes ahead and implement the feature even though I lost the bet.