Home Flutter Books Dart Apprentice

3
Types & Operations Written by Jonathan Sande & Matt Galloway

Life is full of variety, and that variety expresses itself in different types. What type of toothpaste do you use? Spearmint? Cinnamon? What’s your blood type? A? B? O+? What type of ice cream do you like? Vanilla? Strawberry? Praline pecan fudge swirl? Having names for all of these different things helps you talk intelligently about them. It also helps you to recognize when something is out of place. After all, no one brushes their teeth with praline pecan fudge swirl. Though it does sound kind of nice.

Programming types are just as useful as real life types. They help you to categorize all the different kinds of data you use in your code.

In Chapter 2, you learned how to name data using variables and also got a brief introduction to Dart data types. In this chapter, you’ll learn even more about types and what you can do with them, with a particular focus on strings, which are used to represent text.

Data types in Dart

In Dart, a type is a way to tell the compiler how you plan to use some data. By this point in this book, you’ve already met the following types:

  • int
  • double
  • num
  • dynamic
  • String

The last one in that list, String, is the type used for text like 'Hello, Dart!'.

Just as you don’t brush your teeth with ice cream, Dart types keep you from trying to do silly things like multiplying text or removing whitespace from a number.

Dart has even more built-in types than just the ones listed above. The basic ones, such as int, double, and num will serve you adequately in a great variety of programming scenarios, but when working on projects with specific needs, it will be convenient to create custom types instead. A weather app, for example, may need a Weather type, while a social media app may need a User type. You’ll learn how to create your own types in Chapter 4 and Chapter 8.

As you learned in Chapter 2, the root of all types is the Object type. This type defines a few core operations, such as testing for equality and describing the object in text. Every other type in Dart is a subtype of Object, and as a subtype, shares Object’s basic functionality.

Type inference

In the previous chapter, you also got a sneak peak at type inference in Dart, but you’ll take some time to look at it in a little more depth now.

Annotating variables explicitly

It’s fine to always explicitly add the type annotation when you declare a variable. This means writing the data type before the variable name.

int myInteger = 10;
double myDouble = 3.14;

Creating constant variables

Declaring variables as in the example above makes them mutable. If you want to make them immutable, but still keep the type annotation, you can add const or final in front.

const int myInteger = 10;
const double myDouble = 3.14;
final int myInteger = 10;
final double myDouble = 3.14;

Letting the compiler infer the type

While it’s permissible to include the type annotation as in the example above, it’s redundant. You’re smart enough to know that 10 is an int and 3.14 is a double, and it turns out the Dart compiler can deduce this as well. The compiler doesn’t need you to explicitly tell it the type every time — it can figure the type out on its own through a process called type inference. Not all programming languages have type inference, but Dart does — and it’s a key component behind Dart’s power as a language.

const myInteger = 10;
const myDouble = 3.14;

Checking the inferred type in VS Code

Sometimes, it can be useful to check the inferred type of a variable or constant. You can do this in VS Code by hovering your mouse pointer over the variable name. VS Code will display a popover like this:

Checking the type at runtime

Your code can’t hover a mouse pointer over a variable to check the type, but Dart does have a programmatic way of doing the same thing: the is keyword:

num myNumber = 3.14;
print(myNumber is double);
print(myNumber is int);
true
false
print(myNumber.runtimeType);

Type conversion

Sometimes, you’ll have data in one type, but need to convert it to another. The naïve way to attempt this would be like so:

var integer = 100;
var decimal = 12.5;
integer = decimal;
A value of type 'double' can't be assigned to a variable of type 'int'.
integer = decimal.toInt();

Operators with mixed types

So far, you’ve only seen operators acting independently on integers or doubles. But what if you have an integer that you want to multiply with a double?

const hourlyRate = 19.5;
const hoursWorked = 10;
const totalCost = hourlyRate * hoursWorked;
const totalCost = (hourlyRate * hoursWorked).toInt();
Const variables must be initialized with a constant value.
final totalCost = (hourlyRate * hoursWorked).toInt();

Ensuring a certain type

Sometimes you want to define a constant or variable and ensure it remains a certain type, even though what you’re assigning to it is of a different type. You saw earlier how you can convert from one type to another. For example, consider the following:

