This article is only for developers who are new to Flutter. The original link: blog. Myweb. Kim/flutter/Flu…

Introduction to the

Flutter is an open-source mobile development framework (based on the “Dart” language) developed by Google. The APP developed with Flutter runs on both IOS and Android platforms. Also, Flutter comes with Material and Cupertino theme packages by default (the former for Android and the latter for IOS). You can quickly develop an IOS or Android theme package… Demo…

  • cross-platform

    Flutter does not use WebView or native controls of the operating system. Instead, Flutter uses a high-performance rendering engine of its own that makes component rendering UI very efficient. This way, Flutter can ensure the same UI performance on IOS and Android, and developers don’t have to worry too much about platform differences. For startups, saving upfront development costs is the best funding…

  • A high performance

    Unlike React Native’s cross-platform, RN converts JS components into Native components for rendering. Flutter is based on the underlying Skia graphics library to render (I think it is similar to the CANVAS in DOM, you get a canvas from the platform and render on the canvas yourself), all rendering is done by Skia.

    Skia extension…

    Flutter uses Skia as its 2D rendering engine. Skia is a Google LIBRARY of 2D graphics processing functions that provide efficient and compact representations of fonts, coordinate transformations, and bitmaps. Skia is cross-platform and provides a very friendly API. Flutter uses Skia as its drawing engine for both Google Chrome and Android. It is worth noting that Because Android already has Skia built-in, Flutter does not need to put Skia into APK when it packages Android applications. However, Skia is not built into iOS, so when building an iPA, Skia must be packaged together. This is the main reason why the Android installation package of Flutter APP is smaller than that of iOS.

    Because of its own rendering mechanism, it does not need frequent communication with the native platform, which reflects its high efficiency and high performance. Dart directly controls Flutter layout and rendering, showing its performance in interactions such as sliding. RN’s rendering in this aspect communicates with the native platform and continuously synchronises information, which costs a lot on mobile phones.

    At the rendering layer, Flutter also has a virtual DOM component underneath it that performs diff algorithms after UI changes are made.

  • Development efficiency

    The Flutter was developed with a feature called thermal overload. As with WebPack and browsers, the interface changes immediately after you save it in the editor. This is also the case with Flutter. When an APP is debugged in a virtual container or on a real device, it responds immediately when saved. It saves a lot of time.

Preliminary understanding of Dart

Since Flutter is based on the Dart language, we also need to learn more or less about how Dart is written, and what its syntax and structure are. Although the Demo on the official website notes that “familiarity with object-oriented and basic programming concepts such as variables, loops, and conditional controls should complete this tutorial, you don’t need to know Dart or have mobile development experience.” emmmm… That’s bullshit…

If you don’t know Dart, just see how the Demo is written…

Dart comes from Google. Is a strongly typed object-oriented programming language with a syntax somewhat like Java and JavaScript combined.

Official Learning Materials

Here is the basic Dart syntax to use Flutter:

(The following content is excerpted from the official website document, there is no need to look closely, can be a quick over, just to understand.)

Variable declarations

  1. var

    Like var in JavaScript, it can accept any type of variable, but the major difference is that the type of a var variable is determined once it is assigned to Dart and cannot be changed. For example:

    var t;
    t="hi world";
    // Dart will report an error because t is of type String,
    // Once the type is determined, it cannot be changed.
    t=1000;
    Copy the code

    Dart is a strongly typed language. Any variable has a definite type. In Dart, when a variable is declared with var, Dart is compiled to infer the type of the first assigned data, and the type is already determined after compilation. JavaScript is a purely weakly typed scripting language, and VAR is just a way of declaring variables.

  2. The dynamic and Object

    Dynamic and Object are similar in function to var. Both types are automatically inferred during assignment. The difference is that the type can be changed after assignment, for example:

    dynamic t;
    t="hi world";
    // There is no problem with the following code
    t=1000;
    Copy the code

    Object is the base class of all dart objects. That is, all types are subclasses of Object. Therefore, any type of data can be assigned to an Object declared by Object.

  3. Final and const

    If you never intend to change a variable, use final or const, not var, and not a type. A final variable can only be set once. The difference is that a const variable is a compile-time constant and a final variable is initialized the first time it is used. A variable whose type is final or const can be omitted, for example:

    // You can omit the String type declaration
    final str = "hi world";
    //final str = "hi world"; 
    const str1 = "hi world";
    //const String str1 = "hi world";
    Copy the code

