This article is for iOS developers who want to apply their existing iOS development experience to Flutter development. If you understand the fundamentals of the iOS framework, you can use this article as a starting point for learning about Flutter development.

Part 1 of this series: A Guide to Flutter for iOS Developers (Part 1)

The structure of this paper is as follows:

1. Views (top)

2. Navigation (I)

3. Threads and Asynchrony (PART 1)

4. Engineering structure, Localization, Dependencies and Resources (I)

5. ViewControllers (下)

6. Layout (II)

7. Gesture detection and Touch event Processing (Part 2)

8. Theme and Text (II)

9. Form input (ii)

10. Interaction with hardware, third-party services and system platforms (Part 2)

11. Databases and Local Storage (Part 2)

12. Notice (ii)


Fifth, ViewControllers

5.1 What does a ViewController correspond to in A Flutter?

In iOS, a ViewController is part of the user interface, usually used as a screen or part of it. These are combined to form a complex user interface that is constantly augmented by the application’S UI. In Flutter, this task falls to the Widget. As mentioned in the navigation chapter, screens in Flutter are also represented using Widgets because “everything is a widget!” . Use Naivgator to switch between different routes that represent different screens or pages, or different states, or render the same data.

5.2 How Do I Listen to the iOS Lifecycle?

In iOS, you can override ViewController methods to capture its own lifecycle, or register lifecycle callbacks in an AppDelegate. Would not have these two concepts in the Flutter, but you can use in the observer WidgetsBinding hook, but can be by listening didChangeAppLifecycleState () event, to realize the corresponding function.

The life cycle events that can be listened on are:

  • Inactive – The application is currently inactive and does not receive user input events. This event only works on iOS, there is no similar status on Android.

  • Paused – The application is invisible to the user and does not receive user input events, but still runs in the background.

  • Resumed – The application is visible and also responds to user input.

  • Kickbacks – apps are suspended, there’s no such thing on iOS.

See the AppLifecycleStatus documentation for more details.

Six, layout,

6.1 What does UITableView and UICollectionView correspond to in Flutter?

On iOS, you might use a UITableView or a UICollectionView to display a list. With Flutter, you can use ListView to achieve a similar implementation. In iOS, you use the Delegate method to determine the number of rows to display, the cell in its position, and the size of the cell.

Because of the immutable nature of the widgets in Flutter, you need to pass a list of widgets to the ListView. Flutter ensures that scrolling is fast and smooth.