const wantADouble = 3;
final actuallyDouble = 3.toDouble();
const double actuallyDouble = 3;
const wantADouble = 3.0;

Casting down

At other times, you may have a variable of some general supertype, but you need functionality that is only available in a subtype. If you’re sure that the value of the variable actually is the subtype you need, then you can use the as keyword to change the type. This is known as type casting.

num someNumber = 3;
print(someNumber.isEven);
The getter 'isEven' isn't defined for the type 'num'.
final someInt = someNumber as int;
print(someInt.isEven);
num someNumber = 3;
final someDouble = someNumber as double;
_CastError (type 'int' is not a subtype of type 'double' in type cast)
final someDouble = someNumber.toDouble();

Mini-exercises

  1. Create a constant called age1 and set it equal to 42. Create another constant called age2 and set it equal to 21. Check that the type for both constants has been inferred correctly as int by hovering your mouse pointer over the variable names in VS Code.
  2. Create a constant called average and set it equal to the average of age1 and age2 using the operation (age1 + age2) / 2. Hover your mouse pointer over average to check the type. Then check the result of average. Why is it a double if the components are all int?

Strings

Numbers are essential in programming, but they aren’t the only type of data you need to work with in your apps. Text is also an extremely common data type, representing things such as people’s names, their addresses, or even the complete text of a book. All of these are examples of text that an app might have to handle.

How computers represent strings

Computers think of strings as a collection of individual characters. Numbers are the language of CPUs, and all code, in every programming language, can be reduced to raw numbers. Strings are no different.

Unicode

In isolation, a computer is free to choose whatever character set mapping it likes. If the computer wants the letter a to equal the number 10, then so be it. But when computers start talking to each other, they need to use a common character set.

Working with strings in Dart

Dart, like any good programming language, can work directly with strings. It does so through the String data type. In this section, you’ll learn about this data type and how to work with it.

Strings and characters

You already worked with Dart strings back in Chapter 1 where you printed the contents of a string:

print('Hello, Dart!');
var greeting = 'Hello, Dart!';
print(greeting);
var greeting = 'Hello, Dart!';
greeting = 'Hello, Flutter!';

Getting characters

Note: The code examples below contain emoji characters that may be difficult to input on your keyboard. You can find all of them to conveniently copy-and-paste by opening starter/bin/starter.dart in the Chapter 3 supplemental materials for this book.

const letter = 'a';
var salutation = 'Hello!';
print(salutation.codeUnits);
[72, 101, 108, 108, 111, 33]
const dart = '🎯';
print(dart.codeUnits);
// [55356, 57263]
const dart = '🎯';
print(dart.runes);
// (127919)

Unicode grapheme clusters

Unfortunately, language is messy and so is Unicode. Have a look at this example:

const flag = '🇲🇳';
print(flag.runes);
// (127474, 127475)   
const family = '👨‍👩‍👧‍👦';
print(family.runes);
// (128104, 8205, 128105, 8205, 128103, 8205, 128102)           
const family = '👨‍👩‍👧‍👦';

family.length;           // 11
family.codeUnits.length; // 11
family.runes.length;     // 7            

Adding the characters package

This is a good opportunity to try out your first Pub package. In the root folder of your project, open pubspec.yaml.

dependencies:
  characters: ^1.0.0
pub get
import 'package:characters/characters.dart';
const family = '👨‍👩‍👧‍👦';
family.characters.length; // 1          

Single-quotes vs. double-quotes

Dart allows you to use either single-quotes or double-quotes for string literals. Both of these are fine:

'I like cats'
"I like cats"
"my cat's food"
'my cat\'s food'

Concatenation

You can do much more than create simple strings. Sometimes you need to manipulate a string, and one common way to do so is to combine it with another string. This is called concatenation…with no relation to the aforementioned felines.

var message = 'Hello' + ' my name is ';
const name = 'Ray';
message += name;
// 'Hello my name is Ray'
final message = StringBuffer();
message.write('Hello');
message.write(' my name is ');
message.write('Ray');
message.toString();
// "Hello my name is Ray"

Interpolation

You can also build up a string by using interpolation, which is a special Dart syntax that lets you build a string in a manner that’s easy for other people reading your code to understand:

