Flutter for SwiftUI Developers
Learn how to apply SwiftUI developer knowledge when building Flutter apps.
SwiftUI developers who want to write mobile apps using Flutter should review this guide. It explains how to apply existing SwiftUI knowledge to Flutter.
Flutter is a framework for building cross-platform applications that uses the Dart programming language. To understand some differences between programming with Dart and programming with Swift, see Learning Dart as a Swift Developer and Flutter concurrency for Swift developers.
Your SwiftUI knowledge and experience are highly valuable when building with Flutter.
Flutter also makes a number of adaptations to app behavior when running on iOS and macOS. To learn how, see Platform adaptations.
This document can be used as a cookbook by jumping around and finding questions that are most relevant to your needs. This guide embeds sample code. By using the "Open in DartPad" button that appears on hover or focus, you can open and run some of the examples on DartPad.
Overview
#As an introduction, watch the following video. It outlines how Flutter works on iOS and how to use Flutter to build iOS apps.
Flutter and SwiftUI code describes how the UI looks and works. Developers call this type of code a declarative framework.
Views vs. Widgets
#SwiftUI represents UI components as views. You configure views using modifiers.
Text("Hello, World!") // <-- This is a View
.padding(10) // <-- This is a modifier of that View
Flutter represents UI components as widgets.
Both views and widgets only exist until they need to be changed. These languages call this property immutability. SwiftUI represents a UI component property as a View modifier. By contrast, Flutter uses widgets for both UI components and their properties.
Padding( // <-- This is a Widget
padding: EdgeInsets.all(10.0), // <-- So is this
child: Text("Hello, World!"), // <-- This, too
)
To compose layouts, both SwiftUI and Flutter nest UI components within one another. SwiftUI nests Views while Flutter nests Widgets.
Layout process
#SwiftUI lays out views using the following process:
- The parent view proposes a size to its child view.
-
All subsequent child views:
- propose a size to their child's view
- ask that child what size it wants
- Each parent view renders its child view at the returned size.
Flutter differs somewhat with its process:
-
The parent widget passes constraints down to its children. Constraints include minimum and maximum values for height and width.
-
The child tries to decide its size. It repeats the same process with its own list of children:
- It informs its child of the child's constraints.
- It asks its child what size it wishes to be.
-
The parent lays out the child.
- If the requested size fits in the constraints, the parent uses that size.
- If the requested size doesn't fit in the constraints, the parent limits the height, width, or both to fit in its constraints.
Flutter differs from SwiftUI because the parent component can override the child's desired size. The widget cannot have any size it wants. It also cannot know or decide its position on screen as its parent makes that decision.
To force a child widget to render at a specific size, the parent must set tight constraints. A constraint becomes tight when its constraint's minimum size value equals its maximum size value.
In SwiftUI, views might expand to the available space or limit their size to that of its content. Flutter widgets behave in similar manner.
However, in Flutter parent widgets can offer unbounded constraints. Unbounded constraints set their maximum values to infinity.
UnboundedBox(
child: Container(
width: double.infinity, height: double.infinity, color: red),
)
If the child expands and it has unbounded constraints, Flutter returns an overflow warning:
UnconstrainedBox(
child: Container(color: red, width: 4000, height: 50),
)
To learn how constraints work in Flutter, see Understanding constraints.
Design system
#Because Flutter targets multiple platforms, your app doesn't need to conform to any design system. Though this guide features Material widgets, your Flutter app can use many different design systems:
- Custom Material widgets
- Community built widgets
- Your own custom widgets
- Cupertino widgets that follow Apple's Human Interface Guidelines
If you're looking for a great reference app that features a custom design system, check out Wonderous.
UI Basics
#This section covers the basics of UI development in Flutter and how it compares to SwiftUI. This includes how to start developing your app, display static text, create buttons, react to on-press events, display lists, grids, and more.
Getting started
#In SwiftUI, you use App to start your app.
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
HomePage()
}
}
}
Another common SwiftUI practice places the app body within a struct
that conforms to the View protocol as follows:
struct HomePage: View {
var body: some View {
Text("Hello, World!")
}
}
To start your Flutter app, pass in an instance of your app to
the runApp function.
void main() {
runApp(const MyApp());
}
App is a widget. The build method describes the part of the
user interface it represents.
It's common to begin your app with a WidgetApp
class,
like CupertinoApp.
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// Returns a CupertinoApp that, by default,
// has the look and feel of an iOS app.
return const CupertinoApp(home: HomePage());
}
}
The widget used in HomePage might begin with the Scaffold class.
Scaffold implements a basic layout structure for an app.
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(body: Center(child: Text('Hello, World!')));
}
}
Note how Flutter uses the Center
widget.
SwiftUI renders a view's contents in its center by default.
That's not always the case with Flutter.
Scaffold doesn't render its body widget at the center of the screen.
To center the text, wrap it in a Center widget.
To learn about different widgets and their default behaviors, check out
the Widget catalog.
Adding Buttons
#In SwiftUI, you use the Button struct to create a button.
Button("Do something") {
// this closure gets called when your
// button is tapped
}
To achieve the same result in Flutter,
use the CupertinoButton class:
CupertinoButton(
onPressed: () {
// This closure is called when your button is tapped.
},
const Text('Do something')