Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions feedback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- Maybe talk about Imperative vs Declarative instead of "expressive" vs
declarative? E.g. "UIKit is imperative, Flutter and SwiftUI are declarative.
That means...".
- After introducing Cupertino package, it feels like everything should use Cupertino widgets.
- General writing style: There are a lot of run-on sentences. See if you can split sentences apart in some cases. I've tried to provide a few examples through my edits!
4 changes: 2 additions & 2 deletions public/buttons/instructions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Buttons

Buttons in almost every application that has a user interface play an important role. They allow the user to take action, that's why sometimes we call them CTA, Call to Action, which in fact is a marketing term!
Buttons are found in almost every application and play an important role: they allow the user to take an action. That's why sometimes we refer to them as Calls to Action (CTA), which in fact is a marketing term!

## Buttons in SwiftUI vs Flutter

Expand Down Expand Up @@ -58,7 +58,7 @@ class HomePage extends StatelessWidget {
}
}
```

<!-- CupertinoButton? -->
Note the usage of `TextButton` here. `TextButton` in the Material library of Flutter is the equivalent of `Button` in SwiftUI and `UIButton` in UIKit. Note that Material library in Flutter creates its UI components according to Google's material design. If you want to have iOS-like UI components, as mentioned before, you will have to use the Cupertino library in Flutter. All you'll have to do is to change the `TextButton` to `CupertinoButton` and the `AlertDialog` to `CupertinoAlertDialog`. Yes, it's really that simple in Flutter to change the look and feel of your apps!

## Challenge
Expand Down
1 change: 1 addition & 0 deletions public/displaying_images/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public struct AsyncImage<Content> : View where Content : View {
...
```

<!-- ImageProvider in Flutter serves a similar purpose as Image object? Maybe discuss NetworkImageProvider? Not sure if important at this stage... -->
In Flutter, when working with image, we usually don't have the separate concept of an image object and the view that display the image. All we need really is the `Image` widget and that widget has factory constructors that allow you to create network, memory and bundled images! However, in Flutter if you wish to load the data for your images from disk for instance, you can place the read bytes into a list of bytes (which we won't go into details of right now) and then pass those read bytes later into the `Image.memory()` factory constructor to display the image to the user!

## Challenge
Expand Down
14 changes: 8 additions & 6 deletions public/flutter_vs_uikit_vs_swiftui/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

<iframe width="560" height="315" src="https://www.youtube.com/embed/v-KV2N-UUts" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Flutter is Google's UI Framework that allows you to create beautiful applications on mobile, web and desktop with minimum effort. Flutter allows you to create your UI in a declarative way, much like SwiftUI. In that sense, Flutter is quite different from UIKit. UIKit is a lot more expressive in that you will need to specify even the simplest constraints in order to ensure that a component is positioned properly on the screen. Much like SwiftUI, Flutter tries to ensure that the rendering engine takes care of much of the hassle of positioning components, leaving the programmer to focus on declaration of the UI, just like SwiftUI. Therefore in this workshop we will mainly focus on similarities of Flutter and SwiftUI and every now and then draw some comparisons between Flutter and UIKit too.
Flutter is Google's UI Framework that allows you to create beautiful applications on mobile, web and desktop with minimum effort. Flutter allows you to create your UI in a *declarative* way, much like SwiftUI. In that sense, Flutter is quite different from UIKit. UIKit allows you to create your UI in an *imperative* way, meaning you need to specify even the simplest constraints in order to ensure that a component is positioned properly on the screen.

Much like SwiftUI, Flutter tries to ensure that the rendering engine takes care of much of the hassle of positioning components, leaving the programmer to focus on declaration of the UI, just like SwiftUI. Therefore in this workshop we will mainly focus on similarities of Flutter and SwiftUI and every now and then draw some comparisons between Flutter and UIKit too.

## Flutter vs UIKit Brief Comparison

Let's say that you want to create an instance of `UILabel` on a view controller in UIKit. Your code probably would look something like this in Swift:
Let's say that you want to create an instance of `UILabel` on a view controller in Swift using UIKit. Your code might look like this:

```
import UIKit
Expand All @@ -28,7 +30,7 @@ class ViewController: UIViewController {
}
```

