• Develop your first Application with Flutter
  • Originally written by Gahfy
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: mysterytony
  • Reviewer: Rockzhai, Zhaochuanxing

Develop your first application with Flutter

A week ago, Flutter launched its first public beta at THE MWC in Barcelona. The main purpose of this article is to show you how to develop the first fully functional application with Flutter.

This article will explain how the Flutter is installed and how it works, so it will be a little longer than usual.

We will develop an application that shows the user a list of posts retrieved from the JSONPlaceholder API.

What is Flutter?

Flutter is an SDK that allows you to develop native apps based on Android, iOS or Google’s next operating system, Fuschia. It uses Dart as its primary programming language.

Install required tools

Git, Android Studio and XCode

To acquire Flutter, you need to clone its official repository. If you want to develop Android apps, you’ll also need Android Studio. If you want to develop an iOS app, you also need XCode.

IntelliJ IDEA

You also need IntelliJ IDEA (this is not required, but can be useful). After installing IntelliJ IDEA, add Dart and Flutter plug-ins to IntelliJ IDEA.

To obtain the Flutter

All you have to do is clone the Flutter official repository:

git clone -b beta https://github.com/flutter/flutter.git
Copy the code

Then, you need to add the PATH to the bin folder to the PATH environment variable. Just like that, you can now start developing applications with Flutter.

Although this is more than enough, I’ve shortened the installation process to make this article less verbose. If you need a more complete guide, please go to the official documentation.

Develop the first project

Let’s now open IntelliJ IDEA and create our first project. In the left panel, select Flutter (if not, install the Flutter and Dart plug-ins into your IDE).

We name it as follows:

  • Project name: Feedme
  • Description: A sample JSON API project
  • Organization: net. Gahfy
  • Android language: Kotlin
  • IOS language: Swift

Run the first project and explore Flutter

IntelliJ’s editor opens a file called main.dart, which is the main file of the application. If you don’t already know Dart, don’t panic, the rest of this tutorial will be necessary at times.

Now, plug your Android or iOS phone into your computer, or run an emulator.

You can now run the app by clicking on the Run button (with the green triangle) in the upper right corner:

Click the float action button at the bottom to increase the number displayed. We will not delve into the code now, but we will find some interesting features with Flutter.

Thermal overload Flutter

As you can see, the main color of the app is blue. We could change it to red. In the main.dart file, find the following code:

return new MaterialApp(
  title: 'Flutter Demo',
  theme: new ThemeData(
    // This is the theme of your application.
    //
    // Try running your application with "flutter run". You'll see the
    // application has a blue toolbar. Then, without quitting the app, try
    // changing the primarySwatch below to Colors.green and then invoke
    // "hot reload" (press "r" in the console where you ran "flutter run",
    // or press Run > Flutter Hot Reload in IntelliJ). Notice that the
    // counter didn't reset back to zero; the application is not restarted.
    primarySwatch: Colors.blue,
  ),
  home: new MyHomePage(title: 'Flutter Demo Home Page'));Copy the code

In this section, use colors.red instead of colors.blue. Flutter allows you to hot load applications, meaning that the current state of the application will not be changed, but new code will be used.

In the application, click the floating + button at the bottom to add counter.

Then, in the upper right corner of the IntelliJ, click the Hot Reload button (with yellow lightning). You can drive until the main color turns red, but the counter keeps the same number.

Develop the final application

Wouldn’t it be a better way to learn if we now deleted everything in the main.dart file?

Smallest application

The first thing we need to do is develop the smallest application, the least amount of code that will run. Because we will be designing our application with Material Design, we will first import the package that contains the Material Design Widgets.

import 'package:flutter/material.dart';
Copy the code

Now let’s create a class that inherits the StatelessWidget to create an instance of our application (more on StatelessWidget later).

import 'package:flutter/material.dart';
 
class MyApp extends StatelessWidget {}Copy the code

IntelliJ IDEA is underlined in red under MyApp. In fact, the StatelessWidget is an abstract class that needs to implement the Build () method. To do this, move the cursor to MyApp and press Alt + Enter.

import 'package:flutter/material.dart';
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build}}Copy the code

Now that we implement the build() method, we can see that it must return an instance of the Widget. We will return a MaterialApp when we build the application here. To do this, add the following code to build() :

return new MaterialApp();
Copy the code

The MaterialApp documentation tells us to initialize at least home, Routes, onGenerateRoute or Builder. We’re only going to define the home property here. This will be the main interface of the application. Because we want our application to be based on Material Design, we set home to an empty Scaffold:

import 'package:flutter/material.dart';
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        home: newScaffold() ); }}Copy the code

Finally we need to set up the MyApp application that we want to run when main.dart is run. Therefore, we need to add the following line after the import statement:

