Flutter Fare Reboot

Rebooting our Flutter journey

presented by Jack Frosch

  • I originally launched Flutter Fare in October 2019
  • 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

About me

In my first career,

I drove these...

In my second career,

I mostly drive these...

About Me

My other Meetup

1st Tuesday of the month

About The Group

  • Independent Meetup (not Google sponsored)
  • Our Meetup is for learning about Flutter and Dart
  • Oriented toward Flutter and Dart newbies
  • Previous programming knowledge is a prerequisite
  • Syntax will be relatable to Java, TypeScript/JavaScript, Kotlin

Some of the topics we'll be covering

Dart

Flutter

  • Types and Variables
  • Object-oriented Dart
  • Async Dart, including Streams
  • Interesting Packages
  • Much more!
  • Widgets galore
  • Layouts and Themes
  • Navigation
  • iOS L&F
  • Interacting with servers
  • Responsive, adaptive apps
  • Much more!

2020 Calendar

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

Your contribution

  • 2021 Presenters wanted
  • Backup (co-organizer) needed

Agenda

  • Why Flutter?
  • Getting Started
  • Dart Intro
  • Flutter Intro

Why Flutter?

What is Flutter?

Mobile Application Development

One App, Two Code Bases

iOS

Why Not Code Native Apps?

iOS

  • The platform languages and approaches are different

  • Multiple teams likely needed

  • 2X+ development and maintenance costs

  • Drift and deviation

and

Why Not Code Native Apps?

iOS

  • If U.S. is your only market, iPhone dominates

  • Worldwide, Android dominates

  • Customers will not change their phone to use your app

or

Why Not Code Native Apps?

One App, One Code Base

Ideally, we would write code once and have it work on both major mobile platforms

One App, One Code Base

  • JavaScript
  • Not same as React, but close
  • Use same tool set for mobile & web apps
  • Toolset compiles JS to native app UI widgets
  • Non-UI code runs as JS
  • App is packaged as a native app
  • Some pre-built components, partly adaptive to OS

React Native

One App, One Code Base

  • JavaScript
  • Not compiled to native app code
  • You just build a normal web application
  • Web app is rendered in a web control
  • App is packaged as a native app
  • Loads of pre-built, fully adaptive components

Ionic

One App, One Code Base

  • Uses the Dart language
  • Compiles to native code
  • Does not delegate to mobile OS widgets
  • Pixel-perfect, fast rendering
  • App packaged as native app
  • Tons of prebuilt components, many with iOS L&F

Flutter

One App

Write Once

Write Twice

Java / Kotlin Android

Swift / Objective C iOS

React Native

Flutter

Ionic

Codebase

One App

Learn Once

Learn Twice

Java / Kotlin Android

Swift / Objective C iOS

Ionic

Flutter

React Native

Knowledgebase

One App

Must Write Your Own

Native Widgets

Included

Java / Kotlin Android

Swift / Objective C iOS

React Native

Widget Library

Ionic

Flutter

Getting Started

  • Install Git (not covered)
  • Install Flutter (also installs Dart)
  • Install Editor
    • Recommend Android Studio - Also does iOS
    • Alternative Recommendation:  VS Code

A (very) Brief Intro to Dart

  • Important language concepts
  • A simple program
  • Flow control & Exceptions
  • Built-in Types
  • Lists, Sets, Maps
  • Functions & Classes

Dart Language Concepts

  • Everything is an object of some class type
  • Strongly typed, but supports a dynamic type
  • Generics supported (List<String>)
  • Functions can be defined at top-level, in a class, or nested
  • Variables can be defined at top-level or in a class
  • Code organized into libraries we import to use
  • Visibility of library items is public or private (indicated with _)
  • Variable names start with letter or _, then can have digits
  • Expressions have resulting value vs statements which don't

The DartPad

// 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

main()

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

constructors - positional parameters

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

constructors - with field assignments

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

constructors - named parameters

// 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

Flow Control

  • if and else
  • for loops
  • while and do-while loops
  • break and continue
  • switch and case
  • assert

Dart Types

  • numbers
  • strings
  • booleans
  • lists (also known as arrays)
  • sets
  • maps
  • runes (for expressing Unicode characters in a string)
  • symbols

Lists, Sets, Maps

  • List - index ordered, non-unique collection
  • Set - unordered, unique collection
  • Map - key/value dictionary of entries
// 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

A (very) Brief Intro to Flutter

  • Intro video
  • In Flutter, everything is a widget!
  • Demo - Creating a new app
  • Flutter on DartPad Examples
  • Flutter on the Web Examples

Intro Video

In Flutter, (almost) everything is a widget

Getting started

A note about targeting iOS

  • First - Android Studio works for both iOS and Android!
  • When running an iOS simulator, you need iOS bits
  • When deploying to App Store, deployment must be signed with a certificate provided by Apple
  • You really need access to a Mac
  • Alternative: CodeMagic

Creating a New App

via commandline

% cd ~/temp
% flutter create my_app
% cd my_app
% studio .

Flutter on DartPad Demos

Flutter for the web

Summary

  • Dart is a very approachable language
  • Everything in Flutter is a widget
  • You'll need access to a Mac to do iOS testing and deployment* 
  • It's a great time to start learning Flutter!

Resources

Questions?

Flutter Fare Reboot

By Jack Frosch

Flutter Fare Reboot

A pandemic-friendly re-launch

  • 1,195