[go: up one dir, main page]

0% found this document useful (0 votes)
10 views33 pages

1BASIC

Download as pdf or txt
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 33

Basics of Dart & Flutter:

Part I
Shan-Hung Wu
CS, NTHU
Let’s Roll Dice
• Today’s topics:
• Dart language
• Flutter app overview
• Stateless vs stateful
widgets

2
Evolution of Dart
• Dart 1.0 (Nov 2013)
• Initially introduced as an alternative to JavaScript
• Pub package manager
• Dart 2.0 (Aug 2018)
• Strong type system
• Sound null safety
• Supports Flutter!
• Dart 3.0 (May 2023)
• Null Safety by default
• Unified dev workflow across different platforms

3
Dart Features
• Platform-independent (Windows, Mac, Linux, and Web)
• Just-In-Time (JIT) compilation in development
• Runs in VM; offering hot reload
• Ahead-Of-Time (AOT) compilation in production
• Native code or JavaScript; high performance
• Auto memory management with Garbage Collection (GC)
• Function as first-class citizen
• Sound null safety
• Object-oriented
• Supports encapsulation, inheritance, polymorphism, interface,
extension, etc.
• Async, await, and concurrency (Isolates)
• Foreign Function Interface (FFI)
• Free and open-source
4
Warm up:

What’s the difference between


statements & expressions?

5
Terms Revisited
• Statement: command that ends with “;”
• print('Hello world!');
• Expression: command evaluated to a single value
• 'Hello ' + 'world!'
• Keyword: word reserved for compiler
• int, String, if, for, static, final, etc.
• Identifier: name of variable, function, class, etc.
• int age;
• Literal: value directly written in source code
• double pi = 3.14;

6
Hello World!

• Let’s run it on DartPad (Dart à JavaScript):


7
Variables void main() {
String firstName = 'John';
var lastName = 'Smith';
String address = 'USA';
int age = 20;
double height = 5.9;
bool isMarried = false;

print('Name is $firstName $lastName');


print('Address is $address');
print('Age is $age');
print('Height is $height');
print('Married status is $isMarried');
}

• Use var with type inference


• Use final or const to declare fixed values:
const pi = 3.14159;
8
User Input
import 'dart:io';

void main() {
print('Enter your name:');
String? name = stdin.readLineSync();
if (name != null) {
print('Hello ${name}');
}
}
• Package dart:io provided by Dart
• Only runs in command line via dart run
• String? means “name could be null”
• We will discuss this feature later

9
Built-in Data Types
bool • All types are object types
int • Extending Object class
double • Passed “by reference”, not “by value”
num // int or double
runs // String's Unicode
String
List
Set
Map
Function
void main() {
var bob = ...;
changeName(bob);
}
void changeName(Map person) { ... }
10
Immutable Types
• bool, int, double and String types are immutable
• 'Hello ' + 'world!' creates new String object

• In effect, these types behave like value types in other


languages

• Why not just use value types?


• Object types can have methods:
const pi = 3.14159;
print('Pi is ${pi.toStringAsFixed(2)}');

11
Memory Management

• In Dart, the garbage collector periodically finds


“unused” objects in heap and frees their memory
• No need to call free() as in C
• What “unused” objects?
• Those not referenced by variables in stack
12
Lists, Maps, and Generics
• List (array): List<int> myList = [1, 2, 3];
myList.add(4); // Adds 4 to the end
myList[0] = 10;
myList.removeAt(0);
print(myList[0]); // Prints what?

• Map: Map<String, String> myMap = {


'lang': 'Dart',
'client': 'Flutter',
'server': 'Firebase'
};
myMap['store'] = 'Google Play';
myMap['lang'] = 'Dart language';
myMap.remove('server');
print(myMap['lang']); // Prints what?

13
Iterating Lists & Maps
• List: for (var e in myList) {
print(e);
}

• Map: for (var e in myMap.entries) {


print('${e.key}: ${e.value}');
}

for (var k in myMap.keys) {


print(k);
}

for (var v in myMap.values) {


print(v);
}
14
Functions
void add(num num1, num num2){
// num1 and num2 are parameters
num sum = num1 + num2;
print('The sum is $sum');
}

void main(){
// 10 and 20 are arguments
add(10, 20);
}

15
Parameters & Default Values
// Optional positional parameters
void sayMessage(String message, [String? author]) {
print("$message — ${author ?? 'Anonymous'}");
}

// Named parameters & default values


void setDimensions({int width = 10, int height = 10}) {
print("Width: $width, Height: $height");
}

void main() {
sayMessage('Hello, Dart!');
setDimensions(height: 20, width: 30);
}

• Named param is optional by default


• Use required to make it mandatory
16
Generics in Functions
// Returns the first element of any list
T firstElement<T>(List<T> list) {
if (list.isEmpty) {
throw Exception('The list is empty');
}
return list.first;
}

// Returns the larger argument


T maxValue<T extends Comparable>(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}

• Helps ensure type safety

17
Multiple Return Values
final json = <String, dynamic>{ // "dynamic" means "any type"
'name': 'Dash',
'age': 10,
'color': 'blue',
};

// Returns multiple values in a record:


(String, int) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['age'] as int);
}

// Destructures using a record pattern:


var (name, age) = userInfo(json);