void main() => runApp(new MyApp());
Copy the code

You are now ready to run your application. Right now it’s just a white screen with no content. So the first thing we need to do now is add some user interface.

Developing the user interface

A few words about status

There are probably two kinds of user interfaces to develop. One is a user interface that is independent of the current application state, and the other is a user interface that is relevant to the current state.

When we talk about states, we mean that the user interface may change when the event is triggered, and that’s exactly what we’re going to do:

  • Application startup event: –The loop progress bar is displayed
    • Run an operation to retrieve a post
  • End of API request:
    • If successful, the result of retrieving the post is displayed
    • If it fails, the Snackbar with failure information is displayed on the blank screen

Currently, we only use the StatelessWidget, which, as you might guess, doesn’t involve application state. So let’s first initialize a StatefulWidget.

Initialize StatefulWidget

Let’s add a class that inherits StatefulWidget to our application:

import 'package:flutter/material.dart';
 
void main() => runApp(new MyApp());
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        home: newPostPage() ); }}class PostPage extends StatefulWidget {
  PostPage({Key key}) : super(key: key);
 
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState}}Copy the code

As we can see, we need to implement the createState() method that returns a State object. So let’s create a class that inherits State:

class PostPage extends StatefulWidget {
  PostPage({Key key}) : super(key: key);
 
  @override
  _PostPageState createState() => new _PostPageState();
}
 
class _PostPageState extends State<PostPage>{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build}}Copy the code

As you can see, we need to implement the build() method to return a Widget. To do this, we create an empty part (Row) :

class _PostPageState extends State<PostPage>{
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text('FeedMe'),
        ),
        body: new Row()//TODO add the widget for current state); }}Copy the code

We actually return a Scaffold object because our application’s toolbar does not change and is not dependent on the current state. Except that his body will depend on the current state.

Let’s now create a method that returns the Widget to display the current status, and a method that returns a Widget containing a centered loop progress bar:

class _PostPageState extends State<PostPage>{
  Widget _getLoadingStateWidget(){
    return new Center(
      child: new CircularProgressIndicator(),
    );
  }
 
  Widget getCurrentStateWidget(){
    Widget currentStateWidget;
    currentStateWidget = _getLoadingStateWidget();
    return currentStateWidget;
  }
 
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text('FeedMe'), ), body: getCurrentStateWidget() ); }}Copy the code

If you run the application now, you will see a circular progress bar with a center.

Display a list of posts

Let’s define the Post object first, because it’s defined in the JSONPlaceholder API. To do this, create a post.dart file that contains the following:

class Post {
  final int userId;
 
  final int id;
 
  final String title;
 
  final String body;
 
  Post({
    this.userId,
    this.id,
    this.title,
    this.body
  });
}
Copy the code

Now we define a PostState class in the same file to design the current state of the application:

class PostState{
  List<Post> posts;
  bool loading;
  bool error;
 
  PostState({
    this.posts = const[].this.loading = true.this.error = false});void reset(){
    this.posts = [];
    this.loading = true;
    this.error = false; }}Copy the code

All you need to do now is define a method in the PostState class to get the list of posts from the API. We’ll see how to do this later, because for now we can only return a static LIST of POSTS asynchronously:

Future<void> getFromApi() async{
  this.posts = [
    new Post(userId: 1, id: 1, title: "Title 1", body: "Content 1"),
    new Post(userId: 1, id: 2, title: "Title 2", body: "Content 2"),
    new Post(userId: 2, id: 3, title: "Title 3", body: "Content 3")];this.loading = false;
  this.error = false;
}
Copy the code

Now that we’re done, let’s go back to the PostPageState class in the main.dart file to see how we can use the class we just defined. We initialize a postState property in the PostPageState class:

class _PostPageState extends State<PostPage>{
  final PostState postState = new PostState();
 
  // ...
}
Copy the code

If IntelliJ IDEA shows a red underline under PostState, it means that the PostState class is not defined in the current file. So you need to import it. Move the cursor to the red underlined section, then press Alt + Enter, and select Import.

Now, let’s define a method that returns a Widget when we successfully get the Post list:

Widget _getSuccessStateWidget(){
  return new Center(
    child: new Text(postState.posts.length.toString() + " posts retrieved")); }Copy the code

If we successfully get the list of POSTS, all we need to do now is edit the getCurrentStateWidget() method to display the Widget:

Widget getCurrentStateWidget(){
  Widget currentStateWidget;
  if(! postState.error && ! postState.loading) { currentStateWidget = _getSuccessStateWidget(); }else{
    currentStateWidget = _getLoadingStateWidget();
  }
  return currentStateWidget;
}
Copy the code