1import 'package:flutter/material.dart'; 2 3void main() { 4 runApp(SampleApp()); 5} 6 7class SampleApp extends StatelessWidget { 8 // This widget is the root of your application. 9 @override10 Widget build(BuildContext context) {11 return MaterialApp(12 title: 'Sample App',13 theme: ThemeData(14 primarySwatch: Colors.blue,15 ),16 home: SampleAppPage(),17 ); 18 }19}2021class SampleAppPage extends StatefulWidget {22 SampleAppPage({Key key}) : super(key: key); 2324 @override25 _SampleAppPageState createState() => _SampleAppPageState(); 26}2728class _SampleAppPageState extends State<SampleAppPage> {29 @override30 Widget build(BuildContext context) {31 return Scaffold(32 appBar: AppBar(33 title: Text("Sample App"),34 ),35 body: ListView(children: _getListData()),36 ); 37 }3839 _getListData() {40 List<Widget> widgets = []; 41 for (int i = 0; i < 100; Add (Padding(Padding: EdgeInsets. All (10.0), child: Text("Row $I ")); 43 }44 return widgets; 46 45}}Copy the code

6.2 How do I determine the clicked element in the list?

In the iOS tableView: didSelectRowAtIndexPath: proxy method can be used to realize the function. In Flutter, the touch response processing passed in from the widget is needed.

 1import 'package:flutter/material.dart';2 3void main() { 4 runApp(SampleApp()); 5} 6 7class SampleApp extends StatelessWidget { 8 // This widget is the root of your application. 9 @override10 Widget build(BuildContext context) {11 return MaterialApp(12 title: 'Sample App',13 theme: ThemeData(14 primarySwatch: Colors.blue,15 ),16 home: SampleAppPage(),17 ); 18 }19}2021class SampleAppPage extends StatefulWidget {22 SampleAppPage({Key key}) : super(key: key); 2324 @override25 _SampleAppPageState createState() => _SampleAppPageState(); 26}2728class _SampleAppPageState extends State<SampleAppPage> {29 @override30 Widget build(BuildContext context) {31 return Scaffold(32 appBar: AppBar(33 title: Text("Sample App"),34 ),35 body: ListView(children: _getListData()),36 ); 37 }3839 _getListData() {40 List<Widget> widgets = []; 41 for (int i = 0; i < 100; I++) {42 widgets. Add (padding-right: edgeinset. All (padding-right: edgeinset. All (padding-right: edgeinset. All (padding-right: edgeinset. Text("Row $i"),46 ),47 onTap: () {48 print('row tapped'); 49}, 50)); 51 }52 return widgets; 53}} 54Copy the code

6.3 How Do I Dynamically Update data?

In iOS, you can update the list data by calling the reloadData method to notify the tableView or collectionView.

In Flutter, if you update the list of widgets in setState(), you will notice that the displayed data will not be updated immediately. This is because when setState() is called, the rendering engine of Flutter goes back to retrieve whether the widget tree has changed. When it gets a ListView, it does a == judgment and finds that the two ListViews are equal. No changes were found, so no updates were made.

An easy way to update the ListView is to create a new List in setState() and then copy all the data from the old List into the new List. This is simple, but not recommended when there is a large amount of data, as in the following example.

1import 'package:flutter/material.dart'; 2 3void main() { 4 runApp(SampleApp()); 5} 6 7class SampleApp extends StatelessWidget { 8 // This widget is the root of your application. 9 @override10 Widget build(BuildContext context) {11 return MaterialApp(12 title: 'Sample App',13 theme: ThemeData(14 primarySwatch: Colors.blue,15 ),16 home: SampleAppPage(),17 ); 18 }19}2021class SampleAppPage extends StatefulWidget {22 SampleAppPage({Key key}) : super(key: key); 2324 @override25 _SampleAppPageState createState() => _SampleAppPageState(); 26}2728class _SampleAppPageState extends State<SampleAppPage> {29 List widgets = []; 3031 @override32 void initState() {33 super.initState(); 34 for (int i = 0; i < 100; i++) {35 widgets.add(getRow(i)); 36 }37 }3839 @override40 Widget build(BuildContext context) {41 return Scaffold(42 appBar: AppBar(43 title: Text("Sample App"),44 ),45 body: ListView(children: widgets),46 ); 47}4849 Widget getRow(int I) {50 return GestureDetector(51 child: Padding(52 Padding: EdgeInsets. All (10.0),53 child: Text("Row $i"),54 ),55 onTap: () {56 setState(() {57 widgets = List.from(widgets); 58 widgets.add(getRow(widgets.length + 1)); 59 print('row $i'); 60}); 61}, 62); 64 63}}Copy the code

An efficient and effective way to build a list is to use ListView.Builder. This is great when you have a lot of data and need to build dynamic lists.

1import 'package:flutter/material.dart'; 2 3void main() { 4 runApp(SampleApp()); 5} 6 7class SampleApp extends StatelessWidget { 8 // This widget is the root of your application. 9 @override10 Widget build(BuildContext context) {11 return MaterialApp(12 title: 'Sample App',13 theme: ThemeData(14 primarySwatch: Colors.blue,15 ),16 home: SampleAppPage(),17 ); 18 }19}2021class SampleAppPage extends StatefulWidget {22 SampleAppPage({Key key}) : super(key: key); 2324 @override25 _SampleAppPageState createState() => _SampleAppPageState(); 26}2728class _SampleAppPageState extends State<SampleAppPage> {29 List widgets = []; 3031 @override32 void initState() {33 super.initState(); 34 for (int i = 0; i < 100; i++) {35 widgets.add(getRow(i)); 36 }37 }3839 @override40 Widget build(BuildContext context) {41 return Scaffold(42 appBar: AppBar(43 title: Text("Sample App"),44 ),45 body: ListView.builder(46 itemCount: widgets.length,47 itemBuilder: (BuildContext context, int position) {48 return getRow(position); 49}, 50), 51); 53} Widget getRow(int I) {55 return GestureDetector(56 child: Padding(57 Padding: EdgeInsets. All (10.0),58 child: Text("Row $i"),59 ),60 onTap: () {61 setState(() {62 widgets.add(getRow(widgets.length + 1)); 63 print('row $i'); 64}); 65}, 66); 68 67}}Copy the code

Instead of creating a ListView,

Two key parameters are required to create the ListView.Builder: initialize the list length and the ItemBuilder function.

The ItemBuilder method is very similar to the cellForItemAt proxy method in that it takes the location argument and then returns the cell that you want to render at that location.

Finally, and most importantly, note that the onTap() method does not recreate the list, but adds it using the.add method.

6.4 What is a ScrollView equivalent to in a Flutter?

In iOS, putting a view inside a ScrollView allows the user to scroll through content as needed.

Using the ListView Widget is the easiest way to use Flutter. It behaves like a ScrollView and TableView on iOS, and it can layout its widgets vertically.

1@override 2Widget build(BuildContext context) { 3 return ListView( 4 children: <Widget>[ 5 Text('Row One'), 6 Text('Row Two'), 7 Text('Row Three'), 8 Text('Row Four'), 9 ],10 ); 11}Copy the code

For more details on layout in Flutter, see the Layout tutorial.

Gesture detection and Touch event processing

7.1 How do I add click events to a Flutter widget?

In iOS, GestureRecognizer is bound to UIView to handle click events. There are two ways to add event listeners to Flutter:

1. If the widget itself supports event detection, pass handlers directly to it. For example, RaisedButton has an onPressed argument:

1@override2Widget build(BuildContext context) {3 return RaisedButton(4 onPressed: () {5 print("click"); 6 },7 child: Text("Button"),8 ); 9}Copy the code

2. If the widget itself does not support event detection, encapsulate it in a GestureDetector and pass a function to its onTap argument:

1class SampleApp extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 return Scaffold( 5 body: Center(6 Child: GestureDetector(7 Child: FlutterLogo(8 size: 200.0, 9),10 onTap: () {11 print("tap"); 12},13),14),15); 17 16}}Copy the code

7.2 How do I handle other gestures on widgets?

You can use GestureDetector to listen for more gestures, for example:

Click event

OnTapDown – a real-time action that occurs by tapping the screen in a specific area.

OnTapUp – an instant operation that touches the lift in a specific area.

OnTap – The action of a single click from after a tap on the screen to when the touch is lifted.

OnTapCancel – Triggers onTapDown, but does not trigger tap.

Double-click the event

OnDoubleTap – The user quickly clicks the screen twice in the same location.

Long press event

OnLongPress – The user touches the screen in the same location for a long time.

Vertical drag event

OnVerticalDragStart – The user’s finger touches the screen and a vertical movement event is about to take place.

OnVerticalDragUpdate – The user’s finger touches the screen, has started to move vertically, and continues to move.

OnVerticalDragEnd – The user touched the screen with his finger and moved it vertically, moving it at a certain rate before stopping contact.

Horizontal drag event

OnHorizontalDragStart – The user finger touches the screen and is about to move horizontally.

OnHorizontalDragUpdate – The user’s finger touches the screen, has already started moving horizontally, and continues to do so.

OnHorizontalDragEnd – The user’s finger touches the screen and moves horizontally, moving at a rate before stopping contact.

The following example shows how GestureDetector can rotate the logo of a Flutter when double-click:

1AnimationController controller; 2CurvedAnimation curve; 3 4@override 5void initState() { 6 controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); 7 curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); 8} 910class SampleApp extends StatelessWidget {11 @override12 Widget build(BuildContext context) {13 return Scaffold(14 body: Center(15 child: GestureDetector(16 child: RotationTransition(17 turns: curve,18 child: FlutterLogo(19 size: 200.0,20)),21 onDoubleTap: () {22 if (controller.iscompleted) {23 controller.reverse(); 24 } else {25 controller.forward(); 26}27},28),29),30); 31} 32}Copy the code