If you decided to write the same code in Flutter, your code would look probably like this:
Dart is the programming language for writing Flutter applications, much like Swift is the language for writing iOS applications. If you decided to write the same code in Flutter, your code would look probably like this:

```dart
class HomePage extends StatelessWidget {
Expand All @@ -45,11 +47,11 @@ class HomePage extends StatelessWidget {
}
```

Take a moment to look at the expressive style of Flutter's UI generation. `Scaffold`, `SafeArea` and `Text` in this example are all widgets. We will talk more about widgets soon.
Take a moment to look at the declarative style of Flutter's UI generation. `Scaffold`, `SafeArea` and `Text` in this example are all widgets. We will talk more about widgets soon.

## Flutter vs SwiftUI Brief Comparison

If we took the Dart code (Dart is the programming language for writing Flutter applications, much like Swift is the language for writing iOS applications), and re-wrote the same code in SwiftUI, we'd probably end up with something like this:
If we took the Dart code, and re-wrote the same code in SwiftUI, we'd probably end up with something like this:

```
import SwiftUI
Expand All @@ -68,7 +70,7 @@ struct ContentView: View {
}
```

Note the similarities between expressiveness of both SwiftUI and Flutter in that the focus in these two frameworks is mainly to declare *how* the UI is structured on the screen than how it should be programmed and constraints to be created.
Note the similarities between SwiftUI and Flutter: both frameworks focus on declaring *how* the UI is structured on the screen instead of how it should be programmed.

## A Small Challenge

Expand Down
7 changes: 4 additions & 3 deletions public/lists/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// Does it make sense to use normal ListView(children: [Text, Text]) here for a direct comparison, then introduce builder as a speed improvement? Not sure if that would help folks visualize the similarities.
return Scaffold(
body: ListView.builder(
itemCount: 2,
Expand Down Expand Up @@ -66,7 +67,7 @@ class Person {
const Person(this.name);
}

const persons = [
const people = [
Person('Foo'),
Person('Bar'),
];
Expand All @@ -77,10 +78,10 @@ class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: persons.length,
itemCount: people.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(persons[index].name),
title: Text(people[index].name),
);
},
),
Expand Down
17 changes: 14 additions & 3 deletions public/rendering_in_flutter_vs_native_ios/instructions.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Rendering in Flutter vs Native iOS

If we take the example from the previous section and run it on an iOS Simulator while doing 3D debugging in Xcode, you will see quite a few layers rendered on the screen. If you do the same code in Flutter, you'll see that the Flutter's rendering engine draws up a single view controller called `FlutterViewController` and renders your text on it. But how does Flutter do that? Flutter uses Metal on iOS/ipadOS/macOS in order to render its contents.
If we take the example from the previous section and run it on an iOS Simulator while doing 3D debugging in Xcode, you will see quite a few layers rendered on the screen. If you do the same code in Flutter, you'll see that the Flutter's rendering engine draws up a single view controller called `FlutterViewController` and renders your text on it. How does Flutter do that? Flutter uses Metal on iOS/ipadOS/macOS in order to render its contents.

That's right! Everything that you see on the screen rendered by Flutter, is done through Flutter's rendering engine, Skia. Skia takes the code that you've written in your Flutter application, and uses native rendering techniques in order to display your application's UI to the user. That means your UI is very flat and rendered by Metal on iOS and OpenGL ES on Android in order to ensure maximum performance.
That's right! Everything Flutter displays on screen is drawn by Flutter's rendering engine, Skia. Skia takes the code that you've written in your Flutter application, and uses native rendering techniques in order to display your application's UI to the user. That means your UI is very flat and rendered by Metal on iOS and OpenGL ES on Android in order to ensure maximum performance.

If you look at how the previous example is rendered in a native SwiftUI application, your UI in the 3D Debugger will look like this:

Expand All @@ -16,10 +16,20 @@ Notice the absence of any label or text component on the screen? That's because

## Cupertino Package

<-- Hm, I'm not sure if this is quite accurate. By default, WidgetsApp has no styling. MaterialApp sets up an app for the Material Design system, CupertinoApp sets up an app for the Human interface guidelines. This might just be me though haha, I've worked on a few apps that use WidgetsApp and we built a customized design system.

