内容简介:用Android Studio和VS Code创建的Flutter应用,源码模板是一个计数器示例,通过讲解分析计数器示例的源码,可以让读者对Flutter应用程序结构有个最基本的了解,在后续的文章中,笔者将会基于此示例,一步一步添加新的功能来介绍Flutter应用的其他概念与技术。(1)、Stateful widget可以拥有状态,这些状态在Widget生命周期中是可以变的,而Stateless widget是不可变的。(2)、Stateful widget至少由两个类组成:
用Android Studio和VS Code创建的Flutter应用,源码模板是一个计数器示例,通过讲解分析计数器示例的源码,可以让读者对Flutter应用程序结构有个最基本的了解,在后续的文章中,笔者将会基于此示例,一步一步添加新的功能来介绍Flutter应用的其他概念与技术。
示例源码
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter Demo Home Page'), ); } } 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> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override 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), ), ); } } 复制代码
运行效果
源码分析
1、导包
import 'package:flutter/material.dart'; 复制代码
- 此行代码作用是导入了Material UI组件库。Material是一种标准的移动端和web端的视觉设计语言,Flutter默认提供了一套丰富的Material风格的UI组件。
2、主函数(应用入口)
void main() => runApp(MyApp()); 复制代码
- 与C/C++、 Java 类似,Flutter应用中
main
函数为应用程序的入口,main
函数中调用了runApp
方法,它的功能是启动Flutter应用,它接受一个Widget
参数,在本示例中它是MyApp
类的一个实例,该参数代表Flutter应用。 -
main
函数使用了(=>
)符号,这是Dart中单行函数或方法的简写。
3、应用结构
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( //应用名称 title: 'Flutter Demo', //应用主题 theme: new ThemeData( //蓝色主题 primarySwatch: Colors.blue, ), //应用首页路由 home: new MyHomePage(title: 'Flutter Demo Home Page'), ); } } 复制代码
-
MyApp
类代表Flutter应用,它继承了StatelessWidget
类,这意味着应用本身也是一个Widget。在Flutter中,一切皆为组件(Widget),包括对齐(alignment)、填充(padding)和布局(layout)。 - Flutter在构建页面时,会调用组件的
build
方法,Widget的主要工作是提供一个build
方法来描述如何构建UI界面(通常都是通过组合、拼装其他基础Widget)。 -
MaterialApp
是Material库中提供的Flutter APP框架,通过它可以设置应用的名称、主题、语言和首页及路由列表等。MaterialApp
也是一个Widget。 -
home
为Flutter应用的首页,它也是一个widget。
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 widget),那么Stateful widget和Stateless widget有什么区别呢?
(1)、Stateful widget可以拥有状态,这些状态在Widget生命周期中是可以变的,而Stateless widget是不可变的。
(2)、Stateful widget至少由两个类组成:
①、一个 StatefulWidget
类;
②、一个 State
类, StatefulWidget
类本身是不变的,但是 State
类中持有的状态在Widget生命周期中可能会发生变化。
-
_MyHomePageState
类是MyHomePage
类对应的状态类。看到这里,细心的读者可能已经发现,和MyApp
类不同的是,MyHomePage
类中并没有build
方法,取而代之的是,build
方法被挪到了_MyHomePageState
方法中,至于为什么这么做,先留个疑问,在分析完完整代码后再来解答,接下来,我们看看_MyHomePageState中都包含哪些东西:-
状态
int _counter = 0; 复制代码
_count
为保存屏幕右下角带“+”号按钮点击次数的状态。 -
设置状态的自增函数
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,它提供了默认的导航栏、标题和包含主屏幕widget树的body属性。路由默认都是通过Scaffold
创建。 -
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
作为其处理函数。
-
5、总结
现在,我们将整个流程串起来:当右下角的floatingActionButton按钮被点击之后,会调用 _incrementCounter
函数,在 _incrementCounter
函数中,首先会自增 _counter
状态(计数器),然后 setState
会通知Flutter框架状态发生变化,接着,Flutter会调用 build
方法以新的状态重新构建UI,最终显示在设备屏幕上。
疑问解答
为什么要将build方法放在State中,而不是放在StatefulWidget中?
现在,我们回答之前提出的问题,为什么 build
方法在State中,而不是StatefulWidget中?这主要是为了开发的灵活性。如果将 build
方法在StatefulWidget中则会有两个问题:
-
状态访问不便
试想一下,如果我们的Stateful widget有很多状态,而每次状态改变都要调用
build
方法。由于状态是保存在State中的,如果将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
中,可以给开发带来很大的灵活性。 -
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Sovereign Individual
James Dale Davidson、William Rees-Mogg / Free Press / 1999-08-26 / USD 16.00
Two renowned investment advisors and authors of the bestseller The Great Reckoning bring to light both currents of disaster and the potential for prosperity and renewal in the face of radical changes ......一起来看看 《The Sovereign Individual》 这本书的介绍吧!