presented by Jack Frosch
In March 2020, we lost our meeting facility due to coronavirus concerns
Hoped we'd get back to normal in June
Online is our new normal for 2020
In my first career,
I drove these...
In my second career,
I mostly drive these...
My other Meetup
1st Tuesday of the month
Date | Topic |
---|---|
September 15th | Widget layouts, themes, fonts, and images |
October 20th | Menus, routing, dialogs, drawers, and more |
November 17th | State management & asynchronous programming |
December 15th | Flutter for Web and Desktop & adaptive apps |
The platform languages and approaches are different
Multiple teams likely needed
2X+ development and maintenance costs
Drift and deviation
If U.S. is your only market, iPhone dominates
Worldwide, Android dominates
Customers will not change their phone to use your app
Write Once
Write Twice
Java / Kotlin Android
Swift / Objective C iOS
React Native
Flutter
Ionic
Learn Once
Learn Twice
Java / Kotlin Android
Swift / Objective C iOS
Ionic
Flutter
React Native
Must Write Your Own
Native Widgets
Included
Java / Kotlin Android
Swift / Objective C iOS
React Native
Ionic
Flutter
// main() is the entry point to a Dart application
// It takes an optional argument of type List<String>
void main() {
for (int i = 0; i < 5; i++) {
print('hello ${i + 1}');
}
}
// Outputs
hello 1
hello 2
hello 3
hello 4
hello 5
class Dog {
String name;
String breed;
Dog(String name, String breed) {
this.name = name;
this.breed = breed;
}
String toString() {
return 'name: $name, breed: $breed';
}
}
void main() {
print(new Dog('Killer', 'Chihuahua'));
}
// outputs: name: Killer, breed: Chihuahua
class Dog {
String name;
String breed;
Dog(this.name, this.breed);
String toString() {
return 'name: $name, breed: $breed';
}
}
void main() {
print(new Dog('Killer', 'Chihuahua'));
print(Dog('Tiny', 'Great Dane')); // the new operator is optional
}
// outputs:
// name: Killer, breed: Chihuahua
// name: Tiny, breed: Great Dane
class Dog {
String name;
String breed;
Dog({this.name, this.breed});
String toString() {
return 'name: $name, breed: $breed';
}
}
void main() {
var dog = Dog(name: 'Tiny', breed: 'Great Dane');
print(dog); // the new operator is optional
}
// outputs:
// name: Tiny, breed: Great Dane
// Top-level function.
int square(int a) { return a * a; }
class SquareUtil {
dynamic squareAnything(dynamic val) {
bool isNumber = val is num // this is how we can do runtime type checking
return isNumber ? val * val : val + val; // ternary operator supported
}
}
// This is the app entry point.
main() {
dynamic result;
var intVal = 9; // Declare and initialize a variable. Type inferred
result = square(intVal); // Call a function
print("Squaring int value ${intVal} yields ${result}");
var sqr = SquareUtil(); // instantiate an object of type SquareUtil
double doubleVal = 2.0;
result = sqr.squareAnything(doubleVal);
print("Squaring double value ${doubleVal} yields ${result}");
String sVal = 'frou';
result = sqr.squareAnything(sVal);
print("Squaring string value '${sVal}' yields ${result}");
}
A simple example
// Working with Lists
void main() {
var list = [ 3, 4, 5 ]; // type inferred as List<int>
var constList = const [ 6, 7, 8]; // const means fixed at compile-time
print("'Classic' C-style for loop");
for (int i = 0; i < list.length; i++) {
print("list[i] = ${list[i]}");
}
print("\nNewer-style for loop");
for (var value in list) {
print("value = $value");
}
list.add(6);
print("\nAdd 6 to the list: $list");
var newList = [ 2, ...list ];
print("\nNew list using spread operator: $newList");
var newListWithConditional = [
if (newList[0] == 2) 1,
...newList
];
print("\nNew list with conditional entry: $newListWithConditional");
var cubes = newListWithConditional.map((it) {
return it * it * it;
}).toList();
print("Cubes of newListWithConditional: $cubes");
// constList.add(9); // yields error
}
Working with Lists
// Working with Sets
void main() {
var items = { 'A', 'B', 'C'}; // type inferred as Set<String>
print("Items in set: $items");
print('\nAdd another String:');
items.add('D');
print("Items in set after add: $items");
print('\nAdd one or many:');
var newSet = <Object>{}; // element type explicitly Object
newSet.addAll(items);
newSet.add(5);
print("Items in newSet: $newSet");
print('\Remove an item:');
newSet.remove(5);
print("Items in newSet after remove: $newSet");
print('\nTry to add a duplicate:');
bool wasItAdded = newSet.add('B');
print("Item was added? $wasItAdded\nItems in newSet after trying to add duplicate: $newSet");
print('\nMake an immutable set:');
var immutableSet = const { 'X', 'Y', 'Z'};
print("Items in immutableSet: $immutableSet");
// immutableSet.add('Q');
}
Working with Sets
// Working with Maps
void main() {
var map = { 1: 'Chocolate', 2: 'Vanilla', 3: 'Strawberry' }; // type inferred as Map<int, String>
print("Created map = $map");
print('\nAccess individual entry:');
print("Value for key 2: ${map[2]}");
print('\nAccess missing key:');
print("Value for key 99: ${map[99]}");
print('\nAdd a new entry:');
map[4] = 'Moose Tracks';
print("Map after adding entry: $map");
print('\nOutput map using keys and values:');
map.forEach((key, value) {
print("key = $key, value = $value");
});
print('\nOutput map using map entries:');
map.entries.forEach((entry) {
print("key = ${entry.key}, value = ${entry.value}");
});
print('\nMake a new map out of the old:');
var newMap = Map<int, String>();
newMap.addAll(map);
newMap[5] = 'Mint Chocolate';
print("newMap = $newMap, length = ${newMap.length}");
var immutableMap = const { 'A123': 46, 'B456': 78, 'C789': 42 }; // type inferred as Map<String, int>
print("immutableMap = $immutableMap");
// immutableMap.remove('B456');
// final immutableReference = immutableMap;
// immutableReference = { 'A': 65, 'B': 66 };
}
Working with Maps
// Working with classes and functions
// main() is the entry point and a top level function
void main() {
print('Output Person using custom toString');
var person = Person(firstName: 'Fred', lastName: 'Flintstone');
print("Person person = $person");
print('\nOutput Dog:');
var dog = Dog(person, 'Buster');
print("Dog dog = $dog");
print("Dog using properties: name = ${dog.name}, breed = ${dog.breed}, owner = ${dog.owner}");
print('\nOutput Dog created with named constructor:');
var dog2 = Dog.poodle(person, 'Fluffy');
print("Dog using properties: name = ${dog2.name}, breed = ${dog2.breed}, owner = ${dog2.owner}");
print("Is dog a poodle? ${dog2.isPoodle()}");
print("Is dog a pitbull? ${dog2.isPitbull()}");
}
class Pet {
String name;
Pet(this.name);
}
class Dog extends Pet {
Person owner;
String breed;
Dog(Person owner, String name, [String breed]) : super(name) { // breed is optional
this.owner = owner;
this.breed = breed;
}
Dog.poodle(Person owner, String name) : super(name){
this.owner = owner;
this.breed = 'Poodle';
}
bool isPitbull() {
return this.breed == 'Pitbull';
}
bool isPoodle() => this.breed == 'Poodle';
}
class Person {
String firstName;
String middleName;
String lastName;
Person({this.firstName, this.middleName, this.lastName});
// instance method
String toString() {
return middleName == null ? "$firstName $lastName" : "$firstName $middleName $lastName";
}
}
Functions & Classes
//...
class Dog extends Pet {
Person owner;
String breed;
Dog(Person owner, String name, [String breed]) : super(name) { // breed is optional
this.owner = owner;
this.breed = breed;
}
Dog.poodle(Person owner, String name) : super(name){
this.owner = owner;
this.breed = 'Poodle';
}
bool isPitbull() {
return this.breed == 'Pitbull';
}
bool isPoodle() => this.breed == 'Poodle';
}
class Person {
String firstName;
String middleName;
String lastName;
Person({@required this.firstName, this.middleName, @required this.lastName});
// instance method
String toString() {
return middleName == null ? "${firstName} ${lastName}" : "${firstName} ${middleName} ${lastName}";
}
}
Functions & Classes
Intro Video
% cd ~/temp
% flutter create my_app
% cd my_app
% studio .
Dart
Flutter
CodeMagic: https://codemagic.io
Me
Email: jackfrosch@flutterfare.com
Twitter: @jackfrosch, @flutterfare
LinkedIn: jackfrosch