Beginner's guide on Flutter for iOS developers
Learn the main concepts of Flutter from a native iOS developer's point of view.
17 Jul 2023 · 8 min read
Recently, I was experimenting with Flutter for a small project of mine. Flutter is an open source framework by Google for building cross-platform applications from a single codebase.
Knowledge in Swift and SwiftUI are valuable when building apps with Flutter. With this article, I would like to give you a overview of the main concepts of Flutter from a native iOS developer's point of view.
Let's get started.

Dart as programming language
Dart is the programming language used by Flutter developers.
For a Swift developer, learning Dart feels familiar. Both languages share concepts like type safety, type inference, optionals, generics, concurrency with async/await, error handling, extensions and more.
Let's look at the following Dart example:
API api = API(); // 1.try {User user = await api.login(username, password);} catch (error) {// Handle error.}class User {String _username; // 2.User(this._username); // 3.}class API {User? _currentUser;Future<User> login(String username, String password) async {// 4.if (_currentUser != null) {throw UserAlreadyLoggedIn(_currentUser);}// Network requestreturn user;}}
To a Swift developer, the code above should be almost self-explanatory. Some things to point out here are:
- Variables defined without any keyword are similar to a var in Swift. To define a constant variable in Dart, we can use the final keyword which is similar to the let keyword in Swift.
- Underscores in Dart are used to make variables or classes private.
- Since writing constructors to assign all class fields is often redundant, Dart offers syntactic sugar that looks like shown in the example above.
- Optionals in Dart are defined and handled just like in Swift with ? and !. To unwrap an optional safely, Dart offers the if statement as shown in the example above. Inside the if-statement, _currentUser is treated as a non-optional.
Widgets as UI elements
Just like SwiftUI, Flutter provides a declarative API to build user interfaces. While SwiftUI represents UI elements as views, Flutter does the same with so called widgets. To compose layouts, both SwiftUI and Flutter nest UI components within one another.
For example, the following SwiftUI code:
Text("Hello world!").padding(5)
looks as follows in Flutter:
Padding(padding: EdgeInsets.all(5.0),child: Text("Hello world!"),)));
As we can see above, while SwiftUI uses view modifiers to configure views, Flutter uses widget properties.
In SwiftUI, views may expand to the available space or limit their size to their content. Flutter widgets behave similar.
When layouting views in SwiftUI, the parent view proposes a size to its child view and then renders it at the returned size. Flutter differs at that point because the parent component can override the child's returned desired size. The parent widget passes constraints to its child widget which includes minimum and maximum values for height and width. If the requested size doesn't fit in the constraints, the parent limits the child view to fit the constraints.
Just like SwiftUI, Flutter provides a widget catalog which offers widgets for aligning and positioning their children. For example, equivalents to SwiftUI's HStack and VStack are Row and Column in Flutter.
Aligning in SwiftUI:
VStack {Text("Hello"),Text("world!")}
Aligning in Flutter:
Column(mainAxisAlignment: MainAxisAlignment.center,children: const [Text('Hello'),Text('world!'),],),
Other examples of Flutter widgets are ListView to create lists, GridView for displaying a grid or SingleChildScrollView for creating a scroll view. These are also resembling their equivalents in SwiftUI.
Managing state
Just like SwiftUI, Flutter follows the declarative approach when it comes to state management. Which means that every state change leads to a redraw of the user interface.
While SwiftUI provides property wrappers like @State & @Binding and StateObject & ObservedObject, Flutter works with so called stateful widgets which consist of a subclass of StatefulWidget and State, for example:
class MyStatefulWidget extends StatefulWidget {const MyStatefulWidget({super.key});@overrideState<MyStatefulWidget> createState() => _MyStatefulWidgetState();}class _MyStatefulWidgetState extends State<MyStatefulWidget> {int _selectedIndex = 0;void _onItemTapped(int index) {setState(() {_selectedIndex = index;});}@overrideWidget build(BuildContext context) {return Scaffold(appBar: ...,body: ...,bottomNavigationBar: BottomNavigationBar(items: const <BottomNavigationBarItem>[BottomNavigationBarItem(icon: Icon(Icons.home),label: 'Home',),...],currentIndex: _selectedIndex,selectedItemColor: Colors.amber[800],onTap: _onItemTapped,),);}}
As we can see above, the State object stores the widget's state. When we call setState() like in the _onItemTapped function, the framework redraws the widget.
If a widget does not depend on anything other than the initial configuration we can use a StatelessWidget instead.

Newsletter
Like to support my work?
Say hi
Related tags
Articles with related topics
Latest articles and tips