Flutter入门并开发天气预报APP(2)——Flutter计数器Demo

本文样例都是使用的AndroidStudio,如果你使用Xcode或者vscode,代码都是一样的,只是IDE创建项目的方式以及运行按键位置不同而已。

创建Flutter项目

AndroidStudio中选择File->New->New Flutter Project->Flutter Application->NEXT。接着填写你的项目名称等一系列信息后就创建成功了,我们就会看到AndroidStudio已经为我们创建好了一个Demo,接着连上你的手机或者打开你的虚拟机,按下AndroidStudio的运行键,不一会儿,你就会惊喜的发现你的手机上就有一个Flutter计数器Demo了。 计数器运行Demo

介绍下AndroidStudio界面

AndroidStudio界面 这是我的AndroidStudio界面,接下来我分三个部分介绍下AndroidStudio的大致界面。

控制区

控制区

一次介绍每一个按钮的功能。

  1. 第一个是用来选择运行设备的,比方说我现在就选的是我的虚拟机,然后我按下运行后就可以将项目运行到我的虚拟机上而不会运行到连着我的电脑的手机上去;
  2. 第二个是选择运行哪个Flutter module,如果你同一个项目里面有多个module,你就可以在此选择你想要运行的module;
  3. 第三个灰色的选项框我也没弄懂她是干嘛的,我至今都没能选择过,一直都是灰的;
  4. 绿色的三角形,大家熟知,运行键;
  5. 红色的瓢虫,大家熟知,debug键;
  6. 不知道干嘛的;
  7. FlutterAPP的性能监视器;
  8. 闪电,这个是重点,也是Flutter的一大特性,热重载键,他能做到不需要重新编译代码就能将你修改的代码的效果在你的APP上显示出来;
  9. 不知道干嘛的;
  10. 红色的正方形,停止运行按钮。

Run运行区

Run运行区 就是Flutter的信息显示区,类似于AndroidStudio的Logcat,能显示FlutterAPP运行中的一些信息。

Flutter Outline/Flutter Inspector区

Flutter Outline Flutter Inspecto 用于显示当前界面的一些构建信息。

Flutter项目结构

Flutter项目结构

我们挑一些重要的说一下:

  1. .idea文件夹,AndroidStudio识别项目的必备文件夹,不用管;
  2. Android文件夹,存放Flutter构建的Android项目的文件夹;
  3. ios文件夹,存放Flutter构建的iOS项目的文件夹;
  4. lib文件夹,存放Flutter项目和资源的文件夹;
  5. pubspec.yaml文件,存放Flutter项目相关信息,如项目名称、版本、依赖等等,类似于Android项目的build.gradle文件。

main.dart文件

我们通过讲这个文件来给大家大致介绍下Flutter项目代码结构。

全部源代码在此

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

import

首先我们看最开始的import区。

这块相当于C/C++的include,反而不像java的import。我为什么这么说呢,你想一下,你写java程序的时候,你是需要哪个类就直接在代码中输入类名,然后IDE会自动给你显示有这个类的所有的包,然后让你选包去导入;而写dart是需要你先导入包,才能在代码中写这个包中的类,如果你没有先导入包而直接到下面写的话,IDE是不会提示你这个类可能会在哪个包中,而是直接报错,这点和C/C++比较类似。

那我们为什么导入的是flutter/material.dart这个包呢?我在第一节里面说过,Flutter他内置了Android的Material Design风格和iOS的Cupertino风格的UI;所以这块就是导入Material Design风格UI文件,而如果你要使用Cupertino风格UI文件的话,把它改成Cupertino就行了。

main()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ), 
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
  • 第一行,指明项目的入口就是MyApp()。固定格式,可以不用管。

  • 第二行就定义了一个类MyApp继承自StatelessWidget,并重写了父类的build()方法。Widget可以看成Flutter中的页面,只要是Flutter中的页面,都必须继承自Widget。他有点类似于Android中的View,但是不同于View的是,Flutter中的paddingalignlayout等居然也是Widget

  • 如果是StatelessWidget,则代表她是一个“状态少”的Widget,我的理解就是状态相当于Widget的一些数值,比方说页面要显示计数器的计数count,那么这个count就是一个状态,只要有状态就使用StatefulWidget。像MyApp它没有状态,于是就用StatelessWidget

  • 继承自StatelessWidget的类必须实现build()方法,相当于这个类是一个死页面,只需要展示固定的内容,也就是不需要状态,所以直接通过build()直接写入界面就行。

  • build()方法必须同样返回一个Widget,所以我们就使用MaterialApp,其中我们给他的titlethemehome进行了赋值,同学们应该都能大致读懂啥意思,首页home则给了MyHomePage.

MyHomePage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

MyHomePage

我们在前面说过,如果一个页面有数据也就是“状态”的话,那么他就得继承自StatefulWidget

如果一个类继承自StatefulWidget,那这个类会很简单,只需要创建一个构造方法,构造方法中写入key以及该Widget需要的数据。然后重写createState()方法。

MyHomePageState

如果一个类继承自StatefulWIdget,那么一定会有一个他的State类并继承自State<该类>,就像class _MyHomePageState extends State<MyHomePage>一样。

那我们来看看State类里面有些啥东西:

首先先定义“状态”:由于我们这个APP主要的功能是用来计数,所以必须定义一个int型变量来计数:

1
int _counter = 0;

接着如果我们这个_counter变量变了,那就得通知flutter说状态变了,需要更新UI:

1
2
3
4
5
void _incrementCounter() {
  setState(() {
     _counter++;
  });
}

其中,setState()方法就是用来通知Flutter这个状态改变了,要去更新UI的。

最后重写build()方法来写入界面:

  • Scaffold是一个组件,它提供了默认的导航栏、标题和包含主屏幕widget树(后同“组件树”或“部件树”)的body属性。
  • body后面是一个Center组件,它的作用是将它的子Widget定位到父Widget的中间。
  • Center后面是一个Column组件,这个组件类似于Android中的LinearLayoutorientation: Vertical,也就是垂直向的线性布局。同时Flutter也提供了Row这个水平向的线性布局。他们的子Widgetchildren属性,也就是可以写多个Widget
  • Column里面是两个Text,这个没啥讲的
  • 接下来是一个FloatingActionButton,这就是一个浮动按钮,Android里面有这个控件,核心是他的onPressed属性,这个就相当于onClick(),用于处理点击事件。

体验热重启

首先先让项目在虚拟机或者手机上运行着,然后我们把页面Text中的"You have pushed the button this many times:“改为"You have clicked the button this many times:",然后按热重启键(macOS版AndroidStudio默认是commond+s键),接着你就能里面在虚拟机或者手机上看到修改之后的结果。 flutter热重启