Welcome to this chapter where we will dive into Dart programming, an essential language for developing Flutter apps.
Although we can't cover everything in detail in this course, our But don't worry. My goal is to cover enough information to get us started with Dart and writing our own apps. I assume you have a little prior programming experience. It doesn’t matter which language it is.
Setting up Stage
To begin, we need a playground or project where we can write and test our Dart code. While some developers prefer using tools like DartPad, we will take a different approach in this course. Since our ultimate aim is to release a Flutter app for iOS and Android, it makes sense to create an actual Flutter project from the start. By doing so, we can familiarize ourselves with the development environment and gradually build upon our code.
This initial investment of time in setting up a Flutter project will pay off in the upcoming chapters, where we will continue working on the same project. It provides continuity and allows us to bridge the gap between test code and actual application development. You'll gain a better understanding of how to work with Flutter and see the practical value of your code.
Now, let's proceed with setting up a test application. Follow the steps below.
Setting Up Your Flutter Project
To begin, we need to set up a project where you can write and test your Dart code. While some developers prefer using online editors like DartPad, we'll take a more practical approach by creating a real Flutter project on your local machine. This way, you'll get familiar with the Flutter framework as a whole.
To create a new Flutter project, follow these steps:
- Open your preferred terminal or command prompt.
- Navigate to the folder where you usually keep your projects (e.g.,
dev/projects/flutter). - Use the
flutter createcommand followed by your desired project name, like "learning_dart". This command creates a new Flutter project structure.
1flutter create learning_dart
Once the project is created, open it in your favorite code editor. In this example, we'll use Visual Studio Code.
- In the terminal, navigate to the project directory (
cd learning_dart). - Launch Visual Studio Code with the command
code .. Make sure you have the "code" command installed and available in your system's PATH.
💡 Setting up VS code for flutter is very easy. If you need any help, then you can write me here and I’ll help you with that.
Selecting a Device
Before we run the Flutter project, we need to choose a device or emulator on which to test our app. In this case, we'll use the Android Simulator for demonstration purposes. If you're on a different platform, adjust accordingly.
- Open the command palette in Visual Studio Code (Command + Shift + P on macOS, Ctrl + Shift + P on Windows/Linux).
- Search for "Flutter: Select Device" and press Enter.
- Choose the iOS Simulator option to start the simulator.
Writing Your First Dart Code
Now that everything is set up, let's explore the project's structure and start writing some Dart code. The main file we'll focus on is lib/main.dart, where most of the code resides.
For now, don't worry about understanding all the code in main.dart. We'll gradually learn and modify it as we progress. Our goal in this chapter is to inject our own code and get familiar with Dart.
To run the Flutter app on the selected simulator:
- Ensure the Android Simulator is running(see above)
- In Visual Studio Code, go to the Run menu and choose "Run Without Debugging".
Depending on your machine's specifications, the compilation process might take a few seconds or more. Once completed, the Flutter app will launch on the iOS Simulator.
Understanding Keywords, Data Types, Constants, and Variables
When diving into mobile app development using Flutter, it's essential to grasp key concepts like keywords, data types, constants, and variables. Now, we'll explore these fundamental concepts in a beginner-friendly manner. By the end, you'll have a better understanding of these concepts and how they relate to Dart and Flutter.
Keywords in Dart
Keywords are crucial for understanding any programming language, including Dart. Think of them as reserved words that have special meanings within the language. They act as your interface with the programming language, allowing you to communicate your intentions to the code. Some common Dart keywords include show, import, extends, async, and await. By using these keywords correctly, you can convey your desired actions to the programming language.
1// Example of using keywords in Dart
2import 'dart:core';
3
4void main() {
5 int count = 0;
6
7 for (int i = 0; i < 10; i++) {
8 if (i % 2 == 0) {
9 count += i;
10 }
11 }
12
13 print('The count is $count');
14}
Exploring Data Types
In Dart, data types are categorizations of information based on their appearance or contents. They help us define the nature of the data we're working with.
Think of data types as similar to components in a figma design system. Just as you have different tools in design software, Dart offers various data types such as int (for integers), String (for textual data), List (for collections of items)., and sets. By assigning a data type to a piece of data, you provide it with a stamp that describes its specific characteristics.
1// Example of using data types in Dart
2void main() {
3 String name = 'John';
4 int age = 25;
5 bool isStudent = true;
6 List<String> hobbies = ['reading', 'gaming', 'coding'];
7 Set<String> languages = {'Dart', 'Java', 'Python'};
8
9 print('Name: $name');
10 print('Age: $age');
11 print('Is Student: $isStudent');
12 print('Hobbies: $hobbies');
13 print('Languages: $languages');
14}
Understanding Constants
Constants are values whose internal data remains unchanged during both the program's writing and execution phases. They are immutable, meaning their values cannot be modified once assigned.
In Dart, we have compile-time constants and runtime constants, both of which retain their constant nature throughout the program's lifecycle. For simplicity, we'll focus on constants as values that don't change during the program's execution.
1// Example of using constants in Dart
2void main() {
3 const int daysInWeek = 7;
4 const double pi = 3.14159;
5 const String appName = 'MyApp';
6
7 print('Days in a week: $daysInWeek');
8 print('Value of pi: $pi');
9 print('App Name: $appName');
10}
Variables and their Flexibility
Variables, unlike constants, can hold values that may change during the program's execution. They provide flexibility by allowing you to assign and modify values as needed.
In Dart, you declare a variable using the var keyword, followed by the variable's name and an initial value. For example, var name = 'foo'; assigns the value foo to the variable name. Later, you can update the variable by reassigning a new value, like name = 'anthony';.
1// Example of using variables in Dart
2void main() {
3 var score = 0;
4 var playerName = 'John';
5
6 print('Player: $playerName');
7
8 score = 100;
9 print('Score: $score');
10
11 playerName = 'Alex';
12 print('New Player: $playerName');
13}
Final Variables and Their Immutability
In Dart, we also have final variables, which are similar to constants but with some flexibility. A final variable's value cannot be changed once assigned, similar to a constant.
However, it provide flexibility to assign a value later in the code but ensure that it remains constant afterward. This is particularly useful when you need to calculate a value or initialize it based on certain conditions before assigning it as final.
1// Example of using final variables in Dart
2void main() {
3 final int maxScore = 100;
4 final String appName;
5
6 appName = 'MyApp';
7
8 print('Maximum Score: $maxScore');
9 print('App Name: $appName');
10}
Let’s summarize. Keywords help you communicate with the programming language, data types categorize your information, constants provide immutable values, and variables offer flexibility for changing values during program execution.
Operators
Operators are symbols or keywords that perform specific operations on operands. In Dart, we have three types of operators: prefix, infix, and suffix.
Prefix Operators
A prefix operator is applied before its operand. For example, the prefix operator -- can be used to decrement a value:
1void test() {
2 var h = 20;
3 h--; // Decrease the value of h by 1
4 print('Decrement: $h'); // Output: 19
5}
In this example, the -- operator is used as a prefix to decrement the value of h by 1.
Infix Operators
An infix operator is placed between its two operands. For example, the division operator / performs division between two values:
1void test() {
2 final h = 20;
3 final halfOfH = h / 2; // Divide h by 2
4 print('Half of h: $halfOfH'); // Output: 10.0
5}
Here, the / operator divides the value of h by 2.
Suffix Operators
A suffix operator is applied after its operand. For example, the ++ operator can be used to increment a value:
1void test() {
2 var h = 20;
3 h++; // increase the value of h by 1
4 print('Decrement: $h'); // Output: 21
5}
Armed with this knowledge, you're well on your way to move ahead.
Functions in Flutter
Functions play a crucial role in grouping lines of code, accepting arguments, and returning values. Let's begin our journey by understanding the fundamentals and conventions of functions.
What is a Function?
A function is a logical grouping of lines of code, forming a body of code that performs a specific task. It takes in one or more arguments or parameters and may return a value. Think of a function as a box that processes inputs and produces outputs. In Dart, the programming language used in Flutter, functions have a return type, a name, and a set of parentheses that enclose the function arguments.
Creating a Simple Function
Let's create a simple function to illustrate the concept. Suppose we want to concatenate a person's first name and last name with a space in between. We can define a function called getFullName, which returns a string. Following the Dart naming convention, we'll use camel case for the function name, capitalizing each word except the first. Here's the code:
1String getFullName(String firstName, String lastName) {
2 return '$firstName $lastName';
3}
In the above code, we declare the return type as String and define two parameters: firstName and lastName. Within the function body, we use the return keyword to specify the concatenated string, using string interpolation ($) to insert the parameter values.
Using the Function
To use our getFullName function, we can call it with the desired arguments. For example, we can print the result using the print function. Here's an example:
1void main() { 2 String fullName = getFullName('John', 'Doe'); 3 print(fullName); // Output: John Doe 4}
In the above code, we invoke the getFullName function with the arguments 'John' and 'Doe'. The returned value is stored in the fullName variable, which we then print to the console.
String Formatting for Better Readability
While the previous example achieved the desired result, Dart provides string formatting options that can make our code more readable. Instead of concatenating strings with the + operator, we can utilize string interpolation directly within the return statement. Here's an improved version of the getFullName function:
1String getFullName(String firstName, String lastName) {
2 return '$firstName $lastName';
3}
In this updated code, we use the $ symbol before the variable names within the string. Dart automatically replaces these placeholders with the corresponding values, resulting in the desired string concatenation.
Understanding Void Functions
Not all functions need to return a value. Sometimes, a function's purpose is to perform certain actions without generating an output. In Dart, such functions are referred to as "void" functions. Although specifying void explicitly is not required, it is considered good practice to indicate that the function does not return a value. Here's an example:
1void printName(String name) {
2 print('Hello, $name!');
3}
In the above code, we define a void function called printName, which takes a single parameter name. The function's body consists of a single line that prints a greeting to the console.
Named Arguments vs Positional Arguments
When working with functions in Flutter, you have two options for passing arguments: named arguments and positional arguments. Understanding the differences between these two approaches can greatly simplify your function calls and make your code more readable.
Positional Arguments: Traditional and Sequential
Positional arguments are the most straightforward way of passing arguments to a function. With positional arguments, the order in which you pass the arguments is crucial. The function's parameters are mapped to the arguments based on their position. Let's see an example:
1void greet(String name, String message) {
2 print('$name says $message');
3}
4
5void main() {
6 greet('John', 'Hello'); // Positional arguments
7}
In this example, the function greet takes two positional arguments: name and message. When we call the greet function, we provide the arguments in the same order as the parameters: 'John' is assigned to name, and 'Hello' is assigned to message. This traditional approach works well when you have a small number of arguments, and the order of the arguments is easy to remember.
Named Arguments: Flexibility and Clarity
Named arguments, on the other hand, provide more flexibility and clarity in your function calls. Instead of relying on the order of the arguments, you explicitly specify the name of the parameter you want to assign a value to. Take a look at the following example:
1void greet({String name, String message}) {
2 print('$name says $message');
3}
4
5void main() {
6 greet(name: 'John', message: 'Hello'); // Named arguments
7}
In this modified example, we use curly braces {} around the function parameters to define them as named arguments. Now, when we call the greet function, we explicitly state the names of the parameters and assign the corresponding values. This approach offers several advantages:
- Improved Readability: By explicitly naming the arguments, the function call becomes more self-explanatory. It's easier for other developers (including your future self) to understand the purpose of each argument.
- Flexibility in Argument Order: With named arguments, you can pass the arguments in any order you prefer. You're not constrained by the position of the parameters anymore. This can be particularly useful when dealing with functions that have a large number of optional arguments.
- Default Values: Named arguments can have default values, allowing you to make certain arguments optional. If no value is provided for an argument, it will fall back to its default value. Here's an example:
1void greet({String name = 'Anonymous', String message = 'Hi'}) {
2 print('$name says $message');
3}
4
5void main() {
6 greet(); // No arguments provided, defaults will be used
7 greet(name: 'John'); // Only name argument provided
8}
In this updated example, we've assigned default values to the name and message arguments. If no arguments are provided, the function will use the default values. You can still override the default values by explicitly providing arguments when calling the function.
Arrow Functions
Dart provides a concise syntax called arrow functions, also known as fat arrow functions, for defining functions with a single expression. This syntax is particularly useful for short and simple functions.
The syntax for arrow functions in Dart is straightforward. Instead of using the traditional function declaration syntax, arrow functions use a concise form with the => arrow operator.
Here's an example:
1void greet(String name) => print('Hello, $name!');
In the code above, we define a function named greet using the arrow function syntax. The function takes a String parameter named name and prints a personalized greeting message. To invoke this function, we can use the same approach as before:
1greet('Bob');
This will output "Hello, Bob!" to the console.
Limitations of Arrow Functions
While arrow functions offer convenience for certain scenarios, it's important to be aware of their limitations:
- Arrow functions can only contain a single expression. If you need multiple statements or more complex logic, you should use a regular function.
- Arrow functions cannot have a function body with curly braces
{}. They can only consist of a single expression that is directly returned.
Anonymous Functions
In Dart, you can also define anonymous functions, which are functions without a name. Anonymous functions are commonly used as callbacks or to create higher-order functions. Here's an example:
1void Function(String) printMessage = (String message) { 2 print(message); 3};
In the code above, we define an anonymous function and assign it to the variable printMessage. The anonymous function takes a String parameter named message and prints the provided message. To invoke the anonymous function, we can use the variable name followed by parentheses:
1printMessage('Hello, world!');
This will output "Hello, world!" to the console.
Conclusion
In this article, we've taken the first steps into the world of Dart programming for Flutter beginners. We started by setting up a Flutter project on our local machine and selecting a device to run our app. Then, we dived into the basics of Dart programming, including keywords, data types, constants, and variables.
We explored how keywords act as reserved words in Dart, allowing us to communicate our intentions to the programming language.
Next, we delved into data types, which help us categorize information and define the nature of the data we work with. We covered data types such as int for integers, String for textual data, List for collections, and Set for unique collections. Through examples, we learned how to assign values to variables of different data types.
Constants, as immutable values, were another important concept we explored. We differentiated between compile-time and runtime constants and saw how to declare and use them in our code.
We also learned about variables and their flexibility in holding values that can change during program execution.
To summarize the article, we discussed the differences between named arguments and positional arguments in function calls. We saw how positional arguments rely on the order of arguments, while named arguments offer flexibility, improved readability, and the ability to provide default values.
By mastering these concepts, you're well on your way to becoming proficient in Dart programming for Flutter development.
Happy coding, and stay tuned for more exciting lessons in Dart programming for Flutter beginners!