function

Dart is a true object-oriented language, so even functions are objects and have a type Function. This means that functions can be assigned to variables or passed as arguments to other functions, a typical feature of functional programming.

  1. Function declaration

    bool isNoble(int atomicNumber) {
      return_nobleGases[atomicNumber] ! =null;
    }
    Copy the code

    Dart function declarations that do not declare the return value type are treated as dynamic by default. Note that there is no type inference for the return value:

    typedef bool CALLBACK();
    
    // The return type is not specified. The default is dynamic, not bool
    isNoble(int atomicNumber) {
      return_nobleGases[atomicNumber] ! =null;
    }
    
    void test(CALLBACK cb){
       print(cb()); 
    }
    // isNoble is not a bool
    test(isNoble);
    Copy the code
  2. For functions that contain only one expression, you can use shorthand syntax

    boolIsNoble (intAtomicNumber) => _nobleGases [atomicNumber]! =null ;   
    Copy the code
  3. Function as variable

    var say= (str){
      print(str);
    };
    say("hi world");
    Copy the code
  4. Functions are passed as arguments

    void execute(var callback){
        callback();
    }
    execute(()=>print("xxx"))
    Copy the code
  5. Optional position parameter

    Wrap a set of function arguments, marked with [] as optional positional arguments:

    String say(String from, String msg, [String device]) {
      var result = '$from says $msg';
      if(device ! =null) {
        result = '$result with a $device';
      }
      return result;
    }
    Copy the code

    Here is an example of calling this function with no optional arguments:

    say('Bob'.'Howdy'); // Result: Bob says Howdy
    Copy the code

    Here is an example of calling this function with the third argument:

    say('Bob'.'Howdy'.'smoke signal'); Bob says Howdy with a smoke signal
    Copy the code
  6. Optional named parameter

    When defining functions, use {param1, param2… }, used to specify named parameters. Such as:

    // Set the [bold] and [hidden] flags
    void enableFlags({bool bold, bool hidden}) {
        // ... 
    }
    Copy the code

    When calling a function, you can use the specified named arguments. For example, paramName: value

    enableFlags(bold: true, hidden: false);
    Copy the code

    Optional named parameters are used extensively in Flutter.

Asynchronous support

The Dart library has many functions that return Future or Stream objects. These functions are called asynchronous functions: they only return after setting up some operation that takes a certain amount of time, such as an IO operation. Instead of waiting for this operation to complete.

The async and await keywords support asynchronous programming, running asynchronous code that looks a lot like synchronous code.

  1. Future

A Future, much like a Promise in JavaScript, represents the final completion (or failure) of an asynchronous operation and the representation of its resulting value. In simple terms, it is used to handle asynchronous operations, executing the successful operation if the asynchronous process succeeds, and catching errors or stopping subsequent operations if the asynchronous process fails. A Future only has one outcome, either success or failure.

Since it has many functions, we only introduce its common apis and features here. Also, remember that the return value of all Future apis is still a Future object, so it’s easy to chain calls.

  1. Future.then

For example convenience, in this example we created a delayed task using future.delayed (which would have been a really time-consuming task, such as a network request) that returned the result string “Hi world!” after 2 seconds. Then we receive the asynchronous result in then and print the result as follows:

Future.delayed(new Duration(seconds: 2), () {return "hi world!";
}).then((data){
   print(data);
});
Copy the code
  1. Future.catchError

If an error occurs in an asynchronous task, we can catch the error in catchError.

Future.delayed(new Duration(seconds: 2), () {//return "hi world!" ;
   throw AssertionError("Error");  
}).then((data){
   // Successful execution will go here
   print("success");
}).catchError((e){
   // Execution failure leads to this
   print(e);
});
Copy the code

In this example, we throw an exception in the asynchronous task, and the then callback will not be executed, but the catchError callback will be called instead; However, not only the catchError callback can catch errors, but the then method also has an optional argument onError, which can also catch exceptions:

Future.delayed(new Duration(seconds: 2), () {
	//return "hi world!" ;
	throw AssertionError("Error");
}).then((data) {
	print("success");
}, onError: (e) {
	print(e);
});
Copy the code
  1. Future.whenComplete

Sometimes we have scenarios where asynchronous tasks need to do something whether they succeed or fail, such as pop up the load dialog before the network request and close it after the request. There are two ways to do this. The first way is to close the dialog box in then or catch, respectively. The second way is to use the whenComplete callback of the Future.