Viii. Theme and text

8.1 How Do I Set an Application Theme?

Flutter implements a beautiful set of Material Design components and is available out of the box, providing many commonly used styles and themes.

To take full advantage of the Material Components in your application, declare a top-level widget, MaterialApp, as your application entry point. MaterialApp is a widget that encapsulates a number of commonly used Material Design components. It adds Material functionality based on WidgetsApp.

But Flutter is flexible and expressive enough to implement any design language. On iOS, you can use Cupertino Library to create a Human Interface Guidelines Interface. For a complete collection of these widgets, see the Cupertino Widgets Gallery.

WidgetApp can also be used as an application portal, which provides a partially similar functional interface but is not as powerful as MaterialApp.

Define all child component colors and styles, and you can pass the ThemeData object directly to the MaterialApp Widget. For example, in the following code, the primary swatch is set to blue and the selected text is set to red:

1class SampleApp extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 return MaterialApp( 5 title: 'Sample App', 6 theme: ThemeData( 7 primarySwatch: Colors.blue, 8 textSelectionColor: Colors.red 9 ),10 home: SampleAppPage(),11 ); 13 12}}Copy the code

8.2 How Do I Set a Customized font for a Text Widget?

In iOS, you can import any TTF font file into your project and declare and reference it in the info.plist file. In Flutter, put the font into a folder and reference it in the pubspec.yaml file just like references to images.

