Busy Developer's Guide to Flutter

ted@tedneward.com | Blog: http://blogs.tedneward.com | Twitter: tedneward | Github: tedneward | LinkedIn: tedneward

Objectives

What are we doing today?

Dart Overview

In a nutshell...

Dart Overview

In a nutshell...

Dart Overview

In a nutshell...

Dart Overview

More tidbits

Dart

A Dart language tour/overview:

Flutter Overview

What is this thing?

Flutter Overview

Flutter is...

"a mobile app SDK for building high-performance, high-fidelity apps for iOS and Android, from a single codebase."

https://fltter.io/technical-overview/

Flutter Overview

Flutter is...

Flutter Overview

Flutter's paradigms

Getting Started

Early steps with Flutter

Getting Started

Installing Flutter

Getting Started

Hello, Flutter

Getting Started

Hello, Flutter

import 'package:flutter/material.dart';

void main() {
  runApp(
    new Center(
      child: new Text(
        'Hello, world!',
        textDirection: TextDirection.ltr,
      ),
    ),
  );
}

Getting Started

Default/scaffolded app

Getting Started

Generating Flutter app

Getting Started

Hot reloading

Project structure

What have we got here?

Project structure

Manifest and main

Project structure

Platforms

Widgets

How Flutter maps objects to pixels and behavior

Widgets

Flutter's UI is built out of Widgets

Widgets

Widgets form a deep tree

Widgets

Simplest hello_world app

import 'package:flutter/material.dart';

void main() {
  runApp(
    new Center(
      child: new Text(
        'Hello, world!',
        textDirection: TextDirection.ltr,
      ),
    ),
  );
}

Widgets

Two styles of Widgets

Widgets

Common widgets

Widgets

Common widgets

Widgets

Common widgets

Widgets

Common widgets

Widgets

Usually, to build a UI, you must build custom widgets

Widgets

Widgets are fundamentally of two types

Widgets

StatefulWidgets

Widgets

Managing state

Widgets

Why separate objects (state and widget)?

Widgets

How do we capture changes across components?

UI Navigation

Using more than one screen

UI Navigation

In Flutter, "screens" are widgets

Movement between them are called "routes"

UI Navigation

Page (widget) navigation

UI Navigation

The first page

class FirstRoute extends StatelessWidget {
  @override Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('First Route'),),
      body: Center(
        child: RaisedButton(
          child: Text('Open route'),
          onPressed: () {
            Navigator.push(context, 
              MaterialPageRoute(builder: (context) => SecondRoute()),
            );
          },
        ),
      ),
    );
  }
}

UI Navigation

Routes can be named

UI Navigation

Same app, using named routes

void main() {
  runApp(MaterialApp(
    title: 'Named Routes Demo',
    // Start the app with the "/" named route. In our case, the app will start
    // on the FirstScreen Widget
    initialRoute: '/',
    routes: {
      // When we navigate to the "/" route, build the FirstScreen Widget
      '/': (context) => FirstScreen(),
      // When we navigate to the "/second" route, build the SecondScreen Widget
      '/second': (context) => SecondScreen(),
    },
  ));
}

UI Navigation

In FirstScreen/RaisedButton:

onPressed: () {
            // Navigate to the second screen using a named route
            Navigator.pushNamed(context, '/second');
          },

In SecondScreen/RaisedButton:

onPressed: () {
            // Navigate to the second screen using a named route
            Navigator.pop();
          },

UI Navigation

Data can be passed across routes

Flutter Networking

Getting data from device to server and back again

Flutter Networking

Serializing data to/from JSON

Flutter Networking

Inline serialization

// Given JSON of:
// {
//  "name": "John Smith",
//  "email": "john@example.com"
//}
// ... in jsonString ...
//
Map<String, dynamic> user = jsonDecode(jsonString);

print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');

Flutter Networking

To/from model object classes

class User {
  final String name;
  final String email;

  User(this.name, this.email);

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        email = json['email'];

  Map<String, dynamic> toJson() =>
    {
      'name': name,
      'email': email,
    };
}

Flutter Networking

HTTP communication: http

Flutter Networking

Simple one-shot HTTP req/resp

import 'package:http/http.dart' as http;

var url = 'http://example.com/whatsit/create';
var response = await http.post(url, body: {'name': 'doodle', 'color': 'blue'});
print('Response status: ${response.statusCode}');
print('Response body: ${response.body}');

print(await http.read('http://example.com/foobar.txt'));

Flutter Networking

For frequent access to the same server...

var client = new http.Client();
try {
  var uriResponse = await client.post('http://example.com/whatsit/create',
      body: {'name': 'doodle', 'color': 'blue'});
  print(await client.get(uriResponse.bodyFields['uri']));
} finally {
  client.close();
}

Flutter Networking

To customize HTTP session...

class UserAgentClient extends http.BaseClient {
  final String userAgent;
  final http.Client _inner;

  UserAgentClient(this.userAgent, this._inner);

  Future<StreamedResponse> send(BaseRequest request) {
    request.headers['user-agent'] = userAgent;
    return _inner.send(request);
  }
}

Testing Flutter code

Making sure it's all working correctly

Testing

Testing Flutter code

Testing

Example test script: widget_test.dart

// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:basic/main.dart';

void main() {
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp());

    // Verify that our counter starts at 0.
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // Tap the '+' icon and trigger a frame.
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Verify that our counter has incremented.
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}

Testing

Flutter testing comes in three forms:

Testing

To write unit tests:

Testing

Tests take place in test calls

import 'package:test/test.dart';
import 'package:counter_app/counter.dart';

void main() {
  test('Counter value should be incremented', () {
    final counter = Counter();

    counter.increment();

    expect(counter.value, 1);
  });
}

Testing

Group tests together using group()

void main() {
  group('Counter', () {
    test('value should start at 0', () {
      expect(Counter().value, 0);
    });
    test('value should be incremented', () {
      final counter = Counter();
      counter.increment();
      expect(counter.value, 1);
    });
    test('value should be decremented', () {
      final counter = Counter();
      counter.decrement();
      expect(counter.value, -1);
    });
  });
}

Testing

Widget tests

Testing

Widget testing requires a bit more scaffolding

Testing

void main() {
  testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
    await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));

    // Create our Finders
    final titleFinder = find.text('T');
    final messageFinder = find.text('M');
    expect(titleFinder, findsOneWidget);
    expect(messageFinder, findsOneWidget);
  });
}

Dart Resources

Where to find out more

Dart Resources

Official

Dart Resources

Official

Flutter Resources

Where to go for more

Flutter Resources

Official Resources

Flutter Resources

Official Resources

Credentials

Who is this guy?