const name = 'Ray';
const introduction = 'Hello my name is $name';
// 'Hello my name is Ray'
const oneThird = 1 / 3;
const sentence = 'One third is $oneThird.';
One third is 0.3333333333333333.
final sentence = 'One third is ${oneThird.toStringAsFixed(3)}.';
One third is 0.333.

Multi-line strings

Dart has a neat way to express strings that contain multiple lines, which can be rather useful when you need to use very long strings in your code.

const bigString = '''
You can have a string
that contains multiple
lines
by
doing this.''';
print(bigString);
You can have a string
that contains multiple
lines
by
doing this.
const oneLine = 'This is only '
    'a single '
    'line '
    'at runtime.';
const oneLine = 'This is only ' +
    'a single ' +
    'line ' +
    'at runtime.';
This is only a single line at runtime.
const twoLines = 'This is\ntwo lines.';
This is
two lines.
const rawString = r'My name \n is $name.';
My name \n is $name.

Inserting characters from their codes

Similar to the way you can insert a newline character into a string using the \n escape sequence, you can also add Unicode characters if you know their codes. Take the following example:

print('I \u2764 Dart\u0021');

print('I love \u{1F3AF}');

Mini-exercises

  1. Create a string constant called firstName and initialize it to your first name. Also create a string constant called lastName and initialize it to your last name.
  2. Create a string constant called fullName by adding the firstName and lastName constants together, separated by a space.
  3. Using interpolation, create a string constant called myDetails that uses the fullName constant to create a string introducing yourself. For example, Ray Wenderlich’s string would read: “Hello, my name is Ray Wenderlich.”

Object and dynamic types

Dart grew out of the desire to solve some problems inherent in JavaScript. JavaScript is a dynamically-typed language. Dynamic means that something can change, and for JavaScript that means the types can change at runtime.

var myVariable = 42;
myVariable = "hello";
var answer = myVariable * 3; // runtime error
var myVariable = 42;
myVariable = 'hello'; // compile-time error
dynamic myVariable = 42;
myVariable = 'hello'; // OK
var myVariable; // defaults to dynamic
myVariable = 42;      // OK
myVariable = 'hello'; // OK
Object myVariable = 42;
myVariable = 'hello'; // OK

Challenges

Before moving on, here are some challenges to test your knowledge of types and operations. It’s best if you try to solve them yourself, but solutions are available if you get stuck.

Challenge 1: Teacher’s grading

You’re a teacher, and in your class, attendance is worth 20% of the grade, the homework is worth 30% and the exam is worth 50%. Your student got 90 points for her attendance, 80 points for her homework and 94 points on her exam. Calculate her grade as an integer percentage rounded down.

Challenge 2: Same same, but different

This string has two flags that look the same. But they aren’t! One of them is the flag of Chad and the other is the flag of Romania.

const twoCountries = '🇹🇩🇷🇴';    

Challenge 3: How many?

Given the following string:

const vote = 'Thumbs up! 👍🏿';  

Challenge 4: Find the error

What is wrong with the following code?

const name = 'Ray';
name += ' Wenderlich';

Challenge 5: What is the type of value?

const value = 10 / 2;

Challenge 6: What is the value of summary?

What is the value of the constant named summary?

const number = 10;
const multiplier = 5;
final summary = '$number \u00D7 $multiplier = ${number * multiplier}';

Key points

  • Type conversion allows you to convert values of one type into another.
  • When doing operations with basic arithmetic operators (+, -, *, /) and mixed types, the result will be a double.
  • Type inference allows you to omit the type when Dart can figure it out.
  • Unicode is the standard representation for mapping characters to numbers.
  • Dart uses UTF-16 values known as code units to encode Unicode strings.
  • A single mapping in Unicode is called a code point, which is known as a rune in Dart.
  • User-perceived characters may be composed of one or more code points and are called Unicode grapheme characters.
  • You can combine strings by using the addition operator.
  • You can make multi-line strings using three single-quotes or double quotes.
  • You can use string interpolation to build a string in-place.
  • Dart is an optionally-typed language. While it’s preferable to choose statically-typed variables, you may write Dart code in a dynamically-typed way by explicitly adding the dynamic type annotation in front of variables.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2020 Razeware LLC

You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.