18
Arrow Functions
• If a function consists of just one line of code that
returns a value, it has simpler syntax:
// Normal function
int add(int a, int b) { return a + b; }

// Arrow function
int add(int a, int b) => a + b;

• Don’t use arrow if function doesn’t return anything

19
Functions as First-Class Citizens
// Basic operations
int add(int a, int b) => a + b;
int multiply(int a, int b) => a * b;

// Higher-order function that accepts functions as params


void printOPResult(int a, int b, Function(int, int) op) {
var result = op(a, b);
print('Result: $result');
}

void main() {
// Assigning functions to variables
Function(int, int) op = add;
printOPResult(4, 2, op);
op = multiply;
printOPResult(4, 2, op); • Functions can be assigned to
} variables and passed around

20
Anonymous Functions
• E.g., list manipulation: • E.g., map manipulation:
for (int e in myList) { for (var k in myMap.keys) {
print(e); print(k);
} }

myList.forEach((e) { myMap.keys.forEach((k) {
print(e); print(k);
}); });

// Multiply each el by 2 var entries = myMap.map(


var myList2 = myList.map( (k, v) =>
(e) => e * 2 MapEntry(..., ...)
).toList(); );
var myMap2 = Map<..., ...>
.fromEntries(entries);

21
Variables are Block-scoped

void outerFunction() {
var outerStr = "I'm outside!";

void innerFunction() {
var innerStr = "I'm inside!";
print(outerStr); // Accessible in nested func!
}

// print(innerStr); // Error: undefined 'innerStr'


}

• Lexical scoping: variable’s scope is determined at


compile time, not at runtime (dynamic scoping)
22
Closures & Captured Variables
Function makeAdder(int base) {
return (int i) => base + i; // return func
}

void main() {
var addFrom2 = makeAdder(2);
var addFrom3 = makeAdder(3);
print(addFrom2(10)); // Output: 12
print(addFrom3(10)); // Output: 13
}

• Closures are functions that capture and retain variables


from their lexical scope
• Captured variables stay in memory even after the
outer function executes
23
List<Function> createCounters() {
var counters = <Function>[];
No Bad for (var i = 0; i < 3; i++) {
counters.add(() => i);
Closures in }
return counters;
Loops* }

void main() {
var counters = createCounters();
for (var counter in counters) {
print(counter()); // Prints 0, 1, 2
}
}

• JavaScript prints “3, 3, 3” (bad closure)


• Dart prints “0, 1, 2”
• Each closure in loop captures its own copy of i

24
Classes & Custom Types
class Person {
// Instance variables
String name; • Class as the blueprint (type)
int age;
of your custom objects
// Constructor
Person({required this.name, required this.age});

// Method
void displayInfo() {
print('Name: $name, Age: $age');
}
}
void main() {
var alice = Person('name': 'Alice', age': 20); // Instance
alice.displayInfo(); // Output: Name: Alice, Age: 20
var bob = Person('name': 'Bob', age': 21); // Instance
bob.displayInfo(); // Output: Name: Bob, Age: 21

assert(alice is Person); // No AssertionError thrown


} 25
Named Constructors
class Person {
String name;
int age;

// Constructor
Person({required this.name, required this.age});

// Named constructor
Person.fromMap(Map<String, dynamic> data)
: name = data['name'],
age = data['age'];
...
}

void main() {
var map = {'name': 'Dave', 'age': 25};
var person = Person.fromMap(map);
person.displayInfo();
}

26
Inheritance
(1/3) class Student extends Person {
String university;

Student({
required super.name,
required super.age,
required this.university
});

Student.fromMap(Map<String, dynamic> data)


: university = data['university'],
super.fromMap(data);

@override
void displayInfo() {
super.displayInfo();
print('University: $university');
}

void study() { ... } // custom method


}
27
Inheritance
(2/3) class Employee extends Person {
String company;

Student({
required super.name,
required super.age,
required this.company
});

Student.fromMap(Map<String, dynamic> data)


: company = data['company'],
super.fromMap(data);

@override
void displayInfo() {
super.displayInfo();
print(Company: $company');
}

void work() { ... } // custom method


}
28
Inheritance (3/3)

void main() {
Person alice = Student(..., university: ...);
alice.displayInfo(); // with "University: ..."

assert(alice is Person);
assert(alice is Student);
assert(alice is! Employee);

print(alice.runtimeType); // Output: 'Student'

var studentAlice = alice as Student;


studentAlice.study();
}

29
Entering Flutter
• Project structure
• lib/main.dart
• MaterialApp widget
• build() in widgets
• Defines widget hierarchy
• Button and rollDice()
callback in
dice_roller.dart
• Random() and dynamic
image file name

30
Wait, the App Doesn’t Work
• When button pressed, dice image doesn’t change
as expected
• The rollDice() callback is indeed invoked
• We can verify this by inserting
print(currentDiceRoll)into its body
• What's wrong?

31
Stateless vs. Stateful Widgets
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});

@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0; // Initial counter value

void _incrementCounter() {
setState(() {
_counter++; • setState() tells Flutter to
});
} rerun build() and updates UI
@override
Widget build(BuildContext context) {
return Text('Counter: $_counter');
}
}
32
References
• Introduction to Dart

33

You might also like