Future.delayed(new Duration(seconds: 2), () {//return "hi world!" ;
   throw AssertionError("Error");
}).then((data){
   // Successful execution will go here
   print(data);
}).catchError((e){
   // Execution failure leads to this
   print(e);
}).whenComplete((){
   // Success or failure will lead us here
});
Copy the code
  1. Future.wait

Sometimes, we need to wait for the completion of multiple asynchronous tasks before performing some operations. For example, we have an interface that needs to obtain data from two network interfaces. After obtaining the data, we need to perform specific processing on the data from the two interfaces before displaying the data on the UI. The answer is future. wait, which takes an array of futures and fires the success callback for then only if all of the futures in the array have been successfully executed. Below, we simulated the two asynchronous tasks of data acquisition by simulating future.delayed. When both asynchronous tasks were successfully executed, the results of the two asynchronous tasks were combined and printed as follows:

Future.wait([
  // Return the result after 2 seconds
  Future.delayed(new Duration(seconds: 2), () {
    return "hello";
  }),
  // Return the result after 4 seconds
  Future.delayed(new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});
Copy the code

Execute the code above and after 4 seconds you will see “Hello World” in the console.

Async/await

The async/await functionality and usage in Dart is exactly the same as the async/await functionality and usage in JavaScript. If you already know the async/await usage in JavaScript, you can skip this section.

  1. Callback Hell

If there is a lot of asynchronous logic in your code, and a lot of asynchronous tasks depend on the results of other asynchronous tasks, the future.then callback is bound to occur within a callback. For example, there is a requirement scenario where the user logs in first and then obtains the user Id after successful login, and then requests the user’s personal information through the user Id. After obtaining the user’s personal information, we need to cache it in the local file system for convenience, with the code as follows:

// Define each asynchronous task separately
Future<String> login(String userName, String pwd){
	...
    // User login
};
Future<String> getUserInfo(String id){
	...
    // Get user information
};
Future saveUserInfo(String userInfo){
	...
	// Save the user information
}; 
Copy the code

Next, execute the entire task flow:

login("alice"."* * * * * *").then((id){
 // After the login succeeds, id is used to obtain user information
 getUserInfo(id).then((userInfo){
    // Get the user information and save it
    saveUserInfo(userInfo).then((){
       // Save the user information, then perform other operations. }); }); })Copy the code

As you can see, if the business logic has a lot of asynchronous dependencies, the above situation will occur. Excessive nesting can lead to code readability and error rate, and can be very difficult to maintain. This problem is known as Callback hell. The callback hell problem was one of the most ridiculed issues in JavaScript, but with the release of ECMAScript6 and ECMAScript7 standards, it has been solved. ECMAScript6 introduced Promise and ECMAScript7 introduced async/await. Dart almost completely shifts the two in JavaScript: Future equals Promise, while async/await doesn’t even change its name. Let’s look at how the nesting problem in the above example can be eliminated with Future and async/await.

  1. Eliminate callback Hell with the Future
login("alice"."* * * * * *").then((id){
  	return getUserInfo(id);
}).then((userInfo){
    return saveUserInfo(userInfo);
}).then((e){
   // Perform the following operations
}).catchError((e){
  // Error handling
  print(e);
});
Copy the code

As mentioned above, “The return value of all Future apis is still a Future object, so it is convenient to make chain calls.” If a Future is returned in a THEN, the Future will be executed, and then callbacks will be triggered, and so on down. You avoid layers of nesting.

  1. Eliminate callback hell with async/await

The method of returning the Future through the Future callback avoids layers of nesting, but there is still a layer of callback. Is there a way to perform asynchronous tasks like writing synchronous code without using callbacks? The answer is yes, we need to use async/await.

task() async {
   try{
    String id = await login("alice"."* * * * * *");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    // Perform the following operations
   } catch(e){
    // Error handling
    print(e); }}Copy the code
  • asyncUsed to indicate that a function is asynchronous, and the defined function returns oneFutureObject that you can add callback functions using the then method.
  • awaitAnd then there’s aFutureIs displayed after the asynchronous task is complete.awaitMust be present atasyncInside the function.

As you can see, we are representing an asynchronous stream in synchronous code with async/await.

In either JavaScript or Dart, async/await is just a syntactic sugar that the compiler or interpreter will eventually convert into a Promise (Future) chain of calls.

Stream

A Stream is also used to receive asynchronous event data and, unlike a Future, can receive the results (success or failure) of multiple asynchronous operations. That is, when an asynchronous task is executed, result data or error exceptions can be passed by firing success or failure events multiple times. Stream is commonly used in asynchronous task scenarios where data is read multiple times, such as network content download and file read and write. Here’s an example:

Stream.fromFutures([
  // Return the result after 1 second
  Future.delayed(new Duration(seconds: 1), () {
    return "hello 1";
  }),
  // Throw an exception
  Future.delayed(new Duration(seconds: 2), () {throw AssertionError("Error");
  }),
  // The result is returned after 3 seconds
  Future.delayed(new Duration(seconds: 3), () {
    return "hello 3";
  })
]).listen((data){
   print(data);
}, onError: (e){
   print(e.message);
},onDone: (){

});
Copy the code

The above code in turn prints:

I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3
Copy the code

The code is so simple that I won’t go over it.

Question: Since a Stream can receive multiple events, can a Stream be used to implement a subscriber mode event bus?

conclusion

From the above introduction, I believe you should have a preliminary impression of Dart. Since I use Java and JavaScript in my daily life, I would like to talk about my own views based on my experience and the combination of Java and JavaScript.

Dart is compared to Java and JavaScript because they are examples of strongly typed and weakly typed languages, respectively, and Dart’s syntax borrows a lot from Java and JavaScript.

Dart vs Java

To be fair, Dart is syntactically more expressive than Java; At the VM level, Dart VM has been repeatedly optimized in terms of memory reclamation and throughput. However, I did not find relevant test data for specific performance comparison, but in my opinion, as long as Dart language is popular, VM performance should not be worried. After all, Google already has a lot of expertise in Go (no VM but GC), javascript (V8), and Dalvik (Java VM for Android). It is worth noting that Dart can already achieve GC within 10ms in Flutter, so the deciding factor for Dart is not in terms of performance compared to Java. Dart is more expressive than Java in terms of syntax. Most importantly, Dart’s support for functional programming is much stronger than Java’s (lamda). Dart’s real weakness is ecology, but I believe that with Futter’s increasing popularity, Dart’s ecology will be accelerated. For Dart, what is needed now is time.

Dart vs JavaScript

JavaScript has long been short on weak typing, so TypeScript, Coffeescript, and even Facebook flow (which, while not a superset of JavaScript, does provide static type checking through annotation and packaging tools) are in the market. Of all the scripting languages I’ve used (Python, PHP), JavaScript has the best dynamic support. For example, in JavaScript, you can dynamically extend attributes to any object at any time, which is a great sword for JavaScript experts. However, every coin has two sides, and the powerful dynamic nature of JavaScript is also a double-edged sword. You can often hear another argument that JavaScript’s dynamic nature sucks, that being too flexible makes code unpredictable and can’t limit unwanted changes. After all, there are people who are always insecure about their own code or someone else’s code, who want it to be manageable, and who expect a static type checking system to help them reduce errors. Because of this, Dart almost abandons the dynamic nature of the scripting language with Flutter, such as no support for reflection or dynamic function creation. Dart also enforces Strong Mode in 2.0. Checked Mode and optional Type will fade out, so from a type-safety perspective, Dart is similar to TypeScript and Coffeescript, so by itself Dart doesn’t have much of an advantage, but the combination of server-side scripting, APP development, and Web development adds up!

Screenshot of the official PPT promotion

An outline of the underlying structure of Flutter:

Material and Cupertino are two libraries of uI-style components that Flutter officially provides (the former for Android and the latter for IOS).

In Flutter, everything is a Widget. A button is a Widget, a piece of text is a Widget, an image is a Widget, and a route navigation is a Widget. Therefore, you can learn how to use the TWO UI libraries in Advance of Flutter. (Personal opinion)

Base Component library

Material component library

Cupertino component library

Setting up the development environment

  • Scaffolding on Windows
  • Scaffolding on macOS
  • Scaffolding on Linux

The setup process is simple, download the SDK package and configure the environment variables.

Editor Recommendation

VScode, lightweight and simple.

To configure the Flutter environment, simply install a Flutter plugin.

Official Configuration Tutorial

The first Demo

After installing the plugin in VScode, press shift + command + p to enter flutter and select New Project.

You may need to select the location of the Flutter SDK when it is first created.

The following Demo is the code given on the official website, sorted out a complete.

  1. Start by adding a dependency to pubspec.yaml: english_words, a toolkit of randomly generated English words written in the Dart language.

    Pubspec. yaml is the Flutter configuration file, which can be understood as package.json in NPM

    Find line 21 of the file:

    dependencies:
      flutter:
        sdk: flutter
    
      # The following adds the Cupertino Icons font to your application.
      # Use with the CupertinoIcons class for iOS style icons.
      cupertino_icons: ^ 0.1.2
      
      # add version number here to follow Semantic Versioning
      english_words: ^ 3.1.5
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
    
    Copy the code

    Flutter has an official package management platform, pub.dartlang.org, similar to NPM

    After adding, type Flutter Packages Get in the console or right-click pubspes.yaml in the editor and select Get Packages

    That is, install new dependencies.

  2. Replacing the Demo code

    This Demo is a randomly generated English name application with an infinitely scrolling list that allows the user to search for favorite names with a red flag, and then click on the upper right corner to see the favorite names (implemented by route jump).

    Remove all the code in lib/main.dart and replace it with the following:

    The following code is a compilation of the code in the official website Demo, can not care about the results or the specific meaning of each code, first run the Demo in the simulator.

    import 'package:flutter/material.dart';
    import 'package:english_words/english_words.dart';
    
    // Program entry
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Startup Name Generator',
          home: new RandomWords(),
          theme: newThemeData( primaryColor: Colors.white, ), ); }}class RandomWords extends StatefulWidget {
      @override
      createState() => new RandomWordsState();
    }
    
    class RandomWordsState extends State<RandomWords> {
      final _suggestions = <WordPair>[];
    
      final _saved = new Set<WordPair>();
    
      final _biggerFont = const TextStyle(fontSize: 18.0);
      @override
      Widget build(BuildContext context) {
        return new Scaffold (
          appBar: new AppBar(
            title: new Text('Startup Name Generator'),
            actions: <Widget>[
              new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
            ],
          ),
          body: _buildSuggestions(),
        );
      }
    
      void _pushSaved() {
        Navigator.of(context).push(
          new MaterialPageRoute(
            builder: (context) {
              final tiles = _saved.map(
                (pair) {
                  return new ListTile(
                    title: newText( pair.asPascalCase, style: _biggerFont, ), ); });final divided = ListTile
                .divideTiles(
                  context: context,
                  tiles: tiles,
                )
                .toList();
    
                return new Scaffold(
                  appBar: new AppBar(
                    title: new Text('Saved Suggestions'),
                  ),
                  body: newListView(children: divided), ); })); } Widget _buildRow(WordPair pair) {final alreadySaved = _saved.contains(pair);
        return new ListTile(
          title: new Text(
            pair.asPascalCase,
            style: _biggerFont,
          ),
          trailing: new Icon(
            alreadySaved ? Icons.favorite : Icons.favorite_border,
            color: alreadySaved ? Colors.red : null,
          ),
          onTap: () {
            setState(() {
              if (alreadySaved) {
                _saved.remove(pair);
              } else{ _saved.add(pair); }}); }); } Widget _buildSuggestions() {return new ListView.builder(
          padding: const EdgeInsets.all(16.0),
          // The itemBuilder is called once for each suggested word pair, and the word pair is added to the ListTile row
          // On even lines, the function adds a ListTile row to the word pair.
          // On the odd line, add a divider widget to separate adjacent pairs of words.
          // Note that on a small screen, the splitter line can look a bit labored.
          itemBuilder: (context, i) {
            // Before each column, add a 1 pixel high divider widget
            if (i.isOdd) return new Divider();
    
            // The syntax "I ~/ 2" means I divided by 2, but the return value is integer (rounded down), such as I: 1, 2, 3, 4, 5
            //, the result is 0, 1, 1, 2, 2, which calculates the actual number of word pairs in the ListView after subtracting the separator lines
            final index = i ~/ 2;
            // If it is the last word pair in the suggested list
            if (index >= _suggestions.length) {
              / /... This is then regenerated into 10 word pairs, which are then added to the suggested list
              _suggestions.addAll(generateWordPairs().take(10));
            }
            return_buildRow(_suggestions[index]); }); }}Copy the code
  3. Select Debug > Start Debug and select ios Emulator and wait for it to start. (This is for macOS. Windows can only select the Emulator on Android. All that is required now is that your Flutter environment is set up successfully.)

    After successful operation, the picture is as follows:

Links to official learning materials

Because Chinese website

Flutter of actual combat

Above, to the restless heart…