1fonts:2   - family: MyCustomFont3     fonts:4       - asset: fonts/MyCustomFont.ttf5       - style: italicCopy the code

Then specify the font in the Text Widget:

1@override 2Widget build(BuildContext context) { 3 return Scaffold( 4 appBar: AppBar( 5 title: Text("Sample App"), 6 ), 7 body: Center( 8 child: Text( 9 'This is a custom font text',10 style: TextStyle(fontFamily: 'MyCustomFont'),11 ),12 ),13 ); 14}Copy the code

8.3 How do I style my Text Widget?

In addition to fonts, you can also customize other styles for Text widgets. The Text Widget accepts the parameters of a TextStyle object and can specify many parameters, such as:

  • color

  • decoration

  • decorationColor

  • decorationStyle

  • fontFamily

  • fontSize

  • fontStyle

  • fontWeight

  • hashCode

  • height

  • inherit

  • letterSpacing

  • textBaseline

  • wordSpacing

9. Form input

9.1 How to use forms in Flutter? How do I get user input?

We know that Flutter uses immutable and state-separated widgets. You may be wondering how to handle user input in this case. On iOS, you typically query the value or action of the current component when submitting data. So what happens in Flutter?

As with other parts of Flutter, form processing is implemented through specific widgets. If you have a TextField or TextFormField, you can get the user’s input via the TextEditingController:

