第一个Flutter程序-计数器及项目结构分析
文章目录一、目标二、新建计数器项目2.1 创建项目,设置项目名及配置SDK2.2 设置包名2.3 运行项目2.4 计数器三、项目结构分析3.1 导包3.2 应用入口3.3 应用结构3.4 首页3.5 _MyHomePageState详解3.6 流程小结四、解惑build为何放在State中一、目标通过一个计数器示例,对Flutter应用程序结构有个基本了解,对其概念和技术有一定的认识,也方便后..
文章目录
一、目标
通过一个计数器示例,对Flutter应用程序结构有个基本了解,对其概念和技术有一定的认识,也方便后续对Flutter其他概念和技术的学习。
二、新建计数器项目
此过程比较简单,通过Android Studio创建新的Flutter工程即可。
2.1 创建项目,设置项目名及配置SDK

2.2 设置包名
在此,仅勾选了Kotlin support,如果在iOS上需要使用Swift的话,在此勾选即可。
2.3 运行项目
至此,工程创建完毕。在Terminal窗口,运行flutter run即可将应用安装到当前连接的手机上。
2.4 计数器
机智的你,也许发现,此时点击程序右下角的"➕",页面中的数字会一直增加,简单吧,不需要手写一行代码,一个计数器应用就完成了。
三、项目结构分析
Flutter是采用Dart语言来编写的,该示例项目中,主要Dart代码在lib/main.dart中,接下来就对该文件中的代码进行分析。
3.1 导包
import 'package:flutter/material.dart';
该行代码主要导入Material UI组件库。Material是一种标准的移动端和web端的视觉设计语言,Flutter默认提供了一套丰富的Material风格的UI组件。
3.2 应用入口
void main() => runApp(MyApp());
Flutter应用中,main函数是整个应用程序的入口。main函数中调用了runApp方法,其功能是启动Flutter应用,接收一个widget参数,在该示例中,它是MyApp类的一个实例,该参数代表Flutter应用。
main函数使用了=>符号,这是Dart中单行函数或方法的简写。
3.3 应用结构
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类代表Flutter应用,它继承了StatelessWidget类,也意味着应用本身也是一个widget- 在
Flutter中,大多数东西都是widget,包括对其(alignment)、填充(padding)和布局(layout) Flutter在构建页面时,会调用组建的build方法,widget的主要工作是提供一个build()方法来描述如何构建UI界面,通常是通过组合、拼装其他基础widgetMaterialApp是Material库提供的Flutter App框架,通过它可以设置应用的名称、主题、首页以及路由列表等,MaterialApp也是一个widgethome为Flutter应用的首页,它也是一个widget
3.4 首页
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
...
}
MyHomePage是应用的首页,它继承自StatefulWidget类,表示他是一个有状态的widget(Stateful widght),目前,可以简单认为Stateful Widget和Stateless Widget有两点不同:
a. Stateful widget可以拥有状态,这些状态在widget生命周期中时可以变的,而Stateless widget是不可变的
b. Stateful widget至少由两个类组成:
- 一个
StatefulWidget类 - 一个
State类,StatefulWidget类本身是不可变的,但State类中持有的状态在widget生命周期中可能会发生变化
_MyHomePageState类时MyHomePage类对应的状态类。看到这里,也许机智的你已经发现,和MyApp类不同,MyHomePage类中没有build方法,取而代之的是,build方法被挪到了_MyHomePageState方法中,至于为什么这么做,先留个疑问,在分析完完整代码后再来解答。
3.5 _MyHomePageState详解
接下来,一起看看_MyHomePageState中都包含哪些东西。
- 状态
int _counter = 0;_counter为保存屏幕右下角"➕"号按钮点击次数的状态。 - 设置状态的自增函数
当按钮点击时,会调用此函数,该函数的作用是先自增void _incrementCounter() { setState(() { _counter++; }); }_counter,然后调用setState方法。setState方法的作用是通知Flutter框架,有状态发生了变化,Flutter框架收到通知后,会执行build方法来根据新的状态重新构建界面,Flutter对此方法做了优化,使重新执行变得很快,所以你可以重新构建任何需要更新的东西,而无须分别去修改各个widget。 - 构建
UI界面
构建UI界面的逻辑在build方法中,当MyHomePage第一次创建时,_MyHomePageState类会被创建,当初始化完成后,Flutter框架会调用Widget的build方法来构建widget树,最终将widget树渲染到设备屏幕上,所以,来看一下_MyhomePageState的build方法都干了什么事。
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: new Icon(Icons.add),
),
);
}
Scaffold是Material库中提供的页面脚手架,提供了默认的导航栏、标题和包含主屏幕widget树的body属性,widget树可以很复杂body的widget树中包含了一个Center widget,Center可以将其子widget树对齐到屏幕中心,Center子widget是一个Column widget,Column的作用是将其所有字widget沿屏幕垂直方向依次排列,此例中Column包含两个Text子widget,第一个Text widget显示固定为文本You have pushed the button this many times:,第二个Text widget显示_counter状态的数值
-floatingActionButton是页面右下角的带"➕"的炫富按钮,它的onPressed属性接受一个毁掉参数,代表它被点击后的处理事件,本例中将_incrementCounter作为其处理函数
3.6 流程小结
现在,我们将整个流程串起来:当右下角的floatingActionButton按钮被点击后,会调用_incrementCounter,在_incrementCounter中,首先会自增_counter计数器(状态),然后setState会通知Flutter框架状态发生变化, 接着,Flutter会调用build方法以新的状态重新构建UI,最终显示在设备屏幕上。
四、解惑build为何放在State中
现在,我们可以回答之前的问题,为什么build方法在State,而不是StatefulWidget中?
这个主要是为了灵活性,如果将build方法放在StatefulWidget中则会有两个问题:
- 状态访问不便
试想一下,如果我们的Stateful widget有很多状态,而每次状态改变都要调用build方法,由于状态是保存在State中的,如果将build方法放在StatefulWidget中,那么构建时读取状态会很不方便。如果真的将build方法放在StatefulWidget中的话,由于构建用户界面过程需要依赖State,所以build方法必须加一个State参数,大概是下面这样:
Widget build(BuildContext context, State state){
//state.counter
...
}
这样的话,就只能将State的所有状态声明为公开的状态,这样才能在State类外部访问状态,但将状态设置为公开后,状态将不再具有私密性,这样依赖,对状态的修改将会变得不可控。但将build方法放入State中的话,构建过程可以直接访问状态,会很方便。
- 继承
StatefulWidget不便
例如,Flutter中有一个动画widget的基类AnimatedWidget,它继承自StatefulWidget。AnimatedWidget中引入了一个抽象方法build(BuildContext context),继承自AnimatedWidget的动画widget都要实现这个build方法。 现在试想一下,如果StatefulWidget类中已经有了一个build方法,正如上面所述,此时build方法需要接受一个state对象,这意味着AnimatedWidget必须先将自己的State对象(记为_animatedWidgetState)提供给其自雷,因为子类需要在其build方法中调用父类的build`方法,代码可能如下:
class MyAnimationWidget extends AnimatedWidget{
@override
Widget build(BuildContext context, State state){
//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState,
//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState
//暴露给其子类
super.build(context, _animatedWidgetState)
}
}
这样显然是不合理的,因为:
AnimatedWidget的状态对象是AnimatedWidget内部实现细节,不应该暴露给外部- 如果要将父类状态暴露给子类,那么必须有一种传递机制,二做这一套传递机制是毫无意义的,因为父子类之间状态的传递和子类本身逻辑是无关的。
综上所述,对于StatefulWidget,将build方法放在State中,可以给开发带来很大的灵活性,更加规范。
更多推荐
所有评论(0)