The last, and perhaps most important, thing to do is run the request to retrieve the list of posts. To do this, define a _getPosts() method and call it when the state is initialized:

@override
void initState() {
  super.initState();
  _getPosts();
}
 
_getPosts() async {
  if(! mounted)return;
 
  await postState.getFromApi();
  setState((){});
}
Copy the code

Knock knock, you can run the application to see the results. In fact, if the loop progress bar is shown at all, it’s almost impossible to see it. This is because retrieving the list of posts is so fast that it disappears almost immediately.

Retrieve the list of posts from the API

To ensure that the loop progress bar is actually displayed, let’s retrieve the post from the JSONPlaceholder API. If we look at the API’s POST service, we can see that it returns a JSON array of posts.

Therefore, we must first add a static method to the Post class to convert the JSON array of POSTS to a LIST of posts:

static List<Post> fromJsonArray(String jsonArrayString){
  List data = JSON.decode(jsonArrayString);
  List<Post> result = [];
  for(var i=0; i<data.length; i++){
    result.add(new Post(
        userId: data[i]["userId"],
        id: data[i]["id"],
        title: data[i]["title"],
        body: data[i]["body"])); }return result;
}
Copy the code

We now just need to edit the method that retrieves the Post list in the PostState class to actually retrieve the Post from the API:

Future<void> getFromApi() async{
  try {
    var httpClient = new HttpClient();
    var request = await httpClient.getUrl(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
    var response = await request.close();
    if (response.statusCode == HttpStatus.OK) {
      var json = await response.transform(UTF8.decoder).join();
      this.posts = Post.fromJsonArray(json);
      this.loading = false;
      this.error = false;
    }
    else{
      this.posts = [];
      this.loading = false;
      this.error = true; }}catch (exception) {
    this.posts = [];
    this.loading = false;
    this.error = true; }}Copy the code

You can now run the application and see the loop progress bar more or less depending on the network speed.

Display a list of posts

Currently, we only show the number of posts retrieved, but not the list of posts as we expected. To display it, let’s edit the _getSuccessStateWidget() method of the PostPageState class:

Widget _getSuccessStateWidget(){
  return new ListView.builder(
    itemCount: postState.posts.length,
    itemBuilder: (context, index) {
      return new Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          new Text(postState.posts[index].title,
            style: new TextStyle(fontWeight: FontWeight.bold)),
 
          new Text(postState.posts[index].body),
 
          newDivider() ] ); }); }Copy the code

If you run the application again, you’ll see a list of posts.

Handling errors

We have one last thing to do: deal with errors. You can try running the application in airplane mode and see an infinite loop of progress bars. So we return a blank error:

Widget _getErrorState(){
  return new Center(
    child: new Row(),
  );
}
 
Widget getCurrentStateWidget(){
  Widget currentStateWidget;
  if(! postState.error && ! postState.loading) { currentStateWidget = _getSuccessStateWidget(); }else if(! postState.error){ currentStateWidget = _getLoadingStateWidget(); }else{
    currentStateWidget = _getErrorState();
  }
  return currentStateWidget;
}
Copy the code

It now displays a blank screen when an error occurs. You can change the content to display the error screen. But as we said, we want to display a Snackbar so we can retry in the event of an error. To do this, let’s develop the showError() and Retry () methods in the PostPageState class:

class _PostPageState extends State<PostPage>{
  // ...
  BuildContext context;
 
  // ...
  _retry(){
    Scaffold.of(context).removeCurrentSnackBar();
    postState.reset()
    setState((){});
    _getPosts();
  }
 
  void _showError(){
    Scaffold.of(context).showSnackBar(new SnackBar(
      content: new Text("An unknown error occurred"),
      duration: new Duration(days: 1), // Make it permanent
      action: new SnackBarAction(
        label : "RETRY", onPressed : (){_retry(); }))); }/ /...
}
Copy the code

As we’ve seen, we need a BuildContext to get ScaffoldState, which makes SnackBars come and go. However, we must use the Scaffold object BuildContext to obtain ScaffoldState. To do this, we need to edit the build() method of the PostPageState class:

Widget currentWidget = getCurrentStateWidget();
return new Scaffold(
    appBar: new AppBar(
      title: new Text('FeedMe'),
    ),
    body: new Builder(builder: (BuildContext context) {
      this.context = context;
      returncurrentWidget; }));Copy the code

Now run your application in airplane mode and it will now display the Snackbar. If you leave airplane mode and click Retry, you can see the post.

conclusion

We learned that it is not difficult to develop a fully functional application with Flutter. All Material Design elements are provided, and just now you developed an application using them on Android and iOS.

All source code for the project is available at the Feed-Me Flutter Project on GitHub.


If you liked this article, you can follow me on Twitter to get the next one.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.