1class _MyFormState extends State<MyForm> { 2 // Create a text controller and use it to retrieve the current value. 3 //  of the TextField! 4 final myController = TextEditingController(); 5 6 @override 7 void dispose() { 8 // Clean up the controller when disposing of the Widget. 9 myController.dispose(); 10 super.dispose(); 11 }1213 @override14 Widget build(BuildContext context) {15 return Scaffold(16 appBar: AppBar(17 title: Text('Retrieve Text Input'),18),19 Body: Padding(20 Padding: const EdgeInsets. All (16.0),21 child: TextField(22 controller: myController,23 ),24 ),25 floatingActionButton: FloatingActionButton(26 // When the user presses the button, show an alert dialog with the27 // text the user has typed into our text field.28 onPressed: () {29 return showDialog(30 context: context,31 builder: (context) {32 return AlertDialog(33 // Retrieve the text the user has typed in using our34 // TextEditingController35 content: Text(myController.text),36 ); 37}, 38); 39 },40 tooltip: 'Show me the value! ',41 child: Icon(Icons.text_fields),42 ),43 ); 45 44}}Copy the code

You can find more about this and a detailed code list in the Retrieve the Value of a text field in the Flutter Cookbook.

9.2 What does the placeholder in a Text field correspond to?

Within Flutter, you can easily display Text box prompts, or placeholder, by passing an InputDecoration object to a Text widget.

1body: Center(2  child: TextField(3    decoration: InputDecoration(hintText: "This is a hint"),4  ),5)Copy the code

9.3 How Can I Display Authentication Error Information?

Just like displaying a prompt, you can do this by passing an InputDecoration to the Text Widget.

However, you don’t want to start with an error message. Instead, after the user enters illegal data, the status should be updated and a new InputDecoration object passed.

1class SampleApp extends StatelessWidget { 2 // This widget is the root of your application. 3 @override 4 Widget build(BuildContext context) { 5 return MaterialApp( 6 title: 'Sample App', 7 theme: ThemeData( 8 primarySwatch: Colors.blue, 9 ),10 home: SampleAppPage(),11 ); 12 }13}1415class SampleAppPage extends StatefulWidget {16 SampleAppPage({Key key}) : super(key: key); 1718 @override19 _SampleAppPageState createState() => _SampleAppPageState(); 20}2122class _SampleAppPageState extends State<SampleAppPage> {23 String _errorText; 2425 @override26 Widget build(BuildContext context) {27 return Scaffold(28 appBar: AppBar(29 title: Text("Sample App"),30 ),31 body: Center(32 child: TextField(33 onSubmitted: (String text) {34 setState(() {35 if (! isEmail(text)) {36 _errorText = 'Error: This is not an email'; 37 } else {38 _errorText = null; 39}} 40); 41 },42 decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()),43 ),44 ),45 ); 46 }4748 _getErrorText() {49 return _errorText; 50 }5152 bool isEmail(String em) {53 String emailRegexp =54 r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,; : \ s @ \ "] +) *) | (\ ". + \ ")) @ ((\ [[0-9] {1, 3} \. [0-9] {1, 3} \. [0-9] {1, 3} \. [0-9] {1, 3} \]) | ((\ [a zA - Z - 0-9] + \.) +[a-zA-Z]{2,}))$'; 5556 RegExp regExp = RegExp(p); 5758 return regExp.hasMatch(em); 59}} 60Copy the code

10. Interact with hardware, third-party services, and system platforms

10.1 How to interact with the system platform and platform native code?

Flutter does not run code directly on the platform; Instead, it runs natively on the device as Dart code, bypassing the limitations of the platform’s SDK. This means, for example, that if you make a network request with Dart, it will run directly in the context of Dart. You don’t need to call the same apis you usually use to write iOS or Android native apps. Your Flutter application is still managed as a view by the native platform ViewController, but you cannot directly access the ViewController itself or the corresponding native framework.

This does not mean that Flutter applications cannot interact with native apis or native code. Flutter provides platform channels for communicating and exchanging data with the host ViewController. Platform Channels is essentially an asynchronous communication model that Bridges the Dart code with the host ViewController and iOS framework. You can use Platform Channels to implement native code methods or get data such as sensor information from the device.

In addition to using Platform Channels directly, you can also use a series of existing plug-ins that incorporate native code and Dart code to implement specific functionality. For example, you can use plug-ins to access albums or device cameras directly in Flutter without having to reintegrate them yourself. Pub is a repository of open source packages for Dart and Flutter, where you can find the necessary plug-ins. Some packages may support integration with iOS or Android, or both.

If you can’t find the package you need in Pub, you can write one yourself and post it to Pub.

10.2 How Can I Access the GPS Sensor?

Use the Geolocator plug-in, provided by the community.

10.3 How Can I Access a Camera?

Image_picker is a commonly used plug-in to access the camera.

10.4 How do I log in to Facebook?

You can log into Facebook using the Flutter_facebook_login community plugin.

10.5 How Can I Integrate Firebase functions?

Most Firebase features are included in First Party plugins. These plug-ins are maintained by the Flutter team:

  • Use Firebase AdMob with the Firebase_AdMob plugin

  • Use Firebase Analytics with the Firebase_analytics plug-in

  • Use Firebase Auth with the Firebase_Auth plug-in

  • Use the Firebase core library with the Firebase_core plug-in

  • Use Firebase RTDB with the Firebase_DATABASE plug-in

  • Use Firebase Cloud Storage with the Firebase_storage plug-in

  • Use Firebase Messaging (FCM) with the Firebase_messaging plugin

  • Use Firebase Cloud Firestore with cloud_firestore plug-in

You can also find some third-party Firebase plugins on Pub, which mainly implement functions not directly implemented by official plugins.

10.6 How can I Build my own plug-in?

If there are some Flutter and missing platform features, you can build your own plug-ins based on developing packages and plugins.

The plugin structure of Flutter, in a nutshell, is more like the Android Event Bus: you send a message and the recipient processes it and gives you the result. In this case, the recipient is native code on iOS or Android.

Databases and local storage

11.1 How to access UserDefaults in Flutter?

In iOS, you can use property lists to store a collection of key-value pairs, called UserDefaults.

In Flutter, you can use the Shared Preferences plugin to achieve the same functionality. This plugin encapsulates UserDefaults and similar SharedPreferences in Android.

11.2 What does CoreData correspond to in Flutter?

In iOS, you can use CoreData to store structured data. This is an upper-level encapsulation of an SQL database that makes it easier to query relational models.

This can be done with Flutter using the SQFlite plugin.

Xii. Notice

12.1 How Do I Set Push Notification?

In iOS, you need to register with the Developer Center to allow push notifications.

In Flutter, use the Firebase_messaging plugin to do this.

For more information about the Firebase Cloud Messaging API, see the Firebase_Messaging plug-in documentation.

(after)


We sincerely invite you to participate in the official Flutter developer survey by scanning the QR code below and filling out the questionnaire. Flutter is better because of you:

Guide to Flutter for iOS Developers (Part 1)
Guide to Flutter for front-end engineers
Google engineer | Chinese speech understanding Flutter of high-performance graphics rendering