Perhaps: "Flutter supports two design systems out of the box: the material package follows Material Design, and the cupertino package follows Human Interface Guidelines. Most Flutter apps make use of Material design. However... "

or

"Most Flutter tutorials use a package called Material..."

-->

By default, Flutter uses a package called Material to display its UI components on the screen. However, if you want to achieve a more iOS-ish style for your iOS apps, you can use the Cupertino package. This package gives a more iOS-y look and feel to your UI components. But remember, at the end of the day it's Skia that is just rendering your components to the screen. Let's have a look at an example:

```dart
import 'package:flutter/material.dart';
import 'package:flutter/material.dart'; // IMO, get rid of this package!
import 'package:flutter/cupertino.dart';

void main() {
Expand All @@ -35,6 +45,7 @@ class HomePage extends StatelessWidget {

@override
Widget build(BuildContext context) {
// Scaffold comes from the Material Package. Maybe use CupertinoPageScaffold instead?
return const Scaffold(
appBar: CupertinoNavigationBar(
middle: Text('Hello from Flutter'),
Expand Down
1 change: 1 addition & 0 deletions public/rendering_in_flutter_vs_native_ios/snippet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';

void main() {
runApp(
// Maybe leave this as a MaterialApp and ask them to convert it to a CupertinoApp?
const CupertinoApp(
home: HomePage(),
),
Expand Down
2 changes: 2 additions & 0 deletions public/rendering_text/snippet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';

void main() {
runApp(
// CupertinoApp?
const MaterialApp(
home: HomePage(),
),
Expand All @@ -13,6 +14,7 @@ class HomePage extends StatelessWidget {

@override
Widget build(BuildContext context) {
// CupertinoPageScaffold?
return const Scaffold(
body: Text(
'Hello, world!',
Expand Down
16 changes: 11 additions & 5 deletions public/rows/instructions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Rows

A row, in UI frameworks, usually is a UI component that arranges its children horizontally. In SwiftUI a row is constructed using `HStack`, in UIKit a row is created with a `UIStackView` with a horizontal axis and in Flutter a row, well, is created with the `Row` widget.
A row is a UI component that arranges its children horizontally. In SwiftUI, a row is constructed using `HStack`, in UIKit a row is created with a `UIStackView` with a horizontal axis. In Flutter, a row is created with the `Row` widget.

# Rows in SwiftUI vs Flutter

Expand Down Expand Up @@ -38,13 +38,17 @@ class HomePage extends StatelessWidget {
}
```

Note the usage of the `SafeArea` widget. You've now seen this in a few of the examples in this workshop and probably already know its purpose. In Flutter, the `SafeArea` widget ensures that its child widget is displayed in a way that is not obstructed by, for instance, the iOS notch, or any other obstacles.
In SwiftUI, the constructor of many UI components, such as `HStack`, has a trailing parameter to actually create the components to be displayed. In Flutter, the components are instead passed to the constructor as a named argument called `children`, which accepts a `List<Widget>`. `List` in Dart, as its name indicates, is how Dart creates arrays. The same format in Swift would be `[Widget]`.

## SafeArea

Whereas in SwiftUI the constructor of many UI components, such as `HStack`, has a trailing parameter as a closure to actually create the components that are to be displayed inside the `HStack`, in Flutter, the children of a widget such as `Row` are passed to the row, also inside the constructor, but as a named argument called `children` which is a `List<Widget>`. `List` in Dart, as its name indicates, is how Dart creates arrays. The same format in Swift would be `[Widget]`.
Note the usage of the `SafeArea` widget. You've now seen this in a few of the examples in this workshop and probably already know its purpose. In Flutter, the `SafeArea` widget ensures that its child widget is displayed in a way that is not obstructed by, for instance, the iOS notch, or any other obstacles.

## Main and Cross Axis in `Row`

Row is a component that arranges its children horizontally. Therefore its *main axis* is horizontal and is across the screen from left to right. The *cross axis* of the `Row` class is perpendicular to its main axis, meaning that the cross axis of a row is vertical, runs from top to bottom. It's important to understand the difference between these axis since there are properties both inside `Row` and `Column` (which we will talk about later) that control the arrangement of the children of these components. In SwiftUI, these alignment options are exposed through `HorizontalAlignment` and `VerticalAlignment`. In Flutter, they are exposed as `MainAxisAlignment` and `CrossAxisAlignment` where the main axis alignment of a row controls the horizontal positioning of its children since the main axis of a row is horizontal and its cross axis alignment controls the vertical positioning of its children since cross axis of a row is vertical.
Row is a component that arranges its children horizontally. Therefore its *main axis* is horizontal and is across the screen from left to right. The *cross axis* of the `Row` class is perpendicular to its main axis, meaning that the cross axis of a row is vertical, runs from top to bottom. It's important to understand the difference between these axis since there are properties both inside `Row` and `Column` (which we will talk about later) that control the arrangement of the children of these components.

In SwiftUI, these alignment options are exposed through `HorizontalAlignment` and `VerticalAlignment`. In Flutter, they are exposed as `MainAxisAlignment` and `CrossAxisAlignment` where the main axis alignment of a row controls the horizontal positioning of its children since the main axis of a row is horizontal and its cross axis alignment controls the vertical positioning of its children since cross axis of a row is vertical.

For instance, if you want a row to place all its children in the center of its available horizontal space (main axis, since row is a horizontal component), you'd write your code like this:

Expand All @@ -60,4 +64,6 @@ Row(

## Challenge

Now that you know about cross and main axis alignment in Flutter, see if you can complete the challenge on the right hand side by following the instructions I've provided for you in form of comments. Good luck!
Now that you know about cross and main axis alignment in Flutter, see if you can complete the challenge on the right hand side by following the instructions I've provided for you in form of comments. Good luck!

<!-- Maybe let them know about ctrl+enter inside the constructor to display a list of code completion options, including `mainAxisAlignment`? >
5 changes: 4 additions & 1 deletion public/statefulwidget/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class HomePage extends StatefulWidget {
}

class _HomePageState extends State<HomePage> {
// Random Thought: Keep accepting the title as part of the constructor and display it below. Then, introduce a new variable called currentTime which stores and displays this information? That way you can show them how to use constructor params in StatefulWidget?
String title = DateTime.now().toIso8601String();

@override
Expand All @@ -47,10 +48,11 @@ class _HomePageState extends State<HomePage> {
}
```

This might look like a lot of code, and compared to the SwiftUI variant, it is, but it's mostly boilerplate code which we will go through in this chapter.
This might look like a lot of code, and compared to the SwiftUI variant, it is. However, it's mostly boilerplate code which we will go through in this chapter.

## What is `StatefulWidget`?

<!-- this is tricky... the StatefulWidget doesn't really have access to setState, but rather the State class does. Might be a good idea to talk about the two separate objects up front, then dive into what each one does? Example from the docs, could be rewritten in more friendly language: "StatefulWidget instances themselves are immutable and store their mutable state separate State objects that are created by the createState method." E.g. "The StatefulWidgets accepts configuration data and creates a State object. The State object manages the data that can change over time and rebuilds when it does." -->
A stateful widget, as its name indicates, is an instance of `StatefulWidget` class in Flutter. This class has access to a special function called `setState()`. The `setState()` function, allows you to signal to the Flutter engine that you are making changes to the UI of this widget and need the engine to redraw the modified part of the UI after your call to `setState()` is done. If you look at the code on how we call this function:

```dart
Expand Down Expand Up @@ -84,4 +86,5 @@ The takeaway from this for you as a SwiftUI or UIKit developer should be that in

## Challenge

<!-- Could potentially ask them to use Dartpad to "Convert to StatefulWidget" as well. Helpful to teach about Dart IDE tools. -->
In this challenge, you'll need to execute the `setState()` function as you learned here, and upon the text button being pressed, you'll need to get the `Text` widget to display the current time. Look at the comments in the code and see if you can figure it out! Good luck.
2 changes: 2 additions & 0 deletions public/statefulwidget/snippet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';

void main() {
runApp(
// CupertinoApp?
const MaterialApp(
home: HomePage(),
),
Expand All @@ -22,6 +23,7 @@ class _HomePageState extends State<HomePage> {

@override
Widget build(BuildContext context) {
// CupertinoPageScaffold?
return Scaffold(
body: SafeArea(
child: Column(children: [
Expand Down
Loading